fix: exclude correlation fields from the flex-auth request digest
Verified the digest join against flex-auth's T03 replay fixtures and found request_digest was hashing fields docs/canonical-request-digest.md excludes. The material is tenant, subject, action, resource, context only: id is correlation, policy_version lives in provenance, caring_context is hashed separately. This engine included all three when present. Because the join adopts the served request id, every real production request would have carried one, so the computed digest would have matched no issued decision and failed closed against every correct allow. Same unsatisfiable shape as the removed AUTHORITY constant. The old pinned constant was computed with the id inside the material, so it was wrong and its passing proved nothing. Replaced with fixture-driven tests over two real envelopes (vendored with provenance) plus a structural test that correlation fields do not move the digest. Both fixtures are needed: input_claim_digests.context appears only with a non-empty context. Also stops computing the native claim digest. The claim's binding.action and binding.target speak approval-engine's vocabulary while ours speaks the catalog's, and no mapping is published; flex-auth makes no cross-check and states the correspondence is ours via pdp_digest. A claim recording no pdp_digest now fails closed naming the missing mapping rather than comparing two different languages. That mapping is a prerequisite for destroy. 274 tests pass. Production still fails closed. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01M65ovP3eiiPHubibvWs9mD Assistant: claude-code Assistant-Model: opus Assistant-Process: 393550@bnt-lap001 Assistant-Session: 4bb359f9-1f12-4410-9e76-079cf23c82e4
This commit is contained in:
parent
083bee7333
commit
6e9c15228c
11 changed files with 562 additions and 24 deletions
|
|
@ -409,6 +409,50 @@ package/version to configure. Next inbound is the `T03` replay fixture — a rea
|
|||
DecisionEnvelope from this package including `provenance.registry_snapshot_digest`
|
||||
— to verify the digest join unchanged.
|
||||
|
||||
Verified 2026-09-06 against flex-auth's `T03` replay fixtures
|
||||
(`FLEX-WP-0021-T03`, commit `9e10d1c`), vendored to
|
||||
`tests/fixtures/flex-auth-replay/`. Running them found a defect of exactly the
|
||||
class we had just been warning about.
|
||||
|
||||
- **`request_digest` was hashing fields the contract excludes.** The material is
|
||||
`tenant`, `subject`, `action`, `resource`, `context` only; `id` is correlation,
|
||||
`policy_version` lives in provenance, and `caring_context` is hashed
|
||||
separately. This engine included all three when present. Because the join
|
||||
adopts the served request id, every real production request would have carried
|
||||
one, so the computed digest would have matched no issued decision and failed
|
||||
closed against every correct allow — the same unsatisfiable-rule shape as the
|
||||
removed `AUTHORITY` constant and as flex-auth's own destroy rule in `68ad039`.
|
||||
Fixed by `digest_material`; both fixtures now reproduce their published
|
||||
digests exactly.
|
||||
- The previously pinned digest constant was computed *with* the id inside the
|
||||
material, so it was wrong and its passing was not evidence. It is replaced by
|
||||
fixture-driven contract tests over two real envelopes plus a structural test
|
||||
that correlation fields do not move the digest. Both fixtures are required:
|
||||
`provenance.input_claim_digests.context` appears only with a non-empty context.
|
||||
- **The native claim digest is unusable and is no longer computed.** The claim's
|
||||
`binding.action`/`binding.target` speak approval-engine's vocabulary while ours
|
||||
speaks the catalog's, and no mapping is published; flex-auth makes no
|
||||
cross-check and states the correspondence is ours via `pdp_digest`. A claim
|
||||
recording no `pdp_digest` now fails closed naming the missing mapping rather
|
||||
than comparing two vocabularies. **A published mapping is a prerequisite for
|
||||
making `destroy` reachable under this task.** flex-auth offered to co-author it
|
||||
with approval-engine.
|
||||
- Narrowing accepted from approval-engine: `entries` is UNIQUE on
|
||||
`(approval_id, subject_id)`, so approver distinctness is a storage invariant,
|
||||
not a count this engine was previously protecting. What we stopped verifying is
|
||||
that approval-engine applies its own invariant correctly — still a real
|
||||
reduction in defence in depth, but narrower than "the threshold is now
|
||||
unverified". They also added `threshold` objects to issuance and use events
|
||||
(`87e55e2`) so the evaluation is reconstructable under §9.6.
|
||||
- flex-auth corrected the `destroy` dual-control rule (`68ad039`) before
|
||||
generating these fixtures: it had required `context.approval.status ==
|
||||
'approved'` and an approver list, neither of which exists in the claim schema.
|
||||
Our acknowledgement of the earlier rule crossed that fix. The corrected rule
|
||||
requires only kind/issuer/`valid_now`, and our claim validation is unchanged.
|
||||
- One global pin confirmed correct: `secrets-engine.catalog-lane.lifecycle` v1 is
|
||||
one package covering all twelve actions. Do not build per-action pin
|
||||
configuration for a distinction that does not exist.
|
||||
|
||||
Define and enforce the decision contract needed by production commands. A
|
||||
resolved approval must bind at least:
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue