Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions lib/utils/allow-scripts-writer.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ const npa = require('npm-package-arg')
const { log } = require('proc-log')
const {
getTrustedRegistryIdentity,
matchesResolvedSource,
resolvedSourceSpecs,
} = require('@npmcli/arborist/lib/script-allowed.js')

Expand Down Expand Up @@ -180,8 +181,7 @@ const keyTargetsNode = (key, node) => {
case 'file':
case 'directory':
case 'remote':
return resolvedSourceSpecs(node)
.some(resolved => resolved === parsed.saveSpec || resolved === parsed.fetchSpec)
return matchesResolvedSource(node, parsed)
default:
return false
}
Expand Down
21 changes: 12 additions & 9 deletions workspaces/arborist/lib/script-allowed.js
Original file line number Diff line number Diff line change
Expand Up @@ -85,9 +85,8 @@ const matches = (node, key, failClosed) => {
return matchGit(node, parsed)
case 'file':
case 'directory':
return matchFileOrDir(node, parsed)
case 'remote':
return matchRemote(node, parsed)
return matchesResolvedSource(node, parsed)
case 'alias':
// Disallowed: aliases as policy keys do not match anything.
// The user has to address the real package name.
Expand Down Expand Up @@ -327,14 +326,17 @@ const matchGit = (node, parsed) => {
return nodeCommittish.startsWith(keyCommittish)
}

const matchFileOrDir = (node, parsed) => {
// A local dep's `resolved` is built by consistent-resolve.js as `file:`
// glued onto the absolute fetchSpec. npa keeps saveSpec in the form the key
// was written in and always forward-slashes it, so a relative key never
// equals that spec, and on Windows no key form does. Relative keys resolve
// against the cwd npa was called from, the project root for `npm install`.
const matchesResolvedSource = (node, parsed) => {
const absoluteFileSpec = `file:${parsed.fetchSpec}`
return resolvedSourceSpecs(node)
.some(resolved => resolved === parsed.saveSpec || resolved === parsed.fetchSpec)
}

const matchRemote = (node, parsed) => {
return resolvedSourceSpecs(node)
.some(resolved => resolved === parsed.fetchSpec || resolved === parsed.saveSpec)
.some(resolved => resolved === parsed.saveSpec ||
resolved === parsed.fetchSpec ||
resolved === absoluteFileSpec)
}

const isRegistryNode = (node) => {
Expand Down Expand Up @@ -381,4 +383,5 @@ module.exports.matches = matches
module.exports.isExactVersionDisjunction = isExactVersionDisjunction
module.exports.getTrustedRegistryIdentity = getTrustedRegistryIdentity
module.exports.resolvedSourceSpecs = resolvedSourceSpecs
module.exports.matchesResolvedSource = matchesResolvedSource
module.exports.trustedDisplay = trustedDisplay
19 changes: 18 additions & 1 deletion workspaces/arborist/test/script-allowed.js
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,7 @@ t.test('directory key — npa parses absolute paths as type=directory', t => {

t.test('local tarball key — npa parses *.tgz paths as type=file', t => {
// npa treats `*.tgz` paths as { type: 'file' }, separate from
// 'directory'. Both share the matchFileOrDir body.
// 'directory'. Both share the matchesResolvedSource body.
const tgzNode = node({
name: 'local-pkg',
packageName: 'local-pkg',
Expand All @@ -196,6 +196,23 @@ t.test('local tarball key — npa parses *.tgz paths as type=file', t => {
t.end()
})

t.test('local tarball key — relative key matches an absolute resolved', t => {
// The installed tree carries the absolutized `file:` spec that
// consistent-resolve.js builds, while the allowScripts key keeps the
// relative form the user wrote.
const tgzPath = require('node:path').resolve('local-pkg.tgz')
const tgzNode = node({
name: 'local-pkg',
packageName: 'local-pkg',
version: '1.0.0',
resolved: `file:${tgzPath}`,
})
t.equal(isScriptAllowed(tgzNode, { 'file:local-pkg.tgz': true }), true)
t.equal(isScriptAllowed(tgzNode, { [`file:${tgzPath}`]: true }), true)
t.equal(isScriptAllowed(tgzNode, { 'file:other-pkg.tgz': true }), null)
t.end()
})

t.test('remote tarball — exact resolved match', t => {
const remoteNode = node({
name: 'pkg',
Expand Down
Loading