fix(arborist): honor allow-remote=root for root-direct remote tarballs#9510
Merged
owlstronaut merged 1 commit intoJun 8, 2026
Merged
Conversation
…s in linked strategy
Contributor
Author
|
@owlstronaut, how do we ensure that every feature implementation in npm takes isolated mode into consideration? |
owlstronaut
approved these changes
Jun 8, 2026
Contributor
|
@manzoorwanijk it's a good question. Maybe we can find a way to parameterize tests over install strategies? Open to suggestions, because you are right, this will keep happening |
Contributor
Author
Thank you for the pointer. I will explore a few options to see what we can do about it. |
Contributor
|
🎉 Backport to |
manzoorwanijk
added a commit
to manzoorwanijk/npm-cli
that referenced
this pull request
Jun 8, 2026
npm#9510) In continuation of our exploration of using `install-strategy=linked` in the [Gutenberg monorepo](WordPress/gutenberg#75814), which powers the WordPress Block Editor. Under `install-strategy=linked` with `allow-remote=root`, a fresh install fails with `EALLOWREMOTE` on a genuine remote (non-registry) tarball that is a direct dependency of the project root or a workspace. The standard (hoisted) reifier installs the same dependency fine under `allow-remote=root`; only the linked strategy rejects it. ``` npm error code EALLOWREMOTE npm error Fetching non-root packages of type "remote" have been disabled npm error Refusing to fetch "@react-native-community/slider@https://raw.githubusercontent.com/wordpress-mobile/react-native-slider/v3.0.2-wp-5/react-native-community-slider-3.0.2-wp-5.tgz" ``` ## Why The `allow-remote=root` gate is enforced at reify time by computing `_isRoot` and passing it to `pacote.extract` in `reify.js`. A node counts as "root" if it satisfies at least one valid dependency edge from the project root or a workspace, which is derived from `node.edgesIn`. In the linked strategy, store nodes are `IsolatedNode` instances with no `edgesIn` to recompute root-ness from, so `_isRoot` was always `false`, every remote tarball was treated as non-root, and pacote refused even a legitimate root/workspace direct dependency. This is the sibling of the registry-tarball fix (npm#9495). That change carried `isRegistryDependency` onto store nodes so the registry-tarball exemption still applied; this change carries the analogous root-ness signal so the `allow-remote=root` gate resolves correctly for genuine remote tarballs, which are not registry-mediated and so do not qualify for the registry exemption. This only widens `allow-remote=root`. `allow-remote=none` still rejects all remote specs (pacote refuses regardless of `_isRoot`), and a genuinely transitive remote dependency still fails the resolution-layer `#checkAllow` gate during ideal-tree construction. Hoisted is unaffected because its nodes retain real `edgesIn`. ## How Carry a root-ness flag from the source tree node onto the store node, rather than weakening the guard: 1. `IsolatedNode` gains an `isRootDependency` field (default `false`), settable from constructor options. 2. `#externalProxy` computes `isRootDependency` from the real tree node's `edgesIn` using the same predicate the reifier applies (a valid edge from the project root or a workspace). 3. `#generateChild` passes it through to the store `IsolatedNode`. 4. The `_isRoot` computation in `reify.js` falls back to `node.isRootDependency`. Hoisted nodes do not have the property, so they fall through to the existing edge-based check unchanged. ## References Fixes npm#9509 Follows-up npm#9495
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
In continuation of our exploration of using
install-strategy=linkedin the Gutenberg monorepo, which powers the WordPress Block Editor.Under
install-strategy=linkedwithallow-remote=root, a fresh install fails withEALLOWREMOTEon a genuine remote (non-registry) tarball that is a direct dependency of the project root or a workspace. The standard (hoisted) reifier installs the same dependency fine underallow-remote=root; only the linked strategy rejects it.Why
The
allow-remote=rootgate is enforced at reify time by computing_isRootand passing it topacote.extractinreify.js. A node counts as "root" if it satisfies at least one valid dependency edge from the project root or a workspace, which is derived fromnode.edgesIn. In the linked strategy, store nodes areIsolatedNodeinstances with noedgesInto recompute root-ness from, so_isRootwas alwaysfalse, every remote tarball was treated as non-root, and pacote refused even a legitimate root/workspace direct dependency.This is the sibling of the registry-tarball fix (#9495). That change carried
isRegistryDependencyonto store nodes so the registry-tarball exemption still applied; this change carries the analogous root-ness signal so theallow-remote=rootgate resolves correctly for genuine remote tarballs, which are not registry-mediated and so do not qualify for the registry exemption.This only widens
allow-remote=root.allow-remote=nonestill rejects all remote specs (pacote refuses regardless of_isRoot), and a genuinely transitive remote dependency still fails the resolution-layer#checkAllowgate during ideal-tree construction. Hoisted is unaffected because its nodes retain realedgesIn.How
Carry a root-ness flag from the source tree node onto the store node, rather than weakening the guard:
IsolatedNodegains anisRootDependencyfield (defaultfalse), settable from constructor options.#externalProxycomputesisRootDependencyfrom the real tree node'sedgesInusing the same predicate the reifier applies (a valid edge from the project root or a workspace).#generateChildpasses it through to the storeIsolatedNode._isRootcomputation inreify.jsfalls back tonode.isRootDependency. Hoisted nodes do not have the property, so they fall through to the existing edge-based check unchanged.References
Fixes #9509
Follows-up #9495