# How OmniRoute's Guardrails Framework Handles PII Masking and Prompt Injection Detection

> OmniRoute's guardrails framework masks PII and detects prompt injection attacks. Learn how its pluggable pipeline protects LLM requests and responses with configurable pattern matching and blocking.

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

---

**OmniRoute's guardrails framework protects LLM requests through a pluggable pipeline that masks personally identifiable information (PII) in both requests and responses while detecting prompt injection attacks via configurable pattern matching, severity thresholds, and blocking modes.**

The **OmniRoute** open-source routing layer for large language models implements a modular **guardrails framework** that intercepts every HTTP request before it reaches providers like OpenAI or Anthropic. Written in TypeScript, this system orchestrates pre-call sanitization and post-call filtering through a priority-based registry, ensuring sensitive data never leaks upstream while malicious prompts get blocked at the edge.

## Architecture of the Guardrail Pipeline

The framework centers on the `GuardrailRegistry` class defined in [`src/lib/guardrails/registry.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/lib/guardrails/registry.ts). This orchestrator maintains a registry of guardrail instances and executes them in two distinct phases:

1. **Pre-call hooks**: Execute after request validation but before the downstream provider call
2. **Post-call hooks**: Execute on the provider's response before returning to the client

The execution order follows a priority system where lower numbers run first. The **PII Masker** runs at priority **10**, ensuring personal data is redacted before the **Prompt Injection Guardrail** (priority **20**) analyzes the sanitized content for attack patterns.

## How PII Masking Works in OmniRoute

The **PII Masker Guardrail** ([`src/lib/guardrails/piiMasker.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/lib/guardrails/piiMasker.ts)) operates during both pipeline phases to redact personally identifiable information using deep-cloning and regex-based detection.

### Pre-Call Request Sanitization

When `PII_REDACTION_ENABLED` equals `"true"` and `INPUT_SANITIZER_MODE` equals `"redact"`, the guardrail invokes `cloneAndMaskRequestPayload` to create a sanitized copy of the request:

```typescript
// src/lib/guardrails/piiMasker.ts – lines 73-85
function cloneAndMaskRequestPayload(payload: unknown) {
  const clonedPayload: JsonRecord = JSON.parse(JSON.stringify(payload));
  // Redacts system prompts, message arrays, and input fields
  if (typeof clonedPayload.system === "string") { /* ... */ }
  if (Array.isArray(clonedPayload.messages)) { /* ... */ }
  return { detections, modified, payload: modified ? clonedPayload : payload };
}

```

This approach uses `JSON.parse(JSON.stringify(payload))` to deep-clone the object, ensuring the original request remains immutable while strings are processed through `processPII` detection regexes.

### Post-Call Response Redaction

After the LLM provider returns a response, the guardrail's `postCall` method in [`src/lib/guardrails/piiMasker.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/lib/guardrails/piiMasker.ts) (lines 88-107) applies `sanitizePIIResponse` to the cloned response object. The `maskResponsesOutput` function additionally traverses any `output` arrays to redact embedded text, returning the sanitized payload via `modifiedResponse`.

## Prompt Injection Detection and Blocking Mechanisms

