Skip to content

Commit 85c13ee

Browse files
iseki0HundredBai
andcommitted
fix: SCA-294 include npm v3 peer deps and preserve alias names
commit b948438ef253c5c40a0d4940b1e9ca3400ee4080 Author: hundredbai <yubaichao2008@126.com> Date: Fri Mar 6 17:16:59 2026 +0800 SCA-294 fix: include npm v3 peer deps and preserve alias names Co-authored-by: hundredbai <yubaichao2008@126.com>
1 parent 5c95d7c commit 85c13ee

2 files changed

Lines changed: 132 additions & 1 deletion

File tree

module/npm/lockfile_v3.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ type v3Package struct {
2121
Dependencies map[string]string `json:"dependencies"`
2222
DevDependencies map[string]string `json:"devDependencies"`
2323
OptionalDependencies map[string]string `json:"optionalDependencies"`
24+
PeerDependencies map[string]string `json:"peerDependencies"`
2425
Dev bool `json:"dev"`
2526
}
2627

@@ -77,7 +78,11 @@ func _visitV3[T any](lockfile *v3Lockfile, pred *v3Package, rPath []string, path
7778
}
7879
pathVisited[succPath] = struct{}{}
7980
var succV = [2]string{succ.Name, succ.Version}
80-
if succV[0] == "" {
81+
// Preserve alias dependency names (e.g. "string-width-cjs": "npm:string-width@^4.2.0").
82+
// If succName is empty, fall back to lockfile package name.
83+
if succName != "" {
84+
succV[0] = succName
85+
} else if succV[0] == "" {
8186
succV[0] = succName
8287
}
8388
var doNext = func(v T) { _visitV3(lockfile, &succ, succSegments, pathVisited, v, handler, pruneSet) }
@@ -93,6 +98,7 @@ func _visitV3[T any](lockfile *v3Lockfile, pred *v3Package, rPath []string, path
9398
}
9499
traversalDependencies(false, pred.Dependencies)
95100
traversalDependencies(false, pred.OptionalDependencies)
101+
traversalDependencies(false, pred.PeerDependencies)
96102
traversalDependencies(true, pred.DevDependencies)
97103
}
98104

module/npm/lockfile_v3_test.go

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
package npm
2+
3+
import (
4+
"encoding/json"
5+
"testing"
6+
7+
"github.com/murphysecurity/murphysec/model"
8+
"github.com/stretchr/testify/assert"
9+
"github.com/stretchr/testify/require"
10+
)
11+
12+
func TestProcessLockfileV3_PeerDepsAndAliasNames(t *testing.T) {
13+
lock := map[string]any{
14+
"name": "renderer",
15+
"version": "0.0.1",
16+
"lockfileVersion": 3,
17+
"packages": map[string]any{
18+
"": map[string]any{
19+
"dependencies": map[string]any{
20+
"vite": "^5.2.14",
21+
"@isaacs/cliui": "^8.0.2",
22+
},
23+
},
24+
"node_modules/vite": map[string]any{
25+
"name": "vite",
26+
"version": "5.2.14",
27+
"peerDependencies": map[string]any{
28+
"terser": "^5.4.0",
29+
"@types/node": "^18.0.0 || >=20.0.0",
30+
},
31+
},
32+
"node_modules/terser": map[string]any{
33+
"name": "terser",
34+
"version": "5.31.0",
35+
"dependencies": map[string]any{
36+
"@jridgewell/source-map": "^0.3.3",
37+
"commander": "^2.20.0",
38+
"source-map-support": "~0.5.20",
39+
},
40+
},
41+
"node_modules/terser/node_modules/commander": map[string]any{
42+
"name": "commander",
43+
"version": "2.20.3",
44+
},
45+
"node_modules/@jridgewell/source-map": map[string]any{
46+
"name": "@jridgewell/source-map",
47+
"version": "0.3.6",
48+
},
49+
"node_modules/source-map-support": map[string]any{
50+
"name": "source-map-support",
51+
"version": "0.5.21",
52+
"dependencies": map[string]any{
53+
"buffer-from": "^1.0.0",
54+
"source-map": "^0.6.0",
55+
},
56+
},
57+
"node_modules/buffer-from": map[string]any{
58+
"name": "buffer-from",
59+
"version": "1.1.2",
60+
},
61+
"node_modules/source-map": map[string]any{
62+
"name": "source-map",
63+
"version": "0.6.1",
64+
},
65+
"node_modules/@types/node": map[string]any{
66+
"name": "@types/node",
67+
"version": "20.12.7",
68+
"dependencies": map[string]any{
69+
"undici-types": "~5.26.4",
70+
},
71+
},
72+
"node_modules/undici-types": map[string]any{
73+
"name": "undici-types",
74+
"version": "5.26.5",
75+
},
76+
"node_modules/@isaacs/cliui": map[string]any{
77+
"name": "@isaacs/cliui",
78+
"version": "8.0.2",
79+
"dependencies": map[string]any{
80+
"string-width-cjs": "npm:string-width@^4.2.0",
81+
"strip-ansi-cjs": "npm:strip-ansi@^6.0.1",
82+
"wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0",
83+
},
84+
},
85+
"node_modules/string-width-cjs": map[string]any{
86+
"name": "string-width",
87+
"version": "4.2.3",
88+
},
89+
"node_modules/strip-ansi-cjs": map[string]any{
90+
"name": "strip-ansi",
91+
"version": "6.0.1",
92+
},
93+
"node_modules/wrap-ansi-cjs": map[string]any{
94+
"name": "wrap-ansi",
95+
"version": "7.0.0",
96+
},
97+
},
98+
}
99+
data, err := json.Marshal(lock)
100+
require.NoError(t, err)
101+
parsed, err := processLockfileV3(data)
102+
require.NoError(t, err)
103+
104+
got := map[string]string{}
105+
var walk func(items []model.DependencyItem)
106+
walk = func(items []model.DependencyItem) {
107+
for _, it := range items {
108+
got[it.CompName] = it.CompVersion
109+
walk(it.Dependencies)
110+
}
111+
}
112+
walk(parsed.Dependencies)
113+
114+
assert.Equal(t, "0.3.6", got["@jridgewell/source-map"])
115+
assert.Equal(t, "20.12.7", got["@types/node"])
116+
assert.Equal(t, "1.1.2", got["buffer-from"])
117+
assert.Equal(t, "2.20.3", got["commander"])
118+
assert.Equal(t, "0.6.1", got["source-map"])
119+
assert.Equal(t, "0.5.21", got["source-map-support"])
120+
assert.Equal(t, "4.2.3", got["string-width-cjs"])
121+
assert.Equal(t, "6.0.1", got["strip-ansi-cjs"])
122+
assert.Equal(t, "5.31.0", got["terser"])
123+
assert.Equal(t, "5.26.5", got["undici-types"])
124+
assert.Equal(t, "7.0.0", got["wrap-ansi-cjs"])
125+
}

0 commit comments

Comments
 (0)