Repository navigation
authz: hide decision registry mutation capability from consumers #171
Description
Problem
Independent exact-head review of #651e0c124 found one remaining authority-minting path after #170. _decision(...) was fail closed and _DECISION_ISSUANCE_IDS was gone, but _DECISION_SNAPSHOT_REGISTRY was still a mutable module attribute. Consumer code could allocate an exact uninitialized AuthorizationDecision, build a valid snapshot with _validated_decision_snapshot(...), insert (weakref.ref(decision), snapshot) under id(decision), and make decision.allowed expose forged allow authority without evaluate_purpose_bound_access(...).
This is not a claim of absolute tamper resistance against arbitrary interpreter memory/reflection. It is the narrower executable boundary already claimed by #170: ordinary consumer code must not receive a module-level authority-storage mutation capability merely by importing the adapter.
Test-first repair in #65
RED 98ad797a8374925d20091d2885a2224cce337970 adds the direct registry-insertion attack. fc4fb5c330f10bfd1eb0f8be27420d0703790aa1 refines that regression so a read-only replacement may reject mutation while a forged exact decision must remain unreadable. Initial production 3ea988edf6bf50d15765784b8b85725d055f4af5 moved backing mutation to closure state and exposed only a read-only mapping view.
A further owner-side RED 05d54aff528c6bfa9d0e23199e1297608a33e70e tightened the boundary: there is no product need to expose even a read-only final-decision registry to module consumers. Final production repair a5d598300cbc804f71a17bf5c0aacb899ded00ed removes _DECISION_SNAPSHOT_REGISTRY from module state entirely. _build_decision_runtime() now retains the registry only in closure state and returns a lookup-only closure plus the evaluator. AuthorizationDecision._snapshot() can read issued evidence through that lookup closure, while ordinary consumers receive neither the backing registry nor a registration function. Direct construction and legacy _decision(...) remain fail closed; valid registration remains evaluator-local after issued request/policy validation.
Acceptance
Keep this issue open. Current canonical #65 head is a5d598300cbc804f71a17bf5c0aacb899ded00ed on protected develop@ef1b143368cb6249c9520ca8cae10ebe844a5aa1. Require fresh terminal Keyverse package, People API, repository-integrity/security evidence at the owned 100% statement/branch contract and independent current-head review before ordinary protected integration. Predecessor checks/reviews do not transfer.
Do not copy this repair into #163/#165 or consume mutable #65 as a released dependency. Descendants adopt only ordinary protected truth through non-force integration.
Activity