Skip to content

Commit 14c63ea

Browse files
CopilotshinoCopilot
authored
fix(contrib/trivy): O(N²) library duplication in trivy-to-vuls ClassLangPkg handling (#2450)
* Initial plan * fix: remove inner loop causing O(N²) library duplication in ClassLangPkg and add regression test Co-authored-by: shino <10225+shino@users.noreply.github.com> * refactor: rename test to nodePkgUniqueFilePaths to describe scenario, not absence of bug Co-authored-by: shino <10225+shino@users.noreply.github.com> * Update contrib/trivy/parser/v2/parser_test.go Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: shino <10225+shino@users.noreply.github.com> Co-authored-by: Shunichi Shinohara <shino.shun@gmail.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
1 parent 5a6e06d commit 14c63ea

2 files changed

Lines changed: 121 additions & 9 deletions

File tree

contrib/trivy/parser/v2/parser_test.go

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,10 @@ func TestParse(t *testing.T) {
3939
vulnJSON: includeDevDependenciesTrivy,
4040
expected: includeDevDependenciesSR,
4141
},
42+
"nodePkgUniqueFilePaths": {
43+
vulnJSON: nodePkgUniqueFilePathsTrivy,
44+
expected: nodePkgUniqueFilePathsSR,
45+
},
4246
}
4347

4448
for testcase, v := range cases {
@@ -3005,6 +3009,116 @@ var includeDevDependenciesSR = &models.ScanResult{
30053009
Optional: nil,
30063010
}
30073011

3012+
// nodePkgUniqueFilePathsTrivy is a test input with node-pkg type where each
3013+
// package has a unique FilePath (e.g. node_modules/<pkg>/package.json), resulting
3014+
// in a separate LibraryScanner entry per package.
3015+
var nodePkgUniqueFilePathsTrivy = []byte(`{
3016+
"SchemaVersion": 2,
3017+
"CreatedAt": "2026-03-10T00:00:00Z",
3018+
"ArtifactName": "myapp:latest",
3019+
"ArtifactType": "container_image",
3020+
"Metadata": {
3021+
"ImageConfig": {
3022+
"architecture": "amd64",
3023+
"created": "0001-01-01T00:00:00Z",
3024+
"os": "linux",
3025+
"rootfs": {
3026+
"type": "",
3027+
"diff_ids": null
3028+
},
3029+
"config": {}
3030+
}
3031+
},
3032+
"Results": [
3033+
{
3034+
"Target": "Node.js",
3035+
"Class": "lang-pkgs",
3036+
"Type": "node-pkg",
3037+
"Packages": [
3038+
{
3039+
"Name": "express",
3040+
"Identifier": {
3041+
"PURL": "pkg:npm/express@4.18.2"
3042+
},
3043+
"Version": "4.18.2",
3044+
"FilePath": "node_modules/express/package.json",
3045+
"Layer": {}
3046+
},
3047+
{
3048+
"Name": "lodash",
3049+
"Identifier": {
3050+
"PURL": "pkg:npm/lodash@4.17.21"
3051+
},
3052+
"Version": "4.17.21",
3053+
"FilePath": "node_modules/lodash/package.json",
3054+
"Layer": {}
3055+
},
3056+
{
3057+
"Name": "debug",
3058+
"Identifier": {
3059+
"PURL": "pkg:npm/debug@4.3.4"
3060+
},
3061+
"Version": "4.3.4",
3062+
"FilePath": "node_modules/debug/package.json",
3063+
"Layer": {}
3064+
}
3065+
],
3066+
"Vulnerabilities": []
3067+
}
3068+
]
3069+
}
3070+
`)
3071+
3072+
var nodePkgUniqueFilePathsSR = &models.ScanResult{
3073+
JSONVersion: 4,
3074+
ServerName: "myapp",
3075+
Family: "pseudo",
3076+
ScannedBy: "trivy",
3077+
ScannedVia: "trivy",
3078+
ScannedCves: models.VulnInfos{},
3079+
Packages: models.Packages{},
3080+
SrcPackages: models.SrcPackages{},
3081+
LibraryScanners: models.LibraryScanners{
3082+
{
3083+
Type: "node-pkg",
3084+
LockfilePath: "/node_modules/debug/package.json",
3085+
Libs: []models.Library{
3086+
{
3087+
Name: "debug",
3088+
Version: "4.3.4",
3089+
PURL: "pkg:npm/debug@4.3.4",
3090+
FilePath: "node_modules/debug/package.json",
3091+
},
3092+
},
3093+
},
3094+
{
3095+
Type: "node-pkg",
3096+
LockfilePath: "/node_modules/express/package.json",
3097+
Libs: []models.Library{
3098+
{
3099+
Name: "express",
3100+
Version: "4.18.2",
3101+
PURL: "pkg:npm/express@4.18.2",
3102+
FilePath: "node_modules/express/package.json",
3103+
},
3104+
},
3105+
},
3106+
{
3107+
Type: "node-pkg",
3108+
LockfilePath: "/node_modules/lodash/package.json",
3109+
Libs: []models.Library{
3110+
{
3111+
Name: "lodash",
3112+
Version: "4.17.21",
3113+
PURL: "pkg:npm/lodash@4.17.21",
3114+
FilePath: "node_modules/lodash/package.json",
3115+
},
3116+
},
3117+
},
3118+
},
3119+
Optional: map[string]any{"TRIVY_IMAGE_NAME": "myapp", "TRIVY_IMAGE_TAG": "latest"},
3120+
}
3121+
30083122
func TestParseError(t *testing.T) {
30093123
cases := map[string]struct {
30103124
vulnJSON []byte

contrib/trivy/pkg/converter.go

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -211,15 +211,13 @@ func Convert(results types.Results, artifactType ftypes.ArtifactType, artifactNa
211211

212212
libScanner := libraryScannerPaths[lockfilePath]
213213
libScanner.Type = trivyResult.Type
214-
for _, p := range trivyResult.Packages {
215-
libScanner.Libs = append(libScanner.Libs, models.Library{
216-
Name: p.Name,
217-
Version: p.Version,
218-
PURL: getPURL(p),
219-
FilePath: p.FilePath,
220-
Dev: p.Dev,
221-
})
222-
}
214+
libScanner.Libs = append(libScanner.Libs, models.Library{
215+
Name: p.Name,
216+
Version: p.Version,
217+
PURL: getPURL(p),
218+
FilePath: p.FilePath,
219+
Dev: p.Dev,
220+
})
223221
libraryScannerPaths[lockfilePath] = libScanner
224222
}
225223
default:

0 commit comments

Comments
 (0)