From fd700f319276bd3754b00af24ce1d020c453652e Mon Sep 17 00:00:00 2001 From: Maceo Thompson Date: Wed, 25 Sep 2024 13:03:25 -0400 Subject: [PATCH] internal/govulncheck: add sbom message type Adds a minimal SBOM message to govulncheck output. This message contains information about the code that govulncheck is scanning, like the go version used for the standard library, modules and their version, and the root packages gathered from the user inputted package patterns. Change-Id: I5db597ffaaa654394faea8dda82e1f18c5f5975a Reviewed-on: https://go-review.googlesource.com/c/vuln/+/616061 Reviewed-by: Zvonimir Pavlinovic LUCI-TryBot-Result: Go LUCI --- internal/govulncheck/govulncheck.go | 24 ++++++++++++++++++++++++ internal/govulncheck/handler.go | 6 ++++++ internal/govulncheck/jsonhandler.go | 5 +++++ internal/openvex/handler.go | 6 ++++++ internal/sarif/handler.go | 5 +++++ internal/scan/text.go | 5 +++++ internal/test/handler.go | 11 +++++++++++ internal/vulncheck/binary.go | 2 ++ internal/vulncheck/source.go | 2 ++ 9 files changed, 66 insertions(+) diff --git a/internal/govulncheck/govulncheck.go b/internal/govulncheck/govulncheck.go index 9c6c2cb6..377a3787 100644 --- a/internal/govulncheck/govulncheck.go +++ b/internal/govulncheck/govulncheck.go @@ -39,6 +39,7 @@ const ( type Message struct { Config *Config `json:"config,omitempty"` Progress *Progress `json:"progress,omitempty"` + SBOM *SBOM `json:"SBOM,omitempty"` // OSV is emitted for every vulnerability in the current database // that applies to user modules regardless of their version. If a // module is being used at a vulnerable version, the corresponding @@ -85,6 +86,29 @@ type Config struct { ScanMode ScanMode `json:"scan_mode,omitempty"` } +// SBOM contains minimal information about the artifacts govulncheck is scanning. +type SBOM struct { + // The go version used by govulncheck when scanning, which also defines + // the version of the standard library used for detecting vulns. + GoVersion string `json:"go_version,omitempty"` + + // The set of modules included in the scan. + Modules []*Module `json:"modules,omitempty"` + + // The roots of the scan, as package paths. + // For binaries, this will be the main package. + // For source code, this will be the packages matching the provided package patterns. + Roots []string `json:"roots,omitempty"` +} + +type Module struct { + // The full module path. + Path string `json:"path,omitempty"` + + // The version of the module. + Version string `json:"version,omitempty"` +} + // Progress messages are informational only, intended to allow users to monitor // the progress of a long running scan. // A stream must remain fully valid and able to be interpreted with all progress diff --git a/internal/govulncheck/handler.go b/internal/govulncheck/handler.go index 5676ea94..5ce7d532 100644 --- a/internal/govulncheck/handler.go +++ b/internal/govulncheck/handler.go @@ -17,6 +17,9 @@ type Handler interface { // Config communicates introductory message to the user. Config(config *Config) error + // SBOM shows information about what govulncheck is scanning. + SBOM(sbom *SBOM) error + // Progress is called to display a progress message. Progress(progress *Progress) error @@ -45,6 +48,9 @@ func HandleJSON(from io.Reader, to Handler) error { if msg.Progress != nil { err = to.Progress(msg.Progress) } + if msg.SBOM != nil { + err = to.SBOM(msg.SBOM) + } if msg.OSV != nil { err = to.OSV(msg.OSV) } diff --git a/internal/govulncheck/jsonhandler.go b/internal/govulncheck/jsonhandler.go index d1ea78af..b1586e09 100644 --- a/internal/govulncheck/jsonhandler.go +++ b/internal/govulncheck/jsonhandler.go @@ -33,6 +33,11 @@ func (h *jsonHandler) Progress(progress *Progress) error { return h.enc.Encode(Message{Progress: progress}) } +// SBOM writes the SBOM block in JSON to the underlying writer. +func (h *jsonHandler) SBOM(sbom *SBOM) error { + return h.enc.Encode(Message{SBOM: sbom}) +} + // OSV writes an osv entry in JSON to the underlying writer. func (h *jsonHandler) OSV(entry *osv.Entry) error { return h.enc.Encode(Message{OSV: entry}) diff --git a/internal/openvex/handler.go b/internal/openvex/handler.go index 374c4d8a..a2d32829 100644 --- a/internal/openvex/handler.go +++ b/internal/openvex/handler.go @@ -28,6 +28,7 @@ const ( type handler struct { w io.Writer cfg *govulncheck.Config + sbom *govulncheck.SBOM osvs map[string]*osv.Entry // findings contains same-level findings for an // OSV at the most precise level of granularity @@ -54,6 +55,11 @@ func (h *handler) Progress(progress *govulncheck.Progress) error { return nil } +func (h *handler) SBOM(s *govulncheck.SBOM) error { + h.sbom = s + return nil +} + func (h *handler) OSV(e *osv.Entry) error { h.osvs[e.ID] = e return nil diff --git a/internal/sarif/handler.go b/internal/sarif/handler.go index 34248578..3d6f6894 100644 --- a/internal/sarif/handler.go +++ b/internal/sarif/handler.go @@ -37,6 +37,7 @@ func NewHandler(w io.Writer) *handler { findings: make(map[string][]*govulncheck.Finding), } } + func (h *handler) Config(c *govulncheck.Config) error { h.cfg = c return nil @@ -46,6 +47,10 @@ func (h *handler) Progress(p *govulncheck.Progress) error { return nil // not needed by sarif } +func (h *handler) SBOM(s *govulncheck.SBOM) error { + return nil // not needed by sarif +} + func (h *handler) OSV(e *osv.Entry) error { h.osvs[e.ID] = e return nil diff --git a/internal/scan/text.go b/internal/scan/text.go index e82ecf3e..20dce778 100644 --- a/internal/scan/text.go +++ b/internal/scan/text.go @@ -115,6 +115,11 @@ func (h *TextHandler) Config(config *govulncheck.Config) error { return h.err } +func (h *TextHandler) SBOM(info *govulncheck.SBOM) error { + // TODO: implement sbom in text mode + return nil +} + // Progress writes progress updates during govulncheck execution. func (h *TextHandler) Progress(progress *govulncheck.Progress) error { if h.showVerbose { diff --git a/internal/test/handler.go b/internal/test/handler.go index c577e2af..0d71d463 100644 --- a/internal/test/handler.go +++ b/internal/test/handler.go @@ -17,6 +17,7 @@ import ( // For use in tests. type MockHandler struct { ConfigMessages []*govulncheck.Config + SBOMMessages []*govulncheck.SBOM ProgressMessages []*govulncheck.Progress OSVMessages []*osv.Entry FindingMessages []*govulncheck.Finding @@ -31,6 +32,11 @@ func (h *MockHandler) Config(config *govulncheck.Config) error { return nil } +func (h *MockHandler) SBOM(sbom *govulncheck.SBOM) error { + h.SBOMMessages = append(h.SBOMMessages, sbom) + return nil +} + func (h *MockHandler) Progress(progress *govulncheck.Progress) error { h.ProgressMessages = append(h.ProgressMessages, progress) return nil @@ -91,6 +97,11 @@ func (h *MockHandler) Write(to govulncheck.Handler) error { return err } } + for _, sbom := range h.SBOMMessages { + if err := to.SBOM(sbom); err != nil { + return err + } + } seen := map[string]bool{} for _, finding := range h.FindingMessages { if !seen[finding.OSV] { diff --git a/internal/vulncheck/binary.go b/internal/vulncheck/binary.go index 8084a521..73da4c95 100644 --- a/internal/vulncheck/binary.go +++ b/internal/vulncheck/binary.go @@ -47,6 +47,8 @@ func Binary(ctx context.Context, handler govulncheck.Handler, bin *Bin, cfg *gov // It does not compute call graphs so the corresponding // info in Result will be empty. func binary(ctx context.Context, handler govulncheck.Handler, bin *Bin, cfg *govulncheck.Config, client *client.Client) (*Result, error) { + // TODO: Pass SBOM to handler + graph := NewPackageGraph(bin.GoVersion) mods := append(bin.Modules, graph.GetModule(internal.GoStdModulePath)) if bin.Main != nil { diff --git a/internal/vulncheck/source.go b/internal/vulncheck/source.go index 975ecb49..fe107466 100644 --- a/internal/vulncheck/source.go +++ b/internal/vulncheck/source.go @@ -57,6 +57,8 @@ func source(ctx context.Context, handler govulncheck.Handler, cfg *govulncheck.C }() } + // TODO: pass SBOM to handler + if err := handler.Progress(&govulncheck.Progress{Message: fetchingVulnsMessage}); err != nil { return nil, err }