fix(vex): don't use reused BOM (#9604)

This commit is contained in:
DmitriyLewen
2025-10-08 15:45:43 +06:00
committed by GitHub
parent b9e3e0b5f9
commit 7422cc7168
6 changed files with 46 additions and 15 deletions

View File

@@ -56,8 +56,7 @@ func NewMarshaler(version string) Marshaler {
// MarshalReport converts the Trivy report to the CycloneDX format
func (m *Marshaler) MarshalReport(ctx context.Context, report types.Report) (*cdx.BOM, error) {
// Convert into an intermediate representation
opts := core.Options{GenerateBOMRef: true}
bom, err := sbomio.NewEncoder(opts).Encode(report)
bom, err := sbomio.NewEncoder(sbomio.WithBOMRef()).Encode(report)
if err != nil {
return nil, xerrors.Errorf("failed to marshal report: %w", err)
}

View File

@@ -19,21 +19,49 @@ import (
"github.com/aquasecurity/trivy/pkg/types"
)
type Encoder struct {
bom *core.BOM
opts core.Options
type EncoderOption func(*Encoder)
// WithBOMRef enables BOM-Ref generation for CycloneDX components
func WithBOMRef() EncoderOption {
return func(e *Encoder) {
e.bomOpts.GenerateBOMRef = true
}
}
func NewEncoder(opts core.Options) *Encoder {
return &Encoder{opts: opts}
// WithParents enables holding parent maps in the BOM structure
func WithParents() EncoderOption {
return func(e *Encoder) {
e.bomOpts.Parents = true
}
}
// ForceRegenerate forces regeneration of BOM instead of reusing existing one
func ForceRegenerate() EncoderOption {
return func(e *Encoder) {
e.forceRegenerate = true
}
}
type Encoder struct {
bom *core.BOM
bomOpts core.Options
forceRegenerate bool
}
func NewEncoder(opts ...EncoderOption) *Encoder {
e := &Encoder{}
for _, opt := range opts {
opt(e)
}
return e
}
func (e *Encoder) Encode(report types.Report) (*core.BOM, error) {
// When report.BOM is not nil, reuse the existing BOM structure.
// When report.BOM is not nil, reuse the existing BOM structure unless ForceRegenerate is set.
// This happens in two scenarios:
// 1. SBOM scanning: When scanning an existing SBOM file to refresh vulnerabilities
// 2. Library usage: When using Trivy as a library with a custom BOM in the report
if report.BOM != nil {
if report.BOM != nil && !e.forceRegenerate {
return e.reuseExistingBOM(report)
}
// Metadata component
@@ -42,7 +70,7 @@ func (e *Encoder) Encode(report types.Report) (*core.BOM, error) {
return nil, xerrors.Errorf("failed to create root component: %w", err)
}
e.bom = core.NewBOM(e.opts)
e.bom = core.NewBOM(e.bomOpts)
if report.BOM != nil {
e.bom.SerialNumber = report.BOM.SerialNumber
e.bom.Version = report.BOM.Version

View File

@@ -1466,8 +1466,7 @@ func TestEncoder_Encode(t *testing.T) {
t.Run(tt.name, func(t *testing.T) {
uuid.SetFakeUUID(t, "3ff14136-e09f-4df9-80ea-%012d")
opts := core.Options{GenerateBOMRef: true}
got, err := sbomio.NewEncoder(opts).Encode(tt.report)
got, err := sbomio.NewEncoder(sbomio.WithBOMRef()).Encode(tt.report)
if tt.wantErr != "" {
require.ErrorContains(t, err, tt.wantErr)
return

View File

@@ -115,7 +115,7 @@ func NewMarshaler(version string, opts ...marshalOption) *Marshaler {
func (m *Marshaler) MarshalReport(ctx context.Context, report types.Report) (*spdx.Document, error) {
// Convert into an intermediate representation
bom, err := sbomio.NewEncoder(core.Options{}).Encode(report)
bom, err := sbomio.NewEncoder().Encode(report)
if err != nil {
return nil, xerrors.Errorf("failed to marshal report: %w", err)
}

View File

@@ -75,7 +75,7 @@ func Filter(ctx context.Context, report *types.Report, opts Options) error {
}
// NOTE: This method call has a side effect on the report
bom, err := sbomio.NewEncoder(core.Options{Parents: true}).Encode(*report)
bom, err := sbomio.NewEncoder(sbomio.WithParents(), sbomio.ForceRegenerate()).Encode(*report)
if err != nil {
return xerrors.Errorf("unable to encode the SBOM: %w", err)
}

View File

@@ -665,13 +665,18 @@ func createCycloneDXBOMWithSpringComponent() *core.BOM {
bom := core.NewBOM(core.Options{})
bom.SerialNumber = "urn:uuid:3e671687-395b-41f5-a30f-a58921a69b79"
bom.Version = 1
pkgIdentifier := ftypes.PkgIdentifier{
// Components got from scanned SBOM files don't have UID
BOMRef: springPackage.Identifier.BOMRef,
PURL: springPackage.Identifier.PURL,
}
// Add the spring component to match vuln1's BOM-Ref
springComponent := &core.Component{
Type: core.TypeLibrary,
Name: springPackage.Identifier.PURL.Name,
Group: springPackage.Identifier.PURL.Namespace,
Version: springPackage.Version,
PkgIdentifier: springPackage.Identifier,
PkgIdentifier: pkgIdentifier,
}
bom.AddComponent(springComponent)
return bom