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, soBash 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.exevariants andosascript,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, soRM -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
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, orcontentis blocked, never silently dropped. - Ambiguous hybrid envelopes. A response that mixes a direct tool call (
type=tool_callortype=function_call) with a siblingtool_calls,choices, orcontentcollection 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 - discountnet = gross - deductionsbalance = credits - debits
- Percentages: fields ending in
_percentor_rateare 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
equalsandrangerules run whenever their field is present
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: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
Combining guards
Warning semantics
A guard result withseverity="warning" passes the guard. Warnings do not flip verified to False on their own. They surface as a separate, visible state on the result:
allow_warnings=False:
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 byResponseVerifier.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 arequest_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:
+00:00 offset.
Zero guards fail closed
verify() with no guards configured returns verified=False. Absence of verification is not success.
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.