Skip to content

fix: fail closed where four checks passed on tampered input - #240

Merged
imran-siddique merged 2 commits into
mainfrom
fix/verifier-binding-gaps
Sep 30, 2026
Merged

imran-siddique merged 2 commits into
mainfrom
fix/verifier-binding-gaps

Conversation

@imran-siddique

@imran-siddique imran-siddique commented Sep 30, 2026 •

Copy link
Copy Markdown
Member

Four places in this repo reported a check as passing when the thing it checks had changed. Each was found with a probe that tampered with the input, backed by controls, and each now fails closed on that same probe.

Capture core, tree_digest (all seven engines). node_modules, __pycache__, .pyc and .pyo were excluded as run artifacts, but Node and CPython load code from all four. Before this change, editing node_modules/helper/index.js or a .pyc produced "agent composition unchanged since your approved baseline". Those are now digested. state/, .cache and *.log stay excluded, but any file with an executable suffix is digested wherever it sits, so a state/run.py no longer hides. A skill containing none of the newly covered files keeps its old digest, which a new test pins, so existing baselines do not drift. Version goes to 0.1.3.

Sentinel, incident-report signature. sign_payload returned sha256(payload) + b"signed", which anyone could recompute, so /verify returned VERIFIED for a forged report. Reports are now Ed25519-signed with the key Sentinel already requires for TRACE claims (TRACE_PRIVATE_KEY_PEM), and signing without it raises.

wcm-azure-skr and wcm-gcp-confidential-space. Both set nonce_echo to the verifier's own challenge nonce and never read the token's, so a token minted for another challenge came back looking fresh. They now read the MAA nonce (top level or x-ms-runtime) and eat_nonce, and refuse when it is missing or different.

OntoGuard proof example, verify_proof.py. The signatures cover exact_bytes and receipt_bytes, but the binding checks read the unsigned sibling JSON. It now parses the signed bytes and fails if the copies disagree. The pack checksum for the script is updated.

Tests: capture core 54 passed (48 before), and all seven engines pass against the local core. Sentinel 13 passed, Azure SKR 31, GCP CS 35, and the proof pack still reads RESULT: PASS. The ruff gate from lint.yml passes.

Two engine tests asserted that a changed cached.pyc raises no alarm, which was the bypass itself. They now require the alarm.

Not verified: the MAA nonce claim location against a live Azure token. The adapter accepts either location and requires every nonce present to match.

Also restores requirements/capture-core.txt. Dependabot's #231 recompiled it without the Python 3.9 markers, so iniconfig==2.3.0 (3.10 and up) broke the 3.9 legs of capture-core and agentrust-codex on main. It is recompiled with the command in its header.

After merge, pushing the capture-core-v0.1.3 tag publishes the core.

🤖 Generated with Claude Code

Capture core digests node_modules, __pycache__ and bytecode, and any
executable file inside an excluded directory; 0.1.3. Sentinel signs
incident reports with its Ed25519 key instead of sha256 + "signed".
The Azure SKR and GCP Confidential Space adapters read the token's nonce
instead of echoing the challenge. The OntoGuard proof example checks the
signed bytes, not the unsigned copies.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Imran Siddique <imran.siddique@opaque.co>
@imran-siddique
imran-siddique requested review from a team and carloshvp as code owners September 30, 2026 17:04
#231 regenerated requirements/capture-core.txt without the 3.9 markers, so
iniconfig 2.3.0, which requires Python 3.10, became unconditional and the
3.9 legs of capture-core and agentrust-codex could not install. Recompiled
with the command in the file's header.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Imran Siddique <imran.siddique@opaque.co>
@imran-siddique
imran-siddique merged commit e77c5a2 into main Sep 30, 2026
32 checks passed
@imran-siddique
imran-siddique deleted the fix/verifier-binding-gaps branch September 30, 2026 17:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant