internal/sarif: compute relative paths for findings
And also make sure the paths are not added in binary mode.
Updates golang/go#61347
Change-Id: If48fe57215cdecb01b8b687fbe042aae584f1d6d
Reviewed-on: https://go-review.googlesource.com/c/vuln/+/558016
Reviewed-by: Maceo Thompson <maceothompson@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Run-TryBot: Zvonimir Pavlinovic <zpavlinovic@google.com>
diff --git a/cmd/govulncheck/testdata/common/testfiles/source-call/source_call_sarif.ct b/cmd/govulncheck/testdata/common/testfiles/source-call/source_call_sarif.ct
index 30598fc..548d608 100644
--- a/cmd/govulncheck/testdata/common/testfiles/source-call/source_call_sarif.ct
+++ b/cmd/govulncheck/testdata/common/testfiles/source-call/source_call_sarif.ct
@@ -113,7 +113,10 @@
"locations": [
{
"physicalLocation": {
- "artifactLocation": {},
+ "artifactLocation": {
+ "uri": "go.mod",
+ "uriBaseId": "%SRCROOT%"
+ },
"region": {
"startLine": 1
}
@@ -131,7 +134,10 @@
"locations": [
{
"physicalLocation": {
- "artifactLocation": {},
+ "artifactLocation": {
+ "uri": "go.mod",
+ "uriBaseId": "%SRCROOT%"
+ },
"region": {
"startLine": 1
}
@@ -148,7 +154,10 @@
"module": "golang.org/vuln",
"location": {
"physicalLocation": {
- "artifactLocation": {},
+ "artifactLocation": {
+ "uri": "vuln.go",
+ "uriBaseId": "%SRCROOT%"
+ },
"region": {
"startLine": 14,
"startColumn": 20
@@ -163,7 +172,10 @@
"module": "github.com/tidwall/gjson",
"location": {
"physicalLocation": {
- "artifactLocation": {},
+ "artifactLocation": {
+ "uri": "gjson.go",
+ "uriBaseId": "%GOMODCACHE%"
+ },
"region": {
"startLine": 297,
"startColumn": 12
@@ -178,7 +190,10 @@
"module": "github.com/tidwall/gjson",
"location": {
"physicalLocation": {
- "artifactLocation": {},
+ "artifactLocation": {
+ "uri": "gjson.go",
+ "uriBaseId": "%GOMODCACHE%"
+ },
"region": {
"startLine": 1881,
"startColumn": 36
@@ -193,7 +208,10 @@
"module": "github.com/tidwall/gjson",
"location": {
"physicalLocation": {
- "artifactLocation": {},
+ "artifactLocation": {
+ "uri": "gjson.go",
+ "uriBaseId": "%GOMODCACHE%"
+ },
"region": {
"startLine": 220,
"startColumn": 17
@@ -222,7 +240,10 @@
"module": "golang.org/vuln",
"location": {
"physicalLocation": {
- "artifactLocation": {},
+ "artifactLocation": {
+ "uri": "vuln.go",
+ "uriBaseId": "%SRCROOT%"
+ },
"region": {
"startLine": 14,
"startColumn": 20
@@ -237,7 +258,10 @@
"module": "github.com/tidwall/gjson",
"location": {
"physicalLocation": {
- "artifactLocation": {},
+ "artifactLocation": {
+ "uri": "gjson.go",
+ "uriBaseId": "%GOMODCACHE%"
+ },
"region": {
"startLine": 297,
"startColumn": 12
@@ -252,7 +276,10 @@
"module": "github.com/tidwall/gjson",
"location": {
"physicalLocation": {
- "artifactLocation": {},
+ "artifactLocation": {
+ "uri": "gjson.go",
+ "uriBaseId": "%GOMODCACHE%"
+ },
"region": {
"startLine": 1881,
"startColumn": 36
@@ -267,7 +294,10 @@
"module": "github.com/tidwall/gjson",
"location": {
"physicalLocation": {
- "artifactLocation": {},
+ "artifactLocation": {
+ "uri": "gjson.go",
+ "uriBaseId": "%GOMODCACHE%"
+ },
"region": {
"startLine": 2587,
"startColumn": 21
@@ -282,7 +312,10 @@
"module": "github.com/tidwall/gjson",
"location": {
"physicalLocation": {
- "artifactLocation": {},
+ "artifactLocation": {
+ "uri": "gjson.go",
+ "uriBaseId": "%GOMODCACHE%"
+ },
"region": {
"startLine": 2631,
"startColumn": 21
@@ -297,7 +330,10 @@
"module": "github.com/tidwall/gjson",
"location": {
"physicalLocation": {
- "artifactLocation": {},
+ "artifactLocation": {
+ "uri": "gjson.go",
+ "uriBaseId": "%GOMODCACHE%"
+ },
"region": {
"startLine": 220,
"startColumn": 17
@@ -321,7 +357,10 @@
"locations": [
{
"physicalLocation": {
- "artifactLocation": {},
+ "artifactLocation": {
+ "uri": "go.mod",
+ "uriBaseId": "%SRCROOT%"
+ },
"region": {
"startLine": 1
}
@@ -338,7 +377,10 @@
"module": "golang.org/vuln",
"location": {
"physicalLocation": {
- "artifactLocation": {},
+ "artifactLocation": {
+ "uri": "vuln.go",
+ "uriBaseId": "%SRCROOT%"
+ },
"region": {
"startLine": 13,
"startColumn": 16
@@ -353,7 +395,10 @@
"module": "golang.org/x/text",
"location": {
"physicalLocation": {
- "artifactLocation": {},
+ "artifactLocation": {
+ "uri": "language/parse.go",
+ "uriBaseId": "%GOMODCACHE%"
+ },
"region": {
"startLine": 228,
"startColumn": 6
@@ -382,7 +427,10 @@
"module": "golang.org/vuln",
"location": {
"physicalLocation": {
- "artifactLocation": {},
+ "artifactLocation": {
+ "uri": "vuln.go",
+ "uriBaseId": "%SRCROOT%"
+ },
"region": {
"startLine": 13,
"startColumn": 16
@@ -397,7 +445,10 @@
"module": "golang.org/x/text",
"location": {
"physicalLocation": {
- "artifactLocation": {},
+ "artifactLocation": {
+ "uri": "language/parse.go",
+ "uriBaseId": "%GOMODCACHE%"
+ },
"region": {
"startLine": 228,
"startColumn": 6
@@ -421,7 +472,10 @@
"locations": [
{
"physicalLocation": {
- "artifactLocation": {},
+ "artifactLocation": {
+ "uri": "go.mod",
+ "uriBaseId": "%SRCROOT%"
+ },
"region": {
"startLine": 1
}
@@ -438,7 +492,10 @@
"module": "golang.org/vuln",
"location": {
"physicalLocation": {
- "artifactLocation": {},
+ "artifactLocation": {
+ "uri": "vuln.go",
+ "uriBaseId": "%SRCROOT%"
+ },
"region": {
"startLine": 14,
"startColumn": 20
@@ -453,7 +510,10 @@
"module": "github.com/tidwall/gjson",
"location": {
"physicalLocation": {
- "artifactLocation": {},
+ "artifactLocation": {
+ "uri": "gjson.go",
+ "uriBaseId": "%GOMODCACHE%"
+ },
"region": {
"startLine": 296,
"startColumn": 17
@@ -482,7 +542,10 @@
"module": "golang.org/vuln",
"location": {
"physicalLocation": {
- "artifactLocation": {},
+ "artifactLocation": {
+ "uri": "vuln.go",
+ "uriBaseId": "%SRCROOT%"
+ },
"region": {
"startLine": 14,
"startColumn": 20
@@ -497,7 +560,10 @@
"module": "github.com/tidwall/gjson",
"location": {
"physicalLocation": {
- "artifactLocation": {},
+ "artifactLocation": {
+ "uri": "gjson.go",
+ "uriBaseId": "%GOMODCACHE%"
+ },
"region": {
"startLine": 296,
"startColumn": 17
diff --git a/cmd/govulncheck/testdata/common/testfiles/source-module/source_module_sarif.ct b/cmd/govulncheck/testdata/common/testfiles/source-module/source_module_sarif.ct
index e1fe76b..b3b5512 100644
--- a/cmd/govulncheck/testdata/common/testfiles/source-module/source_module_sarif.ct
+++ b/cmd/govulncheck/testdata/common/testfiles/source-module/source_module_sarif.ct
@@ -113,7 +113,10 @@
"locations": [
{
"physicalLocation": {
- "artifactLocation": {},
+ "artifactLocation": {
+ "uri": "go.mod",
+ "uriBaseId": "%SRCROOT%"
+ },
"region": {
"startLine": 1
}
@@ -131,7 +134,10 @@
"locations": [
{
"physicalLocation": {
- "artifactLocation": {},
+ "artifactLocation": {
+ "uri": "go.mod",
+ "uriBaseId": "%SRCROOT%"
+ },
"region": {
"startLine": 1
}
@@ -149,7 +155,10 @@
"locations": [
{
"physicalLocation": {
- "artifactLocation": {},
+ "artifactLocation": {
+ "uri": "go.mod",
+ "uriBaseId": "%SRCROOT%"
+ },
"region": {
"startLine": 1
}
@@ -167,7 +176,10 @@
"locations": [
{
"physicalLocation": {
- "artifactLocation": {},
+ "artifactLocation": {
+ "uri": "go.mod",
+ "uriBaseId": "%SRCROOT%"
+ },
"region": {
"startLine": 1
}
diff --git a/cmd/govulncheck/testdata/common/testfiles/source-package/source_package_sarif.ct b/cmd/govulncheck/testdata/common/testfiles/source-package/source_package_sarif.ct
index d82a995..dd17a42 100644
--- a/cmd/govulncheck/testdata/common/testfiles/source-package/source_package_sarif.ct
+++ b/cmd/govulncheck/testdata/common/testfiles/source-package/source_package_sarif.ct
@@ -113,7 +113,10 @@
"locations": [
{
"physicalLocation": {
- "artifactLocation": {},
+ "artifactLocation": {
+ "uri": "go.mod",
+ "uriBaseId": "%SRCROOT%"
+ },
"region": {
"startLine": 1
}
@@ -131,7 +134,10 @@
"locations": [
{
"physicalLocation": {
- "artifactLocation": {},
+ "artifactLocation": {
+ "uri": "go.mod",
+ "uriBaseId": "%SRCROOT%"
+ },
"region": {
"startLine": 1
}
@@ -149,7 +155,10 @@
"locations": [
{
"physicalLocation": {
- "artifactLocation": {},
+ "artifactLocation": {
+ "uri": "go.mod",
+ "uriBaseId": "%SRCROOT%"
+ },
"region": {
"startLine": 1
}
@@ -167,7 +176,10 @@
"locations": [
{
"physicalLocation": {
- "artifactLocation": {},
+ "artifactLocation": {
+ "uri": "go.mod",
+ "uriBaseId": "%SRCROOT%"
+ },
"region": {
"startLine": 1
}
diff --git a/internal/sarif/handler.go b/internal/sarif/handler.go
index 7bf9326..f9aa2ee 100644
--- a/internal/sarif/handler.go
+++ b/internal/sarif/handler.go
@@ -10,6 +10,7 @@
"io"
"sort"
+ "golang.org/x/vuln/internal"
"golang.org/x/vuln/internal/govulncheck"
"golang.org/x/vuln/internal/osv"
"golang.org/x/vuln/internal/traces"
@@ -165,7 +166,10 @@
// Attach result to the go.mod file for source analysis.
// But there is no such place for binaries.
locs = []Location{{PhysicalLocation: PhysicalLocation{
- // TODO: add artifact location for go.mod
+ ArtifactLocation: ArtifactLocation{
+ URI: "go.mod",
+ URIBaseID: SrcRootID,
+ },
Region: Region{StartLine: 1}, // for now, point to the first line
}}}
}
@@ -174,8 +178,8 @@
RuleID: fs[0].OSV,
Level: level(fs[0], h.cfg),
Message: Description{Text: resultMessage(fs, h.cfg)},
- Stacks: stacks(fs),
- CodeFlows: codeFlows(fs),
+ Stacks: stacks(h, fs),
+ CodeFlows: codeFlows(h, fs),
Locations: locs,
}
results = append(results, res)
@@ -250,14 +254,14 @@
}
}
-func stacks(fs []*govulncheck.Finding) []Stack {
+func stacks(h *handler, fs []*govulncheck.Finding) []Stack {
if fs[0].Trace[0].Function == "" { // not call level findings
return nil
}
var stacks []Stack
for _, f := range fs {
- stacks = append(stacks, stack(f))
+ stacks = append(stacks, stack(h, f))
}
// Sort stacks for deterministic output. We sort by message
// which is effectively sorting by full symbol name. The
@@ -267,8 +271,9 @@
}
// stack transforms call stack in f to a sarif stack.
-func stack(f *govulncheck.Finding) Stack {
+func stack(h *handler, f *govulncheck.Finding) Stack {
trace := f.Trace
+ top := trace[len(trace)-1] // belongs to top level module
var frames []Frame
for i := len(trace) - 1; i >= 0; i-- { // vulnerable symbol is at the top frame
@@ -277,19 +282,24 @@
if frame.Position != nil {
pos = *frame.Position
}
- frames = append(frames, Frame{
- Module: frame.Module,
- Location: Location{
- Message: Description{Text: symbol(frame)}, // show the (full) symbol name
- PhysicalLocation: PhysicalLocation{
- // TODO: add artifact location
- Region: Region{
- StartLine: pos.Line,
- StartColumn: pos.Column,
- },
+
+ sf := Frame{
+ Module: frame.Module,
+ Location: Location{Message: Description{Text: symbol(frame)}}, // show the (full) symbol name
+ }
+ if h.cfg.ScanMode != govulncheck.ScanModeBinary {
+ sf.Location.PhysicalLocation = PhysicalLocation{
+ ArtifactLocation: ArtifactLocation{
+ URI: pos.Filename,
+ URIBaseID: uriID(top.Module, frame.Module),
},
- },
- })
+ Region: Region{
+ StartLine: pos.Line,
+ StartColumn: pos.Column,
+ },
+ }
+ }
+ frames = append(frames, sf)
}
return Stack{
@@ -298,7 +308,7 @@
}
}
-func codeFlows(fs []*govulncheck.Finding) []CodeFlow {
+func codeFlows(h *handler, fs []*govulncheck.Finding) []CodeFlow {
if fs[0].Trace[0].Function == "" { // not call level findings
return nil
}
@@ -316,7 +326,7 @@
var codeFlows []CodeFlow
for fr, fs := range m {
- tfs := threadFlows(fs)
+ tfs := threadFlows(h, fs)
codeFlows = append(codeFlows, CodeFlow{
ThreadFlows: tfs,
// TODO: should we instead show the message from govulncheck text output?
@@ -330,10 +340,12 @@
return codeFlows
}
-func threadFlows(fs []*govulncheck.Finding) []ThreadFlow {
+func threadFlows(h *handler, fs []*govulncheck.Finding) []ThreadFlow {
var tfs []ThreadFlow
for _, f := range fs {
trace := traces.Compact(f)
+ top := trace[len(trace)-1] // belongs to top level module
+
var tf []ThreadFlowLocation
for i := len(trace) - 1; i >= 0; i-- { // vulnerable symbol is at the top frame
// TODO: should we, similar to govulncheck text output, only
@@ -343,20 +355,36 @@
if frame.Position != nil {
pos = *frame.Position
}
- tf = append(tf, ThreadFlowLocation{
- Module: frame.Module,
- Location: Location{
- Message: Description{Text: symbol(frame)}, // show the (full) symbol name
- PhysicalLocation: PhysicalLocation{
- // TODO: add artifact location
- Region: Region{
- StartLine: pos.Line,
- StartColumn: pos.Column,
- },
+
+ tfl := ThreadFlowLocation{
+ Module: frame.Module,
+ Location: Location{Message: Description{Text: symbol(frame)}}, // show the (full) symbol name
+ }
+ if h.cfg.ScanMode != govulncheck.ScanModeBinary {
+ tfl.Location.PhysicalLocation = PhysicalLocation{
+ ArtifactLocation: ArtifactLocation{
+ URI: pos.Filename,
+ URIBaseID: uriID(top.Module, frame.Module),
},
- }})
+ Region: Region{
+ StartLine: pos.Line,
+ StartColumn: pos.Column,
+ },
+ }
+ }
+ tf = append(tf, tfl)
}
tfs = append(tfs, ThreadFlow{Locations: tf})
}
return tfs
}
+
+func uriID(top, module string) string {
+ if top == module {
+ return SrcRootID
+ }
+ if module == internal.GoStdModulePath {
+ return GoRootID
+ }
+ return GoModCacheID
+}
diff --git a/internal/sarif/sarif.go b/internal/sarif/sarif.go
index fd00e2f..53d2ba3 100644
--- a/internal/sarif/sarif.go
+++ b/internal/sarif/sarif.go
@@ -5,8 +5,13 @@
// Package sarif defines Static Analysis Results Interchange Format
// (SARIF) types supported by govulncheck.
//
-// See https://www.oasis-open.org/committees/tc_home.php?wg_abbrev=sarif
-// for more information on the SARIF format.
+// 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.
package sarif
import "golang.org/x/vuln/internal/govulncheck"
@@ -151,6 +156,12 @@
Region Region `json:"region,omitempty"`
}
+const (
+ SrcRootID = "%SRCROOT%"
+ GoRootID = "%GOROOT%"
+ GoModCacheID = "%GOMODCACHE%"
+)
+
// ArtifactLocation is a path to an offending file.
type ArtifactLocation struct {
// URI is a path to the artifact. If URIBaseID is empty, then