Skip to content

Commit a4a07c2

Browse files
authored
fix: Improve issue flagging in golang's standard library and ensure proper fix version is suggested [CF-1834] (#166)
* fix: Use 'DetectionPriority' flag to detect issues in golang's standard library [CF-1834] * fix: Ensure proper comparison of semver versions [CF-1834] * fix: Determine whether file is a go.mod file [CF-1834] * fix: Expected update version in tests [CF-1834]
1 parent 85f86ca commit a4a07c2

File tree

6 files changed

+121
-163
lines changed

6 files changed

+121
-163
lines changed

docs/multiple-tests/pattern-vulnerability-medium/results.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
<error
3030
source="vulnerability_medium"
3131
line="3"
32-
message="Insecure dependency golang/[email protected] (CVE-2023-39326: golang: net/http/internal: Denial of Service (DoS) via Resource Consumption via HTTP requests) (update to 1.20.12)"
32+
message="Insecure dependency golang/[email protected] (CVE-2023-39326: golang: net/http/internal: Denial of Service (DoS) via Resource Consumption via HTTP requests) (update to 1.21.5)"
3333
severity="warning"
3434
/>
3535
<error
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
module example
22

3-
go 1.21.0
4-
53
toolchain go1.21.4
64

5+
go 1.22.0
6+
77
require golang.org/x/net v0.16.0

docs/multiple-tests/pattern-vulnerability/results.xml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -67,19 +67,19 @@
6767
<!-- stdlib -->
6868
<error
6969
source="vulnerability"
70-
line="3"
70+
line="5"
7171
message="Insecure dependency golang/[email protected] (CVE-2023-45288: golang: net/http, x/net/http2: unlimited number of CONTINUATION frames causes DoS) (update to 1.21.9)"
7272
severity="error"
7373
/>
7474
<error
7575
source="vulnerability"
76-
line="3"
76+
line="5"
7777
message="Insecure dependency golang/[email protected] (CVE-2024-24790: golang: net/netip: Unexpected behavior from Is methods for IPv4-mapped IPv6 addresses) (update to 1.21.11)"
7878
severity="error"
7979
/>
8080
<error
8181
source="vulnerability"
82-
line="3"
82+
line="5"
8383
message="Insecure dependency golang/[email protected] (CVE-2024-34156: encoding/gob: golang: Calling Decoder.Decode on a message which contains deeply nested structures can cause a panic due to stack exhaustion) (update to 1.22.7)"
8484
severity="error"
8585
/>

internal/tool/golang.go

Lines changed: 0 additions & 135 deletions
This file was deleted.

internal/tool/tool.go

