# How OmniRoute Implements PII Masking, Prompt Injection Detection, and Vision Bridge Guardrails

> Discover how OmniRoute implements PII masking prompt injection detection and vision bridge guardrails using a unified framework. Learn about the BaseGuardrail abstract class and secure AI routing.

- Repository: [Diego Rodrigues de Sa e Souza/OmniRoute](https://github.com/diegosouzapw/OmniRoute)
- Tags: architecture
- Published: 2026-07-04

---

**OmniRoute implements PII masking, prompt injection detection, and vision bridge capabilities through a unified guardrail framework that intercepts requests before and after routing via the `BaseGuardrail` abstract class.**

The open-source AI routing layer **OmniRoute** (`diegosouzapw/OmniRoute`) provides a pluggable **guardrail framework** to secure traffic against data leakage, adversarial inputs, and incompatible model capabilities. Every request passes through a central registry of validators that can block, modify, or annotate payloads based on configurable pre-call and post-call policies.

## Guardrail Framework Architecture

All guardrails extend the abstract class `BaseGuardrail` defined in [`src/lib/guardrails/base.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/lib/guardrails/base.ts). Each implementation provides `preCall` and/or `postCall` methods that return a `GuardrailResult` specifying whether to block the request, modify the payload, or attach metadata for observability.

The **guardrail registry** ([`src/lib/guardrails/registry.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/lib/guardrails/registry.ts)) maintains a singleton list of active guards. During server initialization ([`src/server-init.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/server-init.ts)), `registerDefaultGuardrails()` loads the three built-in guards into the registry. The chat handler ([`src/sse/handlers/chat.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/sse/handlers/chat.ts)) then iterates through this registry before dispatching requests to upstream providers and again after receiving responses.

## PII Masker Guardrail

