Skip to content

Commit 998f750

Browse files
authored
feat: introduce package UIDs for improved vulnerability mapping (aquasecurity#6583)
Signed-off-by: knqyf263 <[email protected]>
1 parent 770b141 commit 998f750

File tree

71 files changed

+1531
-874
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

71 files changed

+1531
-874
lines changed

Dockerfile.protoc

+2-2
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ RUN curl --retry 5 -OL https://github.com/protocolbuffers/protobuf/releases/down
1414

1515
# Install Go tools
1616
RUN go install github.com/twitchtv/twirp/[email protected]
17-
RUN go install google.golang.org/protobuf/cmd/protoc-gen-go@v1.27.1
18-
RUN go install github.com/magefile/mage@v1.14.0
17+
RUN go install google.golang.org/protobuf/cmd/protoc-gen-go@v1.34.0
18+
RUN go install github.com/magefile/mage@v1.15.0
1919

2020
ENV TRIVY_PROTOC_CONTAINER=true

integration/client_server_test.go

+13-4
Original file line numberDiff line numberDiff line change
@@ -283,7 +283,9 @@ func TestClientServer(t *testing.T) {
283283
osArgs = append(osArgs, "--secret-config", tt.args.secretConfig)
284284
}
285285

286-
runTest(t, osArgs, tt.golden, "", types.FormatJSON, runOptions{})
286+
runTest(t, osArgs, tt.golden, "", types.FormatJSON, runOptions{
287+
override: overrideUID,
288+
})
287289
})
288290
}
289291
}
@@ -397,7 +399,9 @@ func TestClientServerWithFormat(t *testing.T) {
397399
t.Setenv("AWS_ACCOUNT_ID", "123456789012")
398400
osArgs := setupClient(t, tt.args, addr, cacheDir, tt.golden)
399401

400-
runTest(t, osArgs, tt.golden, "", tt.args.Format, runOptions{})
402+
runTest(t, osArgs, tt.golden, "", tt.args.Format, runOptions{
403+
override: overrideUID,
404+
})
401405
})
402406
}
403407
}
@@ -475,7 +479,10 @@ func TestClientServerWithToken(t *testing.T) {
475479
for _, tt := range tests {
476480
t.Run(tt.name, func(t *testing.T) {
477481
osArgs := setupClient(t, tt.args, addr, cacheDir, tt.golden)
478-
runTest(t, osArgs, tt.golden, "", types.FormatJSON, runOptions{wantErr: tt.wantErr})
482+
runTest(t, osArgs, tt.golden, "", types.FormatJSON, runOptions{
483+
override: overrideUID,
484+
wantErr: tt.wantErr,
485+
})
479486
})
480487
}
481488
}
@@ -501,7 +508,9 @@ func TestClientServerWithRedis(t *testing.T) {
501508
osArgs := setupClient(t, testArgs, addr, cacheDir, golden)
502509

503510
// Run Trivy client
504-
runTest(t, osArgs, golden, "", types.FormatJSON, runOptions{})
511+
runTest(t, osArgs, golden, "", types.FormatJSON, runOptions{
512+
override: overrideUID,
513+
})
505514
})
506515

507516
// Terminate the Redis container

integration/integration_test.go

+34-3
Original file line numberDiff line numberDiff line change
@@ -192,9 +192,10 @@ func readSpdxJson(t *testing.T, filePath string) *spdx.Document {
192192
return bom
193193
}
194194

195+
type OverrideFunc func(t *testing.T, want, got *types.Report)
195196
type runOptions struct {
196197
wantErr string
197-
override func(want, got *types.Report)
198+
override OverrideFunc
198199
fakeUUID string
199200
}
200201

@@ -262,11 +263,11 @@ func compareRawFiles(t *testing.T, wantFile, gotFile string) {
262263
assert.EqualValues(t, string(want), string(got))
263264
}
264265

