How the promptInjectionGuard in OmniRoute Protects Chat Completions from Injection Attacks
The promptInjectionGuard in OmniRoute intercepts malicious payloads before they reach LLM providers by evaluating request content against configurable regex patterns, aggregating severity scores, and either blocking the request with a 400 error or logging it based on the active guard mode.
OmniRoute is an open-source AI gateway that secures chat completion endpoints through a multi-layer defense system. The promptInjectionGuard serves as the primary barrier against adversarial prompt manipulation, processing every request through a sanitization pipeline before forwarding to upstream providers. This guardrail ensures that system-override directives and malformed injection patterns never compromise model behavior.
Middleware Entry Point and Request Wrapping
Every chat completion request first passes through the withInjectionGuard middleware defined in src/middleware/promptInjectionGuard.ts. This wrapper intercepts POST, PUT, and PATCH requests before they reach route handlers.
The middleware clones the incoming Request object, parses the JSON payload, and passes the parsed body to the guardrail evaluation function:
// src/middleware/promptInjectionGuard.ts
export const withInjectionGuard = (
handler: RequestHandler,
options?: GuardOptions
): RequestHandler => {
return async (req, res) => {
const body = await req.clone().json();
const result = await guard(body, options);
if (result.blocked) {
return res.status(400).json({
error: "Request blocked: potential prompt injection detected"
});
}
return handler(req, res);
};
};
When the guard returns { blocked: true }, the middleware immediately terminates the request with a 400 status code, preventing any downstream provider contact.
The Core Detection Pipeline
The actual detection logic resides in src/lib/guardrails/promptInjection.ts. The evaluatePromptInjection function orchestrates a multi-stage analysis pipeline:
-
Initial Sanitization: The
sanitizeRequestutility fromsrc/shared/utils/inputSanitizer.tsperforms first-pass regex checks against the payload, producing an initial set of detections. -
Pattern Scanning: The system normalizes default patterns (
DEFAULT_GUARD_PATTERNS) and any user-suppliedcustomPatternsusingnormalizePatternEntry, then scans the first 16 KB of joined message contents (bounded byMAX_INJECTION_SCAN_BYTES). -
Severity Aggregation: Detections are merged and flagged, with the overall severity compared against the configured block threshold via
resolveBlockThreshold.
High-severity matches—such as system: override directives or malformed system blocks ( ```system)—trigger immediate escalation.
Configuration Modes and Feature Flags
The guard supports three operational modes determined by a hierarchical resolution chain:
- Database feature flag:
INJECTION_GUARD_MODE(defined insrc/shared/constants/featureFlagDefinitions.ts) - Environment variable:
INJECTION_GUARD_MODEorINPUT_SANITIZER_MODE - Default:
"warn"mode
The resolution logic in src/lib/guardrails/promptInjection.ts reads:
const mode = (await getFeatureFlag('INJECTION_GUARD_MODE'))
?? process.env.INJECTION_GUARD_MODE
?? process.env.INPUT_SANITIZER_MODE
?? 'warn';
The guard is enabled by default via the INPUT_SANITIZER_ENABLED environment variable (defaulting to true if absent). The three modes determine the enforcement level:
block: Rejects requests exceeding the severity thresholdwarn: Allows the request but logs warningslog: Allows the request with informational logging only
Blocking Behavior and Response Handling
When operating in block mode with a severity level above the configured threshold, the guard returns a blocking decision that propagates through the middleware stack. The chat completion route in src/app/api/v1/chat/completions/route.ts implements the same error path, ensuring consistent rejection behavior across endpoints.
The standard error response includes:
{
"error": "Request blocked: potential prompt injection detected"
}
This response is generated both by the middleware wrapper and directly within route handlers that manually invoke the guard evaluation.
Practical Implementation Examples
Wrapping a Custom API Route
Apply the guard to specific endpoints with custom thresholds and patterns:
import { withInjectionGuard } from "@/middleware/promptInjectionGuard";
import { handler as chatHandler } from "./chatCore";
export default withInjectionGuard(chatHandler, {
blockThreshold: "high",
customPatterns: [/my_secret_keyword/i],
});
Configuring Tenant-Specific Mode via Database
Override the guard behavior for specific tenants using the feature flag system:
INSERT INTO feature_flags (name, value)
VALUES ('INJECTION_GUARD_MODE', 'block');
This forces block mode for all requests belonging to that tenant, superseding environment variable configurations.
Manual Guard Evaluation
Access the guard's decision logic directly within business logic:
import { evaluatePromptInjection } from "@/lib/guardrails/promptInjection";
async function myEndpoint(req: Request) {
const body = await req.json();
const decision = evaluatePromptInjection(body, {}, { log: console });
if (decision.blocked) {
return new Response(
JSON.stringify({ error: "Injection detected" }),
{ status: 400 }
);
}
// Proceed with normal processing
}
Summary
- Pre-provider interception: The
withInjectionGuardmiddleware insrc/middleware/promptInjectionGuard.tsprocesses requests before they reach upstream LLM providers. - Multi-layer detection: The pipeline combines
sanitizeRequestregex checks with normalized pattern scanning against the first 16 KB of message content. - Configurable enforcement: Modes (
block,warn,log) are resolved via database feature flagsINJECTION_GUARD_MODE, environment variables, or sensible defaults. - Immediate termination: Blocked requests receive a 400 status code with the message "Request blocked: potential prompt injection detected", preventing any provider forwarding.
- Extensible patterns: Developers can supply
customPatternsand adjustblockThresholdper endpoint or tenant.
Frequently Asked Questions
What types of prompt injection attacks does the guard detect?
The guard identifies system-override directives (e.g., system: override), malformed delimiter blocks ( ```system), and user-defined patterns. It scans the joined message contents using regex patterns from DEFAULT_GUARD_PATTERNS combined with any customPatterns provided during middleware initialization, evaluating against severity thresholds to distinguish between suspicious and benign inputs.
How do I configure the guard to block requests for one tenant while warning for others?
Set the INJECTION_GUARD_MODE feature flag in your database to 'block' for the specific tenant ID. The resolution logic in src/lib/guardrails/promptInjection.ts checks database flags before environment variables, allowing per-tenant overrides. For other tenants, omit the flag or set it to 'warn' to allow requests with logging.
What happens if the detection service fails or times out?
The guard defaults to enabled status (INPUT_SANITIZER_ENABLED defaults to true) and evaluates entirely within the request lifecycle without external service dependencies. If pattern matching or JSON parsing fails, the error propagates as a standard 400 or 500 response rather than silently allowing potentially malicious requests to pass through.
Can I use the promptInjectionGuard outside of chat completion routes?
Yes. The withInjectionGuard middleware and evaluatePromptInjection function are provider-agnostic. You can wrap any POST/PUT/PATCH handler or call the evaluation logic directly in custom endpoints, supplying options like customPatterns and blockThreshold to tailor detection for specific use cases beyond standard chat completions.
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 →