Input Controls
DLP Filter
dlp-filter keeps your own secrets and internal terms away from the model provider. You give it regular expressions, such as the shape of an access key, and literal terms, such as a project name. A request that matches is blocked or redacted.
The control has no built-in list of secrets. It matches only what you configure. Start with exact matches and action: block.
Configuration
Save this as policy-config.yaml in a new test directory. To add it to a running gateway, use the config-first workflow.
pack:
name: dlp-filter-example
version: 1.0.0
enabled: true
policies:
chain:
- dlp-filter
policy:
dlp-filter:
detect_patterns:
- 'AKIA[0-9A-Z]{16}'
- 'ghp_[0-9A-Za-z]{36}'
blocked_terms:
- Project Titan
- internal.acme.corp
action: block
fuzzy_matching: false
sensitivity_level: standardTo replace matches instead of blocking, set action: redact. This also turns on the shared PII redaction on supported text routes. Keep fuzzy matching off if every match must actually be replaced.
Try it without a provider
Add these tests to the end of the file:
testing:
suites:
- name: dlp-filter
cases:
- name: blocks-internal-term
input:
messages:
- role: user
content: "Summarize Project Titan"
expected:
verdict: block
reason_code: dlp.blocked
- name: allows-public-topic
input:
messages:
- role: user
content: "Summarize our public release notes"
expected:
verdict: allow
reason_code: okverdictan policy lint --file policy-config.yaml
verdictan policy test --jsonBoth cases report passed: true. To check the whole request path, deploy the file to a test gateway and send the same two messages from your application.
Fields
| Field | Type | Default | Description |
|---|---|---|---|
detect_patterns | string[] | [] | Regular expressions matched against the joined request text. A pattern without a leading flag group gets (?i), so case does not matter. |
blocked_terms | string[] | [] | Literal terms, matched at word boundaries. Case does not matter. |
action | string | redact | block rejects the request. redact sends it through the shared redaction pass. |
fuzzy_matching | boolean | false | Also matches terms and pattern literals within max_distance edits |
max_distance | integer | 1 | Maximum Levenshtein distance for a fuzzy match. Lint accepts 0 to 8. |
sensitivity_level | string | standard | standard, high, or restricted. high and restricted add the built-in classification marker check. |
How it works
- The gateway joins the content of all request messages into one string.
- The control counts the matches of each
detect_patternsexpression. - If no expression matched and
fuzzy_matchingistrue, it takes the alphanumeric parts of each pattern that are at least three characters long and fuzzy-matches them as one phrase. - It matches each
blocked_termsvalue at word boundaries. If none matched andfuzzy_matchingistrue, it compares each term with word windows of the same length, withinmax_distance. - With
sensitivity_levelhighorrestricted, it looks for the substringstop secret,secret,confidential,fouo,noforn,orcon,propin, andrel to. - No match:
allowwithdlp.clean. - A match with
action: block:blockwithdlp.blocked. - A match with
action: redact:redactwithdlp.redact. The gateway runs the shared redaction pass on the request messages. - With
action: redact, the gateway also redacts the model response and setsx-verdictan-response-redactedwhen it replaces text.
The decision event details contain action, regex_hit_count, blocked_term_hit, fuzzy_matching, max_distance, sensitivity_level, and context_sensitive.
Phase and verdicts
| Item | Value |
|---|---|
| Phase | input |
| Request families | Chat Completions, Completions, Responses, Messages, Embeddings, Audio transcriptions, Moderation |
| Verdicts | allow, redact, block |
| Reason codes | dlp.clean, dlp.blocked, dlp.redact |
A blocked request gets HTTP 400 with error code content_policy_violation and a verdictan.decision object in the body.
Limits and failure behavior
- There are no built-in patterns for API keys, SSNs, IBANs, or health identifiers. Only your patterns and terms, plus the classification markers for
highandrestricted, are checked. highandrestrictedbehave the same at runtime.- The classification check matches substrings:
confidentialalso matchesconfidentiality, andrel tomatchesbarrel to.sensitivity_level: highcan block normal business text. - A
detect_patternsentry with invalid syntax is silently ignored. Lint and test every pattern before you deploy it. - Redaction replaces only
detect_patternsandblocked_termsmatches. A request that matched only by fuzzy matching or a classification marker gets aredactverdict, but its text goes out unchanged. - The redaction pass is the shared PII pipeline. With
action: redact, it also replaces base PII and card numbers in the request and response, and writes each DLP match as[REDACTED:ID]. - With
action: redactandstream: true, the gateway holds the whole response before it streams it. - A
max_distanceabove2matches many ordinary words. Keep it at1or2.
Next steps
- PII Detector: built-in patterns for personal identifiers.
- Data Routing Policy: keep regulated data on approved provider targets.
- verdictan policy test: test your patterns before you deploy them.