265-
func compareReports(t *testing.T, wantFile, gotFile string, override func(want, got *types.Report)) {
266+
func compareReports(t *testing.T, wantFile, gotFile string, override func(t *testing.T, want, got *types.Report)) {
266267
want := readReport(t, wantFile)
267268
got := readReport(t, gotFile)
268269
if override != nil {
269-
override(&want, &got)
270+
override(t, &want, &got)
270271
}
271272
assert.Equal(t, want, got)
272273
}
@@ -307,3 +308,33 @@ func validateReport(t *testing.T, schema string, report any) {
307308
assert.True(t, valid, strings.Join(errs, "\n"))
308309
}
309310
}
311+
312+
func overrideFuncs(funcs ...OverrideFunc) OverrideFunc {
313+
return func(t *testing.T, want, got *types.Report) {
314+
for _, f := range funcs {
315+
if f == nil {
316+
continue
317+
}
318+
f(t, want, got)
319+
}
320+
}
321+
}
322+
323+
// overrideUID only checks for the presence of the package UID and clears the UID;
324+
// the UID is calculated from the package metadata, but the UID does not match
325+
// as it varies slightly depending on the mode of scanning, e.g. the digest of the layer.
326+
func overrideUID(t *testing.T, want, got *types.Report) {
327+
for i, result := range got.Results {
328+
for j, vuln := range result.Vulnerabilities {
329+
assert.NotEmptyf(t, vuln.PkgIdentifier.UID, "UID is empty: %s", vuln.VulnerabilityID)
330+
// Do not compare UID as the package metadata is slightly different between the tests,
331+
// causing different UIDs.
332+
got.Results[i].Vulnerabilities[j].PkgIdentifier.UID = ""
333+
}
334+
}
335+
for i, result := range want.Results {
336+
for j := range result.Vulnerabilities {
337+
want.Results[i].Vulnerabilities[j].PkgIdentifier.UID = ""
338+
}
339+
}
340+
}

integration/registry_test.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -202,12 +202,12 @@ func TestRegistry(t *testing.T) {
202202
// Run Trivy
203203
runTest(t, osArgs, tc.golden, "", types.FormatJSON, runOptions{
204204
wantErr: tc.wantErr,
205-
override: func(_, got *types.Report) {
205+
override: overrideFuncs(overrideUID, func(t *testing.T, _, got *types.Report) {
206206
got.ArtifactName = tc.imageName
207207
for i := range got.Results {
208208
got.Results[i].Target = fmt.Sprintf("%s (alpine 3.10.2)", tc.imageName)
209209
}
210-
},
210+
}),
211211
})
212212
})
213213
}