The **Prompt Injection Guardrail** ([`src/lib/guardrails/promptInjection.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/lib/guardrails/promptInjection.ts)) implements a bounded scanning engine that analyzes request content for dangerous patterns while respecting configurable severity thresholds.

### Configuration Options

The guardrail supports three operating modes controlled via environment variables or runtime options:

- **`INPUT_SANITIZER_MODE`**: Sets the default behavior (`block`, `warn`, or `log`)
- **`INJECTION_GUARD_MODE`**: Overrides the environment setting via database feature flags
- **`INPUT_SANITIZER_ENABLED`**: Global toggle to disable the guardrail entirely

Runtime configuration occurs through the `PromptInjectionGuardrailOptions` interface:

```typescript
// src/lib/guardrails/promptInjection.ts – lines 34-51
export interface PromptInjectionGuardrailOptions {
  blockThreshold?: "low" | "medium" | "high";
  customPatterns?: PatternLike[];
  enabled?: boolean;
  mode?: "block" | "warn" | "log";
  priority?: number;
}

```

### Detection Engine Mechanics

The `evaluatePromptInjection` function performs a bounded scan limited to the first `MAX_INJECTION_SCAN_BYTES` (approximately 16 KB) for performance optimization. The detection flow follows these steps:

1. **Sanitization pass**: Calls `sanitizeRequest` from shared utilities to obtain baseline injection and PII detections
2. **Content extraction**: Flattens all message strings using `extractMessageContents`
3. **Pattern matching**: Executes `detectWithPatterns` against default and user-supplied regex patterns
4. **Severity evaluation**: The `shouldBlock` function compares detection severities against the `blockThreshold` using `SEVERITY_SCORES` mapping

```typescript
// src/lib/guardrails/promptInjection.ts – lines 89-104
function detectWithPatterns(text: string, patterns: ReturnType<typeof normalizePatternEntry>[]) {
  const detections: Detection[] = [];
  for (const rule of patterns) {
    const match = text.match(rule.pattern);
    if (match) {
      detections.push({ 
        pattern: rule.name, 
        severity: rule.severity, 
        match: match[0].slice(0, 50) 
      });
    }
  }
  return detections;
}

```

### Blocking Logic

The guardrail returns a blocking decision when `mode` equals `"block"` and `shouldBlock` returns `true` based on the severity threshold comparison. When blocked, the chat handler in [`src/sse/handlers/chat.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/sse/handlers/chat.ts) receives a `GuardrailResult` with `block: true` and aborts the request before any provider call occurs, returning HTTP 400 with the message "Request rejected: suspicious content detected".

## Integrating Guardrails into the Request Flow

The core chat handler ([`src/sse/handlers/chat.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/sse/handlers/chat.ts), line ~415) orchestrates the guardrail execution:

```typescript
import { guardrailRegistry, resolveDisabledGuardrails } from "@/lib/guardrails";

// Resolve which guardrails to skip based on headers, body, or API key
const disabledGuardrails = resolveDisabledGuardrails({ apiKeyInfo, body, headers });

// Execute pre-call hooks (PII masking → Injection detection)
const preCallGuardrails = await guardrailRegistry.runPreCallHooks(body, {
  log: requestLogger,
  disabledGuardrails,
});

if (preCallGuardrails.blocked) {
  return NextResponse.json({ error: preCallGuardrails.message }, { status: 400 });
}
body = preCallGuardrails.payload; // Use potentially redacted payload

// ... execute provider call ...

// Execute post-call hooks (PII masking on response)
const postCallGuardrails = await guardrailRegistry.runPostCallHooks(providerResponse, {
  log: requestLogger,
  disabledGuardrails,
});

```

The `resolveDisabledGuardrails` function (lines 52-78 in [`src/lib/guardrails/registry.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/lib/guardrails/registry.ts)) aggregates disable flags from three sources: API key metadata (`disabledGuardrails`), request body (`metadata.disabledGuardrails`), and the HTTP header `x-omniroute-disabled-guardrails` (or legacy `x-disabled-guardrails`).

## Configuring and Customizing Guardrails

### Disabling Guardrails Per Request

You can disable specific guardrails via HTTP headers:

```http
POST /v1/chat/completions
x-omniroute-disabled-guardrails: pii-masker,prompt-injection

```

Or via request body metadata:

```json
{
  "metadata": { "disabledGuardrails": ["prompt-injection"] },
  "messages": [{ "role": "user", "content": "Hello" }]
}

```

### Enabling Aggressive PII Redaction

Set these environment variables to enable full redaction:

```bash
export PII_REDACTION_ENABLED=true
export INPUT_SANITIZER_MODE=redact

```

### Adding Custom Injection Patterns

When registering the guardrail programmatically, supply custom regex patterns:

```typescript
guardrailRegistry.register(
  new PromptInjectionGuardrail({
    blockThreshold: "medium",
    customPatterns: [/secret\s+token/i, "DROP TABLE"],
    mode: "block",
    priority: 5, // Run before standard guardrails
  })
);

```

## Summary

- **OmniRoute's guardrails framework** implements a dual-phase pipeline (pre-call and post-call) orchestrated by `GuardrailRegistry` in [`src/lib/guardrails/registry.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/lib/guardrails/registry.ts).
- **PII masking** creates deep clones of payloads via `cloneAndMaskRequestPayload` in [`src/lib/guardrails/piiMasker.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/lib/guardrails/piiMasker.ts), redacting sensitive strings in both requests and responses when `PII_REDACTION_ENABLED` is true.
- **Prompt injection detection** scans the first 16 KB of request content using `evaluatePromptInjection` in [`src/lib/guardrails/promptInjection.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/lib/guardrails/promptInjection.ts), supporting custom patterns and configurable severity thresholds (low, medium, high).
- **Execution priority** ensures PII masking (priority 10) runs before injection detection (priority 20), preventing personal data from interfering with attack pattern matching.
- **Flexible disabling** allows per-request configuration through HTTP headers, body metadata, or API key settings, while global modes are controlled via environment variables.

## Frequently Asked Questions

### How do I disable PII masking for a specific API request?

Send the `x-omniroute-disabled-guardrails` header with the value `pii-masker`, or include `"disabledGuardrails": ["pii-masker"]` in the request body's metadata object. The `resolveDisabledGuardrails` function in [`src/lib/guardrails/registry.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/lib/guardrails/registry.ts) processes these values and excludes the guardrail from execution for that request.

### What is the default behavior when prompt injection is detected?

By default, the **Prompt Injection Guardrail** operates in `"warn"` mode as defined by the `INPUT_SANITIZER_MODE` environment variable. In this mode, suspicious content is logged but the request proceeds to the provider. To block requests automatically, set `INPUT_SANITIZER_MODE=block` or configure `blockThreshold` to trigger on specific severity levels (low, medium, or high).

### Can I use custom regex patterns for injection detection?

Yes, the `PromptInjectionGuardrailOptions` interface accepts a `customPatterns` array that supports both RegExp objects and string literals. These patterns are normalized through `normalizePatternEntry` and evaluated alongside built-in patterns in the `detectWithPatterns` function, allowing you to detect organization-specific sensitive keywords or attack signatures.

### Why does PII masking run before prompt injection detection?

The **PII Masker** is assigned priority **10** while the **Prompt Injection Guardrail** uses priority **20** in the registry. This ordering ensures that personal information (like email addresses or phone numbers) is redacted before the injection detector analyzes the content. Without this sequence, legitimate PII patterns might trigger false positives in the injection detection logic, or malicious prompts could exfiltrate PII before blocking occurs.