Skip to content

Commit 7d083bc

Browse files
authored
fix(nodejs): fix infinity loops for pnpm with cyclic imports (#6857)
1 parent 042d6b0 commit 7d083bc

File tree

4 files changed

+152
-4
lines changed

4 files changed

+152
-4
lines changed

pkg/dependency/parser/nodejs/pnpm/parse.go

+9-4
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,7 @@ func (p *Parser) parseV9(lockFile LockFile) ([]ftypes.Package, []ftypes.Dependen
183183
if dep, ok := lockFile.Importers.Root.DevDependencies[name]; ok && dep.Version == ver {
184184
relationship = ftypes.RelationshipDirect
185185
}
186-
if dep, ok := lockFile.Importers.Root.Dependencies[name]; ok && dep.Version == ver {
186+
if dep, ok := lockFile.Importers.Root.Dependencies[name]; ok && p.trimPeerDeps(dep.Version, lockVer) == ver {
187187
relationship = ftypes.RelationshipDirect
188188
dev = false // mark root direct deps to update `dev` field of their child deps.
189189
}
@@ -208,29 +208,34 @@ func (p *Parser) parseV9(lockFile LockFile) ([]ftypes.Package, []ftypes.Dependen
208208
}
209209
}
210210

211+
visited := make(map[string]struct{})
211212
// Overwrite the `Dev` field for dev deps and their child dependencies.
212213
for _, pkg := range resolvedPkgs {
213214
if !pkg.Dev {
214-
p.markRootPkgs(pkg.ID, resolvedPkgs, resolvedDeps)
215+
p.markRootPkgs(pkg.ID, resolvedPkgs, resolvedDeps, visited)
215216
}
216217
}
217218

218219
return maps.Values(resolvedPkgs), maps.Values(resolvedDeps)
219220
}
220221

221222
// markRootPkgs sets `Dev` to false for non dev dependency.
222-
func (p *Parser) markRootPkgs(id string, pkgs map[string]ftypes.Package, deps map[string]ftypes.Dependency) {
223+
func (p *Parser) markRootPkgs(id string, pkgs map[string]ftypes.Package, deps map[string]ftypes.Dependency, visited map[string]struct{}) {
224+
if _, ok := visited[id]; ok {
225+
return
226+
}
223227
pkg, ok := pkgs[id]
224228
if !ok {
225229
return
226230
}
227231

228232
pkg.Dev = false
229233
pkgs[id] = pkg
234+
visited[id] = struct{}{}
230235

231236
// Update child deps
232237
for _, depID := range deps[id].DependsOn {
233-
p.markRootPkgs(depID, pkgs, deps)
238+
p.markRootPkgs(depID, pkgs, deps, visited)
234239
}
235240
return
236241
}

pkg/dependency/parser/nodejs/pnpm/parse_test.go

+12
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,18 @@ func TestParse(t *testing.T) {
5959
want: pnpmV9,
6060
wantDeps: pnpmV9Deps,
6161
},
62+
{
63+
name: "v9",
64+
file: "testdata/pnpm-lock_v9.yaml",
65+
want: pnpmV9,
66+
wantDeps: pnpmV9Deps,
67+
},
68+
{
69+
name: "v9 with cyclic dependencies import",
70+
file: "testdata/pnpm-lock_v9_cyclic_import.yaml",
71+
want: pnpmV9CyclicImport,
72+
wantDeps: pnpmV9CyclicImportDeps,
73+
},
6274
}
6375

6476
for _, tt := range tests {

pkg/dependency/parser/nodejs/pnpm/parse_testcase.go

+64
Original file line numberDiff line numberDiff line change
@@ -900,4 +900,68 @@ var (
900900
},
901901
},
902902
}
903+
904+
pnpmV9CyclicImport = []ftypes.Package{
905+
{
906+
907+
Name: "update-browserslist-db",
908+
Version: "1.0.16",
909+
Relationship: ftypes.RelationshipDirect,
910+
},
911+
{
912+
913+
Name: "browserslist",
914+
Version: "4.23.0",
915+
Relationship: ftypes.RelationshipIndirect,
916+
},
917+
{
918+
919+
Name: "caniuse-lite",
920+
Version: "1.0.30001627",
921+
Relationship: ftypes.RelationshipIndirect,
922+
},
923+
{
924+
925+
Name: "electron-to-chromium",
926+
Version: "1.4.789",
927+
Relationship: ftypes.RelationshipIndirect,
928+
},
929+
{
930+
931+
Name: "escalade",
932+
Version: "3.1.2",
933+
Relationship: ftypes.RelationshipIndirect,
934+
},
935+
{
936+
937+
Name: "node-releases",
938+
Version: "2.0.14",
939+
Relationship: ftypes.RelationshipIndirect,
940+
},
941+
{
942+
943+
Name: "picocolors",
944+
Version: "1.0.1",
945+
Relationship: ftypes.RelationshipIndirect,
946+
},
947+
}
948+
pnpmV9CyclicImportDeps = []ftypes.Dependency{
949+
{
950+
951+
DependsOn: []string{
952+
953+
954+
955+
956+
},
957+
},
958+
{
959+
960+
DependsOn: []string{
961+
962+
963+
964+
},
965+
},
966+
}
903967
)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
lockfileVersion: '9.0'
2+
3+
settings:
4+
autoInstallPeers: true
5+
excludeLinksFromLockfile: false
6+
7+
importers:
8+
9+
.:
10+
dependencies:
11+
update-browserslist-db:
12+
specifier: 1.0.16
13+
version: 1.0.16([email protected])
14+
15+
packages:
16+
17+
18+
resolution: {integrity: sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==}
19+
engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
20+
hasBin: true
21+
22+
23+
resolution: {integrity: sha512-4zgNiB8nTyV/tHhwZrFs88ryjls/lHiqFhrxCW4qSTeuRByBVnPYpDInchOIySWknznucaf31Z4KYqjfbrecVw==}
24+
25+
26+
resolution: {integrity: sha512-0VbyiaXoT++Fi2vHGo2ThOeS6X3vgRCWrjPeO2FeIAWL6ItiSJ9BqlH8LfCXe3X1IdcG+S0iLoNaxQWhfZoGzQ==}
27+
28+
29+
resolution: {integrity: sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==}
30+
engines: {node: '>=6'}
31+
32+
33+
resolution: {integrity: sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==}
34+
35+
36+
resolution: {integrity: sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==}
37+
38+
39+
resolution: {integrity: sha512-KVbTxlBYlckhF5wgfyZXTWnMn7MMZjMu9XG8bPlliUOP9ThaF4QnhP8qrjrH7DRzHfSk0oQv1wToW+iA5GajEQ==}
40+
hasBin: true
41+
peerDependencies:
42+
browserslist: '>= 4.21.0'
43+
44+
snapshots:
45+
46+
47+
dependencies:
48+
caniuse-lite: 1.0.30001627
49+
electron-to-chromium: 1.4.789
50+
node-releases: 2.0.14
51+
update-browserslist-db: 1.0.16([email protected])
52+
53+
54+
55+
56+
57+
58+
59+
60+
61+
62+
63+
64+
dependencies:
65+
browserslist: 4.23.0
66+
escalade: 3.1.2
67+
picocolors: 1.0.1

0 commit comments

Comments
 (0)