demo(15-non-idempotent-retry): 15 — Retry endpoint POSTed without an idempotency key#164
demo(15-non-idempotent-retry): 15 — Retry endpoint POSTed without an idempotency key#164
Conversation
…idempotency key See scenarios/15-non-idempotent-retry/README.md for the expected verdict.
| return result.Success ? Ok(result) : UnprocessableEntity(result); | ||
| } | ||
|
|
||
| [HttpPost("{id:guid}/retry")] |
There was a problem hiding this comment.
GCI0022 — Idempotency & Retry Safety
[HttpPost] endpoint in src/OrderService/Controllers/PaymentsController.cs has no idempotency key handling.
Evidence:
Line 27: [HttpPost("{id:guid}/retry")]
💡 Suggested action: Add an idempotency key header (e.g. Idempotency-Key), validate it server-side, and cache the response for duplicate requests.
Confidence: Medium | Severity: Warn
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: f345139e94
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| [HttpPost("{id:guid}/retry")] | ||
| public async Task<ActionResult<PaymentResult>> Retry( | ||
| Guid id, | ||
| [FromBody] ChargeRequest request, | ||
| CancellationToken ct) |
There was a problem hiding this comment.
Add idempotency protection to retry POST
This retry action issues a fresh ChargeAsync call but accepts no idempotency token/header and performs no dedup lookup, so any client/network retry of POST /payments/{id}/retry can create duplicate charges. Retry endpoints are the highest-risk path for accidental replays, so this should propagate an idempotency key (or equivalent request identifier) before invoking the payment client.
Useful? React with 👍 / 👎.
|
|
||
| [HttpPost("{id:guid}/retry")] | ||
| public async Task<ActionResult<PaymentResult>> Retry( | ||
| Guid id, |
There was a problem hiding this comment.
Use the route payment ID in retry logic
The route requires {id:guid} but id is never used when building the retry request, so callers can pass any path ID and still trigger a charge based only on the body. That breaks the contract of a resource-specific retry endpoint and prevents validating/retrying the intended prior payment record.
Useful? React with 👍 / 👎.
15 — Retry endpoint POSTed without an idempotency key
Expected verdict: ❌ Fails — GauntletCI should fire GCI0022 (idempotency & retry safety).
What changed
PaymentsControllergains a newRetryaction exposed asPOST /payments/{id}/retry, with no idempotency-key parameter, header,or dedup check anywhere in the handler:
A retry endpoint is the single most likely thing to be invoked twice —
and "twice" on a charge endpoint means a duplicate charge.
Why this is risky
retry button all funnel here. Without an idempotency key, every one
of them attempts a fresh charge.
Idempotency-Keyheader for exactly thisreason; not propagating one through this layer disables it.
Idempotency-Keyheader, hash therequest, and short-circuit on a cache hit.
What GauntletCI catches
GCI0022 Idempotency & Retry Safety— a[HttpPost(...)]endpointadded to the diff with no
IdempotencyKey/Idempotency-Key/RequestId/MessageId/dedupsignal anywhere in the surroundingwindow.