Adversarial Canonicalization Self-Audit (12/12)
08 - Adversarial Canonicalization Self-Audit (12/12)
This is the "catch my own holes before Codex does" pass the user demanded. Each scenario is an attempted
bypass; the design must fail closed or behave exactly as specified. Every hash-related test was
COMPUTED in python (== shasum -a 256), not asserted. Verdict: 12/12 PASS.
| # | attempted bypass | required behaviour | result | by which mechanism |
|---|---|---|---|---|
| 1 | change an active doc's path prefix (strip knowledge/dev/reports/architecture/) |
hash mismatch | PASS (computed) | full-path normalization → active_corpus_membership_sha256 differs; G-CANONICAL-ENCODING-CONTRACT / -HASH-MATCH |
| 2 | reorder YAML fields | canonical hash STABLE (specified) | PASS (computed) | fixed field order + corpus sorted by document_id; canonical input independent of source key order |
| 3 | remove the trailing newline | specified mismatch | PASS (computed) | trailing-LF rule: input ends in exactly one LF; removing it changes the bytes → different digest |
| 4 | switch CRLF/LF | normalized → content-stable, but revision still trips | PASS (computed) | newline normalization CRLF/CR→LF; content hash stable; G-ACTIVE-AUTHORITY-REVISION-MATCH still catches the edit |
| 5 | change doc 00 envelope only | no self-reference loop; manifest mismatch | PASS | envelope in EXCLUDE region (doc 00 content hash unchanged) → no loop; live manifest diverges from detached seal → G-CODEX-DETACHED-SEAL-ANCHOR |
| 6 | change active doc content | corpus mismatch | PASS (computed) | per-doc normalized_active_content_sha256 changes → active_corpus_sha256 + manifest change; revision increments |
| 7 | change marker/fence only | marker registry mismatch | PASS | marker_fence_registry_sha256 changes; host doc body changes (markers are outside EXCLUDE) |
| 8 | change a guard definition only | guard hash mismatch | PASS | guard_set_sha256 (= doc 06 normalized content) + guard_set_revision change |
| 9 | change the blueprint checkpoint only | no effect (it is non-authority) | PASS | NON_AUTHORITY_INDEX: not a member, not a self-host, consumed by no guard/package; using it as authority → G-ACTIVE-AUTHORITY-SCOPE rejects |
| 10 | change the Codex checkpoint path content | detached seal mismatch | PASS | Codex checkpoint pinned by revision + content SHA-256 + MCP read-back; change → ACTIVE_AUTHORITY_DETACHED_SEAL_MISMATCH (G-CODEX-DETACHED-SEAL-ANCHOR) |
| 11 | use unordered serialization | rejected | PASS (computed) | canonical encoding mandates ascending sort + fixed field order; unordered input → different digest; G-CANONICAL-ENCODING-CONTRACT |
| 12 | add an authority-bearing field not in the manifest hash | rejected | PASS (computed) | closed roster: live_fields − ROSTER non-empty → G-ENVELOPE-MANIFEST-AUTHORITY-COMPLETE fails |
Computational evidence (this pass)
Run on the local host (shasum -a 256 == python hashlib):
BASE membership = f2bda8effc7be19b54722828126b82d7d2d48bee5e5e5dc0c8f347ce210fe251 (== sealed envelope value)
T1 prefix-strip -> differs (True)
T2 reorder source (canonical sorts) -> stable (True)
T3 trailing-LF removal -> differs (True)
T4 CRLF -> LF normalization -> content-stable (True)
T6 content-hash change in corpus -> corpus mismatch (True)
T11 unordered != canonical -> True (verifier MUST sort)
T12 unknown authority field -> detected by closed roster (True)
The second-order hole I closed in self-review (not at the next recheck)
Test 5 is the recurring trap: excluding the envelope from doc 00's content hash (to kill the self-reference) would, if left there, make the envelope itself editable after seal. I closed it by anchoring the manifest in the Codex detached seal and binding the live manifest to the sealed manifest. So doc 00 has no self-reference loop and the envelope is immutable after seal — both at once. This is exactly the class of hole the user asked me to find myself; it is found and closed here, not deferred to Codex.
Verdict
ADVERSARIAL_CANONICALIZATION_SELF_AUDIT_PASS — 12/12 fail closed / behave as specified; the
hash-dependent cases were reproduced byte-for-byte.