Skip to main content

Input & Output Controls

Code Sanitizer

code-sanitizer blocks text that contains dangerous commands, such as rm -rf, DROP TABLE, or a request to the cloud metadata address. It checks all message text, not only code blocks, and you can add your own patterns.

Set block_on_match: true if a match must not go through. The default result, redact, records a cleaned copy in the decision details but does not change the actual traffic.

Configuration​

policy-config.yaml
pack:
  name: code-sanitizer-example
  version: 1.0.0
  enabled: true
policies:
  chain:
    - code-sanitizer
policy:
  code-sanitizer:
    enabled: true
    block_on_match: true
    additional_patterns:
      - AKIA[0-9A-Z]{16}

To check the model response instead of the request, set the stage on the chain entry:

Inside policy-config.yaml
policies:
  chain:
    - code-sanitizer:
        stage: pre-response

Verify a match and usual code​

Add these tests to the first file. The first input is text for the test. Do not run it as a command.

Add to policy-config.yaml
testing:
  suites:
    - name: code-sanitizer
      cases:
        - name: blocks-chmod-777
          input: {messages: [{role: user, content: "chmod 777 example.txt"}]}
          expected: {verdict: block, reason_code: code-sanitizer.flagged}
        - name: allows-print
          input: {messages: [{role: user, content: "print(\"Hello\")"}]}
          expected: {verdict: allow, reason_code: ok}

Run verdictan policy test --json. Both cases pass. A question about chmod 777 also matches, because the control cannot tell intent.

Fields​

FieldTypeDefaultDescription
enabledbooleantruefalse turns off every check. The control then always allows with code-sanitizer.ok.
block_on_matchbooleanfalsetrue blocks on a match. false returns redact.
additional_patternslist of strings[]Regular expressions checked after the built-in set. A pattern that does not compile is skipped.

Built-in patterns​

Case does not matter. \s+ matches one or more spaces.

PatternMatches
rm\s+-rfRecursive forced deletion
drop\s+tableSQL table deletion
169\.254\.169\.254Cloud instance metadata address
curl\s+https?://localhostHTTP request to the local host
chmod\s+777World-writable file permission
aws\s+s3\s+cp.+--recursiveRecursive S3 copy

How it works​

  1. The gateway joins the message text of the active stage: the request messages in pre-request, or the response text in pre-response.
  2. The control runs the built-in patterns, then additional_patterns.
  3. Each match becomes a finding, and the matched text is replaced with [redacted code pattern] in a copy.
  4. No findings: allow with code-sanitizer.ok.
  5. Findings with block_on_match: true: block with code-sanitizer.flagged.
  6. Findings with block_on_match: false: redact with code-sanitizer.flagged.

The details contain flagged, sanitized_text, and findings.

Phase and verdicts​

ItemValue
Stagespre-request (default), pre-response
Request familiesChat Completions, Completions, Responses, Messages
Verdictsallow, redact, block
Reason codescode-sanitizer.ok, code-sanitizer.flagged

Limits and failure behavior​

  • A block returns HTTP 400 with error code content_policy_violation. In pre-request, the provider never gets the request. In pre-response, the client never gets the model text.
  • redact does not remove the code. In pre-request, it starts the shared PII redactor, which has no code patterns. In pre-response, the text passes unchanged. Use block_on_match: true when a match must stop.
  • All message text is checked, not only fenced code.
  • Six built-in patterns are not a vulnerability scanner.
  • An additional_patterns entry that does not compile is silently skipped. Test each one with verdictan policy test.
  • Lint rejects unknown fields. A block with a wrong type that bypasses lint runs with all defaults.

Next steps​