Skip to content

fix: reject idempotency key reuse with mismatched payload#596

Open
Ope4196 wants to merge 1 commit into
CalloraOrg:mainfrom
Ope4196:bug/idempotency-payload-mismatch
Open

fix: reject idempotency key reuse with mismatched payload#596
Ope4196 wants to merge 1 commit into
CalloraOrg:mainfrom
Ope4196:bug/idempotency-payload-mismatch

Conversation

@Ope4196

@Ope4196 Ope4196 commented Jun 30, 2026

Copy link
Copy Markdown

PR: Structured rejection of idempotency key reuse with mismatched payload

Summary

Fixes a silent bug where idempotencyMiddleware returned a cached response even when the new request body differed from the original. The middleware now computes a canonical SHA-256 payload fingerprint and returns 409 Conflict with error code IDEMPOTENCY_KEY_REUSE_MISMATCH when fingerprints differ, along with a conflictingSummary that helps clients diagnose the mismatch without exposing stored sensitive values.

Changes

Modified files

  • src/middleware/idempotency.ts
    • Exported IDEMPOTENCY_KEY_REUSE_MISMATCH constant (replaces inline 'IDEMPOTENCY_CONFLICT' string)
    • Mismatch 409 response now includes conflictingSummary with idempotencyKey, incomingPayloadFingerprint, storedPayloadFingerprint, and incomingFields (sorted top-level key names only — no values leaked)
    • Structured logger warning on mismatch with both hashes for ops tracing
  • src/middleware/idempotency.test.ts

Acceptance criteria

Criterion Covered by
Mismatch returns 409 returns 409 with IDEMPOTENCY_KEY_REUSE_MISMATCH when payload differs
Correct error code IDEMPOTENCY_KEY_REUSE_MISMATCH expect(code).toBe(IDEMPOTENCY_KEY_REUSE_MISMATCH)
Same payload returns cached response same payload with different key order still matches
Canonicalization of key order produces the same hash regardless of key order, same payload with different key order still matches
No stored values leaked does NOT leak stored values
conflictingSummary fields present response includes conflictingSummary...
In-progress still works IDEMPOTENCY_IN_PROGRESS when hash matches but status is started

Security

  • conflictingSummary exposes only SHA-256 fingerprints and sorted field names — never stored field values
  • No new external dependencies
  • Existing stored-value security (server-error key deletion) preserved

closes #427

@drips-wave

drips-wave Bot commented Jun 30, 2026

Copy link
Copy Markdown

@Ope4196 Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits.

You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀

Learn more about application limits

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add structured Idempotency-Key rejection when client reuses a key with a different payload

1 participant