Skip to content

Commit 00d45f6

Browse files
committed
analyzer: do not analyze generated go files
This change filters out code that contains the Go standardized generated code header ^// Code generated .* DO NOT EDIT\. per golang/go#13560. Fixes #30
1 parent c75406a commit 00d45f6

7 files changed

+140
-3
lines changed

analyzer.go

+74-3
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,10 @@ import (
2626
"path"
2727
"path/filepath"
2828
"regexp"
29+
"runtime" // #nosec
2930
"sort"
3031
"strconv"
32+
"sync"
3133

3234
"strings"
3335

@@ -157,6 +159,69 @@ func (gosec *Analyzer) Process(buildTags []string, packagePaths ...string) error
157159
return nil
158160
}
159161

162+
var reGeneratedGoFile = regexp.MustCompile(`^// Code generated .* DO NOT EDIT\.`)
163+
164+
// filterOutGeneratedGoFiles parallelizes the proocess of checking the contents
165+
// of the files in fullPaths for the presence of generated Go headers to avoid
166+
// reporting on generated code, per https://github.com/cosmos/gosec/issues/30
167+
func filterOutGeneratedGoFiles(fullPaths []string) (filtered []string) {
168+
// position stores the order "pos" which will later be
169+
// used to sort the paths to maintain original order
170+
// despite the concurrent filtering that'll take place.
171+
type position struct {
172+
pos int
173+
fullPath string
174+
}
175+
176+
posCh := make(chan *position, 10)
177+
go func() {
178+
defer close(posCh)
179+
for i, fullPath := range fullPaths {
180+
posCh <- &position{pos: i, fullPath: fullPath}
181+
}
182+
}()
183+
184+
filteredCh := make(chan *position, 10)
185+
var wg sync.WaitGroup
186+
// Spin up NumCPU goroutines that'll each open up a file
187+
// for as long as there is one to be read on posCh.
188+
for i := 0; i < runtime.NumCPU(); i++ {
189+
wg.Add(1)
190+
go func() {
191+
defer wg.Done()
192+
for pi := range posCh {
193+
blob, err := os.ReadFile(pi.fullPath)
194+
if err != nil {
195+
panic(err)
196+
}
197+
if !reGeneratedGoFile.Match(blob) {
198+
filteredCh <- pi
199+
}
200+
}
201+
}()
202+
}
203+
204+
go func() {
205+
wg.Wait()
206+
close(filteredCh)
207+
}()
208+
209+
ordered := make([]*position, 0, len(fullPaths))
210+
for nonGeneratedGoFilePath := range filteredCh {
211+
ordered = append(ordered, nonGeneratedGoFilePath)
212+
}
213+
sort.Slice(ordered, func(i, j int) bool {
214+
oi, oj := ordered[i], ordered[j]
215+
return oi.pos < oj.pos
216+
})
217+
218+
filtered = make([]string, 0, len(ordered))
219+
for _, oi := range ordered {
220+
filtered = append(filtered, oi.fullPath)
221+
}
222+
return filtered
223+
}
224+
160225
func (gosec *Analyzer) load(pkgPath string, conf *packages.Config) ([]*packages.Package, error) {
161226
abspath, err := GetPkgAbsPath(pkgPath)
162227
if err != nil {
@@ -183,9 +248,9 @@ func (gosec *Analyzer) load(pkgPath string, conf *packages.Config) ([]*packages.
183248
}
184249
}
185250

186-
// step 1/3 create build context.
251+
// step 1/4 create build context.
187252
buildD := build.Default
188-
// step 2/3: add build tags to get env dependent files into basePackage.
253+
// step 2/4: add build tags to get env dependent files into basePackage.
189254
buildD.BuildTags = conf.BuildFlags
190255
buildD.Dir = absGoModPath
191256
basePackage, err := buildD.ImportDir(abspath, build.ImportComment)
@@ -197,6 +262,7 @@ func (gosec *Analyzer) load(pkgPath string, conf *packages.Config) ([]*packages.
197262
for _, filename := range basePackage.GoFiles {
198263
packageFiles = append(packageFiles, path.Join(abspath, filename))
199264
}
265+
200266
for _, filename := range basePackage.CgoFiles {
201267
packageFiles = append(packageFiles, path.Join(abspath, filename))
202268
}
@@ -210,7 +276,12 @@ func (gosec *Analyzer) load(pkgPath string, conf *packages.Config) ([]*packages.
210276
}
211277
}
212278

213-
// step 3/3 remove build tags from conf to proceed build correctly.
279+
// step 3/4: now filter out generated go files as we definitely don't
280+
// want to report on generated code, which is out of our direct control.
281+
// Please see: https://github.com/cosmos/gosec/issues/30
282+
packageFiles = filterOutGeneratedGoFiles(packageFiles)
283+
284+
// step 4/4: remove build tags from conf to proceed build correctly.
214285
conf.BuildFlags = nil
215286
conf.Dir = absGoModPath
216287
pkgs, err := packages.Load(conf, packageFiles...)

analyzer_skip_generated_go_test.go

+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
package gosec
2+
3+
import (
4+
"os"
5+
"path/filepath"
6+
"strings"
7+
"testing"
8+
9+
"github.com/google/go-cmp/cmp"
10+
)
11+
12+
func TestUnitFilterOutGeneratedGoFiles(t *testing.T) {
13+
f, err := os.Open("./testdata")
14+
if err != nil {
15+
t.Fatal(err)
16+
}
17+
defer f.Close()
18+
19+
fiL, err := f.Readdir(-1)
20+
if err != nil {
21+
t.Fatal(err)
22+
}
23+
24+
goFiles := make([]string, 0, 10)
25+
for _, fi := range fiL {
26+
if !fi.IsDir() && strings.HasSuffix(fi.Name(), ".go") {
27+
goFiles = append(goFiles, filepath.Join(f.Name(), fi.Name()))
28+
}
29+
}
30+
31+
filtered := filterOutGeneratedGoFiles(goFiles)
32+
want := []string{
33+
"testdata/without_generated_header.go",
34+
"testdata/with_cgo_import_no_generated_code.go",
35+
"testdata/with_regular_code_comment_about_generated.go",
36+
}
37+
if diff := cmp.Diff(filtered, want); diff != "" {
38+
t.Fatalf("Result mismatch: got - want +\n%s", diff)
39+
}
40+
}

testdata/with_cgo_import_generated_code.go

+6
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package test
2+
3+
import "C"
4+
5+
type fooC = C.int

testdata/with_generated_header.go

+7
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
package test
2+
3+
// This is a comment about "// Code generated by. DO NOT EDIT."

testdata/without_generated_header.go

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package test
2+
3+
import "io"
4+
5+
type r = io.Reader

0 commit comments

Comments
 (0)