@npmcli/arborist 5.0.3 → 5.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -0
- package/lib/arborist/build-ideal-tree.js +22 -2
- package/lib/arborist/load-virtual.js +3 -2
- package/lib/arborist/reify.js +1 -1
- package/lib/audit-report.js +36 -39
- package/lib/edge.js +5 -0
- package/lib/node.js +3 -0
- package/lib/retire-path.js +1 -1
- package/lib/shrinkwrap.js +2 -2
- package/lib/tracker.js +7 -3
- package/lib/version-from-tgz.js +1 -1
- package/package.json +19 -21
package/README.md
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
# @npmcli/arborist
|
|
2
2
|
|
|
3
|
+
[](https://npm.im/@npmcli/arborist)
|
|
4
|
+
[](https://npm.im/@npmcli/arborist)
|
|
5
|
+
[](https://github.com/npm/cli/actions/workflows/ci-npmcli-arborist.yml)
|
|
6
|
+
|
|
3
7
|
Inspect and manage `node_modules` trees.
|
|
4
8
|
|
|
5
9
|

|
|
@@ -355,6 +355,21 @@ Try using the package name instead, e.g:
|
|
|
355
355
|
})
|
|
356
356
|
|
|
357
357
|
.then(tree => {
|
|
358
|
+
// search the virtual tree for invalid edges, if any are found add their source to
|
|
359
|
+
// the depsQueue so that we'll fix it later
|
|
360
|
+
depth({
|
|
361
|
+
tree,
|
|
362
|
+
getChildren: (node) => [...node.edgesOut.values()].map(edge => edge.to),
|
|
363
|
+
filter: node => node,
|
|
364
|
+
visit: node => {
|
|
365
|
+
for (const edge of node.edgesOut.values()) {
|
|
366
|
+
if (!edge.valid) {
|
|
367
|
+
this[_depsQueue].push(node)
|
|
368
|
+
break // no need to continue the loop after the first hit
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
},
|
|
372
|
+
})
|
|
358
373
|
// null the virtual tree, because we're about to hack away at it
|
|
359
374
|
// if you want another one, load another copy.
|
|
360
375
|
this.idealTree = tree
|
|
@@ -743,6 +758,12 @@ This is a one-time fix-up, please be patient...
|
|
|
743
758
|
continue
|
|
744
759
|
}
|
|
745
760
|
|
|
761
|
+
// if the node's location isn't within node_modules then this is actually
|
|
762
|
+
// a link target, so skip it. the link node itself will be queued later.
|
|
763
|
+
if (!node.location.startsWith('node_modules')) {
|
|
764
|
+
continue
|
|
765
|
+
}
|
|
766
|
+
|
|
746
767
|
queue.push(async () => {
|
|
747
768
|
log.silly('inflate', node.location)
|
|
748
769
|
const { resolved, version, path, name, location, integrity } = node
|
|
@@ -750,8 +771,7 @@ This is a one-time fix-up, please be patient...
|
|
|
750
771
|
const useResolved = resolved && (
|
|
751
772
|
!version || resolved.startsWith('file:')
|
|
752
773
|
)
|
|
753
|
-
const id = useResolved ? resolved
|
|
754
|
-
: version || `file:${node.path}`
|
|
774
|
+
const id = useResolved ? resolved : version
|
|
755
775
|
const spec = npa.resolve(name, id, dirname(path))
|
|
756
776
|
const t = `idealTree:inflate:${location}`
|
|
757
777
|
this.addTracker(t)
|
|
@@ -79,7 +79,7 @@ module.exports = cls => class VirtualLoader extends cls {
|
|
|
79
79
|
async [loadRoot] (s) {
|
|
80
80
|
const pj = this.path + '/package.json'
|
|
81
81
|
const pkg = await rpj(pj).catch(() => s.data.packages['']) || {}
|
|
82
|
-
return this[loadWorkspaces](this[loadNode]('', pkg))
|
|
82
|
+
return this[loadWorkspaces](this[loadNode]('', pkg, true))
|
|
83
83
|
}
|
|
84
84
|
|
|
85
85
|
async [loadFromShrinkwrap] (s, root) {
|
|
@@ -264,7 +264,7 @@ module.exports = cls => class VirtualLoader extends cls {
|
|
|
264
264
|
}
|
|
265
265
|
}
|
|
266
266
|
|
|
267
|
-
[loadNode] (location, sw) {
|
|
267
|
+
[loadNode] (location, sw, loadOverrides) {
|
|
268
268
|
const p = this.virtualTree ? this.virtualTree.realpath : this.path
|
|
269
269
|
const path = resolve(p, location)
|
|
270
270
|
// shrinkwrap doesn't include package name unless necessary
|
|
@@ -290,6 +290,7 @@ module.exports = cls => class VirtualLoader extends cls {
|
|
|
290
290
|
optional,
|
|
291
291
|
devOptional,
|
|
292
292
|
peer,
|
|
293
|
+
loadOverrides,
|
|
293
294
|
})
|
|
294
295
|
// cast to boolean because they're undefined in the lock file when false
|
|
295
296
|
node.extraneous = !!sw.extraneous
|
package/lib/arborist/reify.js
CHANGED
|
@@ -1308,7 +1308,7 @@ module.exports = cls => class Reifier extends cls {
|
|
|
1308
1308
|
// to only names that are found in this list
|
|
1309
1309
|
const retrieveUpdatedNodes = names => {
|
|
1310
1310
|
const filterDirectDependencies = node =>
|
|
1311
|
-
!node.isRoot && node.resolveParent.isRoot
|
|
1311
|
+
!node.isRoot && node.resolveParent && node.resolveParent.isRoot
|
|
1312
1312
|
&& (!names || names.includes(node.name))
|
|
1313
1313
|
&& exactVersion(node) // skip update for exact ranges
|
|
1314
1314
|
|
package/lib/audit-report.js
CHANGED
|
@@ -134,16 +134,7 @@ class AuditReport extends Map {
|
|
|
134
134
|
const seen = new Set()
|
|
135
135
|
for (const advisory of advisories) {
|
|
136
136
|
const { name, range } = advisory
|
|
137
|
-
|
|
138
|
-
// don't flag the exact same name/range more than once
|
|
139
|
-
// adding multiple advisories with the same range is fine, but no
|
|
140
|
-
// need to search for nodes we already would have added.
|
|
141
137
|
const k = `${name}@${range}`
|
|
142
|
-
if (seen.has(k)) {
|
|
143
|
-
continue
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
seen.add(k)
|
|
147
138
|
|
|
148
139
|
const vuln = this.get(name) || new Vuln({ name, advisory })
|
|
149
140
|
if (this.has(name)) {
|
|
@@ -151,44 +142,50 @@ class AuditReport extends Map {
|
|
|
151
142
|
}
|
|
152
143
|
super.set(name, vuln)
|
|
153
144
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
145
|
+
// don't flag the exact same name/range more than once
|
|
146
|
+
// adding multiple advisories with the same range is fine, but no
|
|
147
|
+
// need to search for nodes we already would have added.
|
|
148
|
+
if (!seen.has(k)) {
|
|
149
|
+
const p = []
|
|
150
|
+
for (const node of this.tree.inventory.query('packageName', name)) {
|
|
151
|
+
if (!shouldAudit(node, this[_omit], this.filterSet)) {
|
|
152
|
+
continue
|
|
153
|
+
}
|
|
159
154
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
155
|
+
// if not vulnerable by this advisory, keep searching
|
|
156
|
+
if (!advisory.testVersion(node.version)) {
|
|
157
|
+
continue
|
|
158
|
+
}
|
|
164
159
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
160
|
+
// we will have loaded the source already if this is a metavuln
|
|
161
|
+
if (advisory.type === 'metavuln') {
|
|
162
|
+
vuln.addVia(this.get(advisory.dependency))
|
|
163
|
+
}
|
|
169
164
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
165
|
+
// already marked this one, no need to do it again
|
|
166
|
+
if (vuln.nodes.has(node)) {
|
|
167
|
+
continue
|
|
168
|
+
}
|
|
174
169
|
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
170
|
+
// haven't marked this one yet. get its dependents.
|
|
171
|
+
vuln.nodes.add(node)
|
|
172
|
+
for (const { from: dep, spec } of node.edgesIn) {
|
|
173
|
+
if (dep.isTop && !vuln.topNodes.has(dep)) {
|
|
174
|
+
this[_checkTopNode](dep, vuln, spec)
|
|
175
|
+
} else {
|
|
181
176
|
// calculate a metavuln, if necessary
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
177
|
+
const calc = this.calculator.calculate(dep.packageName, advisory)
|
|
178
|
+
p.push(calc.then(meta => {
|
|
179
|
+
if (meta.testVersion(dep.version, spec)) {
|
|
180
|
+
advisories.add(meta)
|
|
181
|
+
}
|
|
182
|
+
}))
|
|
183
|
+
}
|
|
188
184
|
}
|
|
189
185
|
}
|
|
186
|
+
await Promise.all(p)
|
|
187
|
+
seen.add(k)
|
|
190
188
|
}
|
|
191
|
-
await Promise.all(p)
|
|
192
189
|
|
|
193
190
|
// make sure we actually got something. if not, remove it
|
|
194
191
|
// this can happen if you are loading from a lockfile created by
|
package/lib/edge.js
CHANGED
|
@@ -215,6 +215,11 @@ class Edge {
|
|
|
215
215
|
|
|
216
216
|
reload (hard = false) {
|
|
217
217
|
this[_explanation] = null
|
|
218
|
+
if (this[_from].overrides) {
|
|
219
|
+
this.overrides = this[_from].overrides.getEdgeRule(this)
|
|
220
|
+
} else {
|
|
221
|
+
delete this.overrides
|
|
222
|
+
}
|
|
218
223
|
const newTo = this[_from].resolve(this.name)
|
|
219
224
|
if (newTo !== this[_to]) {
|
|
220
225
|
if (this[_to]) {
|
package/lib/node.js
CHANGED
|
@@ -792,6 +792,9 @@ class Node {
|
|
|
792
792
|
target.root = root
|
|
793
793
|
}
|
|
794
794
|
|
|
795
|
+
if (!this.overrides && this.parent && this.parent.overrides) {
|
|
796
|
+
this.overrides = this.parent.overrides.getNodeRule(this)
|
|
797
|
+
}
|
|
795
798
|
// tree should always be valid upon root setter completion.
|
|
796
799
|
treeCheck(this)
|
|
797
800
|
treeCheck(root)
|
package/lib/retire-path.js
CHANGED
package/lib/shrinkwrap.js
CHANGED
|
@@ -807,7 +807,7 @@ class Shrinkwrap {
|
|
|
807
807
|
const pathFixed = !resolved ? null
|
|
808
808
|
: !/^file:/.test(resolved) ? resolved
|
|
809
809
|
// resolve onto the metadata path
|
|
810
|
-
: `file:${resolve(this.path, resolved.
|
|
810
|
+
: `file:${resolve(this.path, resolved.slice(5))}`
|
|
811
811
|
|
|
812
812
|
// if we have one, only set the other if it matches
|
|
813
813
|
// otherwise it could be for a completely different thing.
|
|
@@ -1056,7 +1056,7 @@ class Shrinkwrap {
|
|
|
1056
1056
|
// turn absolute file: paths into relative paths from the node
|
|
1057
1057
|
// this especially shows up with workspace edges when the root
|
|
1058
1058
|
// node is also a workspace in the set.
|
|
1059
|
-
const p = resolve(node.realpath, spec.
|
|
1059
|
+
const p = resolve(node.realpath, spec.slice('file:'.length))
|
|
1060
1060
|
set[k] = `file:${relpath(node.realpath, p)}`
|
|
1061
1061
|
} else {
|
|
1062
1062
|
set[k] = spec
|
package/lib/tracker.js
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
const _progress = Symbol('_progress')
|
|
2
2
|
const _onError = Symbol('_onError')
|
|
3
|
+
const _setProgress = Symbol('_setProgess')
|
|
3
4
|
const npmlog = require('npmlog')
|
|
4
5
|
|
|
5
6
|
module.exports = cls => class Tracker extends cls {
|
|
6
7
|
constructor (options = {}) {
|
|
7
8
|
super(options)
|
|
9
|
+
this[_setProgress] = !!options.progress
|
|
8
10
|
this[_progress] = new Map()
|
|
9
11
|
}
|
|
10
12
|
|
|
@@ -27,7 +29,7 @@ module.exports = cls => class Tracker extends cls {
|
|
|
27
29
|
// 1. no existing tracker, no subsection
|
|
28
30
|
// Create a new tracker from npmlog
|
|
29
31
|
// starts progress bar
|
|
30
|
-
if (this[_progress].size === 0) {
|
|
32
|
+
if (this[_setProgress] && this[_progress].size === 0) {
|
|
31
33
|
npmlog.enableProgress()
|
|
32
34
|
}
|
|
33
35
|
|
|
@@ -76,7 +78,7 @@ module.exports = cls => class Tracker extends cls {
|
|
|
76
78
|
|
|
77
79
|
// remove progress bar if all
|
|
78
80
|
// trackers are finished
|
|
79
|
-
if (this[_progress].size === 0) {
|
|
81
|
+
if (this[_setProgress] && this[_progress].size === 0) {
|
|
80
82
|
npmlog.disableProgress()
|
|
81
83
|
}
|
|
82
84
|
} else if (!hasTracker && subsection === null) {
|
|
@@ -92,7 +94,9 @@ module.exports = cls => class Tracker extends cls {
|
|
|
92
94
|
}
|
|
93
95
|
|
|
94
96
|
[_onError] (msg) {
|
|
95
|
-
|
|
97
|
+
if (this[_setProgress]) {
|
|
98
|
+
npmlog.disableProgress()
|
|
99
|
+
}
|
|
96
100
|
throw new Error(msg)
|
|
97
101
|
}
|
|
98
102
|
}
|
package/lib/version-from-tgz.js
CHANGED
|
@@ -16,7 +16,7 @@ module.exports = (name, tgz) => {
|
|
|
16
16
|
// basename checking. Note that registries can
|
|
17
17
|
// be mounted below the root url, so /a/b/-/x/y/foo/-/foo-1.2.3.tgz
|
|
18
18
|
// is a potential option.
|
|
19
|
-
const tfsplit = u.path.
|
|
19
|
+
const tfsplit = u.path.slice(1).split('/-/')
|
|
20
20
|
if (tfsplit.length > 1) {
|
|
21
21
|
const afterTF = tfsplit.pop()
|
|
22
22
|
if (afterTF === base) {
|
package/package.json
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@npmcli/arborist",
|
|
3
|
-
"version": "5.0.
|
|
3
|
+
"version": "5.0.6",
|
|
4
4
|
"description": "Manage node_modules trees",
|
|
5
5
|
"dependencies": {
|
|
6
6
|
"@isaacs/string-locale-compare": "^1.1.0",
|
|
7
7
|
"@npmcli/installed-package-contents": "^1.0.7",
|
|
8
8
|
"@npmcli/map-workspaces": "^2.0.0",
|
|
9
9
|
"@npmcli/metavuln-calculator": "^3.0.1",
|
|
10
|
-
"@npmcli/move-file": "^
|
|
10
|
+
"@npmcli/move-file": "^2.0.0",
|
|
11
11
|
"@npmcli/name-from-folder": "^1.0.1",
|
|
12
|
-
"@npmcli/node-gyp": "^
|
|
13
|
-
"@npmcli/package-json": "^
|
|
12
|
+
"@npmcli/node-gyp": "^2.0.0",
|
|
13
|
+
"@npmcli/package-json": "^2.0.0",
|
|
14
14
|
"@npmcli/run-script": "^3.0.0",
|
|
15
15
|
"bin-links": "^3.0.0",
|
|
16
16
|
"cacache": "^16.0.0",
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
"mkdirp": "^1.0.4",
|
|
21
21
|
"mkdirp-infer-owner": "^2.0.0",
|
|
22
22
|
"nopt": "^5.0.0",
|
|
23
|
-
"npm-install-checks": "^
|
|
23
|
+
"npm-install-checks": "^5.0.0",
|
|
24
24
|
"npm-package-arg": "^9.0.0",
|
|
25
25
|
"npm-pick-manifest": "^7.0.0",
|
|
26
26
|
"npm-registry-fetch": "^13.0.0",
|
|
@@ -34,17 +34,18 @@
|
|
|
34
34
|
"readdir-scoped-modules": "^1.1.0",
|
|
35
35
|
"rimraf": "^3.0.2",
|
|
36
36
|
"semver": "^7.3.5",
|
|
37
|
-
"ssri": "^
|
|
38
|
-
"treeverse": "^
|
|
37
|
+
"ssri": "^9.0.0",
|
|
38
|
+
"treeverse": "^2.0.0",
|
|
39
39
|
"walk-up-path": "^1.0.0"
|
|
40
40
|
},
|
|
41
41
|
"devDependencies": {
|
|
42
|
-
"@npmcli/
|
|
42
|
+
"@npmcli/eslint-config": "^3.0.1",
|
|
43
|
+
"@npmcli/template-oss": "3.3.2",
|
|
43
44
|
"benchmark": "^2.1.4",
|
|
44
45
|
"chalk": "^4.1.0",
|
|
45
46
|
"minify-registry-metadata": "^2.1.0",
|
|
46
47
|
"nock": "^13.2.0",
|
|
47
|
-
"tap": "^
|
|
48
|
+
"tap": "^16.0.1",
|
|
48
49
|
"tcompare": "^5.0.6"
|
|
49
50
|
},
|
|
50
51
|
"scripts": {
|
|
@@ -57,24 +58,24 @@
|
|
|
57
58
|
"postversion": "npm publish",
|
|
58
59
|
"prepublishOnly": "git push origin --follow-tags",
|
|
59
60
|
"eslint": "eslint",
|
|
60
|
-
"lint": "eslint
|
|
61
|
+
"lint": "eslint \"**/*.js\"",
|
|
61
62
|
"lintfix": "npm run lint -- --fix",
|
|
62
63
|
"benchmark": "node scripts/benchmark.js",
|
|
63
64
|
"benchclean": "rm -rf scripts/benchmark/*/",
|
|
64
65
|
"npmclilint": "npmcli-lint",
|
|
65
|
-
"postlint": "
|
|
66
|
-
"template-
|
|
66
|
+
"postlint": "template-oss-check",
|
|
67
|
+
"template-oss-apply": "template-oss-apply --force"
|
|
67
68
|
},
|
|
68
69
|
"repository": {
|
|
69
70
|
"type": "git",
|
|
70
|
-
"url": "https://github.com/npm/cli",
|
|
71
|
+
"url": "https://github.com/npm/cli.git",
|
|
71
72
|
"directory": "workspaces/arborist"
|
|
72
73
|
},
|
|
73
74
|
"author": "GitHub Inc.",
|
|
74
75
|
"license": "ISC",
|
|
75
76
|
"files": [
|
|
76
|
-
"bin",
|
|
77
|
-
"lib"
|
|
77
|
+
"bin/",
|
|
78
|
+
"lib/"
|
|
78
79
|
],
|
|
79
80
|
"main": "lib/index.js",
|
|
80
81
|
"bin": {
|
|
@@ -96,13 +97,10 @@
|
|
|
96
97
|
"timeout": "360"
|
|
97
98
|
},
|
|
98
99
|
"engines": {
|
|
99
|
-
"node": "^12.13.0 || ^14.15.0 || >=16"
|
|
100
|
+
"node": "^12.13.0 || ^14.15.0 || >=16.0.0"
|
|
100
101
|
},
|
|
101
|
-
"eslintIgnore": [
|
|
102
|
-
"test/fixtures/",
|
|
103
|
-
"!test/fixtures/*.js"
|
|
104
|
-
],
|
|
105
102
|
"templateOSS": {
|
|
106
|
-
"
|
|
103
|
+
"//@npmcli/template-oss": "This file is partially managed by @npmcli/template-oss. Edits may be overwritten.",
|
|
104
|
+
"version": "3.3.2"
|
|
107
105
|
}
|
|
108
106
|
}
|