dependabot-npm_and_yarn 0.373.0 → 0.374.0
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.
- checksums.yaml +4 -4
- data/helpers/lib/npm/vulnerability-auditor.ts +24 -13
- data/helpers/test/npm/fixtures/vulnerability-auditor/fix-hoists-package/package-lock.json +37 -0
- data/helpers/test/npm/fixtures/vulnerability-auditor/fix-hoists-package/package.json +13 -0
- data/helpers/test/npm/fixtures/vulnerability-auditor/fix-removes-package/package-lock.json +30 -0
- data/helpers/test/npm/fixtures/vulnerability-auditor/fix-removes-package/package.json +12 -0
- data/helpers/test/npm/fixtures/vulnerability-auditor/outdated-package-lock/package-lock.json +37 -0
- data/helpers/test/npm/fixtures/vulnerability-auditor/outdated-package-lock/package.json +13 -0
- data/helpers/test/npm/vulnerability-auditor.test.ts +134 -13
- data/lib/dependabot/npm_and_yarn/dependency_grapher.rb +132 -4
- data/lib/dependabot/npm_and_yarn/file_parser/json_lock.rb +1 -4
- metadata +10 -4
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 6d73a3111c08214ea172fa372dc8a3170c06e320ff7584da2b3bbf874de01b53
|
|
4
|
+
data.tar.gz: 64dde7b56d164827bd2a9ad46665b52bd97ffa93cfa35032ef03f0b827f344dc
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: dd062f7ceca3feef8ac45235c5063c45a990347cd31c6702d4fbe377a932398ee556cf94124ca0db27bcdaea9f01ec55b1cdf16d6fb498c158c66fb8735c84ce
|
|
7
|
+
data.tar.gz: d84403b98bf265e074e087a386441929bfd66fdf01a5339503e8c93977107577f2b84b00330f6a049b7c594da104bc3367be36f4c44ff542704e415dfed3a125
|
|
@@ -222,7 +222,7 @@ export async function findVulnerableDependencies(
|
|
|
222
222
|
return left.localeCompare(right);
|
|
223
223
|
});
|
|
224
224
|
const remainingVulnerableTargets = vulnLocations
|
|
225
|
-
.map((loc) =>
|
|
225
|
+
.map((loc) => resolveNodeFromLocation(fixTree, loc)?.version)
|
|
226
226
|
.filter((version): version is string => typeof version === "string");
|
|
227
227
|
|
|
228
228
|
// If any originally vulnerable location still exists after applying the
|
|
@@ -233,7 +233,7 @@ export async function findVulnerableDependencies(
|
|
|
233
233
|
remainingVulnerableTargets[0] ?? fixTree.children.get(name)?.version;
|
|
234
234
|
|
|
235
235
|
for (const update of response.fix_updates) {
|
|
236
|
-
update.target_version =
|
|
236
|
+
update.target_version = resolveNodeFromLocation(
|
|
237
237
|
fixTree,
|
|
238
238
|
update.dependency_location!
|
|
239
239
|
)?.version;
|
|
@@ -358,12 +358,17 @@ function groupBy<T>(
|
|
|
358
358
|
}
|
|
359
359
|
|
|
360
360
|
/**
|
|
361
|
-
*
|
|
362
|
-
*
|
|
363
|
-
* path to the
|
|
364
|
-
* ArboristNode.location
|
|
361
|
+
* Resolve the node at the given location in the tree rooted at `root`, returning
|
|
362
|
+
* null if the location is invalid or doesn't exist in the tree. The location is a
|
|
363
|
+
* string containing the normalized path to the node, relative to the root, and can
|
|
364
|
+
* be taken from ArboristNode.location. It will be of the form "node_modules/foo/node_modules/bar".
|
|
365
|
+
*
|
|
366
|
+
* If the exact package location does not exist in the tree, the function will use
|
|
367
|
+
* Arborist's resolve method to attempt to resolve the target package from the furthest
|
|
368
|
+
* existing ancestor location. This allows us to handle cases where the fixed package
|
|
369
|
+
* is hoisted to a higher level in the tree than the vulnerable package was originally located.
|
|
365
370
|
*/
|
|
366
|
-
function
|
|
371
|
+
function resolveNodeFromLocation(
|
|
367
372
|
root: Arborist.Node,
|
|
368
373
|
location: string
|
|
369
374
|
): Arborist.Node | Arborist.Link | null {
|
|
@@ -374,14 +379,20 @@ function lookupChildLocation(
|
|
|
374
379
|
const parts = location
|
|
375
380
|
.substring("node_modules/".length)
|
|
376
381
|
.split("/node_modules/");
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
382
|
+
const target = parts[parts.length - 1];
|
|
383
|
+
let current: Arborist.Node | Arborist.Link = root;
|
|
384
|
+
for (const pkgName of parts) {
|
|
385
|
+
const next: Arborist.Node | Arborist.Link | undefined =
|
|
386
|
+
current.children.get(pkgName);
|
|
387
|
+
if (!next) {
|
|
388
|
+
// This is the furthest we can resolve down the tree based on the given location.
|
|
389
|
+
// Ask the tree to resolve the target node.
|
|
390
|
+
return current.resolve(target) as Arborist.Node | null;
|
|
381
391
|
}
|
|
382
|
-
current =
|
|
392
|
+
current = next;
|
|
383
393
|
}
|
|
384
|
-
|
|
394
|
+
// We've resolved the entire location successfully.
|
|
395
|
+
return current;
|
|
385
396
|
}
|
|
386
397
|
|
|
387
398
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "test-vulnerability-auditor-fix-hoists-package",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"lockfileVersion": 3,
|
|
5
|
+
"requires": true,
|
|
6
|
+
"packages": {
|
|
7
|
+
"": {
|
|
8
|
+
"name": "test-vulnerability-auditor-fix-hoists-package",
|
|
9
|
+
"version": "1.0.0",
|
|
10
|
+
"dependencies": {
|
|
11
|
+
"@dependabot-fixtures/npm-parent-dependency-with-more-versions": "1.0.0",
|
|
12
|
+
"@dependabot-fixtures/npm-transitive-dependency-with-more-versions": "1.0.0"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"node_modules/@dependabot-fixtures/npm-parent-dependency-with-more-versions": {
|
|
16
|
+
"version": "1.0.0",
|
|
17
|
+
"resolved": "https://registry.npmjs.org/@dependabot-fixtures/npm-parent-dependency-with-more-versions/-/npm-parent-dependency-with-more-versions-1.0.0.tgz",
|
|
18
|
+
"integrity": "sha512-Ys1u0synVJwqj1+bgo6g0uWMMDg3v55IG8O6qEM2WKP0Y9lmxSoN2egArdfBZcKuut+1EBcWmtM89g6P40EFJw==",
|
|
19
|
+
"license": "ISC",
|
|
20
|
+
"dependencies": {
|
|
21
|
+
"@dependabot-fixtures/npm-transitive-dependency-with-more-versions": "^1.0.0"
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
"node_modules/@dependabot-fixtures/npm-parent-dependency-with-more-versions/node_modules/@dependabot-fixtures/npm-transitive-dependency-with-more-versions": {
|
|
25
|
+
"version": "1.0.1",
|
|
26
|
+
"resolved": "https://registry.npmjs.org/@dependabot-fixtures/npm-transitive-dependency-with-more-versions/-/npm-transitive-dependency-with-more-versions-1.0.1.tgz",
|
|
27
|
+
"integrity": "sha512-L25+LUfJNPO3T+/RdhG62Hv2gIwiZLWR05qqqV1mzqD9goLy31/oc5rcF8/0pjOF45Zv/JqZm2whi4qhXa9plw==",
|
|
28
|
+
"license": "ISC"
|
|
29
|
+
},
|
|
30
|
+
"node_modules/@dependabot-fixtures/npm-transitive-dependency-with-more-versions": {
|
|
31
|
+
"version": "1.0.0",
|
|
32
|
+
"resolved": "https://registry.npmjs.org/@dependabot-fixtures/npm-transitive-dependency-with-more-versions/-/npm-transitive-dependency-with-more-versions-1.0.0.tgz",
|
|
33
|
+
"integrity": "sha512-IHtKNrRBm6bDrL2Jf1w+ZMg/4MmAb6MMHmP8CVebKnfn6Za7h39L7hG/ozA0vKI1ZZpGSfkRshvCd9EFFAc8IA==",
|
|
34
|
+
"license": "ISC"
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "test-vulnerability-auditor-fix-hoists-package",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"private": true,
|
|
5
|
+
"description": "",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"test": "echo \"Error: no test specified\" && exit 1"
|
|
8
|
+
},
|
|
9
|
+
"dependencies": {
|
|
10
|
+
"@dependabot-fixtures/npm-parent-dependency-with-more-versions": "1.0.0",
|
|
11
|
+
"@dependabot-fixtures/npm-transitive-dependency-with-more-versions": "1.0.0"
|
|
12
|
+
}
|
|
13
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "test-vulnerability-auditor-fix-removes-package",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"lockfileVersion": 3,
|
|
5
|
+
"requires": true,
|
|
6
|
+
"packages": {
|
|
7
|
+
"": {
|
|
8
|
+
"name": "test-vulnerability-auditor-fix-removes-package",
|
|
9
|
+
"version": "1.0.0",
|
|
10
|
+
"dependencies": {
|
|
11
|
+
"@dependabot-fixtures/npm-remove-dependency": "^10.0.0"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"node_modules/@dependabot-fixtures/npm-remove-dependency": {
|
|
15
|
+
"version": "10.0.0",
|
|
16
|
+
"resolved": "https://registry.npmjs.org/@dependabot-fixtures/npm-remove-dependency/-/npm-remove-dependency-10.0.0.tgz",
|
|
17
|
+
"integrity": "sha512-QMgb6isjtNCnql6Nn+/h2v759qIW4f1ZDER8IbUJaIuyppDq3BqABbiwTFNenynu39yQZ1YAUFbWuNGeECNLyw==",
|
|
18
|
+
"license": "ISC",
|
|
19
|
+
"dependencies": {
|
|
20
|
+
"@dependabot-fixtures/npm-transitive-dependency": "1.0.0"
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
"node_modules/@dependabot-fixtures/npm-transitive-dependency": {
|
|
24
|
+
"version": "1.0.0",
|
|
25
|
+
"resolved": "https://registry.npmjs.org/@dependabot-fixtures/npm-transitive-dependency/-/npm-transitive-dependency-1.0.0.tgz",
|
|
26
|
+
"integrity": "sha512-nFbzQH0TRgdzSA2/FH6MPnxZDpD+5Bgz00aD5Edgbc1wY/k8VC9s7lnk22dBTgJLwoY7MgbrnAf9rAvN08hHVg==",
|
|
27
|
+
"license": "ISC"
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "test-vulnerability-auditor-fix-removes-package",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"private": true,
|
|
5
|
+
"description": "",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"test": "echo \"Error: no test specified\" && exit 1"
|
|
8
|
+
},
|
|
9
|
+
"dependencies": {
|
|
10
|
+
"@dependabot-fixtures/npm-remove-dependency": "^10.0.0"
|
|
11
|
+
}
|
|
12
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "locked-transitive-dependency-outdated",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"lockfileVersion": 3,
|
|
5
|
+
"requires": true,
|
|
6
|
+
"packages": {
|
|
7
|
+
"": {
|
|
8
|
+
"name": "locked-transitive-dependency-outdated",
|
|
9
|
+
"version": "1.0.0",
|
|
10
|
+
"license": "ISC",
|
|
11
|
+
"dependencies": {
|
|
12
|
+
"@dependabot-fixtures/npm-parent-dependency": "2.0.0"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"node_modules/@dependabot-fixtures/npm-intermediate-dependency": {
|
|
16
|
+
"version": "0.0.1",
|
|
17
|
+
"resolved": "https://registry.npmjs.org/@dependabot-fixtures/npm-intermediate-dependency/-/npm-intermediate-dependency-0.0.1.tgz",
|
|
18
|
+
"integrity": "sha512-/N77Dzpfg8BIfFgpJrMk86ueUYTVhmpc4RobuHpIpKSc3GZr4Ltu4au92brnUGk66UkzgrMmtgqRXO8OrOspKQ==",
|
|
19
|
+
"dependencies": {
|
|
20
|
+
"@dependabot-fixtures/npm-transitive-dependency": "1.0.0"
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
"node_modules/@dependabot-fixtures/npm-parent-dependency": {
|
|
24
|
+
"version": "2.0.0",
|
|
25
|
+
"resolved": "https://registry.npmjs.org/@dependabot-fixtures/npm-parent-dependency/-/npm-parent-dependency-2.0.0.tgz",
|
|
26
|
+
"integrity": "sha512-5LtLEL1yzO2TdkNX3R9cvr+nKmhw5h4xM0wkFTJeK14wxlI9d8gEYA+I2hUi+IP96ucBSztAnOgZVwoJHEZb6A==",
|
|
27
|
+
"dependencies": {
|
|
28
|
+
"@dependabot-fixtures/npm-intermediate-dependency": "0.0.1"
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
"node_modules/@dependabot-fixtures/npm-transitive-dependency": {
|
|
32
|
+
"version": "1.0.0",
|
|
33
|
+
"resolved": "https://registry.npmjs.org/@dependabot-fixtures/npm-transitive-dependency/-/npm-transitive-dependency-1.0.0.tgz",
|
|
34
|
+
"integrity": "sha512-nFbzQH0TRgdzSA2/FH6MPnxZDpD+5Bgz00aD5Edgbc1wY/k8VC9s7lnk22dBTgJLwoY7MgbrnAf9rAvN08hHVg=="
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "locked-transitive-dependency-outdated",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "test fixture where the lockfile contains a vulnerability but it's not in sync with this file",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"test": "echo \"Error: no test specified\" && exit 1"
|
|
8
|
+
},
|
|
9
|
+
"keywords": [],
|
|
10
|
+
"author": "",
|
|
11
|
+
"license": "ISC",
|
|
12
|
+
"dependencies": {}
|
|
13
|
+
}
|
|
@@ -52,28 +52,34 @@ describe("findVulnerableDependencies", () => {
|
|
|
52
52
|
},
|
|
53
53
|
];
|
|
54
54
|
const actual = await findVulnerableDependencies(tempDir, advisories);
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
55
|
+
|
|
56
|
+
expect(actual.dependency_name).toBe("@msgpack/msgpack");
|
|
57
|
+
expect(actual.current_version).toBe("2.7.2");
|
|
58
|
+
expect(actual.fix_available).toBe(true);
|
|
59
|
+
expect(actual.target_version).toBe("2.8.0");
|
|
60
|
+
|
|
61
|
+
expect(actual.top_level_ancestors).toHaveLength(2);
|
|
62
|
+
expect(actual.top_level_ancestors).toEqual(
|
|
63
|
+
expect.arrayContaining(["@msgpack/msgpack", "wireql-client"])
|
|
64
|
+
);
|
|
65
|
+
|
|
66
|
+
expect(actual.fix_updates).toHaveLength(2);
|
|
67
|
+
expect(actual.fix_updates).toEqual(
|
|
68
|
+
expect.arrayContaining([
|
|
58
69
|
{
|
|
59
70
|
dependency_name: "@msgpack/msgpack",
|
|
60
71
|
current_version: "2.7.2",
|
|
61
|
-
top_level_ancestors: [],
|
|
62
72
|
target_version: "2.8.0",
|
|
73
|
+
top_level_ancestors: [],
|
|
63
74
|
},
|
|
64
75
|
{
|
|
65
76
|
dependency_name: "@msgpack/msgpack",
|
|
66
77
|
current_version: "3.0.0",
|
|
67
|
-
top_level_ancestors: ["wireql-client"],
|
|
68
78
|
target_version: "3.1.3",
|
|
79
|
+
top_level_ancestors: ["wireql-client"],
|
|
69
80
|
},
|
|
70
|
-
]
|
|
71
|
-
|
|
72
|
-
current_version: "2.7.2",
|
|
73
|
-
fix_available: true,
|
|
74
|
-
target_version: "2.8.0",
|
|
75
|
-
};
|
|
76
|
-
expect(actual).toEqual(expected);
|
|
81
|
+
])
|
|
82
|
+
);
|
|
77
83
|
});
|
|
78
84
|
|
|
79
85
|
it("finds vulnerable dependencies through workspace link nodes", async () => {
|
|
@@ -95,15 +101,130 @@ describe("findVulnerableDependencies", () => {
|
|
|
95
101
|
// chains and the dependency would appear to have no top-level ancestors.
|
|
96
102
|
// Note: the ancestor name comes from the Link target's directory ("app"),
|
|
97
103
|
// not its package.json name ("@test/app").
|
|
104
|
+
expect(actual.dependency_name).toBe(
|
|
105
|
+
"@dependabot-fixtures/npm-parent-dependency"
|
|
106
|
+
);
|
|
107
|
+
expect(actual.current_version).toBe("2.0.0");
|
|
98
108
|
expect(actual.fix_available).toBe(true);
|
|
109
|
+
expect(actual.target_version).toBe("2.0.2");
|
|
110
|
+
|
|
99
111
|
expect(actual.top_level_ancestors).toEqual(["app"]);
|
|
112
|
+
|
|
100
113
|
expect(actual.fix_updates).toEqual([
|
|
101
114
|
{
|
|
102
115
|
dependency_name: "@dependabot-fixtures/npm-parent-dependency",
|
|
103
116
|
current_version: "2.0.0",
|
|
104
|
-
top_level_ancestors: ["app"],
|
|
105
117
|
target_version: "2.0.2",
|
|
118
|
+
top_level_ancestors: ["app"],
|
|
106
119
|
},
|
|
107
120
|
]);
|
|
108
121
|
});
|
|
122
|
+
|
|
123
|
+
it("returns empty fix updates when package-lock.json is outdated", async () => {
|
|
124
|
+
helpers.copyDependencies(
|
|
125
|
+
"vulnerability-auditor/outdated-package-lock",
|
|
126
|
+
tempDir
|
|
127
|
+
);
|
|
128
|
+
|
|
129
|
+
const advisories = [
|
|
130
|
+
{
|
|
131
|
+
dependency_name: "@dependabot-fixtures/npm-transitive-dependency",
|
|
132
|
+
affected_versions: [">= 0, < 1.0.1"],
|
|
133
|
+
},
|
|
134
|
+
];
|
|
135
|
+
const actual = await findVulnerableDependencies(tempDir, advisories);
|
|
136
|
+
const expected = {
|
|
137
|
+
current_version: "1.0.0",
|
|
138
|
+
dependency_name: "@dependabot-fixtures/npm-transitive-dependency",
|
|
139
|
+
fix_available: true,
|
|
140
|
+
fix_updates: [],
|
|
141
|
+
target_version: undefined,
|
|
142
|
+
top_level_ancestors: [],
|
|
143
|
+
};
|
|
144
|
+
expect(actual).toEqual(expected);
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
it("has undefined target_version when a vulnerable package is removed", async () => {
|
|
148
|
+
helpers.copyDependencies(
|
|
149
|
+
"vulnerability-auditor/fix-removes-package",
|
|
150
|
+
tempDir
|
|
151
|
+
);
|
|
152
|
+
|
|
153
|
+
const advisories = [
|
|
154
|
+
{
|
|
155
|
+
dependency_name: "@dependabot-fixtures/npm-transitive-dependency",
|
|
156
|
+
affected_versions: [">= 0, < 1.0.1"],
|
|
157
|
+
},
|
|
158
|
+
];
|
|
159
|
+
const actual = await findVulnerableDependencies(tempDir, advisories);
|
|
160
|
+
const expected = {
|
|
161
|
+
current_version: "1.0.0",
|
|
162
|
+
dependency_name: "@dependabot-fixtures/npm-transitive-dependency",
|
|
163
|
+
fix_available: true,
|
|
164
|
+
fix_updates: [
|
|
165
|
+
{
|
|
166
|
+
current_version: "10.0.0",
|
|
167
|
+
dependency_name: "@dependabot-fixtures/npm-remove-dependency",
|
|
168
|
+
target_version: "10.0.1",
|
|
169
|
+
top_level_ancestors: [],
|
|
170
|
+
},
|
|
171
|
+
],
|
|
172
|
+
target_version: undefined,
|
|
173
|
+
top_level_ancestors: ["@dependabot-fixtures/npm-remove-dependency"],
|
|
174
|
+
};
|
|
175
|
+
expect(actual).toEqual(expected);
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
it("has defined target_versions in fix_updates when a vulnerable package is hoisted", async () => {
|
|
179
|
+
helpers.copyDependencies(
|
|
180
|
+
"vulnerability-auditor/fix-hoists-package",
|
|
181
|
+
tempDir
|
|
182
|
+
);
|
|
183
|
+
|
|
184
|
+
const advisories = [
|
|
185
|
+
{
|
|
186
|
+
dependency_name:
|
|
187
|
+
"@dependabot-fixtures/npm-transitive-dependency-with-more-versions",
|
|
188
|
+
affected_versions: [">= 0, < 1.0.2"],
|
|
189
|
+
},
|
|
190
|
+
];
|
|
191
|
+
const actual = await findVulnerableDependencies(tempDir, advisories);
|
|
192
|
+
|
|
193
|
+
expect(actual.dependency_name).toBe(
|
|
194
|
+
"@dependabot-fixtures/npm-transitive-dependency-with-more-versions"
|
|
195
|
+
);
|
|
196
|
+
expect(actual.current_version).toBe("1.0.0");
|
|
197
|
+
expect(actual.fix_available).toBe(true);
|
|
198
|
+
expect(actual.target_version).toBe("1.0.2");
|
|
199
|
+
|
|
200
|
+
expect(actual.top_level_ancestors).toHaveLength(2);
|
|
201
|
+
expect(actual.top_level_ancestors).toEqual(
|
|
202
|
+
expect.arrayContaining([
|
|
203
|
+
"@dependabot-fixtures/npm-parent-dependency-with-more-versions",
|
|
204
|
+
"@dependabot-fixtures/npm-transitive-dependency-with-more-versions",
|
|
205
|
+
])
|
|
206
|
+
);
|
|
207
|
+
|
|
208
|
+
expect(actual.fix_updates).toHaveLength(2);
|
|
209
|
+
expect(actual.fix_updates).toEqual(
|
|
210
|
+
expect.arrayContaining([
|
|
211
|
+
{
|
|
212
|
+
dependency_name:
|
|
213
|
+
"@dependabot-fixtures/npm-transitive-dependency-with-more-versions",
|
|
214
|
+
current_version: "1.0.1",
|
|
215
|
+
target_version: "1.0.2",
|
|
216
|
+
top_level_ancestors: [
|
|
217
|
+
"@dependabot-fixtures/npm-parent-dependency-with-more-versions",
|
|
218
|
+
],
|
|
219
|
+
},
|
|
220
|
+
{
|
|
221
|
+
dependency_name:
|
|
222
|
+
"@dependabot-fixtures/npm-transitive-dependency-with-more-versions",
|
|
223
|
+
current_version: "1.0.0",
|
|
224
|
+
target_version: "1.0.2",
|
|
225
|
+
top_level_ancestors: [],
|
|
226
|
+
},
|
|
227
|
+
])
|
|
228
|
+
);
|
|
229
|
+
});
|
|
109
230
|
});
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
# typed: strict
|
|
2
2
|
# frozen_string_literal: true
|
|
3
3
|
|
|
4
|
+
require "json"
|
|
5
|
+
require "yaml"
|
|
4
6
|
require "sorbet-runtime"
|
|
5
7
|
|
|
6
8
|
require "dependabot/dependency_graphers"
|
|
@@ -152,12 +154,138 @@ module Dependabot
|
|
|
152
154
|
)
|
|
153
155
|
end
|
|
154
156
|
|
|
155
|
-
# Fetches subdependencies for a given dependency.
|
|
156
|
-
# For npm/yarn/pnpm, we can extract this from the lockfile parser if available.
|
|
157
157
|
sig { override.params(dependency: Dependabot::Dependency).returns(T::Array[String]) }
|
|
158
158
|
def fetch_subdependencies(dependency)
|
|
159
|
-
|
|
160
|
-
|
|
159
|
+
package_relationships.fetch(dependency.name, [])
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
sig { returns(T::Hash[String, T::Array[String]]) }
|
|
163
|
+
def package_relationships
|
|
164
|
+
@package_relationships ||= T.let(
|
|
165
|
+
fetch_package_relationships,
|
|
166
|
+
T.nilable(T::Hash[String, T::Array[String]])
|
|
167
|
+
)
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
sig { returns(T::Hash[String, T::Array[String]]) }
|
|
171
|
+
def fetch_package_relationships
|
|
172
|
+
case detected_package_manager
|
|
173
|
+
when NpmPackageManager::NAME
|
|
174
|
+
fetch_npm_lock_relationships
|
|
175
|
+
when YarnPackageManager::NAME
|
|
176
|
+
fetch_yarn_lock_relationships
|
|
177
|
+
when PNPMPackageManager::NAME
|
|
178
|
+
fetch_pnpm_lock_relationships
|
|
179
|
+
else
|
|
180
|
+
{}
|
|
181
|
+
end
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
sig { returns(T.nilable(Dependabot::DependencyFile)) }
|
|
185
|
+
def npm_lockfile
|
|
186
|
+
return @npm_lockfile if defined?(@npm_lockfile)
|
|
187
|
+
|
|
188
|
+
@npm_lockfile = T.let(
|
|
189
|
+
dependency_files.find { |f| f.name.end_with?(NpmPackageManager::LOCKFILE_NAME) },
|
|
190
|
+
T.nilable(Dependabot::DependencyFile)
|
|
191
|
+
)
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
sig { returns(T.nilable(Dependabot::DependencyFile)) }
|
|
195
|
+
def yarn_lockfile
|
|
196
|
+
return @yarn_lockfile if defined?(@yarn_lockfile)
|
|
197
|
+
|
|
198
|
+
@yarn_lockfile = T.let(
|
|
199
|
+
dependency_files.find { |f| f.name.end_with?(YarnPackageManager::LOCKFILE_NAME) },
|
|
200
|
+
T.nilable(Dependabot::DependencyFile)
|
|
201
|
+
)
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
sig { returns(T.nilable(Dependabot::DependencyFile)) }
|
|
205
|
+
def pnpm_lockfile
|
|
206
|
+
return @pnpm_lockfile if defined?(@pnpm_lockfile)
|
|
207
|
+
|
|
208
|
+
@pnpm_lockfile = T.let(
|
|
209
|
+
dependency_files.find { |f| f.name.end_with?(PNPMPackageManager::LOCKFILE_NAME) },
|
|
210
|
+
T.nilable(Dependabot::DependencyFile)
|
|
211
|
+
)
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
sig { returns(T::Hash[String, T::Array[String]]) }
|
|
215
|
+
def fetch_npm_lock_relationships
|
|
216
|
+
parsed = JSON.parse(T.must(T.must(npm_lockfile).content))
|
|
217
|
+
packages = parsed.fetch("packages", {})
|
|
218
|
+
|
|
219
|
+
# v3/v2 lockfiles use a flat "packages" section
|
|
220
|
+
if packages.is_a?(Hash) && !packages.empty?
|
|
221
|
+
return packages.each_with_object({}) do |(path, details), rels|
|
|
222
|
+
next if path.empty? # skip root package entry
|
|
223
|
+
next unless details.is_a?(Hash)
|
|
224
|
+
|
|
225
|
+
children = details.fetch("dependencies", {}).keys
|
|
226
|
+
next if children.empty?
|
|
227
|
+
|
|
228
|
+
rels[path.split("node_modules/").last] = children
|
|
229
|
+
end
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
# if packages isn't present, attempt a v1 fallback
|
|
233
|
+
fetch_npm_v1_lock_relationships(parsed)
|
|
234
|
+
end
|
|
235
|
+
|
|
236
|
+
sig { params(parsed: T::Hash[String, T.untyped]).returns(T::Hash[String, T::Array[String]]) }
|
|
237
|
+
def fetch_npm_v1_lock_relationships(parsed)
|
|
238
|
+
dependencies = parsed.fetch("dependencies", {})
|
|
239
|
+
return {} unless dependencies.is_a?(Hash)
|
|
240
|
+
|
|
241
|
+
dependencies.each_with_object({}) do |(name, details), rels|
|
|
242
|
+
next unless details.is_a?(Hash)
|
|
243
|
+
|
|
244
|
+
nested = details.fetch("dependencies", nil)
|
|
245
|
+
next unless nested.is_a?(Hash)
|
|
246
|
+
|
|
247
|
+
children = nested.keys
|
|
248
|
+
rels[name] = children unless children.empty?
|
|
249
|
+
rels.merge!(fetch_npm_v1_lock_relationships(details))
|
|
250
|
+
end
|
|
251
|
+
end
|
|
252
|
+
|
|
253
|
+
sig { returns(T::Hash[String, T::Array[String]]) }
|
|
254
|
+
def fetch_yarn_lock_relationships
|
|
255
|
+
parsed = FileParser::YarnLock.new(T.must(yarn_lockfile)).parsed
|
|
256
|
+
|
|
257
|
+
parsed.each_with_object({}) do |(req, details), rels|
|
|
258
|
+
next unless details.is_a?(Hash)
|
|
259
|
+
|
|
260
|
+
parent_name = T.must(req.split(/(?<=\w)\@/).first)
|
|
261
|
+
children = details.fetch("dependencies", {})&.keys || []
|
|
262
|
+
|
|
263
|
+
next if children.empty?
|
|
264
|
+
|
|
265
|
+
rels[parent_name] ||= []
|
|
266
|
+
rels[parent_name].concat(children).uniq!
|
|
267
|
+
end
|
|
268
|
+
end
|
|
269
|
+
|
|
270
|
+
sig { returns(T::Hash[String, T::Array[String]]) }
|
|
271
|
+
def fetch_pnpm_lock_relationships
|
|
272
|
+
parsed = YAML.safe_load(T.must(T.must(pnpm_lockfile).content)) || {}
|
|
273
|
+
|
|
274
|
+
# v9+ uses "snapshots" for resolved dependency details; v6 uses "packages"
|
|
275
|
+
entries = parsed.fetch("snapshots", nil) || parsed.fetch("packages", {})
|
|
276
|
+
|
|
277
|
+
entries.each_with_object({}) do |(key, details), rels|
|
|
278
|
+
next unless details.is_a?(Hash)
|
|
279
|
+
|
|
280
|
+
# Keys are "/name@version" (v6) or "name@version" (v9)
|
|
281
|
+
parent_name = key.sub(%r{^/}, "").split(/(?<=\w)\@/).first
|
|
282
|
+
children = details.fetch("dependencies", {})&.keys || []
|
|
283
|
+
|
|
284
|
+
next if children.empty?
|
|
285
|
+
|
|
286
|
+
rels[parent_name] ||= []
|
|
287
|
+
rels[parent_name].concat(children).uniq!
|
|
288
|
+
end
|
|
161
289
|
end
|
|
162
290
|
|
|
163
291
|
sig { override.params(_dependency: Dependabot::Dependency).returns(String) }
|
|
@@ -71,10 +71,7 @@ module Dependabot
|
|
|
71
71
|
name: package_name,
|
|
72
72
|
version: version,
|
|
73
73
|
package_manager: "npm_and_yarn",
|
|
74
|
-
requirements: []
|
|
75
|
-
metadata: {
|
|
76
|
-
depends_on: details&.fetch("dependencies", {})&.keys || []
|
|
77
|
-
}
|
|
74
|
+
requirements: []
|
|
78
75
|
}
|
|
79
76
|
|
|
80
77
|
if details["bundled"]
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: dependabot-npm_and_yarn
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.374.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Dependabot
|
|
@@ -15,14 +15,14 @@ dependencies:
|
|
|
15
15
|
requirements:
|
|
16
16
|
- - '='
|
|
17
17
|
- !ruby/object:Gem::Version
|
|
18
|
-
version: 0.
|
|
18
|
+
version: 0.374.0
|
|
19
19
|
type: :runtime
|
|
20
20
|
prerelease: false
|
|
21
21
|
version_requirements: !ruby/object:Gem::Requirement
|
|
22
22
|
requirements:
|
|
23
23
|
- - '='
|
|
24
24
|
- !ruby/object:Gem::Version
|
|
25
|
-
version: 0.
|
|
25
|
+
version: 0.374.0
|
|
26
26
|
- !ruby/object:Gem::Dependency
|
|
27
27
|
name: debug
|
|
28
28
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -271,6 +271,12 @@ files:
|
|
|
271
271
|
- helpers/package.json
|
|
272
272
|
- helpers/patches/npm++pacote+9.5.12.patch
|
|
273
273
|
- helpers/run.ts
|
|
274
|
+
- helpers/test/npm/fixtures/vulnerability-auditor/fix-hoists-package/package-lock.json
|
|
275
|
+
- helpers/test/npm/fixtures/vulnerability-auditor/fix-hoists-package/package.json
|
|
276
|
+
- helpers/test/npm/fixtures/vulnerability-auditor/fix-removes-package/package-lock.json
|
|
277
|
+
- helpers/test/npm/fixtures/vulnerability-auditor/fix-removes-package/package.json
|
|
278
|
+
- helpers/test/npm/fixtures/vulnerability-auditor/outdated-package-lock/package-lock.json
|
|
279
|
+
- helpers/test/npm/fixtures/vulnerability-auditor/outdated-package-lock/package.json
|
|
274
280
|
- helpers/test/npm/fixtures/vulnerability-auditor/simple/package-lock.json
|
|
275
281
|
- helpers/test/npm/fixtures/vulnerability-auditor/simple/package.json
|
|
276
282
|
- helpers/test/npm/fixtures/vulnerability-auditor/update-needed-across-two-versions/package-lock.json
|
|
@@ -364,7 +370,7 @@ licenses:
|
|
|
364
370
|
- MIT
|
|
365
371
|
metadata:
|
|
366
372
|
bug_tracker_uri: https://github.com/dependabot/dependabot-core/issues
|
|
367
|
-
changelog_uri: https://github.com/dependabot/dependabot-core/releases/tag/v0.
|
|
373
|
+
changelog_uri: https://github.com/dependabot/dependabot-core/releases/tag/v0.374.0
|
|
368
374
|
rdoc_options: []
|
|
369
375
|
require_paths:
|
|
370
376
|
- lib
|