Skip to content

Commit 1f85b27

Browse files
authored
refactor(vex): improve SBOM reference handling with project standards (#8457)
1 parent da0b876 commit 1f85b27

File tree

2 files changed

+79
-68
lines changed

2 files changed

+79
-68
lines changed

pkg/vex/sbomref.go

+26-31
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,12 @@ package vex
22

33
import (
44
"bytes"
5-
"fmt"
5+
"context"
66
"io"
77
"net/http"
88
"net/url"
99

10+
"github.com/samber/lo"
1011
"golang.org/x/xerrors"
1112

1213
"github.com/aquasecurity/trivy/pkg/fanal/artifact"
@@ -20,15 +21,17 @@ type SBOMReferenceSet struct {
2021
}
2122

2223
func NewSBOMReferenceSet(report *types.Report) (*SBOMReferenceSet, error) {
23-
2424
if report.ArtifactType != artifact.TypeCycloneDX {
2525
return nil, xerrors.Errorf("externalReferences can only be used when scanning CycloneDX SBOMs: %w", report.ArtifactType)
2626
}
2727

28-
var externalRefs = report.BOM.ExternalReferences()
28+
ctx := log.WithContextPrefix(context.Background(), "vex")
29+
ctx = log.WithContextAttrs(ctx, log.String("type", "sbom_reference"))
30+
31+
externalRefs := report.BOM.ExternalReferences()
2932
urls := parseToURLs(externalRefs)
3033

31-
v, err := retrieveExternalVEXDocuments(urls, report)
34+
v, err := retrieveExternalVEXDocuments(ctx, urls, report)
3235
if err != nil {
3336
return nil, xerrors.Errorf("failed to fetch external VEX documents: %w", err)
3437
} else if v == nil {
@@ -38,48 +41,41 @@ func NewSBOMReferenceSet(report *types.Report) (*SBOMReferenceSet, error) {
3841
return &SBOMReferenceSet{VEXes: v}, nil
3942
}
4043

41-
func parseToURLs(refs []core.ExternalReference) []url.URL {
42-
var urls []url.URL
43-
for _, ref := range refs {
44-
if ref.Type == core.ExternalReferenceVEX {
45-
val, err := url.Parse(ref.URL)
44+
func parseToURLs(refs []core.ExternalReference) []*url.URL {
45+
return lo.FilterMap(refs, func(ref core.ExternalReference, _ int) (*url.URL, bool) {
46+
if ref.Type != core.ExternalReferenceVEX {
47+
return nil, false
48+
}
49+
val, err := url.Parse(ref.URL)
50+
if err != nil || (val.Scheme != "https" && val.Scheme != "http") {
4651
// do not concern ourselves with relative URLs at this point
47-
if err != nil || (val.Scheme != "https" && val.Scheme != "http") {
48-
continue
49-
}
50-
urls = append(urls, *val)
52+
return nil, false
5153
}
52-
}
53-
return urls
54+
return val, true
55+
})
5456
}
5557

56-
func retrieveExternalVEXDocuments(refs []url.URL, report *types.Report) ([]VEX, error) {
57-
58-
logger := log.WithPrefix("vex").With(log.String("type", "external_reference"))
59-
58+
func retrieveExternalVEXDocuments(ctx context.Context, refs []*url.URL, report *types.Report) ([]VEX, error) {
6059
var docs []VEX
6160
for _, ref := range refs {
62-
doc, err := retrieveExternalVEXDocument(ref, report)
61+
doc, err := retrieveExternalVEXDocument(ctx, ref, report)
6362
if err != nil {
6463
return nil, xerrors.Errorf("failed to retrieve external VEX document: %w", err)
6564
}
6665
docs = append(docs, doc)
6766
}
68-
logger.Debug("Retrieved external VEX documents", "count", len(docs))
67+
log.DebugContext(ctx, "Retrieved external VEX documents", log.Int("count", len(docs)))
6968

7069
if len(docs) == 0 {
71-
logger.Info("No external VEX documents found")
70+
log.DebugContext(ctx, "No external VEX documents found")
7271
return nil, nil
7372
}
7473
return docs, nil
7574

7675
}
7776

78-
func retrieveExternalVEXDocument(vexUrl url.URL, report *types.Report) (VEX, error) {
79-
80-
logger := log.WithPrefix("vex").With(log.String("type", "external_reference"))
81-
82-
logger.Info(fmt.Sprintf("Retrieving external VEX document from host %s", vexUrl.Host))
77+
func retrieveExternalVEXDocument(ctx context.Context, vexUrl *url.URL, report *types.Report) (VEX, error) {
78+
log.DebugContext(ctx, "Retrieving external VEX document", log.String("url", vexUrl.String()))
8379

8480
res, err := http.Get(vexUrl.String())
8581
if err != nil {
@@ -96,15 +92,14 @@ func retrieveExternalVEXDocument(vexUrl url.URL, report *types.Report) (VEX, err
9692
return nil, xerrors.Errorf("unable to read response into memory: %w", err)
9793
}
9894

99-
if v, err := decodeVEX(bytes.NewReader(val), vexUrl.String(), report); err != nil {
95+
v, err := decodeVEX(bytes.NewReader(val), vexUrl.String(), report)
96+
if err != nil {
10097
return nil, xerrors.Errorf("unable to load VEX from external reference: %w", err)
101-
} else {
102-
return v, nil
10398
}
99+
return v, nil
104100
}
105101

106102
func (set *SBOMReferenceSet) NotAffected(vuln types.DetectedVulnerability, product, subComponent *core.Component) (types.ModifiedFinding, bool) {
107-
108103
for _, vex := range set.VEXes {
109104
if m, notAffected := vex.NotAffected(vuln, product, subComponent); notAffected {
110105
return m, notAffected

pkg/vex/sbomref_test.go

+53-37
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"os"
88
"testing"
99

10+
"github.com/samber/lo"
1011
"github.com/stretchr/testify/require"
1112

1213
"github.com/aquasecurity/trivy/pkg/fanal/artifact"
@@ -22,37 +23,32 @@ const (
2223
)
2324

2425
func setUpServer(t *testing.T) *httptest.Server {
25-
s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
26-
println(r.URL.Path)
27-
if r.URL.Path == vexExternalRef {
26+
return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
27+
switch r.URL.Path {
28+
case vexExternalRef:
2829
f, err := os.Open("testdata/" + vexExternalRef + ".json")
2930
if err != nil {
30-
t.Error(err)
31+
t.Fatalf("failed to open testdata: %s", err)
3132
}
32-
3333
defer f.Close()
3434

35-
_, err = io.Copy(w, f)
36-
if err != nil {
37-
t.Error(err)
35+
if _, err = io.Copy(w, f); err != nil {
36+
t.Fatalf("failed to copy testdata: %s", err)
3837
}
39-
} else if r.URL.Path == vexUnknown {
38+
case vexUnknown:
4039
f, err := os.Open("testdata/" + vexUnknown + ".json")
4140
if err != nil {
42-
t.Error(err)
41+
t.Fatalf("failed to open testdata: %s", err)
4342
}
4443
defer f.Close()
4544

46-
_, err = io.Copy(w, f)
47-
if err != nil {
48-
t.Error(err)
45+
if _, err = io.Copy(w, f); err != nil {
46+
t.Fatalf("failed to copy testdata: %s", err)
4947
}
48+
default:
49+
http.NotFound(w, r)
5050
}
51-
52-
http.NotFound(w, r)
53-
return
5451
}))
55-
return s
5652
}
5753

5854
func setupTestReport(s *httptest.Server, path string) *types.Report {
@@ -80,25 +76,45 @@ func TestRetrieveExternalVEXDocuments(t *testing.T) {
8076
s := setUpServer(t)
8177
t.Cleanup(s.Close)
8278

83-
t.Run("external vex retrieval", func(t *testing.T) {
84-
set, err := vex.NewSBOMReferenceSet(setupTestReport(s, vexExternalRef))
85-
require.NoError(t, err)
86-
require.Len(t, set.VEXes, 1)
87-
})
88-
89-
t.Run("incompatible external vex", func(t *testing.T) {
90-
_, err := vex.NewSBOMReferenceSet(setupTestReport(s, vexUnknown))
91-
require.Error(t, err)
92-
})
93-
94-
t.Run("vex not found", func(t *testing.T) {
95-
_, err := vex.NewSBOMReferenceSet(setupTestReport(s, vexNotFound))
96-
require.Error(t, err)
97-
})
79+
tests := []struct {
80+
name string
81+
input *types.Report
82+
wantVEXes int
83+
wantErr bool
84+
}{
85+
{
86+
name: "external vex retrieval",
87+
input: setupTestReport(s, vexExternalRef),
88+
wantVEXes: 1,
89+
wantErr: false,
90+
},
91+
{
92+
name: "incompatible external vex",
93+
input: setupTestReport(s, vexUnknown),
94+
wantErr: true,
95+
},
96+
{
97+
name: "vex not found",
98+
input: setupTestReport(s, vexNotFound),
99+
wantErr: true,
100+
},
101+
{
102+
name: "no external reference",
103+
input: setupEmptyTestReport(),
104+
wantVEXes: 0,
105+
wantErr: false,
106+
},
107+
}
98108

99-
t.Run("no external reference", func(t *testing.T) {
100-
set, err := vex.NewSBOMReferenceSet(setupEmptyTestReport())
101-
require.NoError(t, err)
102-
require.Nil(t, set)
103-
})
109+
for _, tt := range tests {
110+
t.Run(tt.name, func(t *testing.T) {
111+
got, err := vex.NewSBOMReferenceSet(tt.input)
112+
if tt.wantErr {
113+
require.Error(t, err)
114+
return
115+
}
116+
require.NoError(t, err)
117+
require.Len(t, lo.FromPtr(got).VEXes, tt.wantVEXes)
118+
})
119+
}
104120
}

0 commit comments

Comments
 (0)