RS3B-09 — Registrar Adversarial / Fail-Open Matrix (40 cases) — 2026-06-21
RS3B-09 — Registrar Adversarial / Fail-Open Matrix (40 cases) — 2026-06-21
Macro: RS3B-REGISTRAR-HARDENING-DESIGN (read-only / KB-design)
Deliverable: 09 of 10 · Adversarial / fail-open matrix (Mục tiêu I)
Date: 2026-06-21 · 0 mutations · NO_CODEX_LIVE_READ
Deliverable status: ADVERSARIAL_MATRIX_DEFINED_NOT_EXECUTED — design criteria, not executed (no validator run, no registration). 40 cases (≥ required 35).
Enforcement layers: V = pure validator · R = registrar atomic-consume · F = future producer / interface F · B = dual-writer boundary · T = trigger / closed-at-registration. Anti-fail-open meta-rule: any case yielding PASS, write-intent, or activation when it should reject ⇒ classify FAIL_OPEN ⇒ the hardening macro fails.
| # | Case | Bad input / state | Expected reject / HOLD | Layer | Fail-open condition (forbidden) |
|---|---|---|---|---|---|
| A01 | source unavailable | registrar .ts unreadable (allowlist) |
HOLD_REGISTRAR_SOURCE_NOT_READ; no behavior claim |
— | reconstructing behavior from RP-03 prose |
| A02 | mass scan input | input = glob / "all untracked bin/dot-*" |
MASS_REGISTRATION_ATTEMPTED |
V | scanning + registering many |
| A03 | set input | dot_code is a list |
MASS_REGISTRATION_ATTEMPTED |
V | iterating the set |
| A04 | wrong artifact | artifact_path ≠ admitted path |
ARTIFACT_NOT_ADMITTED |
V/R | registering an unadmitted file |
| A05 | not admitted | admission_ref missing/invalid |
ARTIFACT_NOT_ADMITTED |
V/R | proceeding without admission |
| A06 | admission mutable | admission record not immutable | ADMISSION_NOT_IMMUTABLE |
R/F | trusting a mutable admission |
| A07 | path traversal | artifact_path contains .. |
NON_CANONICAL_PATH |
V/F | resolving the traversal |
| A08 | symlink / alias | path is a symlink to another file | NON_CANONICAL_PATH |
V/F | following the symlink to register a different file |
| A09 | source not deployed | path not proven deployed | SOURCE_NOT_DEPLOYED |
F | registering an undeployed artifact |
| A10 | hash mismatch | reread trusted_attested.hash ≠ proposed |
HASH_MISMATCH |
F | accepting proposed hash |
| A11 | artifact hash null | carrier hash is NULL (e.g. wf_fs_dot_bin_snapshot.hash) |
ARTIFACT_HASH_NULL fail-closed |
F | treating NULL as match |
| A12 | unknown hash algo | hash_algorithm not on allowlist |
UNKNOWN_HASH_ALGO |
V/F | accepting unknown algorithm |
| A13 | canonicalization mismatch | canonicalization_version unsupported |
UNKNOWN_CANONICALIZATION |
V/F | comparing across canonicalizations |
| A14 | stale artifact snapshot | snapshot row older than deployed artifact | SNAPSHOT_STALE / SOURCE_UNPROVEN_FAIL_CLOSED |
F | trusting stale snapshot |
| A15 | snapshot provider unproven | wf_*_snapshot used as manifest |
SNAPSHOT_MANIFEST_SOURCE_UNPROVEN |
F | inferring trust from snapshot row existence |
| A16 | extra_metadata carrier | hash supplied via dot_tools.extra_metadata jsonb |
treat as request_proposed, reject as trusted |
V/F | promoting caller jsonb to attestation |
| A17 | catalog-sync race | dot-catalog-sync writes same code in deploy window |
CATALOG_SYNC_CONFLICT; registrar wins, sync→report |
B | both writers commit |
| A18 | catalog-sync clobber | sync overwrites registrar metadata | CATALOG_SYNC_CLOBBER (fail-open if observed) |
B | silent overwrite |
| A19 | sync auto-registers | sync inserts a discovered file as a row | CATALOG_SYNC_WROTE_REGISTRATION_ROW |
B | accepting sync as registration writer |
| A20 | existing row drift | dot_tools row diverges from admitted artifact |
DOT_TOOLS_ROW_DRIFT |
R | silently overwriting / ignoring drift |
| A21 | guard materialized as row | guard registered as separate dot_tools row |
GUARD_MATERIALIZED_AS_ROW |
B/R | registering guards as rows |
| A22 | duplicate logical key | logical_request_key already consumed |
REPLAY_DUPLICATE / return prior |
R | creating a 2nd effect |
| A23 | attempt-no bypass | same logical key, new attempt_id/attempt_no |
REPLAY_ATTEMPT_NO_BYPASS |
R | re-admitting via attempt increment |
| A24 | fresh nonce dup effect | new authorization_nonce, same logical effect |
reject duplicate effect | R | nonce freshness re-authorizing |
| A25 | reused nonce | already-consumed nonce | REPLAY_NONCE_CONSUMED |
R | accepting consumed nonce |
| A26 | unbound nonce | nonce not bound to envelope/window | NONCE_UNBOUND |
V/R | accepting free-floating nonce |
| A27 | rollback before commit | crash after insert, before commit (S1) | both roll back; uncertain-commit recovery | R | re-issuing without recovery → double effect |
| A28 | post-commit verify fail | verifier rejects committed row (S3) | logical key stays consumed; compensation only | R | recreating registration under new nonce |
| A29 | concurrent attempts | two attempt_id on same key (S4) |
one wins (ON CONFLICT); other ATTEMPT_COLLISION |
R | both commit |
| A30 | stale request vs consumed | stale request, consume row exists | request inadmissible; replay still blocked | V/R | erasing consumed meaning on staleness |
| A31 | iu_route_attempt as store | replay state stored in iu_route_attempt |
REPLAY_SURFACE_NOT_FIT |
R | using non-single-use retry ledger |
| A32 | audit sink fail | event_outbox write fails / unavailable |
AUDIT_SINK_UNAVAILABLE → HOLD |
R | committing without durable failure audit |
| A33 | audit sink mutated | failure audit row later UPDATE/DELETEd | AUDIT_IMMUTABILITY_UNPROVEN fail-closed |
R | trusting mutable sink as durable |
| A34 | trigger emits activation | register tier-B row status='active' → pg_notify |
ACTIVATION_AT_REGISTRATION |
T | emitting context_pack_event at registration |
| A35 | dot_config opened | registration flips real_run/operator_runtime |
WOULD_OPEN_GATE |
R/T | opening a runtime gate |
| A36 | guard standalone reachable | a guard executable outside the primary router | PAIR_GUARD_STANDALONE_REACHABLE |
R | guard runs without primary |
| A37 | guard drift undetected | guard content ≠ admitted artifact hash | GUARD_DRIFT_UNDETECTED |
R/F | running drifted guard |
| A38 | owner absent | governance_object_ownership has no head row (live: 0 rows) |
OWNER_ABSENT fail-closed |
R/F | registering without owner |
| A39 | APR not bound to artifact | approval not bound to this artifact_hash; no register_dot action (live) |
APR_NOT_BOUND_TO_ARTIFACT |
R/F | accepting unbound/transitive approval |
| A40 | request_proposed as trusted | proposed value used without reread trusted row; trusted_attested.* missing |
REQUEST_PROPOSED_AS_TRUSTED / TRUSTED_PRODUCER_ABSENT |
V/F | synthesizing attestation from caller input |
Coverage map vs Mục tiêu I
source-unavailable A01 · mass-scan A02/A03 · wrong-artifact A04/A05 · path-traversal A07 · symlink/alias A08 · hash-mismatch A10 · stale-snapshot A14 · catalog-sync-race A17 · existing-row-drift A20 · duplicate-logical-key A22 · same-nonce-diff-attempt A23 · fresh-nonce-dup A24 · rollback-before-commit A27 · post-commit-verify-fail A28 · audit-sink-fail A32 · trigger-activation A34 · dot_config-opened A35 · guard-standalone A36 · owner-absent A38 · APR-not-bound A39 · register_dot-absent A39 · trusted_attested-missing A40 · request_proposed-as-trusted A40 · artifact-carrier-missing A11/A15 · snapshot-provider-unproven A15. All required categories covered.
Status block
- Deliverable status:
ADVERSARIAL_MATRIX_DEFINED_NOT_EXECUTED(40 cases) - No execution, no validator run, no registration
- Registration gate:
REGISTRATION_HOLD·REGISTRATION_CAN_PROCEED = NO· 0 mutations