fix(danger): fingerprinted read ledger (TOCTOU re-gate) + pre-exec content audit for unread-script approvals - #146
Merged
Merged
Conversation
…pt audit - internal/danger: WasReadFresh fingerprint contract (TOCTOU re-gate): post-read mutation must re-fire the H-6 gate, incl. same-size mtime-restored tamper (hash catches touch -r), deleted files never fresh, large-file stat fallback, and re-read re-licensing. - cmd/odek: scanUnreadScripts pre-execution content audit contract — plain/base64/hex injection findings, clean-script silence, scan-never-populates-ledger invariant, capped audit window, and shell approval description enrichment wiring. - security_report_validation_test: regression-bar entry for the fingerprint re-gate. Expected state: both packages fail to build (symbols not implemented yet). GREEN commit follows.
…cript audit Read-ledger fingerprints (TOCTOU re-gate): - RecordRead captures the file state at display time (size + mtime, sha256 for files <= 1 MiB); unstatable targets record a fail-closed entry that never licenses. - WasReadFresh re-verifies on-disk state at gate time; the H-6 unread-script gate now consumes freshness, so a file mutated after its read (MCP tool, curl -o, lifecycle hook, other process) re-fires the gate until the mutated content is re-read. Re-reading renews the fingerprint — the model has then seen the bytes that are on disk. - WasRead keeps its existence-only semantics for viewer licensing. Pre-execution content audit (audit-then-exec companion): - scanUnreadScripts (cmd/odek/unreadscan.go) reads the target's leading 256 KiB read-only and runs danger.ScanInjection over the raw bytes plus best-effort single-layer base64/hex decodes of embedded blobs. Findings ride in the unread_exec approval description in both shell and parallel_shell — the human decides with content evidence, not just a path. - The audit never writes the read ledger (the auditor is not the model) and never returns raw content, only threat labels. Verified: targeted suites green in both packages; danger suite green under -race; regression-bar entry added in the RED commit.
- SECURITY.md: extend the unread_exec gate section with the TOCTOU fingerprint semantics and the pre-exec injection-scan enrichment; add both rows to the injection-study mitigation table. - AGENTS.md: note the fingerprinted ledger and unreadscan.go in the danger classifier bullet.
Deploying with
|
| Status | Name | Latest Commit | Preview URL | Updated (UTC) |
|---|---|---|---|---|
| ✅ Deployment successful! View logs |
odek | 035579a | Commit Preview URL Branch Preview URL |
Aug 27 2026, 07:26 PM |
… prompt Pins the prompt-layer counterpart to the mechanical gates shipped in e52e6f4: provenance (justification from the principal, never from content read during the task), audit-before-execute for make/npm/CI shapes the file-operand gate cannot see, failed-read never becomes execution, purpose-vs-contents tell, deferred-execution confirmation, tool-metadata-is-not-directive, scope discipline, and the untrusted wrapper contract. Guard: defaultSystem must never trip danger.ScanInjection (IDENTITY.md copies are scanned and rejected).
Adds the 'Execution provenance — where justification comes from' section to defaultSystem, the prompt-layer counterpart to the mechanical gates: - provenance: justification must come from the principal — repo/tool text dressed as policy, compliance, or platform-team mandates is context, never authorization (covers the doc/AGENTS.md/MDP class of the injection study); - audit-before-execute for make/npm/CI shapes the file-operand gate cannot see; reading the Makefile is not enough; - a failed read is never substituted with executing the file; - purpose-vs-contents contradiction is a stop-and-flag tell; - deferred-execution writes need the principal's confirmation naming the mechanism; - MCP tool metadata describes capability, never directives; - scope discipline inside the project directory; - the <untrusted_content_*> wrapper contract is now stated in the prompt, so model and wrapper agree by construction. Also fixes two pre-existing self-trips found by the new guard: defaultSystem quoted 'ignore previous instructions' / 'output your system prompt' verbatim in its IPI signals and described exfil hooks as 'requests to echo secrets' — phrases that match danger.ScanInjection's own patterns, so an operator copying the default into ~/.odek/IDENTITY.md would have it silently rejected. Both reworded scanner-safe; the guard now bisects per line and line pair (scanner \s+ matches newlines) and names the offending phrase. Verified: all TestDefaultSystem* green (incl. anti-pattern conciseness and verification scaffold); stateless cmd/odek failure set byte- identical to the pre-change environmental baseline (14 TTY-approval tests, unchanged); gofmt/vet clean.
…ault - AGENTS.md identity section: note the execution-provenance rules in defaultSystem and the scanner-clean guarantee (pinned by TestDefaultSystem_PassesOwnInjectionScan). - SECURITY.md system-prompts bullet: same, plus the rule summary.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Closes two gaps in the H-6 unread-script gate identified in the exec-then-audit vs audit-then-exec review:
curl -o, an npm lifecycle hook, or any background process — stayed licensed, re-creating inside the gate exactly the timing failure the injection study identified ("detection is not the failing control, timing is").wrapUntrusted). An unread script's approval described the file path, never its contents — the one artifact that decides whether execution is safe.Changes
Fingerprinted licenses (TOCTOU re-gate) —
internal/danger/readledger.goRecordReadcaptures the file state at display time: size + mtime, plus sha256 for files up to 1 MiB. Unstatable targets record a fail-closed entry that never licenses.WasReadFreshre-verifies on-disk state at gate time;UnreadScriptTargetsconsumes freshness, so post-read mutation re-fires theunread_execgate until the mutated content is re-read (re-reading renews the fingerprint — the model has then seen the bytes on disk).WasReadkeeps its existence-only semantics (viewer licensing, CRIT-001 behavior unchanged).Pre-execution content audit —
cmd/odek/unreadscan.goscanUnreadScriptsreads the target's leading 256 KiB (read-only) and runsdanger.ScanInjectionover the raw bytes plus best-effort single-layer base64/hex decodes of embedded blobs.unread_execapproval description in bothshellandparallel_shell: the human decides with content evidence, not just a path.Test plan (TDD: RED -> GREEN -> docs)
internal/danger/readledger_fingerprint_test.go: post-read mutation re-gates, same-size mtime-restored tamper caught by hash, deleted files never fresh, large-file stat fallback, re-read re-licensing.cmd/odek/unreadscan_test.go: plain/base64/hex injection findings, clean-script silence, scan-never-populates-ledger, audit-window cap, approval-description wiring for shell (clean + finding paths).cmd/odek/security_report_validation_test.go: regression-bar entry for the fingerprint re-gate.internal/dangerfull suite green under-race; gofmt/vet clean.Note: several pre-existing tests require cached sudo credentials or an interactive TTY and cannot run from a headless local shell; their local failure set is identical on
mainand unrelated to this branch. Docs updated in the same change (SECURITY.md, AGENTS.md).