Skip to main content

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.

policy-config.yaml
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: standard

To 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:

Add to policy-config.yaml
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: ok
verdictan policy lint --file policy-config.yaml
verdictan policy test --json

Both 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​

FieldTypeDefaultDescription
detect_patternsstring[][]Regular expressions matched against the joined request text. A pattern without a leading flag group gets (?i), so case does not matter.
blocked_termsstring[][]Literal terms, matched at word boundaries. Case does not matter.
actionstringredactblock rejects the request. redact sends it through the shared redaction pass.
fuzzy_matchingbooleanfalseAlso matches terms and pattern literals within max_distance edits
max_distanceinteger1Maximum Levenshtein distance for a fuzzy match. Lint accepts 0 to 8.
sensitivity_levelstringstandardstandard, high, or restricted. high and restricted add the built-in classification marker check.

How it works​

  1. The gateway joins the content of all request messages into one string.
  2. The control counts the matches of each detect_patterns expression.
  3. If no expression matched and fuzzy_matching is true, it takes the alphanumeric parts of each pattern that are at least three characters long and fuzzy-matches them as one phrase.
  4. It matches each blocked_terms value at word boundaries. If none matched and fuzzy_matching is true, it compares each term with word windows of the same length, within max_distance.
  5. With sensitivity_level high or restricted, it looks for the substrings top secret, secret, confidential, fouo, noforn, orcon, propin, and rel to.
  6. No match: allow with dlp.clean.
  7. A match with action: block: block with dlp.blocked.
  8. A match with action: redact: redact with dlp.redact. The gateway runs the shared redaction pass on the request messages.
  9. With action: redact, the gateway also redacts the model response and sets x-verdictan-response-redacted when 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​

ItemValue
Phaseinput
Request familiesChat Completions, Completions, Responses, Messages, Embeddings, Audio transcriptions, Moderation
Verdictsallow, redact, block
Reason codesdlp.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 high and restricted, are checked.
  • high and restricted behave the same at runtime.
  • The classification check matches substrings: confidential also matches confidentiality, and rel to matches barrel to. sensitivity_level: high can block normal business text.
  • A detect_patterns entry with invalid syntax is silently ignored. Lint and test every pattern before you deploy it.
  • Redaction replaces only detect_patterns and blocked_terms matches. A request that matched only by fuzzy matching or a classification marker gets a redact verdict, 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: redact and stream: true, the gateway holds the whole response before it streams it.
  • A max_distance above 2 matches many ordinary words. Keep it at 1 or 2.

Next steps​