Skip to content

fix: invalidate Location and Content-Location URIs on unsafe methods#5514

Open
jeswr wants to merge 2 commits into
nodejs:mainfrom
jeswr:fix/cache-location-invalidation
Open

fix: invalidate Location and Content-Location URIs on unsafe methods#5514
jeswr wants to merge 2 commits into
nodejs:mainfrom
jeswr:fix/cache-location-invalidation

Conversation

@jeswr

@jeswr jeswr commented Jul 4, 2026

Copy link
Copy Markdown
Contributor

Fixes #5509

What

CacheHandler.onResponseStart deleted only the request URI's entry on a 2xx/3xx response to an unsafe method. RFC 9111 §4.4 also says caches SHOULD invalidate the URIs in the response's Location and Content-Location header fields when they share the request URI's origin — so the classic POST /collection201 Location: /collection/123 flow left a previously cached GET /collection/123 stale.

This adds a #deleteLocationHeaderEntries step to the existing invalidation branch: it resolves each header value against the request URI (relative references included), applies the same-origin check from the RFC's security note (invalidating cross-origin URIs could be abused for cache poisoning), and deletes the matching entry from the store.

Repro / evidence

Origin: GET /target served with Cache-Control: public, s-maxage=100; POST /src answers 201 with Location: <origin>/target.

Before (main @ cb4c2f1, Node 22):

  1. GET /target → origin hit, cached
  2. POST /src (response carries Location: <origin>/target)
  3. GET /targetserved from cache (1 origin hit total on /target)

After: step 3 goes back to the origin (2 hits), for both Location and Content-Location, absolute and relative forms. A Location pointing at another origin (even with a path that collides with a cached entry) does not invalidate — covered by the added test in test/interceptors/cache.js.

Tests

  • New node:test case unsafe methods invalidate the URIs in Location and Content-Location response headers in test/interceptors/cache.js (fails on main with expected: 2, actual: 1 origin hits, passes with this change).
  • Full cache test files (test/interceptors/cache*.js, test/cache-interceptor/*): 122 pass / 0 fail (121 baseline + 1 new).
  • mnot cache-tests conformance runner (node test/cache-interceptor/cache-tests.mjs): the eight invalidate-{POST,PUT,DELETE,M-SEARCH}-{location,cl} tests move from failed-optional to passing in every environment — totals go 220 → 228 passed, 58 → 50 failed-optional, still 0 required failures. Nothing needed un-skipping in the runner's ignore list.

Notes

🤖 Generated with Claude Code

CacheHandler.onResponseStart only deleted the request URI's cache entry
on a successful (2xx/3xx) response to an unsafe method. RFC 9111 section
4.4 also says caches SHOULD invalidate the URIs in the response's
Location and Content-Location header fields when they share the request
URI's origin, so the classic "POST /collection -> 201 Location:
/collection/123" flow left a previously cached "GET /collection/123"
stale.

Repro (before): cache GET /target; POST /src returning "Location:
<origin>/target"; GET /target again is served from cache (1 origin hit).
After: the second GET /target is refetched (2 origin hits). Only
same-origin URIs are invalidated, per the RFC's security note.

This also turns the eight invalidate-{POST,PUT,DELETE,M-SEARCH}-{location,cl}
tests of the mnot cache-tests conformance suite from failed-optional
into passing in every test environment (220 -> 228 passed, 58 -> 50
failed-optional, 0 required failures).

Fixes nodejs#5509

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings July 4, 2026 16:45

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@mcollina mcollina left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm

@codecov-commenter

codecov-commenter commented Jul 4, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 92.45283% with 4 lines in your changes missing coverage. Please review.
✅ Project coverage is 93.44%. Comparing base (cb4c2f1) to head (32b0692).

Files with missing lines Patch % Lines
lib/handler/cache-handler.js 92.45% 4 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #5514      +/-   ##
==========================================
- Coverage   93.44%   93.44%   -0.01%     
==========================================
  Files         110      110              
  Lines       37328    37381      +53     
==========================================
+ Hits        34881    34930      +49     
- Misses       2447     2451       +4     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

…4/ubuntu)

'MockAgent - handle delays to simulate work' asserts elapsed >= 50ms on a
50ms setTimeout and measured 49ms (classic timer early-fire/rounding).
Passes 10/10 locally on this branch and 15/15 on main; this PR only
touches lib/handler/cache-handler.js, which MockAgent never loads.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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.

cache: unsafe-method invalidation does not invalidate Location / Content-Location URIs (RFC 9111 §4.4)

4 participants