How OmniRoute Implements PII Masking, Prompt Injection Detection, and Vision Bridge Guardrails
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. 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) maintains a singleton list of active guards. During server initialization (src/server-init.ts), registerDefaultGuardrails() loads the three built-in guards into the registry. The chat handler (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) 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 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.
// 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) 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():
- Sanitizes the payload using
sanitizeRequest()fromsrc/shared/utils/inputSanitizer.ts. - Extracts raw message text via
extractMessageContents(). - Scans the first 16 KB (
MAX_INJECTION_SCAN_BYTES) againstDEFAULT_GUARD_PATTERNSor user-suppliedcustomPatterns. - Aggregates severity scores and compares them against the
blockThreshold(default"high").
The PromptInjectionGuardrail class wraps this evaluation:
// 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) 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:
- Validates enable flags and the per-request disabling header.
- Extracts image parts using
extractImageParts()fromsrc/lib/guardrails/visionBridgeHelpers.ts. - Limits processing to the configured
maxImagesto prevent throughput bottlenecks. - Invokes
callVision()for each image in parallel viaPromise.allSettled. - Replaces image blocks with descriptions using
replaceImageParts(), or retains original images on failure.
// 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) and defaults defined in src/shared/constants/visionBridgeDefaults.ts.
Wiring Guardrails into the Request Flow
The request lifecycle follows a strict pipeline:
- Registration:
src/server-init.tsinvokesregisterDefaultGuardrails()to populate the singleton registry. - Pre-call validation: In
src/sse/handlers/chat.ts, the handler iterates throughguardrailRegistry.list(), executingpreCall()for each guard. If any guard returnsblock: true, OmniRoute returns an early error response. Modified payloads are forwarded to subsequent guards and ultimately to the upstream provider. - 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:
// 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:
// 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:
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
BaseGuardrailinsrc/lib/guardrails/base.tsprocess requests viapreCall()and responses viapostCall(). - PII Masking: Implemented in
src/lib/guardrails/piiMasker.ts, sanitizing both requests (cloneAndMaskRequestPayload) and responses (maskResponsesOutput) using the shared engine insrc/lib/piiSanitizer.ts. - Prompt Injection: Detected in
src/lib/guardrails/promptInjection.tsviaevaluatePromptInjection(), scanning 16 KB of content against configurable patterns with modes:block,warn, orlog. - Vision Bridge: Located in
src/lib/guardrails/visionBridge.ts, translates images to text for non-vision models usingextractImageParts(),callVision(), andreplaceImageParts(). - Integration: The pipeline in
src/sse/handlers/chat.tsexecutes guards sequentially, respecting priority levels and thex-omniroute-disabled-guardrailsheader 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 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().
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →