SIGN IN SIGN UP

fix(arborist): allow-remote exemption for proxy/mirror-fronted registry tarballs (#9550)

With `allow-remote=none` (the default on npm 12) or `allow-remote=root`,
a `npm install` fails with `EALLOWREMOTE` on ordinary registry
dependencies when the configured registry origin differs from the
lockfile `resolved` origin.

```
npm error code EALLOWREMOTE
npm error Fetching packages of type "remote" have been disabled
```

This is the common proxy/mirror case: a committed `package-lock.json`
whose `resolved` URLs point to `https://registry.npmjs.org/...`, while
the machine (or CI) is configured to use a private registry proxy/mirror
with a different origin.
It affects both the hoisted and the linked install strategy.

## Why

When extracting a registry-resolved package, reify hands pacote a
`name@URL` spec, which pacote re-parses as `type=remote` and gates with
allow-remote.
To avoid mis-firing on registry tarballs, `#isRegistryResolvedTarball`
exempts them — but it compared the raw lockfile `resolved` URL against
the configured registry origin.
With a proxy/mirror configured, `resolved` is the canonical
`registry.npmjs.org` URL while the configured registry is the proxy, so
the origins never matched, the exemption returned `false`, and the
registry tarball was rejected as remote.

Crucially, reify already fetches a different URL than the raw
`resolved`: `#registryResolved` applies `replace-registry-host` (default
`npmjs`), rewriting the `registry.npmjs.org` host to the configured
registry while preserving the path.
So npm fetches the tarball from the proxy correctly; only the
allow-remote check was evaluating the wrong (pre-rewrite) URL.

## How

Evaluate the effective URL npm actually fetches, not the raw lockfile
value.

`#isRegistryResolvedTarball` now parses
`this.#registryResolved(node.resolved)` — the host-rewritten URL —
before the same origin + registry-path-prefix comparison.
After rewriting, a public-registry-pinned tarball resolves to the
configured registry and is correctly recognized as registry-mediated.

The existing security boundary is preserved: under the default
`replace-registry-host`, a same-origin tarball pointing outside the
registry path is not rewritten and is still rejected, and a genuinely
URL-declared dependency still fails the `node.isRegistryDependency`
guard.
Under `replace-registry-host=always`, every tarball is routed through
the configured registry, so registry dependencies are no longer treated
as remote — consistent with what `always` means.

## References

Fixes #9548
M
Manzoor Wani committed
8bbd70d939cec237b4e045993955895971c9eb85
Parent: 315e3bd
Committed by GitHub <noreply@github.com> on 6/18/2026, 9:09:51 PM