registrystack / registrystack/registry-stack
BReg change requests: idempotency binding and action ETag vary with submitter-target claims on actions that never consult them
- Dominant language
- Rust
- Stars
- 2
- Forks
- 0
- Avg merge
- 2h 55m
- Merged PRs (30d)
- 130
Description
Found in the review of PR #929 (2026-09-09). Line references are as of that branch; re-resolve after it merges.
`canonical_claim_context` embeds a `submitterTargets` member, each target's own canonical claim context with hashed boundary values, whenever the claim context carries submitter targets (`crates/registry-breg/src/idempotency.rs:369-381`). That value is the `authority` member of every request-action idempotency binding and of the request-action ETag (`crates/registry-breg/src/mutation/request.rs:83-93`).
Submitter-target admission runs only for Submit and Revise (`crates/registry-breg/src/mutation/request.rs:600-612`), and `request_action_target_authority` returns an empty authority list for Submit, Revise and Cancel (`crates/registry-breg/src/api/mod.rs:2069-2071`). A cancel, approve, reject, request-revision or apply therefore carries submitter-target claims in its idempotency identity without consulting them in its decision.
What that costs:
- An exact cancel replay under the same idempotency key is refused as a binding mismatch when the caller's target claims changed between the two calls: the holder's licence row moved out of its person-reference boundary, or the token stopped yielding target authority so the map is empty. Both calls are permitted at the route, because `submitter_targets.unwrap_or_default()` only fails the route closed for Create, Patch, SubmitRequest and ReviseRequest (`crates/registry-breg/src/api/mod.rs:2503-2524`).
- The same holds for the action ETag a client presents in `If-Match`: it changes when unrelated target claims change.
Decision needed, which of the two sides to move:
1. Narrow the binding: carry `submitterTargets` in the canonical authority only for the operations that admit them (create, draft patch, submit, revise). Cancels then replay across an authority change. This changes existing digests for any holder profile that carries targets.
2. Widen admission: admit submitter targets for the other actions too, so the binding's authority is the authority actually consulted. Cancel then refuses with 412 once the holder has lost target authority, which is a behavior change for a request the holder owns.
3. Keep it as is and document the coupling in the change-control reference, since a client that retries a cancel across an authority change has to send a fresh key.
Security-sensitive (authorization and idempotency identity); needs explicit review notes when implemented.
Read `BREG-SEC-49` in `products/breg/contracts/security-invariant-matrix.yaml` before changing anything here. Its enforcement point names "bound idempotency authority" and its refusal preserves "owner reads and cancellation", so the coupling is deliberate for the operations that admit targets, and option 1 has to keep it there while dropping it from cancellation and the review-side actions.
Contributor guide
Research direction
Read BREG-SEC-49 in products/breg/contracts/security-invariant-matrix.yaml, then trace canonical_claim_context in crates/registry-breg/src/idempotency.rs and the request action and admission paths cited in crates/registry-breg/src/mutation/request.rs and crates/registry-breg/src/api/mod.rs. Resolve the design choice with explicit review notes, then verify that idempotency bindings and action ETags include only consulted authority and that owner reads and cancellation remain preserved.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- rust
- Domain
- api, authorization, backend, security
- Issue type
- Bug
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100