fix(gateway): server-derived segment, grant ceiling, owner-only cancel, agent revoke - #29
Merged
Conversation
…nly cancel; agent revoke
Second security-review pass on the money and authorisation paths. Only
the findings that reproduced against the current tree are addressed here;
several from the draft (discount floor, webhook status check, approve
re-verify, ownership-without-tier-fallback, re-register tier reset) were
already closed and are covered by existing tests.
- Segment was buyer-asserted (S-1): an unauthenticated caller could pass
segment=b2b or =new and make a segment-gated offer applicable, and the
fairness ledger then recorded a truthful predicate over a false input.
server_segment() now fixes it to facts Bazaar owns — a claimed b2b is
denied, new/returning follows the agent's completed-order history, any
is left as-is. The merchant's own admin-authenticated console (and the
simulator, which runs with the admin token) may still set a segment
directly, so generated results are unchanged.
- Grant amount was unbounded (S-3): a T2 agent could self-issue a grant
far above its per-order tier ceiling and block that much against the
Reserve Pay ledger. issue_grant now rejects a grant above the agent's
own max_order_paise unless it is admin-issued.
- Unsigned sessions had no owner, so any caller with the (publicly
listed) id could cancel one and release its hold (S-5). Cancel now
requires the admin token for a T0 session; a signed session still
needs its owning key.
- A compromised agent key could not be cut off without a restart
(P1-4): added an admin POST /agents/{keyid}/revoke. A revoked key
fails signature verification on its next request (agent_pub returns
None), so pay-tag routes reject it immediately.
Threat model: documented human_present as a self-asserted flag in the
demo (S-2) with the buyer-signed / AFA production path, and made the
Branded Whisper note precise — prose cannot instruct the ranker, but
merchant-supplied aliases do feed relevance (S-4); alias caps are the
planned hardening.
Four new tests pin S-1, S-3, S-5 and the revoke route. 96 tests, ruff
clean.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BLrj9TWybDCNw1mWxZgAks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Second security-review pass on the money and authorisation paths. I verified each draft finding against the current tree and fixed only the ones that still reproduce — several (discount floor, webhook status check, approve re-verify, ownership without tier-fallback, re-register tier reset) were already closed and are covered by existing tests.
Fixed
segment=b2b/newand make a gated offer applicable; the fairness ledger then logged a truthful predicate over a false input.server_segment()fixes the segment to facts Bazaar owns (b2b denied; new/returning from the agent's completed-order history;anyuntouched). The merchant's own admin-authenticated console — and the simulator, which runs with the admin token — may still set a segment, so generated results are unchanged.issue_grantnow rejects a grant above the agent'smax_order_paiseunless admin-issued.POST /agents/{keyid}/revoke; a revoked key fails signature verification on its next request.Documented (fix is prod-scope or would shift committed results)
human_presentis a self-asserted flag in the demo; the buyer-signed / Razorpay-AFA production path is now named in the threat model.Four new tests pin S-1, S-3, S-5 and the revoke route. 96 tests, ruff clean.
🤖 Generated with Claude Code
https://claude.ai/code/session_01BLrj9TWybDCNw1mWxZgAks