From 87233b7e1fbe7749cfe9d25d71d7b6bc7cd3c764 Mon Sep 17 00:00:00 2001 From: Manzoor Wani Date: Fri, 5 Jun 2026 18:03:38 +0530 Subject: [PATCH] fix(arborist): apply registry-tarball allow-remote exemption in linked strategy --- .../arborist/lib/arborist/isolated-reifier.js | 4 ++ workspaces/arborist/lib/isolated-classes.js | 4 ++ workspaces/arborist/test/arborist/reify.js | 50 +++++++++++++++++++ workspaces/arborist/test/script-allowed.js | 1 + 4 files changed, 59 insertions(+) diff --git a/workspaces/arborist/lib/arborist/isolated-reifier.js b/workspaces/arborist/lib/arborist/isolated-reifier.js index e782d6125e9fa..e7d985a80a7b9 100644 --- a/workspaces/arborist/lib/arborist/isolated-reifier.js +++ b/workspaces/arborist/lib/arborist/isolated-reifier.js @@ -42,6 +42,7 @@ module.exports = cls => class IsolatedReifier extends cls { const newChild = new IsolatedNode({ isInStore, inBundle, + isRegistryDependency: node.isRegistryDependency, location, name: node.packageName || node.name, optional: node.optional, @@ -153,6 +154,9 @@ module.exports = cls => class IsolatedReifier extends cls { result.optional = node.optional result.resolved = node.resolved result.version = node.version + // Carry the source node's registry-dependency flag so the store node retains it. + // IsolatedNode has no edges to recompute it from, and reify's registry-tarball allow-remote exemption depends on it. + result.isRegistryDependency = node.isRegistryDependency return result } diff --git a/workspaces/arborist/lib/isolated-classes.js b/workspaces/arborist/lib/isolated-classes.js index 32bf19972d150..007f2609e5feb 100644 --- a/workspaces/arborist/lib/isolated-classes.js +++ b/workspaces/arborist/lib/isolated-classes.js @@ -20,6 +20,7 @@ class IsolatedNode { inventory = new IsolatedInventory() isInStore = false inBundle = false + isRegistryDependency = false linksIn = new Set() meta = { loadedFromDisk: false } optional = false @@ -50,6 +51,9 @@ class IsolatedNode { if (options.inBundle) { this.inBundle = true } + if (options.isRegistryDependency) { + this.isRegistryDependency = true + } if (options.optional) { this.optional = true } diff --git a/workspaces/arborist/test/arborist/reify.js b/workspaces/arborist/test/arborist/reify.js index 6c427dbb2213a..2abe5a5ed09a2 100644 --- a/workspaces/arborist/test/arborist/reify.js +++ b/workspaces/arborist/test/arborist/reify.js @@ -3854,6 +3854,56 @@ t.test('should preserve exact ranges, missing actual tree', async (t) => { await t.resolves(arb.reify(), 'same-origin tarball is allowed for registry root') }) + t.test('allowRemote=none allows registry tarball under linked install strategy', async t => { + // The linked strategy extracts store nodes as IsolatedNode, which has no edges to recompute isRegistryDependency from. + // The flag must be carried from the source tree node so the registry-tarball allow-remote exemption still applies. + const abbrevPackument5 = JSON.stringify({ + _id: 'abbrev', + _rev: 'lkjadflkjasdf', + name: 'abbrev', + 'dist-tags': { latest: '1.1.1' }, + versions: { + '1.1.1': { + name: 'abbrev', + version: '1.1.1', + dist: { + tarball: 'https://registry.example.com/npm/abbrev/-/abbrev-1.1.1.tgz', + }, + }, + }, + }) + + const testdir = t.testdir({ + project: { + 'package.json': JSON.stringify({ + name: 'myproject', + version: '1.0.0', + dependencies: { + abbrev: '1.1.1', + }, + }), + }, + }) + + tnock(t, 'https://registry.example.com') + .get('/npm/abbrev') + .reply(200, abbrevPackument5) + + tnock(t, 'https://registry.example.com') + .get('/npm/abbrev/-/abbrev-1.1.1.tgz') + .reply(200, abbrevTGZ) + + const arb = new Arborist({ + path: resolve(testdir, 'project'), + registry: 'https://registry.example.com/npm', + cache: resolve(testdir, 'cache'), + allowRemote: 'none', + installStrategy: 'linked', + }) + + await t.resolves(arb.reify(), 'registry tarball is allowed under linked strategy') + }) + t.test('registry with different protocol should swap protocol', async (t) => { const abbrevPackument4 = JSON.stringify({ _id: 'abbrev', diff --git a/workspaces/arborist/test/script-allowed.js b/workspaces/arborist/test/script-allowed.js index 4e20f987d9c87..04acb9a404399 100644 --- a/workspaces/arborist/test/script-allowed.js +++ b/workspaces/arborist/test/script-allowed.js @@ -402,6 +402,7 @@ t.test('isolated mode (linked): bundled IsolatedNode is blocked', async t => { const store = new IsolatedNode({ isInStore: true, + isRegistryDependency: true, // carried from the source node by #externalProxy location: 'node_modules/.store/store-pkg@1.0.0/node_modules/store-pkg', name: 'store-pkg', package: { name: 'store-pkg', version: '1.0.0' },