You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Part of VforVitorio#884 (Fable-5 red-team audit 2026-07-05). Sprint 3 — Audit-log integrity. The audit log's whole purpose (tamper-evident tool-use accountability) is not actually enforced.
S3.1security(audit): HMAC/anchor the hash chain + verify() on startup with a tamper indicator[M][P2] — compute_entry_hash = SHA256(previous_hash + canonical(record)) is a KEYLESS chain (chat/audit/canonical.py:74-82): anyone who can write mcp.log can edit a record and recompute every downstream hash → verify() passes clean; tail TRUNCATION is also undetectable (a shorter genesis-anchored chain still verifies); the docstring's "reordering, truncation, or silent edits are detectable" overstates it (chat/audit/log.py:1-13), and verify() (:137) is never called at runtime. AC: HMAC the chain with a keyring key (edits require the key) or periodically sign/anchor the tail out-of-band; call verify() on startup + surface a tamper indicator; persist expected length/last-hash so truncation is detectable. (P2-5, CONFIRMED)
S3.2fix(audit): move append hashing inside the lock (TOCTOU — the #769 class)[S][P2] — _audited computes each record's previous_hash from read_last_hash() (lock acquired then RELEASED, chat/mcp_server.py:96,108,127,139), then append() re-acquires the lock and rejects when previous_hash != _last_hash (chat/audit/log.py:114-119); a concurrent appender that advances the tail between the read and the append → ValueError("chain break") → the tool wrapper raises → spurious tool_error and the tool never runs (collides when the stdio MCP server + in-process chat share the get_audit_log() singleton, or under concurrent SSE chats). AC: have append derive previous_hash + stamp entry_hash itself inside the critical section, not from a racy pre-read. (P2-6, CONFIRMED)
Full detail: memory/fable_redteam_audit_2026-07-05.md.
Part of VforVitorio#884 (Fable-5 red-team audit 2026-07-05). Sprint 3 — Audit-log integrity. The audit log's whole purpose (tamper-evident tool-use accountability) is not actually enforced.
security(audit): HMAC/anchor the hash chain + verify() on startup with a tamper indicator[M] [P2] —compute_entry_hash = SHA256(previous_hash + canonical(record))is a KEYLESS chain (chat/audit/canonical.py:74-82): anyone who can writemcp.logcan edit a record and recompute every downstream hash →verify()passes clean; tail TRUNCATION is also undetectable (a shorter genesis-anchored chain still verifies); the docstring's "reordering, truncation, or silent edits are detectable" overstates it (chat/audit/log.py:1-13), andverify()(:137) is never called at runtime. AC: HMAC the chain with a keyring key (edits require the key) or periodically sign/anchor the tail out-of-band; callverify()on startup + surface a tamper indicator; persist expected length/last-hash so truncation is detectable. (P2-5, CONFIRMED)fix(audit): move append hashing inside the lock (TOCTOU — the #769 class)[S] [P2] —_auditedcomputes each record'sprevious_hashfromread_last_hash()(lock acquired then RELEASED,chat/mcp_server.py:96,108,127,139), thenappend()re-acquires the lock and rejects whenprevious_hash != _last_hash(chat/audit/log.py:114-119); a concurrent appender that advances the tail between the read and the append →ValueError("chain break")→ the tool wrapper raises → spurioustool_errorand the tool never runs (collides when the stdio MCP server + in-process chat share theget_audit_log()singleton, or under concurrent SSE chats). AC: haveappendderiveprevious_hash+ stampentry_hashitself inside the critical section, not from a racy pre-read. (P2-6, CONFIRMED)Full detail:
memory/fable_redteam_audit_2026-07-05.md.Upstream: VforVitorio#887