/
githubmirror
/
node
Обзор
Документация
Войти
/
githubmirror
/
node
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
deps/npm/test/lib/utils/allow-scripts-writer.js
763 строки
27 KB
npm CLI robot
deps: upgrade npm to 11.18.0
08 июл 2026, 21:19
Не верифицирован
08 июл 2026, 21:19
fd35018
Код
Авторство
О чём код?
const t = require('tap') const path = require('node:path') const { applyApprovalForPackage, applyDenyForPackage, nameKeyFor, versionedKeyFor, isSingleVersionPin, } = require('../../../lib/utils/allow-scripts-writer.js') const node = (overrides = {}) => { const name = overrides.name ?? overrides.packageName ?? 'pkg' const packageName = overrides.packageName ?? name const version = overrides.version ?? '1.0.0' const urlPkg = packageName return { name, packageName, version, resolved: overrides.resolved ?? `https://registry.npmjs.org/${urlPkg}/-/${urlPkg}-${version}.tgz`, location: overrides.location ?? `node_modules/${name}`, isRegistryDependency: overrides.isRegistryDependency ?? true, } } // A registry node with no `resolved` URL in the lockfile. Its trusted name // comes from a dependency edge, but its version isn't trustable, so // versionedKeyFor returns null (npm/cli#9558). const noResolvedNode = (overrides = {}) => { const name = overrides.name ?? 'pkg' const version = overrides.version ?? '1.0.0' return { name, packageName: overrides.packageName ?? name, version, resolved: undefined, location: `node_modules/${name}`, isRegistryDependency: true, edgesIn: new Set([{ name, spec: overrides.spec ?? `^${version}` }]), } } t.test('nameKeyFor / versionedKeyFor — registry', async t => { const n = node({ name: 'canvas', version: '2.11.0' }) t.equal(nameKeyFor(n), 'canvas') t.equal(versionedKeyFor(n), 'canvas@2.11.0') }) t.test('nameKeyFor / versionedKeyFor — git', async t => { const n = node({ name: 'bar', resolved: 'git+ssh://git@github.com/foo/bar.git#deadbeefcafebabe1234567890abcdef12345678', }) t.equal(nameKeyFor(n), 'github:foo/bar') t.equal(versionedKeyFor(n), 'github:foo/bar#deadbeefcafebabe1234567890abcdef12345678') }) t.test('nameKeyFor / versionedKeyFor — file', async t => { const n = node({ name: 'local', resolved: 'file:../local' }) t.equal(nameKeyFor(n), 'file:../local') t.equal(versionedKeyFor(n), 'file:../local') }) t.test('nameKeyFor / versionedKeyFor — local directory link target', async t => { const targetPath = path.resolve('local') const n = { name: 'local', packageName: 'local', version: '1.0.0', resolved: null, path: targetPath, realpath: targetPath, linksIn: new Set([{ resolved: 'file:../local' }]), } t.equal(nameKeyFor(n), 'file:../local') t.equal(versionedKeyFor(n), 'file:../local') t.strictSame( applyApprovalForPackage({}, [n], { pin: true }).allowScripts, { 'file:../local': true } ) t.match( applyApprovalForPackage({ 'file:local': false }, [n], { pin: true }).warning, /denied|versioned deny/ ) }) t.test('nameKeyFor / versionedKeyFor — empty link target has no portable file key', async t => { const targetPath = path.resolve('local') const n = { name: 'local', packageName: 'local', version: '1.0.0', resolved: null, path: targetPath, realpath: targetPath, linksIn: new Set(), } t.equal(nameKeyFor(n), null) t.equal(versionedKeyFor(n), null) t.strictSame(applyApprovalForPackage({}, [n], { pin: true }).allowScripts, {}) }) t.test('isSingleVersionPin', async t => { t.ok(isSingleVersionPin('pkg@1.2.3')) t.notOk(isSingleVersionPin('pkg')) t.notOk(isSingleVersionPin('pkg@^1')) t.notOk(isSingleVersionPin('pkg@1.2.3 || 2.0.0')) t.notOk(isSingleVersionPin('@@@bad')) }) t.test('applyApprovalForPackage — empty allowScripts, --pin', async t => { const { allowScripts, changes } = applyApprovalForPackage( {}, [node({ name: 'canvas', version: '2.11.0' })], { pin: true } ) t.strictSame(allowScripts, { 'canvas@2.11.0': true }) t.strictSame(changes, [{ key: 'canvas@2.11.0', change: 'added' }]) }) t.test('applyApprovalForPackage — empty allowScripts, --no-pin', async t => { const { allowScripts, changes } = applyApprovalForPackage( {}, [node({ name: 'canvas', version: '2.11.0' })], { pin: false } ) t.strictSame(allowScripts, { canvas: true }) t.strictSame(changes, [{ key: 'canvas', change: 'added' }]) }) t.test('applyApprovalForPackage — stale pin rewritten to new installed version', async t => { const { allowScripts, changes } = applyApprovalForPackage( { 'canvas@2.10.0': true }, [node({ name: 'canvas', version: '2.11.0' })], { pin: true } ) t.strictSame(allowScripts, { 'canvas@2.11.0': true }) t.match(changes, [ { key: 'canvas@2.10.0', change: 'removed-stale' }, { key: 'canvas@2.11.0', change: 'added' }, ]) }) t.test('applyApprovalForPackage — multi-version disjunction is preserved', async t => { const { allowScripts } = applyApprovalForPackage( { 'canvas@2.10.0 || 2.11.0': true }, [node({ name: 'canvas', version: '2.11.0' })], { pin: true } ) t.strictSame(allowScripts, { 'canvas@2.10.0 || 2.11.0': true, 'canvas@2.11.0': true, }) }) t.test('applyApprovalForPackage — already-allowed exact version is a no-op', async t => { const { allowScripts, changes } = applyApprovalForPackage( { 'canvas@2.11.0': true }, [node({ name: 'canvas', version: '2.11.0' })], { pin: true } ) t.strictSame(allowScripts, { 'canvas@2.11.0': true }) t.strictSame(changes, []) }) t.test('applyApprovalForPackage — existing deny wins, returns warning', async t => { const { allowScripts, changes, warning } = applyApprovalForPackage( { canvas: false }, [node({ name: 'canvas', version: '2.11.0' })], { pin: true } ) t.strictSame(allowScripts, { canvas: false }) t.strictSame(changes, []) t.match(warning, /canvas is denied/) }) t.test('applyApprovalForPackage — versioned deny wins too', async t => { const { changes, warning } = applyApprovalForPackage( { 'canvas@2.11.0': false }, [node({ name: 'canvas', version: '2.11.0' })], { pin: true } ) t.strictSame(changes, []) t.match(warning, /denied|versioned deny/) }) t.test('applyApprovalForPackage — name-only existing, --no-pin no-op', async t => { const { allowScripts, changes } = applyApprovalForPackage( { canvas: true }, [node({ name: 'canvas', version: '2.11.0' })], { pin: false } ) t.strictSame(allowScripts, { canvas: true }) t.strictSame(changes, []) }) t.test('applyApprovalForPackage — --no-pin downgrades pinned entry to name-only', async t => { const { allowScripts } = applyApprovalForPackage( { 'canvas@2.10.0': true }, [node({ name: 'canvas', version: '2.11.0' })], { pin: false } ) t.strictSame(allowScripts, { canvas: true }) }) t.test('applyApprovalForPackage — multiple installed versions write multiple pins', async t => { const { allowScripts } = applyApprovalForPackage( {}, [ node({ name: 'lodash', version: '4.17.21' }), node({ name: 'lodash', version: '3.10.1' }), ], { pin: true } ) t.strictSame(allowScripts, { 'lodash@3.10.1': true, 'lodash@4.17.21': true }) }) t.test('applyApprovalForPackage — keeps existing pin matching one installed, adds pin for other', async t => { const { allowScripts } = applyApprovalForPackage( { 'lodash@4.17.21': true }, [ node({ name: 'lodash', version: '4.17.21' }), node({ name: 'lodash', version: '3.10.1' }), ], { pin: true } ) t.strictSame(allowScripts, { 'lodash@3.10.1': true, 'lodash@4.17.21': true }) }) t.test('versionedKeyFor — registry dep without a resolved URL has no trustable version', async t => { const n = noResolvedNode({ name: 'esbuild', version: '0.25.0' }) t.equal(nameKeyFor(n), 'esbuild', 'name still recoverable from the dependency edge') t.equal(versionedKeyFor(n), null, 'version cannot be trusted without a resolved URL') }) t.test('applyApprovalForPackage — pin mode falls back to name-only when version is not trustable', async t => { const { allowScripts, changes, warning } = applyApprovalForPackage( {}, [noResolvedNode({ name: 'esbuild', version: '0.25.0' })], { pin: true } ) t.strictSame(allowScripts, { esbuild: true }, 'approved by name instead of silently skipped') t.strictSame(changes, [{ key: 'esbuild', change: 'added' }]) t.match(warning, /no "resolved" URL/, 'explains why a pin was not written') }) t.test('applyApprovalForPackage — mixed resolved/no-resolved collapses to one name-only entry', async t => { const { allowScripts, warning } = applyApprovalForPackage( {}, [ node({ name: 'esbuild', version: '0.21.5' }), noResolvedNode({ name: 'esbuild', version: '0.25.0' }), ], { pin: true } ) t.strictSame(allowScripts, { esbuild: true }, 'no redundant pin alongside the name-only entry') t.match(warning, /no "resolved" URL/) }) t.test('applyApprovalForPackage — existing pin collapses into name-only when a sibling cannot be pinned', async t => { const { allowScripts, changes } = applyApprovalForPackage( { 'esbuild@0.21.5': true }, [ node({ name: 'esbuild', version: '0.21.5' }), noResolvedNode({ name: 'esbuild', version: '0.25.0' }), ], { pin: true } ) t.strictSame(allowScripts, { esbuild: true }) t.match(changes, [ { key: 'esbuild@0.21.5', change: 'removed-pinned-allow' }, { key: 'esbuild', change: 'added' }, ]) }) t.test('applyApprovalForPackage — no-resolved dep already approved by name is a no-op', async t => { const { allowScripts, changes } = applyApprovalForPackage( { esbuild: true }, [noResolvedNode({ name: 'esbuild', version: '0.25.0' })], { pin: true } ) t.strictSame(allowScripts, { esbuild: true }) t.strictSame(changes, []) }) t.test('applyApprovalForPackage — no-resolved dep with name-only deny still loses to the deny', async t => { const { allowScripts, changes, warning } = applyApprovalForPackage( { esbuild: false }, [noResolvedNode({ name: 'esbuild', version: '0.25.0' })], { pin: true } ) t.strictSame(allowScripts, { esbuild: false }, 'deny wins; nothing approved') t.strictSame(changes, []) t.match(warning, /esbuild is denied/) }) t.test('applyDenyForPackage — empty allowScripts adds name-only false', async t => { const { allowScripts, changes } = applyDenyForPackage( {}, [node({ name: 'core-js', version: '3.0.0' })] ) t.strictSame(allowScripts, { 'core-js': false }) t.strictSame(changes, [{ key: 'core-js', change: 'added' }]) }) t.test('applyDenyForPackage — pinned allow is replaced by name-only deny', async t => { const { allowScripts } = applyDenyForPackage( { 'core-js@3.0.0': true }, [node({ name: 'core-js', version: '3.0.0' })] ) t.strictSame(allowScripts, { 'core-js': false }) }) t.test('applyDenyForPackage — already-denied is a no-op', async t => { const { changes } = applyDenyForPackage( { 'core-js': false }, [node({ name: 'core-js', version: '3.0.0' })] ) t.strictSame(changes, []) }) t.test('applyDenyForPackage — name-only true is replaced by name-only false', async t => { const { allowScripts } = applyDenyForPackage( { 'core-js': true }, [node({ name: 'core-js', version: '3.0.0' })] ) t.strictSame(allowScripts, { 'core-js': false }) }) t.test('applyApprovalForPackage — preserves unrelated entries', async t => { const { allowScripts } = applyApprovalForPackage( { other: true, 'unrelated@1.0.0': false }, [node({ name: 'canvas', version: '2.11.0' })], { pin: true } ) t.strictSame(allowScripts, { other: true, 'unrelated@1.0.0': false, 'canvas@2.11.0': true, }) }) t.test('applyApprovalForPackage — git node writes hosted shortcut with commit', async t => { const { allowScripts } = applyApprovalForPackage( {}, [node({ name: 'bar', resolved: 'git+ssh://git@github.com/foo/bar.git#deadbeefcafebabe1234567890abcdef12345678', })], { pin: true } ) t.strictSame(allowScripts, { 'github:foo/bar#deadbeefcafebabe1234567890abcdef12345678': true, }) }) t.test('applyApprovalForPackage — git node --no-pin writes hosted shortcut without commit', async t => { const { allowScripts } = applyApprovalForPackage( {}, [node({ name: 'bar', resolved: 'git+ssh://git@github.com/foo/bar.git#deadbeef', })], { pin: false } ) t.strictSame(allowScripts, { 'github:foo/bar': true }) }) t.test('applyApprovalForPackage — file dep uses resolved as both keys', async t => { const { allowScripts } = applyApprovalForPackage( {}, [node({ name: 'local', resolved: 'file:../local' })], { pin: true } ) t.strictSame(allowScripts, { 'file:../local': true }) }) t.test('applyApprovalForPackage — empty nodes returns unchanged', async t => { const { allowScripts, changes } = applyApprovalForPackage({ x: true }, [], { pin: true }) t.strictSame(allowScripts, { x: true }) t.strictSame(changes, []) }) t.test('applyApprovalForPackage — name-only entry is replaced by pin (RFC table)', async t => { const { allowScripts, changes } = applyApprovalForPackage( { canvas: true }, [node({ name: 'canvas', version: '2.11.0' })], { pin: true } ) // Per RFC table: pkg: true + --pin must upgrade to pkg@x.y.z: true. // Both entries left behind would be wrong. t.strictSame(allowScripts, { 'canvas@2.11.0': true }) t.match(changes, [ { key: 'canvas@2.11.0', change: 'added' }, { key: 'canvas', change: 'replaced-by-pin' }, ]) }) t.test('applyApprovalForPackage — name-only + multi-version installs replaces with all pins', async t => { const { allowScripts } = applyApprovalForPackage( { lodash: true }, [ node({ name: 'lodash', version: '4.17.21' }), node({ name: 'lodash', version: '3.10.1' }), ], { pin: true } ) t.strictSame(allowScripts, { 'lodash@3.10.1': true, 'lodash@4.17.21': true }) }) t.test('applyApprovalForPackage — name-only is preserved when --no-pin', async t => { const { allowScripts, changes } = applyApprovalForPackage( { canvas: true }, [node({ name: 'canvas', version: '2.11.0' })], { pin: false } ) t.strictSame(allowScripts, { canvas: true }) t.strictSame(changes, []) }) t.test('applyApprovalForPackage — name-only NOT dropped when no pinning could happen', async t => { // Node has no version, so installedKeys is empty. The name-only entry // must NOT be dropped or we silently lose the policy. const noVersion = { name: 'pkg', packageName: 'pkg', version: undefined, resolved: 'https://registry.npmjs.org/pkg/-/pkg-1.tgz' } const { allowScripts } = applyApprovalForPackage( { pkg: true }, [noVersion], { pin: true } ) t.strictSame(allowScripts, { pkg: true }) }) t.test('applyApprovalForPackage — convergent: running twice gives the same result', async t => { // Start with stale state including a name-only entry. const start = { canvas: true, 'canvas@2.10.0': true } const nodes = [node({ name: 'canvas', version: '2.11.0' })] const run1 = applyApprovalForPackage(start, nodes, { pin: true }) const run2 = applyApprovalForPackage(run1.allowScripts, nodes, { pin: true }) t.strictSame(run1.allowScripts, { 'canvas@2.11.0': true }) t.strictSame(run2.allowScripts, { 'canvas@2.11.0': true }) t.strictSame(run2.changes, [], 'second run is a no-op') }) t.test('applyApprovalForPackage — deny still wins even when name-only is upgraded', async t => { const { allowScripts, warning } = applyApprovalForPackage( { canvas: true, 'canvas@2.11.0': false }, [node({ name: 'canvas', version: '2.11.0' })], { pin: true } ) // Existing deny on the version blocks the approval. t.strictSame(allowScripts, { canvas: true, 'canvas@2.11.0': false }) t.match(warning, /denied|versioned deny/) }) t.test('keyTargetsNode — unparseable key returns false (via applyApproval)', async t => { // An unparseable key in the existing object should be ignored. const { allowScripts } = applyApprovalForPackage( { '@@@invalid': true }, [node({ name: 'canvas', version: '2.11.0' })], { pin: true } ) t.equal(allowScripts['canvas@2.11.0'], true) t.equal(allowScripts['@@@invalid'], true) }) t.test('applyDenyForPackage — empty nodes array returns unchanged', async t => { const { allowScripts, changes } = applyDenyForPackage({ existing: true }, []) t.strictSame(allowScripts, { existing: true }) t.strictSame(changes, []) }) t.test('applyDenyForPackage — node with no nameable identity is a no-op', async t => { // A node whose resolved field is unparseable as a git URL and has no // version/name produces a null name; the writer must short-circuit. const weird = { name: '', packageName: '', version: undefined, resolved: undefined } const { allowScripts, changes } = applyDenyForPackage({}, [weird]) t.strictSame(allowScripts, {}) t.strictSame(changes, []) }) t.test('applyApprovalForPackage — file dep with deny entry blocks approval', async t => { const { warning } = applyApprovalForPackage( { 'file:../local': false }, [node({ name: 'local', resolved: 'file:../local' })], { pin: true } ) t.match(warning, /denied|versioned deny/) }) t.test('applyApprovalForPackage — remote tarball deny blocks approval', async t => { const remote = { name: 'pkg', packageName: 'pkg', version: '1.0.0', resolved: 'https://example.com/pkg.tgz' } const { warning } = applyApprovalForPackage( { 'https://example.com/pkg.tgz': false }, [remote], { pin: true } ) t.match(warning, /denied|versioned deny/) }) t.test('applyApprovalForPackage — no-pin with no name produces no-op', async t => { const weird = { name: '', packageName: '', resolved: 'git+ssh://no.parse' } const { allowScripts, changes } = applyApprovalForPackage({}, [weird], { pin: false }) t.strictSame(allowScripts, {}) t.strictSame(changes, []) }) t.test('applyApprovalForPackage — pin with no versioned key is a no-op', async t => { const noVersion = { name: 'pkg', packageName: 'pkg', version: undefined, resolved: 'https://registry.npmjs.org/pkg/-/pkg-1.tgz' } const { allowScripts, changes } = applyApprovalForPackage({}, [noVersion], { pin: true }) t.strictSame(allowScripts, {}) t.strictSame(changes, []) }) t.test('applyApprovalForPackage — pin with no versioned key and existing name-only is no-op', async t => { const noVersion = { name: 'pkg', packageName: 'pkg', version: undefined, resolved: 'https://registry.npmjs.org/pkg/-/pkg-1.tgz' } const { changes } = applyApprovalForPackage({ pkg: true }, [noVersion], { pin: true }) t.strictSame(changes, []) }) t.test('keyTargetsNode handles file with directory-typed key', async t => { // A "directory" spec for a relative path. const dirNode = { name: 'local', packageName: 'local', resolved: 'file:./local-dir' } const { allowScripts } = applyApprovalForPackage( {}, [dirNode], { pin: true } ) t.equal(allowScripts['file:./local-dir'], true) }) t.test('nameKeyFor / versionedKeyFor — null node', async t => { t.equal(nameKeyFor(null), null) t.equal(versionedKeyFor(null), null) }) t.test('nameKeyFor / versionedKeyFor — non-hosted git url returns null', async t => { const n = { name: 'pkg', packageName: 'pkg', resolved: 'git+https://example.invalid/foo/bar.git#abc' } t.equal(nameKeyFor(n), null) t.equal(versionedKeyFor(n), null) }) t.test('versionedKeyFor — absolute path resolved field', async t => { const n = { name: 'pkg', packageName: 'pkg', resolved: '/abs/path/local' } t.equal(versionedKeyFor(n), '/abs/path/local') t.equal(nameKeyFor(n), '/abs/path/local') }) t.test('applyApprovalForPackage — node.resolved parse error in keyTargetsNode is safe', async t => { // An existing git-style key for a package whose own resolved field // doesn't parse: the key just doesn't target anything. const gitNode = node({ name: 'bar', resolved: 'git+ssh://git@github.com/foo/bar.git#abc', }) // Add an explicit unparseable existing entry. const { allowScripts } = applyApprovalForPackage( { 'github:other/other': true }, [gitNode], { pin: true } ) // Existing entry unchanged; new git entry added. t.equal(allowScripts['github:other/other'], true) t.equal(allowScripts['github:foo/bar#abc'], true) }) t.test('keyTargetsNode — alias key does not target anything (via writer)', async t => { // Alias-typed key falls through the switch default. const { allowScripts } = applyApprovalForPackage( { 'foo@npm:bar@1.0.0': true }, [node({ name: 'foo', packageName: 'foo', version: '1.0.0' })], { pin: true } ) // Alias entry untouched, new pin added separately. t.equal(allowScripts['foo@npm:bar@1.0.0'], true) t.equal(allowScripts['foo@1.0.0'], true) }) t.test('keyTargetsNode handles tag-type key', async t => { // 'canvas@latest' parses as type='tag'. The writer should treat it like // a name-only match (any installed version of canvas). const { allowScripts } = applyApprovalForPackage( { 'canvas@latest': true }, [node({ name: 'canvas', version: '2.11.0' })], { pin: true } ) // The tag key targets the canvas node (same package name), so the // 'canvas@2.11.0' pin gets added; tag key is preserved. t.equal(allowScripts['canvas@latest'], true) t.equal(allowScripts['canvas@2.11.0'], true) }) t.test('keyTargetsNode handles file-type tarball key matching saveSpec', async t => { // 'file:pkg.tgz' parses as type='file' with saveSpec='file:pkg.tgz'. const tarballNode = { name: 'pkg', packageName: 'pkg', version: '1.0.0', resolved: 'file:pkg.tgz', } const { allowScripts } = applyApprovalForPackage( { 'file:pkg.tgz': false }, [tarballNode], { pin: true } ) // saveSpec match: deny wins, no pin added. t.equal(allowScripts['file:pkg.tgz'], false) }) t.test('keyTargetsNode handles file-type tarball key matching fetchSpec', async t => { // When node.resolved is an absolute path matching parsed.fetchSpec. // Use path.resolve so the absolute path is platform-correct (npa // parses POSIX-style `/abs/...` as a directory on Windows). const absTgz = path.resolve('pkg.tgz') const tarballNode = { name: 'pkg', packageName: 'pkg', version: '1.0.0', resolved: absTgz, } const { allowScripts, warning } = applyApprovalForPackage( { './pkg.tgz': false }, [tarballNode], { pin: true } ) t.equal(allowScripts['./pkg.tgz'], false) t.match(warning, /denied|versioned deny/) }) t.test('versionedKeyFor — git node without committish', async t => { // versionedKeyFor's ternary takes the "no committish" branch. t.equal( versionedKeyFor({ name: 'bar', resolved: 'git+ssh://git@github.com/foo/bar.git', }), 'github:foo/bar' ) }) t.test('versionedKeyFor / nameKeyFor — absolute path resolved field', async t => { // Hits the `resolved.startsWith('/')` branch in both helpers. const n = { name: 'pkg', packageName: 'pkg', resolved: '/abs/local-dir' } t.equal(versionedKeyFor(n), '/abs/local-dir') t.equal(nameKeyFor(n), '/abs/local-dir') }) t.test('keyTargetsNode — git key against a node with no resolved field', async t => { // Defensive: if existing has a git-shaped key and the installed node // has no resolved field, keyTargetsNode bails out and no policy entry // can be derived from untrusted sources. const noResolved = { name: 'bar', packageName: 'bar', resolved: undefined } const { allowScripts } = applyApprovalForPackage( { 'github:foo/bar': true }, [noResolved], { pin: false } ) // Existing entry untouched. No new key written: nameKeyFor returns // null for a node with no trusted identity source. t.equal(allowScripts['github:foo/bar'], true) t.notOk('bar' in allowScripts, 'no entry written under attacker-controlled node.name') }) t.test('applyApprovalForPackage — default args (no options object)', async t => { // Hits the `{ pin = true } = {}` default-arg branch. const { allowScripts } = applyApprovalForPackage( {}, [node({ name: 'canvas', version: '2.11.0' })] ) t.strictSame(allowScripts, { 'canvas@2.11.0': true }) }) t.test('applyApprovalForPackage — deny-wins warning when node has no name', async t => { // Hits the `name || 'this package'` fallback in the warning message. const noName = { name: '', packageName: '', resolved: 'git+ssh://no.parse' } const { warning } = applyApprovalForPackage( { 'github:foo/bar': false }, [noName], { pin: true } ) // No keys target this node (its resolved doesn't parse to a hosted URL), // so deny-wins doesn't trigger. Result is no warning. t.notOk(warning) }) t.test('denyWarning branches on key shape per RFC §approve-scripts', async t => { // Name-only deny: only remedy is to remove the entry. const nameOnly = applyApprovalForPackage( { canvas: false }, [node({ name: 'canvas', version: '2.11.0' })], { pin: true } ) t.match(nameOnly.warning, /remove the entry from allowScripts/) t.notMatch(nameOnly.warning, /widen the deny/) // Pinned deny on a different version: suggest both widen and remove. const pinned = applyApprovalForPackage( { 'canvas@2.10.0': false }, [node({ name: 'canvas', version: '2.10.0' })], { pin: true } ) t.match(pinned.warning, /versioned deny/) t.match(pinned.warning, /npm install-scripts deny canvas/) t.match(pinned.warning, /widen the deny to all versions/) t.match(pinned.warning, /remove the entry/) // Multi-version deny disjunction: same as pinned (versioned). const multi = applyApprovalForPackage( { 'canvas@2.10.0 || 2.11.0': false }, [node({ name: 'canvas', version: '2.10.0' })], { pin: true } ) t.match(multi.warning, /versioned deny/) t.match(multi.warning, /npm install-scripts deny canvas/) }) t.test('denyWarning: tag-type key (pkg@latest: false) is name-only', async t => { // `canvas@latest` parses as type='tag'. Treat the same as a bare name. const { warning } = applyApprovalForPackage( { 'canvas@latest': false }, [node({ name: 'canvas', version: '2.11.0' })], { pin: true } ) t.match(warning, /remove the entry/) t.notMatch(warning, /versioned deny/) }) t.test('applyApprovalForPackage — multi-version entry + --pin=false adds name-only alongside', async t => { // RFC table: existing `pkg@a.b.c || d.e.f: true` + installed `pkg@x.y.z` // + --pin=false adds `pkg: true`. The multi-version disjunction stays // (it captures intent the command can't infer), and the name-only // entry is added. const { allowScripts } = applyApprovalForPackage( { 'canvas@1.0.0 || 2.0.0': true }, [node({ name: 'canvas', version: '3.0.0' })], { pin: false } ) t.strictSame(allowScripts, { 'canvas@1.0.0 || 2.0.0': true, canvas: true, }) }) t.test('versionedKeyFor — registry resolved that versionFromTgz cannot parse returns null', async t => { // Private-registry mirror / alternate CDN URL shape that doesn't match // the standard `/-/name-version.tgz` pattern. Exercises the log.silly // breadcrumb path in versionedKeyFor, including each fallback branch // of the `node.path || node.name || '<unknown>'` label expression. const resolved = 'https://private-mirror.example.com/blobs/abc123' t.equal(versionedKeyFor({ path: '/fake/mystery', name: 'mystery', resolved, isRegistryDependency: true, }), null, 'falls back when node has a path') t.equal(versionedKeyFor({ name: 'mystery', resolved, isRegistryDependency: true, }), null, 'falls back when node has only a name') t.equal(versionedKeyFor({ resolved, isRegistryDependency: true, }), null, 'falls back when node has neither path nor name') })