The **PII Masker** guardrail ([`src/lib/guardrails/piiMasker.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/lib/guardrails/piiMasker.ts)) redacts personally identifiable information from both requests and responses. It inherits from `BaseGuardrail` and runs with **priority 10** (enabled by default).

**Request-side processing** uses `cloneAndMaskRequestPayload()` to recursively traverse the JSON payload, invoking `sanitizePII()` from [`src/lib/piiSanitizer.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/lib/piiSanitizer.ts) on every string value. If redaction occurs, the method returns a modified copy of the payload.

**Response-side processing** applies `maskResponsesOutput()` to iterate over `output_text` and `output` fields, calling `sanitizePII()` and `sanitizePIIResponse()` to ensure sensitive data never leaves the OmniRoute boundary.

```typescript
// src/lib/guardrails/piiMasker.ts
export class PIIMaskerGuardrail extends BaseGuardrail {
  async preCall(payload, _context) {
    const result = cloneAndMaskRequestPayload(payload);
    return { block: false, modifiedPayload: result.payload };
  }

  async postCall(response, _context) {
    const sanitized = maskResponsesOutput(response);
    return { block: false, modifiedResponse: sanitized };
  }
}

```

Enable this guardrail by setting the environment variable `PII_REDACTION_ENABLED=true` and configuring `INPUT_SANITIZER_MODE=redact`.

## Prompt Injection Guardrail

The **Prompt Injection** guardrail ([`src/lib/guardrails/promptInjection.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/lib/guardrails/promptInjection.ts)) detects malicious system-override directives and adversarial patterns. It operates in three modes—`block`, `warn`, or `log`—controlled by the `INJECTION_GUARD_MODE` environment variable or database settings.

The core detection logic resides in `evaluatePromptInjection()`:

1. Sanitizes the payload using `sanitizeRequest()` from [`src/shared/utils/inputSanitizer.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/shared/utils/inputSanitizer.ts).
2. Extracts raw message text via `extractMessageContents()`.
3. Scans the first **16 KB** (`MAX_INJECTION_SCAN_BYTES`) against `DEFAULT_GUARD_PATTERNS` or user-supplied `customPatterns`.
4. Aggregates severity scores and compares them against the `blockThreshold` (default `"high"`).

The `PromptInjectionGuardrail` class wraps this evaluation:

```typescript
// src/lib/guardrails/promptInjection.ts
export class PromptInjectionGuardrail extends BaseGuardrail {
  async preCall(payload, context) {
    const decision = evaluatePromptInjection(payload, this.options, context);
    if (decision.blocked) {
      return { block: true, message: "Request rejected: suspicious content detected" };
    }
    return { block: false, meta: decision.result.flagged ? { flagged: true } : null };
  }
}

```

Disable this guardrail for a specific request by including the header `x-omniroute-disabled-guardrails: prompt-injection`.

## Vision Bridge Guardrail

When a request contains images but the target model lacks vision support, the **Vision Bridge** guardrail ([`src/lib/guardrails/visionBridge.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/lib/guardrails/visionBridge.ts)) intercepts the payload, generates textual descriptions via a configured vision model, and replaces the image blocks with those descriptions. It runs at **priority 5** and is enabled by default.

The decision logic `getComboVisionBridgeDecision` analyzes combo routing tables to determine if any target model requires image translation. If all targets are vision-capable, the guard skips processing.

In `preCall()`, the guardrail executes the following pipeline:

1. Validates enable flags and the per-request disabling header.
2. Extracts image parts using `extractImageParts()` from [`src/lib/guardrails/visionBridgeHelpers.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/lib/guardrails/visionBridgeHelpers.ts).
3. Limits processing to the configured `maxImages` to prevent throughput bottlenecks.
4. Invokes `callVision()` for each image in parallel via `Promise.allSettled`.
5. Replaces image blocks with descriptions using `replaceImageParts()`, or retains original images on failure.

```typescript
// src/lib/guardrails/visionBridge.ts
if (imageParts.length === 0) return { block: false };
const results = await Promise.allSettled(
  limitedParts.map(async (p, i) => {
    const desc = await callVision(p.imageUrl, config);
    return `[Image ${i + 1}]: ${desc}`;
  })
);
const modifiedBody = replaceImageParts(body, descriptions);
return { block: false, modifiedPayload: modifiedBody, meta: { transformed: true } };

```

Configuration sources include the database settings table ([`src/lib/db/settings.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/lib/db/settings.ts)) and defaults defined in [`src/shared/constants/visionBridgeDefaults.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/shared/constants/visionBridgeDefaults.ts).

## Wiring Guardrails into the Request Flow

The request lifecycle follows a strict pipeline:

1. **Registration**: [`src/server-init.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/server-init.ts) invokes `registerDefaultGuardrails()` to populate the singleton registry.
2. **Pre-call validation**: In [`src/sse/handlers/chat.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/sse/handlers/chat.ts), the handler iterates through `guardrailRegistry.list()`, executing `preCall()` for each guard. If any guard returns `block: true`, OmniRoute returns an early error response. Modified payloads are forwarded to subsequent guards and ultimately to the upstream provider.
3. **Post-call sanitization**: After receiving the upstream response, the handler invokes `postCall()` on each guardrail (e.g., for PII redaction on the response body) before streaming data to the client.

Feature flags such as `INJECTION_GUARD_MODE` and database-backed vision-bridge settings allow runtime toggling without redeployment.

## Configuration and Usage Examples

**Disabling guardrails per request:**

```typescript
// Disable prompt-injection detection for a single API call
const resp = await fetch("/api/chat/completions", {
  method: "POST",
  headers: { 
    "x-omniroute-disabled-guardrails": "prompt-injection" 
  },
  body: JSON.stringify({ 
    model: "gpt-4", 
    messages: [{ role: "user", content: "..." }] 
  })
});

```

**Triggering the vision bridge automatically:**

```typescript
// Send an image to a non-vision model (e.g., GPT-3.5)
await fetch("/api/chat/completions", {
  method: "POST",
  body: JSON.stringify({
    model: "gpt-3.5-turbo",
    messages: [{ 
      role: "user", 
      content: [
        { type: "text", text: "Describe this:" },
        { type: "image_url", image_url: "data:image/png;base64,..." }
      ] 
    }]
  })
});

```

OmniRoute automatically invokes the vision model, replaces the image block with the generated description, and forwards the text-only payload to the upstream provider.

**Inspecting guardrail metadata:**

```typescript
const result = await fetch("/api/chat/completions", { ... });
const data = await result.json();
console.log(data.guardrails); 
// Output: [{ name: "pii-masker", detections: 2, redacted: true }]

```

## Summary

- **Architecture**: OmniRoute uses a registry-based pattern where guards extending `BaseGuardrail` in [`src/lib/guardrails/base.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/lib/guardrails/base.ts) process requests via `preCall()` and responses via `postCall()`.
- **PII Masking**: Implemented in [`src/lib/guardrails/piiMasker.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/lib/guardrails/piiMasker.ts), sanitizing both requests (`cloneAndMaskRequestPayload`) and responses (`maskResponsesOutput`) using the shared engine in [`src/lib/piiSanitizer.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/lib/piiSanitizer.ts).
- **Prompt Injection**: Detected in [`src/lib/guardrails/promptInjection.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/lib/guardrails/promptInjection.ts) via `evaluatePromptInjection()`, scanning 16 KB of content against configurable patterns with modes: `block`, `warn`, or `log`.
- **Vision Bridge**: Located in [`src/lib/guardrails/visionBridge.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/lib/guardrails/visionBridge.ts), translates images to text for non-vision models using `extractImageParts()`, `callVision()`, and `replaceImageParts()`.
- **Integration**: The pipeline in [`src/sse/handlers/chat.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/sse/handlers/chat.ts) executes guards sequentially, respecting priority levels and the `x-omniroute-disabled-guardrails` header for selective bypass.

## Frequently Asked Questions

### How do I disable a specific guardrail for a single request?

Include the `x-omniroute-disabled-guardrails` header with a comma-separated list of guardrail names (e.g., `"prompt-injection,vision-bridge"`). The registry in [`src/lib/guardrails/registry.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/lib/guardrails/registry.ts) checks this header before executing any guard's `preCall()` method.

### What happens when the prompt injection guardrail detects suspicious content?

Behavior depends on the configured `mode`. In `"block"` mode (set via `INJECTION_GUARD_MODE` env var), the guard returns `block: true` and OmniRoute rejects the request with a 400-level error. In `"warn"` or `"log"` modes, the request proceeds but metadata about the detection is attached to the response for audit trails.

### How does the vision bridge handle failures when describing images?

The vision bridge uses `Promise.allSettled()` to process images in parallel. If an individual image fails to generate a description (network error or model failure), the guardrail retains the original image block in the payload rather than blocking the entire request, ensuring graceful degradation.

### Can I add custom detection patterns for prompt injection?

Yes. Pass an array of regex patterns via the `customPatterns` option when configuring the `PromptInjectionGuardrail`. These patterns are merged with `DEFAULT_GUARD_PATTERNS` and evaluated alongside the built-in sanitization logic in `evaluatePromptInjection()`.