Skip to content

fix(arborist): recognize registry-mediated tarballs - #9921

Open
martinrrm wants to merge 6 commits into
latestfrom
martinrrm/bug-npm-install-fails-with-eallowremote-on-certa
Open

martinrrm wants to merge 6 commits into
latestfrom
martinrrm/bug-npm-install-fails-with-eallowremote-on-certa

Conversation

@martinrrm

@martinrrm martinrrm commented Aug 27, 2026 •

Copy link
Copy Markdown
Contributor

Summary

  • recognize registry-mediated tarballs served from same-origin sibling paths without trusting arbitrary lockfile URLs
  • preserve alias target identity and scoped authentication for ordinary peers, root-selected aliases shared with ordinary dependencies, and linked installs
  • validate locked versions and use cache-first registry evidence with coalesced abbreviated metadata requests
  • keep requested signature/attestation verification separate from URL-only evidence
  • document cold-cache and offline requirements

Problem

During reification, Arborist passes registry tarballs to pacote as name@URL. npm-package-arg parses that temporary spec as a remote dependency, so Arborist needs a narrow exemption for registry-mediated tarballs.

The existing classifier only recognized tarball paths beneath the configured registry path. Private registries can legitimately advertise tarballs from another path on the same origin. It also selected registries using the installed dependency slot: for hoek: npm:@npm/hoek@6.1.4, that loses the target scope and can select the wrong registry or credentials. Both cases can incorrectly fail with EALLOWREMOTE under the npm 12 default allow-remote=none.

Implementation

Package identity and compatibility

Identity comes from valid dependency specifications, not package/lockfile name fields. Aliases are unwrapped to target names. Ordinary peers constrain the installed slot rather than redefining an alias target. Project/workspace selections also take precedence over ordinary compatible transitive requirements, while disagreeing explicit alias targets remain rejected. A transitive alias cannot supersede a plain root selection.

Linked installs carry this identity through intermediate proxies and synthetic store nodes. Extraction uses the target identity independently of remote-permission mode, so authentication follows the target scope while the install location remains the alias slot.

Sibling-path verification

The configured-registry-path fast path remains. A same-origin URL outside that path is permitted only when registry evidence for the trusted package and exact version advertises the same effective tarball URL.

Locked versions must be exact semantic versions; aliases, ranges, tags, URLs, and other dependency specifications in that field cannot redirect verification. Metadata names and versions must match. Host replacement is applied consistently to both URLs. Fragments are ignored; the remaining URL, including path and query, must match.

Metadata reuse and request behavior

  • Capture compact URL evidence from actual pacote manifest responses during resolution, never from serialized node/lockfile fields.
  • Reuse full or abbreviated packuments already present in the memory cache.
  • Request abbreviated metadata on a verification cache miss and inspect the exact version entry without treating the locked version as a dependency spec.
  • Share pending verification requests per registry/package across instances and versions. Retain compact URL evidence even when the production cache discards large responses or responses without Content-Length.
  • Keep offline requests offline. An abbreviated ENOTCACHED result permits an offline lookup of the full HTTP variant, accounting for Vary: Accept.
  • Treat captured evidence as URL-only regardless of verification flags during resolution. Explicit signature/attestation verification still uses the dedicated full-metadata path; abbreviated old-lock hydration cannot bypass it.

Cold locked sibling-path installs can still require metadata requests. A cached tarball alone does not establish the additional evidence needed for an offline sibling-path install. This is an intentional availability/performance tradeoff, not a promise of zero additional requests.

Policy boundaries

Already-permitted implicit/explicit all, and root-direct requests under root, skip URL verification. Metadata failures, mismatches, unverifiable identities, and cross-origin URLs do not grant an exemption. Explicit remote dependencies remain governed by the configured policy. Extraction continues to receive the locked integrity value.

