Skip to content

Commit

Permalink
internal/sarif,cmd/govulncheck: publicize sarif
Browse files Browse the repository at this point in the history
Also, add and improve documentation.

Fixes golang/go#61347

Change-Id: Ia631615b40b9d23be6efa615b573be6c465fa36b
Reviewed-on: https://go-review.googlesource.com/c/vuln/+/580955
LUCI-TryBot-Result: Go LUCI <[email protected]>
TryBot-Result: Gopher Robot <[email protected]>
Reviewed-by: Ian Cottrell <[email protected]>
Run-TryBot: Zvonimir Pavlinovic <[email protected]>
  • Loading branch information
zpavlinovic committed May 21, 2024
1 parent 7b455ee commit 6b0fd56
Show file tree
Hide file tree
Showing 4 changed files with 73 additions and 36 deletions.
23 changes: 16 additions & 7 deletions cmd/govulncheck/doc.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@ only those that could affect the application.
By default, govulncheck makes requests to the Go vulnerability database at
https://vuln.go.dev. Requests to the vulnerability database contain only module
paths, not code or other properties of your program. See
https://vuln.go.dev/privacy.html for more. Use the -db flag to specify a
different database, which must implement the specification at
https://go.dev/security/vuln/database.
paths with vulnerabilities already known to the database, not code or other
properties of your program. See https://vuln.go.dev/privacy.html for more.
Use the -db flag to specify a different database, which must implement the
specification at https://go.dev/security/vuln/database.
Govulncheck looks for vulnerabilities in Go programs using a specific build
configuration. For analyzing source code, that configuration is the Go version
Expand Down Expand Up @@ -59,12 +59,21 @@ information needed to analyze the binary. This will produce a blob, typically mu
smaller than the binary, that can also be passed to govulncheck as an argument with
'-mode binary'. The users should not rely on the contents or representation of the blob.
Govulncheck exits successfully (exit code 0) if there are no vulnerabilities,
and exits unsuccessfully if there are. It also exits successfully if the -json flag
(or '-format json') is provided, regardless of the number of detected vulnerabilities.
# Integrations
Govulncheck supports streaming JSON. For more details, please see [golang.org/x/vuln/internal/govulncheck].
Govulncheck also supports Static Analysis Results Interchange Format (SARIF) output
format, following the specification at https://www.oasis-open.org/committees/tc_home.php?wg_abbrev=sarif.
For more details, please see [golang.org/x/vuln/internal/sarif].
# Exit codes
Govulncheck exits successfully (exit code 0) if there are no vulnerabilities,
and exits unsuccessfully if there are. It also exits successfully if the
'format -json' ('-json') or '-format sarif' is provided, regardless of the number
of detected vulnerabilities.
# Limitations
Govulncheck has these limitations:
Expand Down
2 changes: 1 addition & 1 deletion cmd/govulncheck/testdata/common/testfiles/usage/usage.ct
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ Usage:
vulnerability database url (default "https://vuln.go.dev")
-format value
specify format output
The supported values are 'text' and 'json' (default 'text')
The supported values are 'text', 'json', and 'sarif' (default 'text')
-json
output JSON (Go compatible legacy flag, see format flag)
-mode value
Expand Down
82 changes: 55 additions & 27 deletions internal/sarif/sarif.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,41 @@
// The implementation covers the subset of the specification available
// at https://www.oasis-open.org/committees/tc_home.php?wg_abbrev=sarif.
//
// If govulncheck is used in source mode, the locations will include a
// physical location implemented as a path relative to either the source
// module (%SRCROOT%), Go root (%GOROOT%), or Go module cache (%GOMODCACHE%)
// URI base id.
// The sarif encoding models govulncheck findings as Results. Each
// Result encodes findings for a unique OSV entry at the most precise
// detected level only. CodeFlows summarize call stacks, similar to govulncheck
// textual output, while Stacks contain call stack information verbatim.
//
// The result Levels are defined by the govulncheck.ScanLevel and the most
// precise level at which the finding was detected. Result error is produced
// when the finding level matches the user desired level of scan precision;
// all other finding levels are then classified as progressively weaker.
// For instance, if the user specified symbol scan level and govulncheck
// detected a use of a vulnerable symbol, then the Result will have error
// Level. If the symbol was not used but its package was imported, then the
// Result Level is warning, and so on.
//
// Each Result is attached to the first line of the go.mod file. Other
// ArtifactLocations are paths relative to their enclosing modules.
// Similar to JSON output format, this makes govulncheck sarif locations
// portable.
//
// The relative paths in PhysicalLocations also come with a URIBaseID offset.
// Paths for the source module analyzed, the Go standard library, and third-party
// dependencies are relative to %SRCROOT%, %GOROOT%, and %GOMODCACHE% offsets,
// resp. We note that the URIBaseID offsets are not explicitly defined in
// the sarif output. It is the clients responsibility to set them to resolve
// paths at their local machines.
//
// All paths use "/" delimiter for portability.
//
// Properties field of a Tool.Driver is a govulncheck.Config used for the
// invocation of govulncheck producing the Results. Properties field of
// a Rule contains information on CVE and GHSA aliases for the corresponding
// rule OSV. Clients can use this information to, say, supress and filter
// vulnerabilities.
//
// Please see the definition of types below for more information.
package sarif