integration/repo_test.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ func TestRepository(t *testing.T) {
3737
name string
3838
args args
3939
golden string
40-
override func(want, got *types.Report)
40+
override func(t *testing.T, want, got *types.Report)
4141
}{
4242
{
4343
name: "gomod",
@@ -378,7 +378,7 @@ func TestRepository(t *testing.T) {
378378
skipFiles: []string{"testdata/fixtures/repo/gomod/submod2/go.mod"},
379379
},
380380
golden: "testdata/gomod-skip.json.golden",
381-
override: func(want, _ *types.Report) {
381+
override: func(_ *testing.T, want, _ *types.Report) {
382382
want.ArtifactType = ftypes.ArtifactFilesystem
383383
},
384384
},
@@ -392,7 +392,7 @@ func TestRepository(t *testing.T) {
392392
input: "testdata/fixtures/repo/custom-policy",
393393
},
394394
golden: "testdata/dockerfile-custom-policies.json.golden",
395-
override: func(want, got *types.Report) {
395+
override: func(_ *testing.T, want, got *types.Report) {
396396
want.ArtifactType = ftypes.ArtifactFilesystem
397397
},
398398
},

integration/sbom_test.go

+56-78
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,11 @@ import (
66
"path/filepath"
77
"testing"
88

9-
ftypes "github.com/aquasecurity/trivy/pkg/fanal/types"
109
v1 "github.com/google/go-containerregistry/pkg/v1"
1110
"github.com/stretchr/testify/assert"
1211
"github.com/stretchr/testify/require"
1312

13+
ftypes "github.com/aquasecurity/trivy/pkg/fanal/types"
1414
"github.com/aquasecurity/trivy/pkg/types"
1515
)
1616

@@ -25,7 +25,7 @@ func TestSBOM(t *testing.T) {
2525
name string
2626
args args
2727
golden string
28-
override types.Report
28+
override OverrideFunc
2929
}{
3030
{
3131
name: "centos7 cyclonedx",
@@ -35,31 +35,17 @@ func TestSBOM(t *testing.T) {
3535
artifactType: "cyclonedx",
3636
},
3737
golden: "testdata/centos-7.json.golden",
38-
override: types.Report{
39-
ArtifactName: "testdata/fixtures/sbom/centos-7-cyclonedx.json",
40-
ArtifactType: ftypes.ArtifactType("cyclonedx"),
41-
Results: types.Results{
42-
{
43-
Target: "testdata/fixtures/sbom/centos-7-cyclonedx.json (centos 7.6.1810)",
44-
Vulnerabilities: []types.DetectedVulnerability{
45-
{
46-
PkgIdentifier: ftypes.PkgIdentifier{
47-
BOMRef: "pkg:rpm/centos/[email protected]?arch=x86_64&distro=centos-7.6.1810",
48-
},
49-
},
50-
{
51-
PkgIdentifier: ftypes.PkgIdentifier{
52-
BOMRef: "pkg:rpm/centos/[email protected]?arch=x86_64&epoch=1&distro=centos-7.6.1810",
53-
},
54-
},
55-
{
56-
PkgIdentifier: ftypes.PkgIdentifier{
57-
BOMRef: "pkg:rpm/centos/[email protected]?arch=x86_64&epoch=1&distro=centos-7.6.1810",
58-
},
59-
},
60-
},
61-
},
62-
},
38+
override: func(t *testing.T, want, got *types.Report) {
39+
want.ArtifactName = "testdata/fixtures/sbom/centos-7-cyclonedx.json"
40+
want.ArtifactType = ftypes.ArtifactCycloneDX
41+
42+
require.Len(t, got.Results, 1)
43+
want.Results[0].Target = "testdata/fixtures/sbom/centos-7-cyclonedx.json (centos 7.6.1810)"
44+
45+
require.Len(t, got.Results[0].Vulnerabilities, 3)
46+
want.Results[0].Vulnerabilities[0].PkgIdentifier.BOMRef = "pkg:rpm/centos/[email protected]?arch=x86_64&distro=centos-7.6.1810"
47+
want.Results[0].Vulnerabilities[1].PkgIdentifier.BOMRef = "pkg:rpm/centos/[email protected]?arch=x86_64&epoch=1&distro=centos-7.6.1810"
48+
want.Results[0].Vulnerabilities[2].PkgIdentifier.BOMRef = "pkg:rpm/centos/[email protected]?arch=x86_64&epoch=1&distro=centos-7.6.1810"
6349
},
6450
},
6551
{
@@ -88,31 +74,17 @@ func TestSBOM(t *testing.T) {
8874
artifactType: "cyclonedx",
8975
},
9076
golden: "testdata/centos-7.json.golden",
91-
override: types.Report{
92-
ArtifactName: "testdata/fixtures/sbom/centos-7-cyclonedx.intoto.jsonl",
93-
ArtifactType: ftypes.ArtifactType("cyclonedx"),
94-
Results: types.Results{
95-
{
96-
Target: "testdata/fixtures/sbom/centos-7-cyclonedx.intoto.jsonl (centos 7.6.1810)",
97-
Vulnerabilities: []types.DetectedVulnerability{
98-
{
99-
PkgIdentifier: ftypes.PkgIdentifier{
100-
BOMRef: "pkg:rpm/centos/[email protected]?arch=x86_64&distro=centos-7.6.1810",
101-
},
102-
},
103-
{
104-
PkgIdentifier: ftypes.PkgIdentifier{
105-
BOMRef: "pkg:rpm/centos/[email protected]?arch=x86_64&epoch=1&distro=centos-7.6.1810",
106-
},
107-
},
108-
{
109-
PkgIdentifier: ftypes.PkgIdentifier{
110-
BOMRef: "pkg:rpm/centos/[email protected]?arch=x86_64&epoch=1&distro=centos-7.6.1810",
111-
},
112-
},
113-
},
114-
},
115-
},
77+
override: func(t *testing.T, want, got *types.Report) {
78+
want.ArtifactName = "testdata/fixtures/sbom/centos-7-cyclonedx.intoto.jsonl"
79+
want.ArtifactType = ftypes.ArtifactCycloneDX
80+
81+
require.Len(t, got.Results, 1)
82+
want.Results[0].Target = "testdata/fixtures/sbom/centos-7-cyclonedx.intoto.jsonl (centos 7.6.1810)"
83+
84+
require.Len(t, got.Results[0].Vulnerabilities, 3)
85+
want.Results[0].Vulnerabilities[0].PkgIdentifier.BOMRef = "pkg:rpm/centos/[email protected]?arch=x86_64&distro=centos-7.6.1810"
86+
want.Results[0].Vulnerabilities[1].PkgIdentifier.BOMRef = "pkg:rpm/centos/[email protected]?arch=x86_64&epoch=1&distro=centos-7.6.1810"
87+
want.Results[0].Vulnerabilities[2].PkgIdentifier.BOMRef = "pkg:rpm/centos/[email protected]?arch=x86_64&epoch=1&distro=centos-7.6.1810"
11688
},
11789
},
11890
{
@@ -123,14 +95,12 @@ func TestSBOM(t *testing.T) {
12395
artifactType: "spdx",
12496
},
12597
golden: "testdata/centos-7.json.golden",
126-
override: types.Report{
127-
ArtifactName: "testdata/fixtures/sbom/centos-7-spdx.txt",
128-
ArtifactType: ftypes.ArtifactType("spdx"),
129-
Results: types.Results{
130-
{
131-
Target: "testdata/fixtures/sbom/centos-7-spdx.txt (centos 7.6.1810)",
132-
},
133-
},
98+
override: func(t *testing.T, want, got *types.Report) {
99+
want.ArtifactName = "testdata/fixtures/sbom/centos-7-spdx.txt"
100+
want.ArtifactType = ftypes.ArtifactSPDX
101+
102+
require.Len(t, got.Results, 1)
103+
want.Results[0].Target = "testdata/fixtures/sbom/centos-7-spdx.txt (centos 7.6.1810)"
134104
},
135105
},
136106
{
@@ -141,14 +111,12 @@ func TestSBOM(t *testing.T) {
141111
artifactType: "spdx",
142112
},
143113
golden: "testdata/centos-7.json.golden",
144-
override: types.Report{
145-
ArtifactName: "testdata/fixtures/sbom/centos-7-spdx.json",
146-
ArtifactType: ftypes.ArtifactType("spdx"),
147-
Results: types.Results{
148-
{
149-
Target: "testdata/fixtures/sbom/centos-7-spdx.json (centos 7.6.1810)",
150-
},
151-
},
114+
override: func(t *testing.T, want, got *types.Report) {
115+
want.ArtifactName = "testdata/fixtures/sbom/centos-7-spdx.json"
116+
want.ArtifactType = ftypes.ArtifactSPDX
117+
118+
require.Len(t, got.Results, 1)
119+
want.Results[0].Target = "testdata/fixtures/sbom/centos-7-spdx.json (centos 7.6.1810)"
152120
},
153121
},
154122
{
@@ -195,20 +163,30 @@ func TestSBOM(t *testing.T) {
195163
osArgs = append(osArgs, tt.args.input)
196164

197165
// Run "trivy sbom"
198-
err := execute(osArgs)
199-
assert.NoError(t, err)
200-
201-
// Compare want and got
202-
switch tt.args.format {
203-
case "json":
204-
compareSBOMReports(t, tt.golden, outputFile, tt.override)
205-
default:
206-
require.Fail(t, "invalid format", "format: %s", tt.args.format)
207-
}
166+
runTest(t, osArgs, tt.golden, outputFile, types.Format(tt.args.format), runOptions{
167+
override: overrideFuncs(overrideSBOMReport, overrideUID, tt.override),
168+
})
208169
})
209170
}
210171
}
211172

173+
func overrideSBOMReport(t *testing.T, want, got *types.Report) {
174+
want.Metadata.ImageID = ""
175+
want.Metadata.ImageConfig = v1.ConfigFile{}
176+
want.Metadata.DiffIDs = nil
177+
for i, result := range want.Results {
178+
for j := range result.Vulnerabilities {
179+
want.Results[i].Vulnerabilities[j].Layer.DiffID = ""
180+
}
181+
}
182+
183+
// when running on Windows FS
184+
got.ArtifactName = filepath.ToSlash(filepath.Clean(got.ArtifactName))
185+
for i, result := range got.Results {
186+
got.Results[i].Target = filepath.ToSlash(filepath.Clean(result.Target))
187+
}
188+
}
189+
212190
// TODO(teppei): merge into compareReports
213191
func compareSBOMReports(t *testing.T, wantFile, gotFile string, overrideWant types.Report) {
214192
want := readReport(t, wantFile)

integration/testdata/almalinux-8.json.golden

+2-1
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,8 @@
5757
"PkgID": "[email protected]_64",
5858
"PkgName": "openssl-libs",
5959
"PkgIdentifier": {
60-
"PURL": "pkg:rpm/alma/[email protected]?arch=x86_64\u0026distro=alma-8.5\u0026epoch=1"
60+
"PURL": "pkg:rpm/alma/[email protected]?arch=x86_64\u0026distro=alma-8.5\u0026epoch=1",
61+
"UID": "3f965238234faa63"
6162
},
6263
"InstalledVersion": "1:1.1.1k-4.el8",
6364
"FixedVersion": "1:1.1.1k-5.el8_5",

integration/testdata/alpine-310.json.golden

+8-4
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,8 @@
5959
"PkgID": "[email protected]",
6060
"PkgName": "libcrypto1.1",
6161
"PkgIdentifier": {
62-
"PURL": "pkg:apk/alpine/[email protected]?arch=x86_64\u0026distro=3.10.2"
62+
"PURL": "pkg:apk/alpine/[email protected]?arch=x86_64\u0026distro=3.10.2",
63+
"UID": "c6c116a4441ec6de"
6364
},
6465
"InstalledVersion": "1.1.1c-r0",
6566
"FixedVersion": "1.1.1d-r0",
@@ -131,7 +132,8 @@
131132
"PkgID": "[email protected]",
132133
"PkgName": "libcrypto1.1",
133134
"PkgIdentifier": {
134-
"PURL": "pkg:apk/alpine/[email protected]?arch=x86_64\u0026distro=3.10.2"
135+
"PURL": "pkg:apk/alpine/[email protected]?arch=x86_64\u0026distro=3.10.2",
136+
"UID": "c6c116a4441ec6de"
135137
},
136138
"InstalledVersion": "1.1.1c-r0",
137139
"FixedVersion": "1.1.1d-r2",
@@ -213,7 +215,8 @@
213215
"PkgID": "[email protected]",
214216
"PkgName": "libssl1.1",
215217
"PkgIdentifier": {
216-
"PURL": "pkg:apk/alpine/[email protected]?arch=x86_64\u0026distro=3.10.2"
218+
"PURL": "pkg:apk/alpine/[email protected]?arch=x86_64\u0026distro=3.10.2",
219+
"UID": "e132dcfcc51772ef"
217220
},
218221
"InstalledVersion": "1.1.1c-r0",
219222
"FixedVersion": "1.1.1d-r0",
@@ -285,7 +288,8 @@
285288
"PkgID": "[email protected]",
286289
"PkgName": "libssl1.1",
287290
"PkgIdentifier": {
288-
"PURL": "pkg:apk/alpine/[email protected]?arch=x86_64\u0026distro=3.10.2"
291+
"PURL": "pkg:apk/alpine/[email protected]?arch=x86_64\u0026distro=3.10.2",
292+
"UID": "e132dcfcc51772ef"
289293
},
290294
"InstalledVersion": "1.1.1c-r0",
291295
"FixedVersion": "1.1.1d-r2",

0 commit comments

Comments
 (0)