From 0fbf953d83b8fffd9f72fb5320b38b8eb5b7fb21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafael=20Cort=C3=AAs?= Date: Sat, 29 Jun 2024 21:46:39 +0100 Subject: [PATCH 1/3] feat: Add workaround for Golang's stdlib --- .../pattern-vulnerability/results.xml | 34 +++++++- .../pattern-vulnerability/src/golang/go.mod | 4 +- internal/tool/golang.go | 77 +++++++++++++++++++ internal/tool/tool.go | 8 +- 4 files changed, 118 insertions(+), 5 deletions(-) create mode 100644 internal/tool/golang.go diff --git a/docs/multiple-tests/pattern-vulnerability/results.xml b/docs/multiple-tests/pattern-vulnerability/results.xml index 992debf..30bcb2e 100644 --- a/docs/multiple-tests/pattern-vulnerability/results.xml +++ b/docs/multiple-tests/pattern-vulnerability/results.xml @@ -8,15 +8,43 @@ - - - + + + + + + + + + + diff --git a/docs/multiple-tests/pattern-vulnerability/src/golang/go.mod b/docs/multiple-tests/pattern-vulnerability/src/golang/go.mod index bed61cc..005018c 100644 --- a/docs/multiple-tests/pattern-vulnerability/src/golang/go.mod +++ b/docs/multiple-tests/pattern-vulnerability/src/golang/go.mod @@ -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 diff --git a/internal/tool/golang.go b/internal/tool/golang.go new file mode 100644 index 0000000..749eacd --- /dev/null +++ b/internal/tool/golang.go @@ -0,0 +1,77 @@ +package tool + +import ( + "bufio" + "os" + "path/filepath" + "strings" + + "github.com/samber/lo" +) + +func patchGoModFilesForStdlib(dir string, files []string) { + lo.ForEach(files, func(file string, _ int) { + if strings.HasSuffix(file, "go.mod") { + patchGoModFileForStdlib(filepath.Join(dir, file)) + } + }) +} + +func patchGoModFileForStdlib(filename string) { + 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 + } +} diff --git a/internal/tool/tool.go b/internal/tool/tool.go index eaefef1..c0fb391 100644 --- a/internal/tool/tool.go +++ b/internal/tool/tool.go @@ -7,6 +7,7 @@ import ( "fmt" "os" "path" + "path/filepath" "strings" "github.com/aquasecurity/trivy/pkg/fanal/secret" @@ -73,6 +74,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 + patchGoModFilesForStdlib(toolExecution.SourceDir, *toolExecution.Files) + config := flag.Options{ GlobalOptions: flag.GlobalOptions{ // CacheDir needs to be explicitly set and match the directory in the Dockerfile. @@ -212,7 +218,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) f, err := os.Open(filePath) if err != nil { return 0, err From a221580bfa29a4382ae2396e0114d81793b1f7b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafael=20Cort=C3=AAs?= Date: Sat, 29 Jun 2024 22:09:27 +0100 Subject: [PATCH 2/3] fix: Success when no files are specified --- internal/tool/tool.go | 10 +++++----- internal/tool/tool_test.go | 13 ++++++++++++- 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/internal/tool/tool.go b/internal/tool/tool.go index c0fb391..193b6ae 100644 --- a/internal/tool/tool.go +++ b/internal/tool/tool.go @@ -46,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 @@ -276,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{} diff --git a/internal/tool/tool_test.go b/internal/tool/tool_test.go index 4856608..0c076d4 100644 --- a/internal/tool/tool_test.go +++ b/internal/tool/tool_test.go @@ -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{} @@ -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{ @@ -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{ @@ -272,6 +282,7 @@ func TestRunScanFilesystemError(t *testing.T) { }, }, SourceDir: sourceDir, + Files: &[]string{file1, file2}, } config := flag.Options{ From 4105c7810162d5008dce5ed42389543bcf8ba314 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafael=20Cort=C3=AAs?= Date: Sun, 30 Jun 2024 09:12:30 +0100 Subject: [PATCH 3/3] fix: Try writing file in /tmp --- internal/tool/golang.go | 55 +++++++++++++++++++++++++++++++++++++++-- internal/tool/tool.go | 2 +- 2 files changed, 54 insertions(+), 3 deletions(-) diff --git a/internal/tool/golang.go b/internal/tool/golang.go index 749eacd..1fe6a77 100644 --- a/internal/tool/golang.go +++ b/internal/tool/golang.go @@ -2,6 +2,7 @@ package tool import ( "bufio" + "io" "os" "path/filepath" "strings" @@ -9,14 +10,24 @@ import ( "github.com/samber/lo" ) -func patchGoModFilesForStdlib(dir string, files []string) { +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 { + 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(dir, file)) + 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) { tempFilename := filename + ".tmp" @@ -75,3 +86,43 @@ func patchGoModFileForStdlib(filename string) { 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 +} diff --git a/internal/tool/tool.go b/internal/tool/tool.go index 193b6ae..8e170ec 100644 --- a/internal/tool/tool.go +++ b/internal/tool/tool.go @@ -82,7 +82,7 @@ func (t codacyTrivy) runVulnerabilityScanning(ctx context.Context, toolExecution // 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 - patchGoModFilesForStdlib(toolExecution.SourceDir, *toolExecution.Files) + toolExecution.SourceDir = patchGoModFilesForStdlib(toolExecution.SourceDir, *toolExecution.Files) config := flag.Options{ GlobalOptions: flag.GlobalOptions{