Lines changed: 35 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ const (
3333
ruleIDVulnerabilityMedium string = "vulnerability_medium"
3434
ruleIDVulnerabilityMinor string = "vulnerability_minor"
3535

36-
// See https://aquasecurity.github.io/trivy/v0.54/docs/scanner/vulnerability/#severity-selection
36+
// See https://aquasecurity.github.io/trivy/v0.59/docs/scanner/vulnerability/#severity-selection
3737
trivySeverityLow string = "low"
3838
trivySeverityMedium string = "medium"
3939
trivySeverityHigh string = "high"
@@ -68,7 +68,7 @@ func (t codacyTrivy) Run(ctx context.Context, toolExecution codacy.ToolExecution
6868
// This is the only way to suppress Trivy logs.
6969
log.InitLogger(false, true)
7070

71-
report, err := t.runBaseScan(ctx, &toolExecution)
71+
report, err := t.runBaseScan(ctx, toolExecution.SourceDir)
7272
if err != nil {
7373
return nil, err
7474
}
@@ -92,13 +92,7 @@ func (t codacyTrivy) Run(ctx context.Context, toolExecution codacy.ToolExecution
9292
}
9393

9494
// runBaseScan will run a vulnerability scan that produces a report to be used for SBOM generation or for vulnerability issues.
95-
// This method can change the `sourceDir` property of `toolExecution`, when scanning go code. This will have no impact for other scans.
96-
func (t codacyTrivy) runBaseScan(ctx context.Context, toolExecution *codacy.ToolExecution) (ptypes.Report, error) {
97-
// Workaround for detecting vulnerabilities in the Go standard library.
98-
// Mimics the behavior of govulncheck by replacing the go version directive with a require statement for stdlib. https://go.dev/blog/govulncheck
99-
// This is only supported by Trivy for Go binaries. https://github.com/aquasecurity/trivy/issues/4133
100-
toolExecution.SourceDir = patchGoModFilesForStdlib(toolExecution.SourceDir, *toolExecution.Files)
101-
95+
func (t codacyTrivy) runBaseScan(ctx context.Context, sourceDir string) (ptypes.Report, error) {
10296
config := flag.Options{
10397
GlobalOptions: flag.GlobalOptions{
10498
// CacheDir needs to be explicitly set and match the directory in the Dockerfile.
@@ -126,7 +120,10 @@ func (t codacyTrivy) runBaseScan(ctx context.Context, toolExecution *codacy.Tool
126120
Scanners: ptypes.Scanners{ptypes.VulnerabilityScanner},
127121
// Instead of scanning files individually, scan the whole source directory since it's faster.
128122
// Then filter issues from files that were not supposed to be analysed.
129-
Target: toolExecution.SourceDir,
123+
Target: sourceDir,
124+
// Detects more vulnerabilities, potentially including some that might be false positives.
125+
// This is REQUIRED for detecting vulnerabilites in go standard library.
126+
DetectionPriority: ftypes.PriorityComprehensive,
130127
},
131128
}
132129

@@ -378,23 +375,48 @@ func fallbackSearchForLineNumber(sourceDir, fileName, pkgName string) int {
378375
scanner := bufio.NewScanner(f)
379376

380377
line := 1
378+
goDirectiveLine := 0
379+
isGoModStdLib := strings.HasSuffix(fileName, "go.mod") && pkgName == "stdlib"
381380
for scanner.Scan() {
382-
if strings.Contains(scanner.Text(), pkgName) {
381+
lineText := strings.TrimSpace(scanner.Text())
382+
383+
// Issues in go standard library are reported in package `stdlib` which does not literally exist in go.mod.
384+
//
385+
// Trivy uses `stdlib` to refer to the standard library defined in `toolchain` or `go` directives in go.mod.
386+
// Trivy supposedly uses the minimum version between `toolchain` and `go` directives (see https://trivy.dev/v0.59/docs/coverage/language/golang/#gomod-stdlib)
387+
// but in reality it ALWAYS uses the version defined in `toolchain` when it exists.
388+
if isGoModStdLib {
389+
// If there is a `toolchain` directive use its line.
390+
if strings.HasPrefix(lineText, "toolchain ") {
391+
return line
392+
}
393+
// Only use the `go` directive line after scanning the whole file and there is no `toolchain` directive
394+
if strings.HasPrefix(lineText, "go ") {
395+
goDirectiveLine = line
396+
}
397+
} else if strings.Contains(lineText, pkgName) {
383398
return line
384399
}
385400
line++
386401
}
387402

388-
return 0
403+
return goDirectiveLine
389404
}
390405

391406
// Find the smallest version increment that fixes a vulnerabillity, assuming semantic version format.
392407
// Doesn't support package managers that use a different versioning scheme. (like Ruby's `~>`)
393408
// Otherwise, return the original versions list.
409+
//
410+
// The semver library we're using requires a `v` prefix for the version.
411+
// Usually, Trivy prefixes `InstalledVersion` but not `FixedVersion`.
412+
// For safety, we sanitize both values, by removing and adding a `v` prefix.
394413
func findLeastDisruptiveFixedVersion(vuln ptypes.DetectedVulnerability) string {
414+
sanitizedInstalledVersion := fmt.Sprintf("v%s", strings.TrimPrefix(vuln.InstalledVersion, "v"))
415+
395416
allUpdates := strings.Split(vuln.FixedVersion, ", ")
396417
possibleUpdates := lo.Filter(allUpdates, func(v string, index int) bool {
397-
return semver.Compare(fmt.Sprintf("v%s", v), fmt.Sprintf("v%s", vuln.InstalledVersion)) > 0
418+
sanitizedPossibleUpdateVersion := fmt.Sprintf("v%s", strings.TrimPrefix(v, "v"))
419+
return semver.Compare(sanitizedPossibleUpdateVersion, sanitizedInstalledVersion) > 0
398420
})
399421
semver.Sort(possibleUpdates)
400422

0 commit comments

Comments
 (0)