Skip to content

fix(gate): validate every hop of a redirect, and release each response - #66

Merged
msalvatti merged 1 commit into
mainfrom
fix/gate-validate-redirect-hops
Aug 2, 2026
Merged

fix(gate): validate every hop of a redirect, and release each response#66
msalvatti merged 1 commit into
mainfrom
fix/gate-validate-redirect-hops

Conversation

@msalvatti

Copy link
Copy Markdown
Member

Brings this library's copy of the shared release gate in line with the four siblings, where these findings were raised and reviewed (bymaxone/nest-queue#65, bymaxone/nest-logger#50, bymaxone/nest-cache#44, bymaxone/nest-notification#32 — all merged).

The security fix

redirect: 'follow' meant the guard checked only the URL written in the README. A public host answering 302 with Location: http://169.254.169.254/… would be followed, reaching the endpoint unroutableReason exists to stop — one hop later. Every IP-literal and private-hostname check was bypassable by a single redirect.

The chain is followed by hand: each hop validated before it is requested, depth bounded at 5, and a refused hop fails rather than degrading to a note.

Refinements from the review

  • Only the statuses Fetch redirects on (301/302/303/307/308) are followed. A 300 or a 304 is a response to return, not a hop.
  • Every response body is cancelled once its status has been read — the intermediate hops, the HEAD before a GET fallback, and the final response. undici holds the connection until the body is read or cancelled, and this runs across every README link at once.
  • A Location that does not parse fails the gate instead of falling through to the transport-error note.
  • The npmjs.com → registry.npmjs.org rewrite is validated when it differs from the README URL. It happened after validation, so the first URL actually requested was never the checked one.

Verified

Depth measured against a local server issuing an unbounded 302 chain: 6 URLs requested — 1 initial plus 5 redirects — then refused. Refusals red-checked per class (metadata range, loopback, unique-local IPv6) with a public-target control so real redirects still follow.

lint and check:published green here. No release needed: scripts/ is outside package.jsonfiles, so dist/ and the published tarball are untouched.

Synced from the sibling libraries, where this was found and reviewed
(bymaxone/nest-queue#65, nest-logger#50, nest-cache#44, nest-notification#32).

redirect: 'follow' meant the guard checked only the URL written in the README:
a public host answering 302 with Location: http://169.254.169.254/ would be
followed, reaching the endpoint unroutableReason exists to stop, one hop later.
The chain is followed by hand now, each hop validated before it is requested
and the depth bounded at 5.

Only the statuses Fetch redirects on are followed — a 300 or 304 is a response
to return, not a hop. Every response body is cancelled once its status has been
read, since undici holds the connection otherwise. A Location that does not
parse fails the gate instead of degrading to a transport note. And the
npmjs.com to registry.npmjs.org rewrite is validated too, because it changes
what is actually requested.
Copilot AI review requested due to automatic review settings August 2, 2026 13:10

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Updates the repository’s release/docs “published surface” gate script to safely validate redirect chains and to proactively release undici connections, aligning behavior with the already-reviewed sibling repositories.

Changes:

  • Replaces redirect: 'follow' with manual redirect handling, validating every redirect hop (bounded to 5) before requesting it.
  • Cancels response bodies for intermediate hops and final responses to avoid holding undici sockets while only status/headers are needed.
  • Re-validates the npmjs.com → registry.npmjs.org rewrite target so the first actually requested URL is also checked.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@msalvatti
msalvatti merged commit c85eb1f into main Aug 2, 2026
15 checks passed
@msalvatti
msalvatti deleted the fix/gate-validate-redirect-hops branch August 2, 2026 13:13
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.

2 participants