-
Notifications
You must be signed in to change notification settings - Fork 4.6k
Expand file tree
/
Copy pathunreviewed-scripts.js
More file actions
107 lines (96 loc) · 3.96 KB
/
Copy pathunreviewed-scripts.js
File metadata and controls
107 lines (96 loc) · 3.96 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
const { isScriptAllowed, isBundledByDependency } = require('./script-allowed.js')
const getInstallScripts = require('./install-scripts.js')
// Shared allowScripts walk used by both the npm CLI
// (lib/utils/check-allow-scripts.js, lib/utils/strict-allow-scripts-preflight.js)
// and libnpmexec (npm exec / npx). It lives in arborist because that is the
// only package both callers can import.
//
// Walks a tree's inventory and returns the dep nodes that have
// install-relevant lifecycle scripts and are not yet covered (or explicitly
// denied) by the allowScripts policy.
//
// Returns an array of `{ node, scripts }` entries. `scripts` is an object
// describing the relevant lifecycle scripts that would run.
const collectUnreviewedScripts = async ({
tree,
policy,
ignoreScripts = false,
dangerouslyAllowAllScripts = false,
includeWhenIgnored = false,
} = {}) => {
// With ignore-scripts set, no scripts run, so execution callers bail out
// here. approve/deny pass includeWhenIgnored so they keep listing
// unreviewed packages, which is what you need to move from a blanket
// ignore-scripts to an allowlist. Listing never runs anything.
if ((ignoreScripts && !includeWhenIgnored) || dangerouslyAllowAllScripts) {
return []
}
if (!tree?.inventory) {
return []
}
const resolvedPolicy = policy || null
const unreviewed = []
for (const node of tree.inventory.values()) {
if (node.isProjectRoot || node.isWorkspace) {
continue
}
if (node.isLink) {
// Linked workspace dependencies are managed by the workspace owner.
continue
}
if (isBundledByDependency(node)) {
// Dependencies bundled inside another package's tarball never run
// their install scripts and cannot be allowlisted, so they are never
// "pending". Skipping them keeps them out of the advisory warning
// and out of strict-allow-scripts. A package that needs a bundled
// dep's script must forward it as one of its own lifecycle scripts.
//
// Uses isBundledByDependency (not node.inBundle): the root
// project's bundleDependencies list only controls what ships in
// the root's published tarball; at install time those deps come
// from the registry like any other direct dep, so their install
// scripts must still surface as pending review (npm/cli#9679).
continue
}
if (node.inert) {
// Inert = an optional dep that can't be installed here (failed the
// os/cpu/libc or engine check, or failed to load). reify drops it
// before any script runs, so its install scripts never execute and it
// must not be flagged (npm/cli#9562).
continue
}
const verdict = isScriptAllowed(node, resolvedPolicy)
if (verdict === true || verdict === false) {
continue
}
const scripts = await getInstallScripts(node)
if (Object.keys(scripts).length === 0) {
continue
}
unreviewed.push({ node, scripts })
}
return unreviewed
}
// Builds the `ESTRICTALLOWSCRIPTS` error thrown by the strict-mode preflight
// from a list of `{ node, scripts }` entries. `remediation` is the
// caller-specific guidance appended after the package list (npm install vs
// npm exec have different remediation commands).
const strictAllowScriptsError = (unreviewed, { remediation } = {}) => {
const lines = unreviewed.map(({ node, scripts }) => {
const events = Object.entries(scripts)
.map(([event, body]) => `${event}: ${body}`)
.join('; ')
const name = node.package?.name || node.name
const version = node.package?.version || ''
const label = version ? `${name}@${version}` : name
return ` ${label} (${events})`
}).join('\n')
return Object.assign(
new Error(
`--strict-allow-scripts: ${unreviewed.length} package(s) have install ` +
`scripts not covered by allowScripts:\n${lines}\n${remediation}`
),
{ code: 'ESTRICTALLOWSCRIPTS' }
)
}
module.exports = { collectUnreviewedScripts, strictAllowScriptsError }