Skip to content

fix(token): only follow pagination links on the registry origin - #10071

Open
ManoharPaturi wants to merge 1 commit into
npm:latestfrom
ManoharPaturi:token-paginate-origin
Open

ManoharPaturi wants to merge 1 commit into
npm:latestfrom
ManoharPaturi:token-paginate-origin

Conversation

@ManoharPaturi

Copy link
Copy Markdown

Problem

npm token list and npm token revoke page through /-/npm/v1/tokens by following the urls.next link taken verbatim from the registry response:

async function paginate (href, opts, items = []) {
  while (href) {
    const result = await fetch.json(href, opts)
    items = items.concat(result.objects)
    href = result.urls.next
  }
  return items
}

Two consequences:

  1. npm-registry-fetch resolves credentials per host, so any link that keeps the registry host but swaps the scheme (an https registry answering with an http:// link) is still sent the Authorization header, now in cleartext. A compromised or hostile registry can use this to expose the bearer token to an on-path attacker.
  2. A link pointing at an arbitrary origin is fetched without user configuration, making the client a request generator for a third party.

Reproducing the first case before this change, with the mock registry replying urls.next = 'http://registry.npmjs.org/-/npm/v1/tokens?next=...', the command issues the plaintext request (blocked in tests only by nock's disableNetConnect):

not ok 1 - request to http://registry.npmjs.org/-/npm/v1/tokens?next=... failed,
reason: Nock: Disallowed net connect for "registry.npmjs.org:80/-/npm/v1/tokens?next=..."

Separately, a response that omits the urls object entirely crashes the command with a TypeError.

Solution

Resolve the next link against the configured registry and only follow it when its origin (scheme, host, port) matches the registry origin. Otherwise stop paginating and warn, so a truncated listing is visible rather than silent. Non-string or missing links, including a missing urls object, now end pagination cleanly.

Test Evidence

Four tests added to test/lib/commands/token.js:

  • a same-origin absolute link is still followed across pages,
  • a scheme downgraded link is not followed, the first page is still listed, and a warning names the ignored link,
  • a link to another host is not followed, with the same warning,
  • a response without a urls object lists tokens instead of crashing.

The downgrade test fails on the previous code with the disallowed plaintext request shown above. Full file is green with this change: npx tap test/lib/commands/token.js.

References

  • Credential resolution by host in npm-registry-fetch: node_modules/npm-registry-fetch/lib/auth.js

The token list pagination url comes straight from the registry response
and was fetched as-is. npm-registry-fetch attaches the registry
credentials to any request whose host matches the registry host, so a
response that swaps the scheme (an https registry answering with an http
link) would send the bearer token in cleartext, and a link pointing at
another origin would turn the client into a request generator for a host
the user never configured.

Only follow pagination links that stay on the configured registry origin,
warn and stop when a link leaves it, and tolerate a response that omits
the urls object entirely, which used to crash the command.
Copilot AI balanced review requested due to automatic review settings October 4, 2026 02:32
@ManoharPaturi
ManoharPaturi requested a review from a team as a code owner October 4, 2026 02:32

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.

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

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