diff --git a/pkg/analysis/passes/gosec/gosec.go b/pkg/analysis/passes/gosec/gosec.go index df0ee259..0d033593 100644 --- a/pkg/analysis/passes/gosec/gosec.go +++ b/pkg/analysis/passes/gosec/gosec.go @@ -4,6 +4,7 @@ import ( "encoding/json" "errors" "fmt" + "io/fs" "os/exec" "path/filepath" "strings" @@ -62,6 +63,17 @@ func run(pass *analysis.Pass) (interface{}, error) { return nil, err } + // gosec exits 1 without output when it finds no Go files, which is indistinguishable from a failed scan. + hasGo, err := hasGoFiles(sourceCodeDir) + if err != nil { + pass.ReportIncomplete("Could not inspect source code for Go files: " + err.Error()) + return nil, nil + } + if !hasGo { + logme.Debugln("no Go files in source code, skipping gosec analysis") + return nil, nil + } + // gosec resolves relative file paths against the module root. goSecCommand := exec.Command( goSecBin, @@ -132,3 +144,19 @@ func run(pass *analysis.Pass) (interface{}, error) { return nil, nil } + +func hasGoFiles(dir string) (bool, error) { + found := false + err := filepath.WalkDir(dir, func(_ string, d fs.DirEntry, err error) error { + if err != nil { + return err + } + if d.IsDir() || filepath.Ext(d.Name()) != ".go" { + return nil + } + + found = true + return fs.SkipAll + }) + return found, err +} diff --git a/pkg/analysis/passes/gosec/incomplete_test.go b/pkg/analysis/passes/gosec/incomplete_test.go index 1368583a..ba8187bf 100644 --- a/pkg/analysis/passes/gosec/incomplete_test.go +++ b/pkg/analysis/passes/gosec/incomplete_test.go @@ -16,6 +16,7 @@ func TestIncompleteScan(t *testing.T) { t.Run(script, func(t *testing.T) { directory := t.TempDir() require.NoError(t, os.WriteFile(filepath.Join(directory, "gosec"), []byte("#!/bin/sh\n"+script+"\n"), 0755)) + require.NoError(t, os.WriteFile(filepath.Join(directory, "main.go"), []byte("package main\n"), 0644)) t.Setenv("PATH", directory) var interceptor testpassinterceptor.TestPassInterceptor pass := &analysis.Pass{AnalyzerName: Analyzer.Name, ResultOf: map[*analysis.Analyzer]any{sourcecode.Analyzer: directory}, Report: interceptor.ReportInterceptor()} @@ -30,6 +31,7 @@ func TestIncompleteScan(t *testing.T) { func TestQuietSuccessfulScan(t *testing.T) { directory := t.TempDir() require.NoError(t, os.WriteFile(filepath.Join(directory, "gosec"), []byte("#!/bin/sh\nexit 0\n"), 0755)) + require.NoError(t, os.WriteFile(filepath.Join(directory, "main.go"), []byte("package main\n"), 0644)) t.Setenv("PATH", directory) var interceptor testpassinterceptor.TestPassInterceptor pass := &analysis.Pass{ @@ -41,3 +43,23 @@ func TestQuietSuccessfulScan(t *testing.T) { require.NoError(t, err) require.Empty(t, interceptor.Diagnostics) } + +// gosec exits 1 without output when the source tree has no Go files. +func TestSourceWithoutGoFiles(t *testing.T) { + binDir := t.TempDir() + require.NoError(t, os.WriteFile(filepath.Join(binDir, "gosec"), []byte("#!/bin/sh\nexit 1\n"), 0755)) + t.Setenv("PATH", binDir) + + sourceDir := t.TempDir() + require.NoError(t, os.WriteFile(filepath.Join(sourceDir, "index.ts"), []byte("export const a = 1;\n"), 0644)) + + var interceptor testpassinterceptor.TestPassInterceptor + pass := &analysis.Pass{ + AnalyzerName: Analyzer.Name, + ResultOf: map[*analysis.Analyzer]any{sourcecode.Analyzer: sourceDir}, + Report: interceptor.ReportInterceptor(), + } + _, err := run(pass) + require.NoError(t, err) + require.Empty(t, interceptor.Diagnostics) +}