Summary
Medium-severity findings from the 2026-08-23 adversarial audit of v0.4.0 @ b460d70, batched; each independent. All CONFIRMED BY EXECUTION unless noted.
1. Blocklist/pattern bypasses (CONFIRMED)
Tool names are exact-match against the blocked list — "Bash", "sh", "powershell" (as a name with clean arguments) all pass; the arguments pattern scan is defeated by base64-encoded payloads. Blocklist normalization (casefold) + common-shell coverage needed; document that pattern scanning is a heuristic, not a boundary.
2. verified semantics conflated in both directions (CONFIRMED)
- Zero guards ⇒
verified=True (tracked in the empty-verifier P1).
- PII-only warning ⇒
verified=False while blocked=False — a warning fails "verified" even though nothing blocked. Executed: email-PII response → verified=False, guards_failed=1, blocked=False.
warn_result docstring says "passes but with warning" while returning passed=False (base.py:100-109).
verified currently means neither "checks ran" nor "nothing bad found." Suggested: verified = all checks passed; warnings carried as a separate visible state; fix the docstring lie.
3. Verification results are freely forgeable (CONFIRMED)
VerificationResult(verified=True, response={...}) is a public constructible dataclass with no binding to any input, no request/tool-call ID, no hash, no attestation. to_dict() emits JSON any downstream can mint, replay, or attach to a different action. Anyone who did not run a verification can manufacture the identical object. (Deeper binding design ties to the ecosystem attestation conversation — qwed-verification #319 et al.)
4. verify_structured_output(output, schema=None, guards=[]) verifies nothing (CONFIRMED)
No schema and no guards ⇒ verified=True for arbitrary output — the empty-verifier semantics again, via a convenience API that appears to promise structure checking.
5. Budget check trusts model-supplied accounting (STATIC)
_check_budget reads response["usage"]["cost"] / total_tokens — fields supplied by the very output under verification. A malicious/compromised model under-reports usage and defeats the cap. Budget should be enforced from trusted-side accounting or signed provider reports.
6. VerifiedOpenAI(guards=None) default (STATIC)
The wrapper whose README line says "Automatically verified before returning" defaults to zero guards — silently inheriting the empty-verifier semantics. Require guards at construction or warn loudly.
7. README claims outrun the implementation (STATIC)
"Deterministic — uses symbolic logic and formal verification rules", "100% Deterministic", "prevents dangerous tool calls" (README.md:16,43,45,68): implementation is configurable regex blocklists + arithmetic checks with the shape/case gaps above. Per the audit standard: either harden the code or state the precise claim boundary.
8. Streaming block_on_failure=False passes failed items through unmodified (STATIC)
Documented warn-only mode — acceptable as an explicit opt-in, but should be labeled as disabling the trust boundary (the blocked-mode replacement item also relies on the consumer honoring system_intervention).
Summary
Medium-severity findings from the 2026-08-23 adversarial audit of v0.4.0 @
b460d70, batched; each independent. All CONFIRMED BY EXECUTION unless noted.1. Blocklist/pattern bypasses (CONFIRMED)
Tool names are exact-match against the blocked list —
"Bash","sh","powershell"(as a name with clean arguments) all pass; the arguments pattern scan is defeated by base64-encoded payloads. Blocklist normalization (casefold) + common-shell coverage needed; document that pattern scanning is a heuristic, not a boundary.2.
verifiedsemantics conflated in both directions (CONFIRMED)verified=True(tracked in the empty-verifier P1).verified=Falsewhileblocked=False— a warning fails "verified" even though nothing blocked. Executed: email-PII response →verified=False, guards_failed=1, blocked=False.warn_resultdocstring says "passes but with warning" while returningpassed=False(base.py:100-109).verifiedcurrently means neither "checks ran" nor "nothing bad found." Suggested: verified = all checks passed; warnings carried as a separate visible state; fix the docstring lie.3. Verification results are freely forgeable (CONFIRMED)
VerificationResult(verified=True, response={...})is a public constructible dataclass with no binding to any input, no request/tool-call ID, no hash, no attestation.to_dict()emits JSON any downstream can mint, replay, or attach to a different action. Anyone who did not run a verification can manufacture the identical object. (Deeper binding design ties to the ecosystem attestation conversation — qwed-verification #319 et al.)4.
verify_structured_output(output, schema=None, guards=[])verifies nothing (CONFIRMED)No schema and no guards ⇒
verified=Truefor arbitrary output — the empty-verifier semantics again, via a convenience API that appears to promise structure checking.5. Budget check trusts model-supplied accounting (STATIC)
_check_budgetreadsresponse["usage"]["cost"]/total_tokens— fields supplied by the very output under verification. A malicious/compromised model under-reports usage and defeats the cap. Budget should be enforced from trusted-side accounting or signed provider reports.6.
VerifiedOpenAI(guards=None)default (STATIC)The wrapper whose README line says "Automatically verified before returning" defaults to zero guards — silently inheriting the empty-verifier semantics. Require guards at construction or warn loudly.
7. README claims outrun the implementation (STATIC)
"Deterministic — uses symbolic logic and formal verification rules", "100% Deterministic", "prevents dangerous tool calls" (README.md:16,43,45,68): implementation is configurable regex blocklists + arithmetic checks with the shape/case gaps above. Per the audit standard: either harden the code or state the precise claim boundary.
8. Streaming
block_on_failure=Falsepasses failed items through unmodified (STATIC)Documented warn-only mode — acceptable as an explicit opt-in, but should be labeled as disabling the trust boundary (the blocked-mode replacement item also relies on the consumer honoring
system_intervention).