import "golang.org/x/vuln/internal/govulncheck"
Expand All @@ -34,7 +65,7 @@ type Log struct {
type Run struct {
Tool Tool `json:"tool,omitempty"`
// Results contain govulncheck findings. There should be exactly one
// Result per a detected OSV.
// Result per a detected use of an OSV.
Results []Result `json:"results,omitempty"`
}

Expand All @@ -45,11 +76,11 @@ type Tool struct {

// Driver provides details about the govulncheck binary being executed.
type Driver struct {
// Name should be "govulncheck"
// Name is "govulncheck"
Name string `json:"name,omitempty"`
// Version should be the govulncheck version
// Version is the govulncheck version
Version string `json:"semanticVersion,omitempty"`
// InformationURI should point to the description of govulncheck tool
// InformationURI points to the description of govulncheck tool
InformationURI string `json:"informationUri,omitempty"`
// Properties are govulncheck run metadata, such as vuln db, Go version, etc.
Properties govulncheck.Config `json:"properties,omitempty"`
Expand All @@ -66,9 +97,9 @@ type Rule struct {
FullDescription Description `json:"fullDescription,omitempty"`
Help Description `json:"help,omitempty"`
HelpURI string `json:"helpUri,omitempty"`
// Properties should contain OSV.Aliases (CVEs and GHSAs) as tags.
// Properties contain OSV.Aliases (CVEs and GHSAs) as tags.
// Consumers of govulncheck SARIF can use these tags to filter
// results based on, say, CVEs.
// results.
Properties RuleTags `json:"properties,omitempty"`
}

Expand All @@ -85,27 +116,28 @@ type Description struct {

// Result is a set of govulncheck findings for an OSV. For call stack
// mode, it will contain call stacks for the OSV. There is exactly
// one Result per detected OSV. Only findings at the lowest possible
// level appear in the Result. For instance, if there are findings
// with call stacks for an OSV, those findings will be in the Result,
// but not the “imports” and “requires” findings for the same OSV.
// one Result per detected OSV. Only findings at the most precise
// detected level appear in the Result. For instance, if there are
// symbol findings for an OSV, those findings will be in the Result,
// but not the package and module level findings for the same OSV.
type Result struct {
// RuleID is the Rule.ID/OSV producing the finding.
RuleID string `json:"ruleId,omitempty"`
// Level is one of "error", "warning", "note", and "none".
// Level is one of "error", "warning", and "note".
Level string `json:"level,omitempty"`
// Message explains the overall findings.
Message Description `json:"message,omitempty"`
// Locations to which the findings are associated.
// Locations to which the findings are associated. Always
// a single location pointing to the first line of the go.mod
// file. The path to the file is "go.mod".
Locations []Location `json:"locations,omitempty"`
// CodeFlows can encode call stacks produced by govulncheck.
// CodeFlows summarize call stacks produced by govulncheck.
CodeFlows []CodeFlow `json:"codeFlows,omitempty"`
// Stacks can encode call stacks produced by govulncheck.
// Stacks encode call stacks produced by govulncheck.
Stacks []Stack `json:"stacks,omitempty"`
// TODO: support Fixes when integration points to the same
}

// CodeFlow describes a detected offending flow of information in terms of
// CodeFlow summarizes a detected offending flow of information in terms of
// code locations. More precisely, it can contain several related information
// flows, keeping them together. In govulncheck, those can be all call stacks
// for, say, a particular symbol or package.
Expand All @@ -128,8 +160,6 @@ type ThreadFlowLocation struct {
Module string `json:"module,omitempty"`
// Location also contains a Message field.
Location Location `json:"location,omitempty"`
// Can also contain Stack field that encodes a call stack
// leading to this thread flow location.
}

// Stack is a sequence of frames and can encode a govulncheck call stack.
Expand Down Expand Up @@ -167,12 +197,10 @@ const (

// ArtifactLocation is a path to an offending file.
type ArtifactLocation struct {
// URI is a path to the artifact. If URIBaseID is empty, then
// URI is absolute and it needs to start with, say, "file://."
// URI is a path relative to URIBaseID.
URI string `json:"uri,omitempty"`
// URIBaseID is offset for URI. An example is %SRCROOT%, used by
// Github Code Scanning to point to the root of the target repo.
// Its value must be defined in URIBaseIDs of a Run.
// URIBaseID is offset for URI, one of %SRCROOT%, %GOROOT%,
// and %GOMODCACHE%.
URIBaseID string `json:"uriBaseId,omitempty"`
}

Expand Down
2 changes: 1 addition & 1 deletion internal/scan/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ func parseFlags(cfg *config, stderr io.Writer, args []string) error {
flags.Var(&modeFlag, "mode", "supports 'source', 'binary', and 'extract' (default 'source')")
flags.Var(&cfg.tags, "tags", "comma-separated `list` of build tags")
flags.Var(&cfg.show, "show", "enable display of additional information specified by the comma separated `list`\nThe supported values are 'traces','color', 'version', and 'verbose'")
flags.Var(&cfg.format, "format", "specify format output\nThe supported values are 'text' and 'json' (default 'text')")
flags.Var(&cfg.format, "format", "specify format output\nThe supported values are 'text', 'json', and 'sarif' (default 'text')")
flags.BoolVar(&version, "version", false, "print the version information")
flags.Var(&scanFlag, "scan", "set the scanning level desired, one of 'module', 'package', or 'symbol' (default 'symbol')")

Expand Down

0 comments on commit 6b0fd56

Please sign in to comment.