Skip to content

Security hardening: response replay, in-flight locks, cache timeout, and doc accuracy#1

Merged
jkyberneees merged 3 commits into
mainfrom
fix/security-review-20260627
Jun 27, 2026
Merged

Security hardening: response replay, in-flight locks, cache timeout, and doc accuracy#1
jkyberneees merged 3 commits into
mainfrom
fix/security-review-20260627

Conversation

@jkyberneees

Copy link
Copy Markdown
Contributor

What changed

This PR addresses the findings from a security-focused code review of the idempotency middleware and brings the docs/demos in line with the actual implementation.

Security & reliability fixes

  • In-flight lock leak: locks are now released on res.close if the client aborts or the response is destroyed before res.end() fires, preventing a per-key denial-of-service.
  • Cache read timeout: added an optional cacheTimeout option (default 5000 ms) so a hanging cache backend cannot hold a lock forever.
  • maxResponseSize bypass: unknown/non-string/non-buffer body types are no longer cached when maxResponseSize is configured.
  • headersSent guard: replay aborts gracefully when headers were already sent by an earlier middleware.

Docs & examples

  • Updated the README options table and behavior notes to match implementation details (cache key scoping by full URL, hop-by-hop header stripping, old cache entry handling, duplicate-header behavior).
  • Replaced the hardcoded USER_ID in the demos with a realistic getCurrentUserId(req) helper.
  • Updated TypeScript declarations to include the new cacheTimeout option.

Tests

  • Improved the mock response to mirror on-http-end's payload shape (including headers).
  • Added coverage for header capture/replay, cacheTimeout validation, cache timeout lock release, unknown body types, headersSent replay abort, and close-event lock cleanup.

Verification

npm test      # 49/49 passing, 100% coverage
npm run lint  # clean

No breaking public API changes were introduced. The only new option, cacheTimeout, is optional and defaults to the previous effective wait behavior with an explicit upper bound.

- Add per-key in-flight locks to prevent concurrent duplicate processing
- Store and replay original status/headers/body on cache hits
- Scope cache keys with HTTP method + URL + idempotency key
- Validate idempotency keys (max 128 chars, safe charset)
- Catch extractor errors gracefully
- Implement keyPrefix and maxResponseSize options
- Cap TTL at 24 hours and validate cache value shape
- Update vulnerable dependencies (npm audit fix)
- Harden CI workflow (checkout/setup-node v4, npm ci, read-only lint)
- Update demos and README with secure usage patterns
- Expand tests for race conditions, key validation, and replay
- Add tests for all validation branches and edge cases
- Cover buffer serialization/deserialization and hop-by-hop header filtering
- Cover concurrent deduplication with three simultaneous requests
- Cover req.originalUrl fallback and falsy body edge cases
- Remove unreachable defensive fallbacks in buildCacheKey
- 43/43 tests passing with 100% statement/branch/function/line coverage
- Fix in-flight lock leak on response close/abort

- Add cacheTimeout option with validation and timeout wrapping

- Prevent unknown body types from bypassing maxResponseSize

- Guard replay when response headers already sent

- Improve test mock to mirror on-http-end payload; add coverage for new behavior

- Align README, demos, and TypeScript declarations with implementation
@jkyberneees jkyberneees force-pushed the fix/security-review-20260627 branch from e411f0f to 0cb9606 Compare June 27, 2026 20:51
@jkyberneees jkyberneees merged commit 6b2010b into main Jun 27, 2026
4 checks passed
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.

1 participant