Skip to main content
QWED Open Responses provides 6 verification guards.

SchemaGuard

Validates AI outputs against JSON Schema.

Options


ToolGuard

Blocks dangerous tool calls and patterns.

Default blocked tools

Blocklist and allowlist matching is case-insensitive, so Bash and POWERSHELL are blocked the same as their lowercase forms. The default blocklist covers:
  • Execution primitives: execute_shell, shell, exec, eval
  • Common shells and OS command interpreters: bash, sh, ash, dash, zsh, ksh, csh, tcsh, fish, cmd, powershell, pwsh, plus their .exe variants and osascript, wscript, cscript
  • File mutation: delete_file, remove_file, write_file, modify_file
  • Side effects: send_email, transfer_money, make_payment

Default dangerous patterns

ToolGuard ships a unified set of 14 dangerous-command patterns, matched case-insensitively. The Python and TypeScript packages enforce the identical superset, so RM -RF / is blocked on both runtimes:
  • SQL: DROP TABLE, DELETE FROM, TRUNCATE TABLE
  • Filesystem: rm -rf, rmdir /s, del /f, format c:
  • Privilege and permissions: sudo, chmod 777
  • Code execution: eval(, exec(, __import__, subprocess, os.system
Custom patterns passed via dangerous_patterns are also compiled case-insensitively. Argument scanning additionally decodes bounded base64 tokens (7 or more alphabet characters) found inside serialized arguments and scans the decoded text with the same patterns, so a payload like ZXhlYyg= (exec() cannot slip through as an encoded string. Pattern scanning is a heuristic, not a security boundary. For real enforcement, prefer allowed_tools allowlists plus OS-level sandboxing.

Recognized tool-call shapes

ToolGuard extracts tool calls from these response envelopes: The type match is case-insensitive, so a Tool_Use block is treated as a tool call. JSON-encoded argument strings are parsed before blocklist and dangerous-pattern checks run, so a call cannot hide arguments inside a string.

Fail-closed rejections

ToolGuard blocks rather than passes when a response cannot be validated unambiguously:
  • Unrecognized tool-like content. A response that looks like a tool call but matches none of the recognized shapes is blocked instead of passing with “No tool calls”.
  • Malformed entries. A non-object item inside tool_calls, choices, or content is blocked, never silently dropped.
  • Ambiguous hybrid envelopes. A response that mixes a direct tool call (type=tool_call or type=function_call) with a sibling tool_calls, choices, or content collection is blocked, because validating one side would let the other escape policy checks.
  • Nameless calls. A tool call without a non-blank string name is blocked. A blank name can never match blocklist or allowlist checks.
  • Oversized arguments. JSON-encoded argument payloads over 10,000 characters or nested deeper than 128 levels are blocked before parsing.

MathGuard

Verifies mathematical calculations.

What it checks

  • Totals: one canonical formula per vocabulary, with absent components defaulting to zero:
    • total = subtotal + tax + shipping - discount
    • net = gross - deductions
    • balance = credits - debits
  • Percentages: fields ending in _percent or _rate are verified against their base and amount
  • Inline calculations in text: "5 + 3 = 8" (every equation in the string is verified, not just the first)
  • Custom rules: configured equals and range rules run whenever their field is present
Each vocabulary (total, net, balance) is verified independently, so an invalid net is never hidden by a valid total.

No verifiable math fails with a warning

A response that contains no verifiable math shape no longer passes vacuously. Prose, plain strings, and objects without recognized math fields fail with a warning-severity result:
In a ResponseVerifier with strict_mode=True (the default), this warning-severity failure blocks the response. Only include MathGuard in stacks where responses are expected to carry verifiable math.

Non-finite values fail closed

NaN, infinity, null, blank strings, and other non-numeric values in totals, components, or percentage fields return explicit errors instead of silently passing tolerance checks.

StateGuard

Validates state machine transitions.

ArgumentGuard

Validates tool call arguments.

Supported types


SafetyGuard

Comprehensive safety checks.

Detections

The Python and TypeScript packages run the same case-insensitive pattern superset. The TypeScript SafetyGuard performs the same harmful-content and IP-address PII checks as Python, and collects all error-severity findings instead of stopping at the first match. Credential detection is value-aware: a bare label like password: required or api_key: not set passes, while a label followed by a real-looking value is blocked. Injection detection requires instruction-override context after a system: marker, so benign text like Operating system: Linux is not flagged.

Content extraction

SafetyGuard scans string content nested up to 12 levels deep, not just top-level keys. This includes:
  • The canonical OpenAI shape: choices[].message.content
  • Anthropic content block envelopes
  • Strings nested inside arbitrary wrapper objects and arrays
Content hidden inside a nested structure is checked for PII, injection, and harmful patterns the same as top-level content. Content nested deeper than 12 levels is not scanned — keep payloads you need verified within that bound.

Combining guards

Warning semantics

A guard result with severity="warning" passes the guard. Warnings do not flip verified to False on their own. They surface as a separate, visible state on the result:
To escalate warnings to failures, create the verifier with allow_warnings=False:
With strict_mode=True (the default), any failed guard blocks the response.

Strict response parsing

verify() accepts a dict or a JSON string that parses to an object. A string that parses to a JSON scalar, array, or null raises ValueError instead of verifying content that guards never inspected. A plain non-JSON string is wrapped as {"type": "text", "content": ...}. The TypeScript parseResponse behaves identically.

Tamper-evident bindings

Results produced by ResponseVerifier.verify() carry a binding: a SHA-256 digest covering the verified response and the guard names. Call verify_binding() to detect a result that was replayed against a different response or had its guard metadata altered:
verify_binding() returns False for hand-constructed results with no binding. Binding digests are runtime-portable between Python and TypeScript. A binding detects mismatches only. It is public, recomputable data and does not authenticate the result, so treat results you did not produce in-process as untrusted.

Correlating results with request IDs

Pass a request_id in the verification context to tie a verdict back to the response that produced it. The value is carried on VerificationResult.request_id (requestId in TypeScript) and included in to_dict() output:
Result timestamps are timezone-aware UTC and carry the +00:00 offset.

Zero guards fail closed

verify() with no guards configured returns verified=False. Absence of verification is not success.
When strict_mode=True (the default), the result is also blocked=True. Pass at least one guard to verify() or set default_guards on the verifier. The TypeScript ResponseVerifier behaves the same way with defaultGuards.