Skip to content

Commit b13ee4d

Browse files
fix(arborist): honor allow-remote=root for root-direct remote tarballs (#9511)
Backport of #9510 to `release/v11`. Co-authored-by: Manzoor Wani <manzoorwani.jk@gmail.com>
1 parent 693bb3d commit b13ee4d

4 files changed

Lines changed: 41 additions & 1 deletion

File tree

workspaces/arborist/lib/arborist/isolated-reifier.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ module.exports = cls => class IsolatedReifier extends cls {
4545
isInStore,
4646
inBundle,
4747
isRegistryDependency: node.isRegistryDependency,
48+
isRootDependency: node.isRootDependency,
4849
location,
4950
name: node.packageName || node.name,
5051
optional: node.optional,
@@ -198,6 +199,10 @@ module.exports = cls => class IsolatedReifier extends cls {
198199
// Carry the source node's registry-dependency flag so the store node retains it.
199200
// IsolatedNode has no edges to recompute it from, and reify's registry-tarball allow-remote exemption depends on it.
200201
result.isRegistryDependency = node.isRegistryDependency
202+
// Same reasoning for allow-remote=root: the store node has no edgesIn, so capture from the source node whether it satisfies a valid edge from the project root or a workspace.
203+
result.isRootDependency = [...node.edgesIn].some(e =>
204+
e.valid && (e.from?.isProjectRoot || e.from?.isWorkspace)
205+
)
201206
return result
202207
}
203208

workspaces/arborist/lib/arborist/reify.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -744,7 +744,8 @@ module.exports = cls => class Reifier extends cls {
744744
integrity: node.integrity,
745745
// A node counts as "root" for allow-* enforcement if it satisfies at least one valid dependency edge declared by the project root or a workspace.
746746
// node.parent is unsafe here: after hoisting, transitive packages can have the project root as their tree parent.
747-
_isRoot: [...node.edgesIn].some(e =>
747+
// In the linked strategy the store node has no edgesIn, so isolated-reifier precomputes isRootDependency from the source node's edges.
748+
_isRoot: node.isRootDependency || [...node.edgesIn].some(e =>
748749
e.valid && (e.from?.isProjectRoot || e.from?.isWorkspace)
749750
),
750751
// pacote's npa re-parses our `name@URL` spec as type=remote, so allowRemote would mis-fire on registry tarballs.

workspaces/arborist/lib/isolated-classes.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ class IsolatedNode {
2222
isInStore = false
2323
inBundle = false
2424
isRegistryDependency = false
25+
isRootDependency = false
2526
linksIn = new Set()
2627
meta = { loadedFromDisk: false }
2728
optional = false
@@ -55,6 +56,9 @@ class IsolatedNode {
5556
if (options.isRegistryDependency) {
5657
this.isRegistryDependency = true
5758
}
59+
if (options.isRootDependency) {
60+
this.isRootDependency = true
61+
}
5862
if (options.optional) {
5963
this.optional = true
6064
}

workspaces/arborist/test/arborist/reify.js

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4027,6 +4027,36 @@ t.test('should preserve exact ranges, missing actual tree', async (t) => {
40274027
await t.resolves(arb.reify(), 'registry tarball is allowed under linked strategy')
40284028
})
40294029

4030+
t.test('allowRemote=root allows root-direct remote tarball under linked install strategy', async t => {
4031+
// The linked strategy extracts store nodes as IsolatedNode, which has no edgesIn to recompute root-ness from.
4032+
// isRootDependency must be carried from the source tree node, otherwise allow-remote=root mis-fires on a genuine remote tarball that is a direct dep of the project root.
4033+
const testdir = t.testdir({
4034+
project: {
4035+
'package.json': JSON.stringify({
4036+
name: 'myproject',
4037+
version: '1.0.0',
4038+
dependencies: {
4039+
abbrev: 'https://remote.example.com/abbrev-1.1.1.tgz',
4040+
},
4041+
}),
4042+
},
4043+
})
4044+
4045+
tnock(t, 'https://remote.example.com')
4046+
.get('/abbrev-1.1.1.tgz')
4047+
.reply(200, abbrevTGZ)
4048+
4049+
const arb = new Arborist({
4050+
path: resolve(testdir, 'project'),
4051+
registry: 'https://registry.example.com',
4052+
cache: resolve(testdir, 'cache'),
4053+
allowRemote: 'root',
4054+
installStrategy: 'linked',
4055+
})
4056+
4057+
await t.resolves(arb.reify(), 'root-direct remote tarball is allowed under linked strategy with allow-remote=root')
4058+
})
4059+
40304060
t.test('registry with different protocol should swap protocol', async (t) => {
40314061
const abbrevPackument4 = JSON.stringify({
40324062
_id: 'abbrev',

0 commit comments

Comments
 (0)