Skip to content

Commit 7422cc7

Browse files
authored
fix(vex): don't use reused BOM (aquasecurity#9604)
1 parent b9e3e0b commit 7422cc7

File tree

6 files changed

+45
-14
lines changed

6 files changed

+45
-14
lines changed

pkg/sbom/cyclonedx/marshal.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,8 +56,7 @@ func NewMarshaler(version string) Marshaler {
5656
// MarshalReport converts the Trivy report to the CycloneDX format
5757
func (m *Marshaler) MarshalReport(ctx context.Context, report types.Report) (*cdx.BOM, error) {
5858
// Convert into an intermediate representation
59-
opts := core.Options{GenerateBOMRef: true}
60-
bom, err := sbomio.NewEncoder(opts).Encode(report)
59+
bom, err := sbomio.NewEncoder(sbomio.WithBOMRef()).Encode(report)
6160
if err != nil {
6261
return nil, xerrors.Errorf("failed to marshal report: %w", err)
6362
}

pkg/sbom/io/encode.go

Lines changed: 35 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,21 +19,49 @@ import (
1919
"github.com/aquasecurity/trivy/pkg/types"
2020
)
2121

22+
type EncoderOption func(*Encoder)
23+
24+
// WithBOMRef enables BOM-Ref generation for CycloneDX components
25+
func WithBOMRef() EncoderOption {
26+
return func(e *Encoder) {
27+
e.bomOpts.GenerateBOMRef = true
28+
}
29+
}
30+
31+
// WithParents enables holding parent maps in the BOM structure
32+
func WithParents() EncoderOption {
33+
return func(e *Encoder) {
34+
e.bomOpts.Parents = true
35+
}
36+
}
37+
38+
// ForceRegenerate forces regeneration of BOM instead of reusing existing one
39+
func ForceRegenerate() EncoderOption {
40+
return func(e *Encoder) {
41+
e.forceRegenerate = true
42+
}
43+
}
44+
2245
type Encoder struct {
23-
bom *core.BOM
24-
opts core.Options
46+
bom *core.BOM
47+
bomOpts core.Options
48+
forceRegenerate bool
2549
}
2650

27-
func NewEncoder(opts core.Options) *Encoder {
28-
return &Encoder{opts: opts}
51+
func NewEncoder(opts ...EncoderOption) *Encoder {
52+
e := &Encoder{}
53+
for _, opt := range opts {
54+
opt(e)
55+
}
56+
return e
2957
}
3058

3159
func (e *Encoder) Encode(report types.Report) (*core.BOM, error) {
32-
// When report.BOM is not nil, reuse the existing BOM structure.
60+
// When report.BOM is not nil, reuse the existing BOM structure unless ForceRegenerate is set.
3361
// This happens in two scenarios:
3462
// 1. SBOM scanning: When scanning an existing SBOM file to refresh vulnerabilities
3563
// 2. Library usage: When using Trivy as a library with a custom BOM in the report
36-
if report.BOM != nil {
64+
if report.BOM != nil && !e.forceRegenerate {
3765
return e.reuseExistingBOM(report)
3866
}
3967
// Metadata component
@@ -42,7 +70,7 @@ func (e *Encoder) Encode(report types.Report) (*core.BOM, error) {
4270
return nil, xerrors.Errorf("failed to create root component: %w", err)
4371
}
4472

45-
e.bom = core.NewBOM(e.opts)
73+
e.bom = core.NewBOM(e.bomOpts)
4674
if report.BOM != nil {
4775
e.bom.SerialNumber = report.BOM.SerialNumber
4876
e.bom.Version = report.BOM.Version

pkg/sbom/io/encode_test.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1466,8 +1466,7 @@ func TestEncoder_Encode(t *testing.T) {
14661466
t.Run(tt.name, func(t *testing.T) {
14671467
uuid.SetFakeUUID(t, "3ff14136-e09f-4df9-80ea-%012d")
14681468

1469-
opts := core.Options{GenerateBOMRef: true}
1470-
got, err := sbomio.NewEncoder(opts).Encode(tt.report)
1469+
got, err := sbomio.NewEncoder(sbomio.WithBOMRef()).Encode(tt.report)
14711470
if tt.wantErr != "" {
14721471
require.ErrorContains(t, err, tt.wantErr)
14731472
return

pkg/sbom/spdx/marshal.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ func NewMarshaler(version string, opts ...marshalOption) *Marshaler {
115115

116116
func (m *Marshaler) MarshalReport(ctx context.Context, report types.Report) (*spdx.Document, error) {
117117
// Convert into an intermediate representation
118-
bom, err := sbomio.NewEncoder(core.Options{}).Encode(report)
118+
bom, err := sbomio.NewEncoder().Encode(report)
119119
if err != nil {
120120
return nil, xerrors.Errorf("failed to marshal report: %w", err)
121121
}

pkg/vex/vex.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ func Filter(ctx context.Context, report *types.Report, opts Options) error {
7575
}
7676

7777
// NOTE: This method call has a side effect on the report
78-
bom, err := sbomio.NewEncoder(core.Options{Parents: true}).Encode(*report)
78+
bom, err := sbomio.NewEncoder(sbomio.WithParents(), sbomio.ForceRegenerate()).Encode(*report)
7979
if err != nil {
8080
return xerrors.Errorf("unable to encode the SBOM: %w", err)
8181
}

pkg/vex/vex_test.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -665,13 +665,18 @@ func createCycloneDXBOMWithSpringComponent() *core.BOM {
665665
bom := core.NewBOM(core.Options{})
666666
bom.SerialNumber = "urn:uuid:3e671687-395b-41f5-a30f-a58921a69b79"
667667
bom.Version = 1
668+
pkgIdentifier := ftypes.PkgIdentifier{
669+
// Components got from scanned SBOM files don't have UID
670+
BOMRef: springPackage.Identifier.BOMRef,
671+
PURL: springPackage.Identifier.PURL,
672+
}
668673
// Add the spring component to match vuln1's BOM-Ref
669674
springComponent := &core.Component{
670675
Type: core.TypeLibrary,
671676
Name: springPackage.Identifier.PURL.Name,
672677
Group: springPackage.Identifier.PURL.Namespace,
673678
Version: springPackage.Version,
674-
PkgIdentifier: springPackage.Identifier,
679+
PkgIdentifier: pkgIdentifier,
675680
}
676681
bom.AddComponent(springComponent)
677682
return bom

0 commit comments

Comments
 (0)