Skip to content

authz: hide decision registry mutation capability from consumers #171

Description

@seonghobae

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions