Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add workaround for Golang's stdlib #72

Merged
merged 3 commits into from
Jul 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 31 additions & 3 deletions docs/multiple-tests/pattern-vulnerability/results.xml
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,43 @@

<file
name="golang/go.mod">
<error source="vulnerability" line="5"
<error source="vulnerability" line="7"
message="Insecure dependency golang.org/x/net@v0.16.0 (CVE-2023-45288: golang: net/http, x/net/http2: unlimited number of CONTINUATION frames causes DoS) (update to 0.23.0)"
severity="error" />
<error source="vulnerability" line="5"
<error source="vulnerability" line="7"
message="Insecure dependency golang.org/x/net@v0.16.0 (CVE-2023-39325: golang: net/http, x/net/http2: rapid stream resets can cause excessive work (CVE-2023-44487)) (update to 0.17.0)"
severity="error" />
<error source="vulnerability" line="5"
<error source="vulnerability" line="7"
message="Insecure dependency golang.org/x/net@v0.16.0 (CVE-2023-44487: HTTP/2: Multiple HTTP/2 enabled web servers are vulnerable to a DDoS attack (Rapid Reset Attack)) (update to 0.17.0)"
severity="error" />
<!-- stdlib -->
<error source="vulnerability" line="3"
message="Insecure dependency stdlib@v1.21.4 (CVE-2023-39326: golang: net/http/internal: Denial of Service (DoS) via Resource Consumption via HTTP requests) (update to 1.21.5)"
severity="error" />
<error source="vulnerability" line="3"
message="Insecure dependency stdlib@v1.21.4 (CVE-2023-45288: golang: net/http, x/net/http2: unlimited number of CONTINUATION frames causes DoS) (update to 1.21.9)"
severity="error" />
<error source="vulnerability" line="3"
message="Insecure dependency stdlib@v1.21.4 (CVE-2023-45289: golang: net/http/cookiejar: incorrect forwarding of sensitive headers and cookies on HTTP redirect) (update to 1.21.8)"
severity="error" />
<error source="vulnerability" line="3"
message="Insecure dependency stdlib@v1.21.4 (CVE-2023-45290: golang: net/http: memory exhaustion in Request.ParseMultipartForm) (update to 1.21.8)"
severity="error" />
<error source="vulnerability" line="3"
message="Insecure dependency stdlib@v1.21.4 (CVE-2024-24783: golang: crypto/x509: Verify panics on certificates with an unknown public key algorithm) (update to 1.21.8)"
severity="error" />
<error source="vulnerability" line="3"
message="Insecure dependency stdlib@v1.21.4 (CVE-2024-24784: golang: net/mail: comments in display names are incorrectly handled) (update to 1.21.8)"
severity="error" />
<error source="vulnerability" line="3"
message="Insecure dependency stdlib@v1.21.4 (CVE-2024-24785: golang: html/template: errors returned from MarshalJSON methods may break template escaping) (update to 1.21.8)"
severity="error" />
<error source="vulnerability" line="3"
message="Insecure dependency stdlib@v1.21.4 (CVE-2024-24789: golang: archive/zip: Incorrect handling of certain ZIP files) (update to 1.21.11)"
severity="error" />
<error source="vulnerability" line="3"
message="Insecure dependency stdlib@v1.21.4 (CVE-2024-24790: golang: net/netip: Unexpected behavior from Is methods for IPv4-mapped IPv6 addresses) (update to 1.21.11)"
severity="error" />
</file>

<file name="gradle/gradle.lockfile">
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
module example

go 1.21.4
go 1.21.0

toolchain go1.21.4

require golang.org/x/net v0.16.0
128 changes: 128 additions & 0 deletions internal/tool/golang.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
package tool

import (
"bufio"
"io"
"os"
"path/filepath"
"strings"

"github.com/samber/lo"
)

func patchGoModFilesForStdlib(srcDir string, files []string) string {
// Copy the files to a temporary directory because /src is read-only
dstDir := "/tmp/src"
if err := CopyFiles(files, srcDir, dstDir); err != nil {
mrfyda marked this conversation as resolved.
Show resolved Hide resolved
return srcDir
}

// Find and patch the go.mod files
lo.ForEach(files, func(file string, _ int) {
if strings.HasSuffix(file, "go.mod") {
patchGoModFileForStdlib(filepath.Join(dstDir, file))
}
})

return dstDir
}

// Find lines in go.mod files that specify the Go version and replace them with a require statement for the stdlib module.
func patchGoModFileForStdlib(filename string) {
mrfyda marked this conversation as resolved.
Show resolved Hide resolved
tempFilename := filename + ".tmp"

// Open the original file for reading
inputFile, err := os.Open(filename)
if err != nil {
return
}
defer inputFile.Close()

// Create a temporary file for writing
tempFile, err := os.Create(tempFilename)
if err != nil {
return
}
defer tempFile.Close()

scanner := bufio.NewScanner(inputFile)
writer := bufio.NewWriter(tempFile)

// Process the file line by line
for scanner.Scan() {
line := scanner.Text()
// Find go version statement
if strings.HasPrefix(line, "go ") {
version := strings.TrimPrefix(line, "go ")
line = "require stdlib v" + version
}
// Find toolchain statement
if strings.HasPrefix(line, "toolchain go") {
version := strings.TrimPrefix(line, "toolchain go")
line = "require stdlib v" + version
}

_, err := writer.WriteString(line + "\n")
if err != nil {
return
}
}

if err := scanner.Err(); err != nil {
return
}

// Flush the writer
if err := writer.Flush(); err != nil {
return
}

// Close both files
inputFile.Close()
tempFile.Close()

// Replace the original file with the temporary file
if err := os.Rename(tempFilename, filename); err != nil {
return
}
}

// CopyFiles copies specific files from the source directory to the destination directory.
func CopyFiles(files []string, srcDir string, dstDir string) error {
for _, file := range files {
srcPath := filepath.Join(srcDir, file)
dstPath := filepath.Join(dstDir, file)

// Ensure the destination directory exists
if err := os.MkdirAll(filepath.Dir(dstPath), os.ModePerm); err != nil {
return err
}

// Copy the file
if err := CopyFile(srcPath, dstPath); err != nil {
return err
}
}
return nil
}

// CopyFile copies a single file from src to dst.
func CopyFile(src, dst string) error {
sourceFile, err := os.Open(src)
if err != nil {
return err
}
defer sourceFile.Close()

destinationFile, err := os.Create(dst)
if err != nil {
return err
}
defer destinationFile.Close()

if _, err := io.Copy(destinationFile, sourceFile); err != nil {
return err
}

return nil
}
18 changes: 12 additions & 6 deletions internal/tool/tool.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"fmt"
"os"
"path"
"path/filepath"
"strings"

"github.com/aquasecurity/trivy/pkg/fanal/secret"
Expand Down Expand Up @@ -45,6 +46,11 @@ func (t codacyTrivy) Run(ctx context.Context, toolExecution codacy.ToolExecution
return []codacy.Result{}, nil
}

if toolExecution.Files == nil || len(*toolExecution.Files) == 0 {
// TODO Run for all files in the source dir?
return []codacy.Result{}, nil
}

err := newConfiguration(*toolExecution.Patterns)
if err != nil {
return nil, err
Expand Down Expand Up @@ -73,6 +79,11 @@ func (t codacyTrivy) runVulnerabilityScanning(ctx context.Context, toolExecution
return []codacy.Result{}, nil
}

// Workaround for detecting vulnerabilities in the Go standard library.
// Mimics the behavior of govulncheck by replacing the go version directive with a require statement for stdlib. https://go.dev/blog/govulncheck
// This is only supported by Trivy for Go binaries. https://github.com/aquasecurity/trivy/issues/4133
toolExecution.SourceDir = patchGoModFilesForStdlib(toolExecution.SourceDir, *toolExecution.Files)

config := flag.Options{
GlobalOptions: flag.GlobalOptions{
// CacheDir needs to be explicitly set and match the directory in the Dockerfile.
Expand Down Expand Up @@ -212,7 +223,7 @@ func filterIssuesFromKnownFiles(issues []codacy.Issue, knownFiles []string) []co
// If the line number is not available in the Trivy result, try to find it in the source file.
// Returns 0 if the line number is not found.
func fallbackSearchForLineNumber(sourceDir, fileName, pkgName string) (int, error) {
filePath := path.Join(sourceDir, fileName)
filePath := filepath.Join(sourceDir, fileName)
codacy-vrhpires marked this conversation as resolved.
Show resolved Hide resolved
f, err := os.Open(filePath)
if err != nil {
return 0, err
Expand Down Expand Up @@ -270,11 +281,6 @@ func (t codacyTrivy) runSecretScanning(patterns []codacy.Pattern, files *[]strin
return []codacy.Result{}, nil
}

if files == nil || len(*files) == 0 {
// TODO Run for all files in the source dir?
return []codacy.Result{}, nil
}

scanner := secret.NewScanner(&secret.Config{})

results := []codacy.Result{}
Expand Down
13 changes: 12 additions & 1 deletion internal/tool/tool_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -210,12 +210,16 @@ func TestRunNoPatterns(t *testing.T) {

func TestRunConfigurationError(t *testing.T) {
// Arrange
file1 := "file-1"
file2 := "file-2"

toolExecution := codacy.ToolExecution{
Patterns: &[]codacy.Pattern{
{
ID: "unknown",
},
},
Files: &[]string{file1, file2},
}

underTest := codacyTrivy{}
Expand All @@ -233,13 +237,16 @@ func TestRunConfigurationError(t *testing.T) {

func TestRunNewRunnerError(t *testing.T) {
// Arrange
file1 := "file-1"
file2 := "file-2"

toolExecution := codacy.ToolExecution{
Patterns: &[]codacy.Pattern{
{
ID: ruleIDVulnerability,
},
},
Files: &[]string{},
Files: &[]string{file1, file2},
}

underTest := codacyTrivy{
Expand All @@ -261,6 +268,9 @@ func TestRunScanFilesystemError(t *testing.T) {
ctx := context.Background()
ctrl := gomock.NewController(t)

file1 := "file-1"
file2 := "file-2"

sourceDir := "src"
toolExecution := codacy.ToolExecution{
Patterns: &[]codacy.Pattern{
Expand All @@ -272,6 +282,7 @@ func TestRunScanFilesystemError(t *testing.T) {
},
},
SourceDir: sourceDir,
Files: &[]string{file1, file2},
}

config := flag.Options{
Expand Down