Regression coverage

  • malformed locked-version specs, including an alias injected into a wildcard lockfile entry, without unauthorized metadata or tarball requests
  • exact metadata name/version/URL matching, query mismatches, missing versions, and preserved semver build metadata
  • full/abbreviated memory caches, real offline HTTP variants, tarball-only caches, concurrent versions, and production-cache eviction conditions
  • alias credentials across permission modes; ordinary/optional peers; root-selected aliases with ordinary consumers; hoisted/linked installs; conflicting explicit selections
  • effective alias overrides, accepted version ranges, and both linked-node transformations
  • old-lock abbreviated metadata missing an attestation pointer present in full metadata, ensuring requested verification still runs and fails closed
  • npm ci tampered-lockfile regressions and updated configuration documentation

Fixes #9796

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@martinrrm
martinrrm requested review from a team as code owners August 27, 2026 17:44
martinrrm and others added 3 commits August 27, 2026 18:01
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
})

t.test('allowRemote=none blocks same-origin tarball outside registry path', async t => {
t.test('allowRemote=none allows registry-advertised tarball outside registry path', async t => {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This test for the main change to fix #9796.

If a registry is serving tarballs from a different path than the actual registry metadata (such as https://registry.example.com/blob/package.tgz, while the registry is https://registry.example.com/npm) this results in EALLOWREMOTE attempting to install anything from it.

await t.resolves(arb.reify(), 'registry-advertised sibling-path tarball is allowed')
})

t.test('allowRemote=none verifies against dependency identity, not lockfile name', async t => {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This test is also important; we are now getting the packument of the package (so a new metadata request if the tarball URL is different from the registry) and validating that the advertised URL are the same.

Validate locked versions, preserve root-selected aliases for ordinary consumers, and reuse registry URL evidence with coalesced abbreviated metadata requests. Keep explicit signature and attestation verification separate from captured URL evidence.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

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.

🟡 Changes recommended

Registry verification is not bound to the locked integrity used for extraction, allowing signatures or attestations for one digest to authorize extraction of different package bytes.

3 open issues
What changed in this PR

This PR addresses #9796 by allowing Arborist to recognize registry-advertised tarballs served from same-origin sibling paths without trusting arbitrary lockfile URLs.

Changes:

  • Verifies sibling-path URLs against exact-version registry metadata, with cache reuse and shared pending requests.
  • Preserves alias target identity and scoped authentication during reification and linked installs.
  • Adds regression coverage and documents cold-cache and offline metadata requirements.
File Description
workspaces/​config/​lib/​definitions/​definitions.js Documents sibling-path verification and offline requirements.
workspaces/​arborist/​test/​registry-tarball.js Tests metadata evidence, caching, exact versions, and verification.
workspaces/​arborist/​test/​registry-package-name.js Tests trusted identity selection and propagation.
workspaces/​arborist/​test/​arborist/​reify.js Adds registry-tarball and alias integration coverage.
workspaces/​arborist/​lib/​registry-tarball.js Adds registry evidence lookup and caching.
workspaces/​arborist/​lib/​registry-package-name.js Derives trusted package identities from dependency edges.
workspaces/​arborist/​lib/​arborist/​reify.js Applies URL exemptions and alias-aware extraction identity.
workspaces/​arborist/​lib/​arborist/​isolated-reifier.js Carries trusted identities through linked-install transformations.
workspaces/​arborist/​lib/​arborist/​build-ideal-tree.js Captures registry URL evidence during resolution.
test/​lib/​commands/​ci.js Adds tampered-lockfile regression coverage.
tap-snapshots/​test/​lib/​docs.js.test.cjs Updates the configuration documentation snapshot.

Review effort: Balanced

retain verified integrity in cache, validate every lock entry and use the verified digest for extraction; preserve the raw-spec identity for conditional override fallbacks; select exact build-qualified manifests while retaining pacote verification.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 9aefcaed-984b-40a9-b82a-57d80f4f8fee
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG] npm install fails with EALLOWREMOTE on certain private registry configurations

2 participants