Skip to content

Commit a783c26

Browse files
authored
fix(yarn-berry): identify workspace member as root in dependency tree (guacsec#486)
Implements TC-4182.
1 parent 93faa7c commit a783c26

9 files changed

Lines changed: 198 additions & 2 deletions

File tree

src/providers/processors/yarn_berry_processor.js

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,10 @@ export default class Yarn_berry_processor extends Yarn_processor {
2020
* @returns {string[]} Command arguments for listing dependencies
2121
*/
2222
listCmdArgs(includeTransitive) {
23-
return ['info', includeTransitive ? '--recursive' : '--all', '--json'];
23+
// --all is needed to include workspace members in the output
24+
return includeTransitive
25+
? ['info', '--recursive', '--all', '--json']
26+
: ['info', '--all', '--json'];
2427
}
2528

2629
/**
@@ -80,7 +83,8 @@ export default class Yarn_berry_processor extends Yarn_processor {
8083
if (!name) {
8184
return false;
8285
}
83-
return name.endsWith("@workspace:.");
86+
// Workspace members use paths like "member-a@workspace:packages/member-a", not just "@workspace:."
87+
return name.startsWith(`${this._manifest.name}@workspace:`);
8488
}
8589

8690
/**

test/providers/javascript.test.js

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,32 @@ suite('testing the javascript-npm data provider', async () => {
106106

107107
});
108108

109+
[
110+
{ providerName: 'yarn-berry', testCase: 'workspace_member' },
111+
].forEach(({ providerName, testCase }) => {
112+
test(`verify workspace member data for ${providerName} - stack analysis`, async () => {
113+
let expectedSbom = fs.readFileSync(`test/providers/tst_manifests/${providerName}/${testCase}/stack_expected_sbom.json`).toString();
114+
let listing = fs.readFileSync(`test/providers/tst_manifests/${providerName}/${testCase}/listing_stack.json`).toString();
115+
116+
const provider = await createMockProvider(providerName, listing);
117+
const manifestPath = `test/providers/tst_manifests/${providerName}/${testCase}/packages/member-a/package.json`;
118+
let providedDataForStack = provider.provideStack(manifestPath);
119+
120+
compareSboms(providedDataForStack.content, expectedSbom);
121+
}).timeout(30000);
122+
123+
test(`verify workspace member data for ${providerName} - component analysis`, async () => {
124+
let expectedSbom = fs.readFileSync(`test/providers/tst_manifests/${providerName}/${testCase}/component_expected_sbom.json`).toString();
125+
let listing = fs.readFileSync(`test/providers/tst_manifests/${providerName}/${testCase}/listing_component.json`).toString();
126+
127+
const provider = await createMockProvider(providerName, listing);
128+
const manifestPath = `test/providers/tst_manifests/${providerName}/${testCase}/packages/member-a/package.json`;
129+
let providedDataForComponent = provider.provideComponent(manifestPath);
130+
131+
compareSboms(providedDataForComponent.content, expectedSbom);
132+
}).timeout(15000);
133+
});
134+
109135
[
110136
{ providerName: 'npm', testCase: 'workspace_member' },
111137
].forEach(({ providerName, testCase }) => {
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
{
2+
"bomFormat": "CycloneDX",
3+
"specVersion": "1.4",
4+
"version": 1,
5+
"metadata": {
6+
"timestamp": "2023-08-07T00:00:00.000Z",
7+
"component": {
8+
"name": "member-a",
9+
"version": "1.0.0",
10+
"purl": "pkg:npm/member-a@1.0.0",
11+
"type": "application",
12+
"bom-ref": "pkg:npm/member-a@1.0.0"
13+
}
14+
},
15+
"components": [
16+
{
17+
"name": "axios",
18+
"version": "0.21.1",
19+
"purl": "pkg:npm/axios@0.21.1",
20+
"type": "library",
21+
"bom-ref": "pkg:npm/axios@0.21.1"
22+
}
23+
],
24+
"dependencies": [
25+
{
26+
"ref": "pkg:npm/member-a@1.0.0",
27+
"dependsOn": [
28+
"pkg:npm/axios@0.21.1"
29+
]
30+
},
31+
{
32+
"ref": "pkg:npm/axios@0.21.1",
33+
"dependsOn": []
34+
}
35+
]
36+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
{
2+
"value": "axios@npm:0.21.1",
3+
"children": {
4+
"Version": "0.21.1",
5+
"Dependencies": [
6+
{
7+
"descriptor": "follow-redirects@virtual:d192f6b3b31cd5d11a443145a3883a70c04cbd7c813c53085dbaf50263735f1162f10fdbddd53c24e162ec3bc37b90966413084323739b7cf942b8bfb4da8831#npm:^1.10.0",
8+
"locator": "follow-redirects@virtual:d192f6b3b31cd5d11a443145a3883a70c04cbd7c813c53085dbaf50263735f1162f10fdbddd53c24e162ec3bc37b90966413084323739b7cf942b8bfb4da8831#npm:1.16.0"
9+
}
10+
]
11+
}
12+
}
13+
{
14+
"value": "member-a@workspace:packages/member-a",
15+
"children": {
16+
"Version": "1.0.0",
17+
"Dependencies": [
18+
{
19+
"descriptor": "axios@npm:0.21.1",
20+
"locator": "axios@npm:0.21.1"
21+
}
22+
]
23+
}
24+
}
25+
{
26+
"value": "test-workspace-root@workspace:.",
27+
"children": {
28+
"Version": "0.0.0"
29+
}
30+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
{
2+
"value": "axios@npm:0.21.1",
3+
"children": {
4+
"Version": "0.21.1",
5+
"Dependencies": [
6+
{
7+
"descriptor": "follow-redirects@virtual:d192f6b3b31cd5d11a443145a3883a70c04cbd7c813c53085dbaf50263735f1162f10fdbddd53c24e162ec3bc37b90966413084323739b7cf942b8bfb4da8831#npm:^1.10.0",
8+
"locator": "follow-redirects@virtual:d192f6b3b31cd5d11a443145a3883a70c04cbd7c813c53085dbaf50263735f1162f10fdbddd53c24e162ec3bc37b90966413084323739b7cf942b8bfb4da8831#npm:1.16.0"
9+
}
10+
]
11+
}
12+
}
13+
{
14+
"value": "follow-redirects@npm:1.16.0",
15+
"children": {
16+
"Instances": 1,
17+
"Version": "1.16.0"
18+
}
19+
}
20+
{
21+
"value": "member-a@workspace:packages/member-a",
22+
"children": {
23+
"Version": "1.0.0",
24+
"Dependencies": [
25+
{
26+
"descriptor": "axios@npm:0.21.1",
27+
"locator": "axios@npm:0.21.1"
28+
}
29+
]
30+
}
31+
}
32+
{
33+
"value": "test-workspace-root@workspace:.",
34+
"children": {
35+
"Version": "0.0.0"
36+
}
37+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"name": "test-workspace-root",
3+
"private": true,
4+
"workspaces": ["packages/*"]
5+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"name": "member-a",
3+
"version": "1.0.0",
4+
"dependencies": {
5+
"axios": "0.21.1"
6+
}
7+
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
{
2+
"bomFormat": "CycloneDX",
3+
"specVersion": "1.4",
4+
"version": 1,
5+
"metadata": {
6+
"timestamp": "2023-08-07T00:00:00.000Z",
7+
"component": {
8+
"name": "member-a",
9+
"version": "1.0.0",
10+
"purl": "pkg:npm/member-a@1.0.0",
11+
"type": "application",
12+
"bom-ref": "pkg:npm/member-a@1.0.0"
13+
}
14+
},
15+
"components": [
16+
{
17+
"name": "axios",
18+
"version": "0.21.1",
19+
"purl": "pkg:npm/axios@0.21.1",
20+
"type": "library",
21+
"bom-ref": "pkg:npm/axios@0.21.1"
22+
},
23+
{
24+
"name": "follow-redirects",
25+
"version": "1.16.0",
26+
"purl": "pkg:npm/follow-redirects@1.16.0",
27+
"type": "library",
28+
"bom-ref": "pkg:npm/follow-redirects@1.16.0"
29+
}
30+
],
31+
"dependencies": [
32+
{
33+
"ref": "pkg:npm/member-a@1.0.0",
34+
"dependsOn": [
35+
"pkg:npm/axios@0.21.1"
36+
]
37+
},
38+
{
39+
"ref": "pkg:npm/axios@0.21.1",
40+
"dependsOn": [
41+
"pkg:npm/follow-redirects@1.16.0"
42+
]
43+
},
44+
{
45+
"ref": "pkg:npm/follow-redirects@1.16.0",
46+
"dependsOn": []
47+
}
48+
]
49+
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
__metadata:
2+
version: 8

0 commit comments

Comments
 (0)