Skip to content

Commit 10cf58a

Browse files
fix: build tag parsing. (#1413)
* fix: build tag parsing. * chore: lint fixes.
1 parent d2d7348 commit 10cf58a

File tree

8 files changed

+218
-56
lines changed

8 files changed

+218
-56
lines changed

analyzer.go

Lines changed: 11 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,6 @@ type Analyzer struct {
187187
trackSuppressions bool
188188
concurrency int
189189
analyzerSet *analyzers.AnalyzerSet
190-
mu sync.Mutex
191190
}
192191

193192
// NewAnalyzer builds a new analyzer.
@@ -251,12 +250,6 @@ func (gosec *Analyzer) LoadAnalyzers(analyzerDefinitions map[string]analyzers.An
251250

252251
// Process kicks off the analysis process for a given package
253252
func (gosec *Analyzer) Process(buildTags []string, packagePaths ...string) error {
254-
config := &packages.Config{
255-
Mode: LoadMode,
256-
BuildFlags: buildTags,
257-
Tests: gosec.tests,
258-
}
259-
260253
type result struct {
261254
pkgPath string
262255
pkgs []*packages.Package
@@ -273,7 +266,7 @@ func (gosec *Analyzer) Process(buildTags []string, packagePaths ...string) error
273266
for {
274267
select {
275268
case s := <-j:
276-
pkgs, err := gosec.load(s, config)
269+
pkgs, err := gosec.load(s, buildTags)
277270
select {
278271
case r <- result{pkgPath: s, pkgs: pkgs, err: err}:
279272
case <-quit:
@@ -326,20 +319,18 @@ func (gosec *Analyzer) Process(buildTags []string, packagePaths ...string) error
326319
return nil
327320
}
328321

329-
func (gosec *Analyzer) load(pkgPath string, conf *packages.Config) ([]*packages.Package, error) {
322+
func (gosec *Analyzer) load(pkgPath string, buildTags []string) ([]*packages.Package, error) {
330323
abspath, err := GetPkgAbsPath(pkgPath)
331324
if err != nil {
332325
gosec.logger.Printf("Skipping: %s. Path doesn't exist.", abspath)
333326
return []*packages.Package{}, nil
334327
}
335328

336329
gosec.logger.Println("Import directory:", abspath)
337-
// step 1/3 create build context.
330+
331+
// step 1/2: build context requires the array of build tags.
338332
buildD := build.Default
339-
// step 2/3: add build tags to get env dependent files into basePackage.
340-
gosec.mu.Lock()
341-
buildD.BuildTags = conf.BuildFlags
342-
gosec.mu.Unlock()
333+
buildD.BuildTags = buildTags
343334
basePackage, err := buildD.ImportDir(pkgPath, build.ImportComment)
344335
if err != nil {
345336
return []*packages.Package{}, fmt.Errorf("importing dir %q: %w", pkgPath, err)
@@ -362,10 +353,12 @@ func (gosec *Analyzer) load(pkgPath string, conf *packages.Config) ([]*packages.
362353
}
363354
}
364355

365-
// step 3/3 remove build tags from conf to proceed build correctly.
366-
gosec.mu.Lock()
367-
conf.BuildFlags = nil
368-
defer gosec.mu.Unlock()
356+
// step 2/2: pass in cli encoded build flags to build correctly.
357+
conf := &packages.Config{
358+
Mode: LoadMode,
359+
BuildFlags: CLIBuildTags(buildTags),
360+
Tests: gosec.tests,
361+
}
369362
pkgs, err := packages.Load(conf, packageFiles...)
370363
if err != nil {
371364
return []*packages.Package{}, fmt.Errorf("loading files from package %q: %w", pkgPath, err)

analyzer_test.go

Lines changed: 67 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ package gosec_test
1616

1717
import (
1818
"errors"
19+
"fmt"
1920
"go/build"
2021
"log"
2122
"regexp"
@@ -783,16 +784,79 @@ var _ = Describe("Analyzer", func() {
783784
Expect(nosecIssues).Should(BeEmpty())
784785
})
785786

786-
It("should pass the build tags", func() {
787+
It("should not panic if a file can not compile", func() {
788+
sample := testutils.SampleCodeCompilationFail[0]
789+
source := sample.Code[0]
790+
analyzer.LoadRules(rules.Generate(false).RulesInfo())
791+
pkg := testutils.NewTestPackage()
792+
defer pkg.Close()
793+
794+
pkg.AddFile("main.go", source)
795+
err := pkg.Build()
796+
Expect(err).ShouldNot(HaveOccurred())
797+
err = analyzer.Process(buildTags, pkg.Path)
798+
Expect(err).ShouldNot(HaveOccurred())
799+
})
800+
801+
It("should exclude a reportable file, if excluded by build tags", func() {
802+
// file has a reportable security issue, but should only be flagged
803+
// to only being compiled in via a build flag.
804+
sample := testutils.SampleCodeG501BuildTag[0]
805+
source := sample.Code[0]
806+
analyzer.LoadRules(rules.Generate(false).RulesInfo())
807+
pkg := testutils.NewTestPackage()
808+
defer pkg.Close()
809+
810+
pkg.AddFile("main.go", source)
811+
err := pkg.Build()
812+
Expect(err).To(BeEquivalentTo(&build.NoGoError{Dir: pkg.Path})) // no files should be found for scanning.
813+
err = analyzer.Process(buildTags, pkg.Path)
814+
Expect(err).ShouldNot(HaveOccurred())
815+
816+
issues, _, _ := analyzer.Report()
817+
Expect(issues).Should(BeEmpty())
818+
})
819+
820+
It("should attempt to analyse a file with build tags", func() {
787821
sample := testutils.SampleCodeBuildTag[0]
788822
source := sample.Code[0]
789823
analyzer.LoadRules(rules.Generate(false).RulesInfo())
790824
pkg := testutils.NewTestPackage()
791825
defer pkg.Close()
792-
pkg.AddFile("tags.go", source)
826+
827+
tags := []string{"tag"}
828+
pkg.AddFile("main.go", source)
829+
err := pkg.Build(testutils.WithBuildTags(tags))
830+
Expect(err).ShouldNot(HaveOccurred())
831+
err = analyzer.Process(tags, pkg.Path)
832+
Expect(err).ShouldNot(HaveOccurred())
833+
834+
issues, _, _ := analyzer.Report()
835+
if len(issues) != sample.Errors {
836+
fmt.Println(sample.Code)
837+
}
838+
Expect(issues).Should(HaveLen(sample.Errors))
839+
})
840+
841+
It("should report issues from a file with build tags", func() {
842+
sample := testutils.SampleCodeG501BuildTag[0]
843+
source := sample.Code[0]
844+
analyzer.LoadRules(rules.Generate(false).RulesInfo())
845+
pkg := testutils.NewTestPackage()
846+
defer pkg.Close()
847+
793848
tags := []string{"tag"}
794-
err := analyzer.Process(tags, pkg.Path)
849+
pkg.AddFile("main.go", source)
850+
err := pkg.Build(testutils.WithBuildTags(tags))
795851
Expect(err).ShouldNot(HaveOccurred())
852+
err = analyzer.Process(tags, pkg.Path)
853+
Expect(err).ShouldNot(HaveOccurred())
854+
855+
issues, _, _ := analyzer.Report()
856+
if len(issues) != sample.Errors {
857+
fmt.Println(sample.Code)
858+
}
859+
Expect(issues).Should(HaveLen(sample.Errors))
796860
})
797861

798862
It("should process an empty package with test file", func() {

helpers.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -553,3 +553,24 @@ func parseGoVersion(version string) (int, int, int) {
553553

554554
return major, minor, build
555555
}
556+
557+
// CLIBuildTags converts a list of Go build tags into the corresponding CLI
558+
// build flag (-tags=form) by trimming whitespace, removing empty entries,
559+
// and joining them into a comma-separated -tags argument for use with go build
560+
// commands.
561+
func CLIBuildTags(buildTags []string) []string {
562+
var buildFlags []string
563+
if len(buildTags) > 0 {
564+
for _, tag := range buildTags {
565+
// remove empty entries and surrounding whitespace
566+
if t := strings.TrimSpace(tag); t != "" {
567+
buildFlags = append(buildFlags, t)
568+
}
569+
}
570+
if len(buildFlags) > 0 {
571+
buildFlags = []string{"-tags=" + strings.Join(buildFlags, ",")}
572+
}
573+
}
574+
575+
return buildFlags
576+
}

helpers_test.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -342,4 +342,26 @@ var _ = Describe("Helpers", func() {
342342
Expect(operands).Should(HaveLen(4))
343343
})
344344
})
345+
346+
Context("when transforming build tags to cli build flags", func() {
347+
It("should return an empty slice when no tags are provided", func() {
348+
result := gosec.CLIBuildTags([]string{})
349+
Expect(result).To(BeEmpty())
350+
})
351+
352+
It("should return a single -tags flag when one tag is provided", func() {
353+
result := gosec.CLIBuildTags([]string{"integration"})
354+
Expect(result).To(Equal([]string{"-tags=integration"}))
355+
})
356+
357+
It("should combine multiple tags into a single -tags flag", func() {
358+
result := gosec.CLIBuildTags([]string{"linux", "amd64", "netgo"})
359+
Expect(result).To(Equal([]string{"-tags=linux,amd64,netgo"}))
360+
})
361+
362+
It("should trim and ignore empty tags", func() {
363+
result := gosec.CLIBuildTags([]string{" linux ", "", "amd64"})
364+
Expect(result).To(Equal([]string{"-tags=linux,amd64"}))
365+
})
366+
})
345367
})

testutils/build_samples.go

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package testutils
2+
3+
import "github.com/securego/gosec/v2"
4+
5+
var (
6+
// SampleCodeCompilationFail provides a file that won't compile.
7+
SampleCodeCompilationFail = []CodeSample{
8+
{[]string{`
9+
package main
10+
11+
func main() {
12+
fmt.Println("no package imported error")
13+
}
14+
`}, 1, gosec.NewConfig()},
15+
}
16+
17+
// SampleCodeBuildTag provides a small program that should only compile
18+
// provided a build tag.
19+
SampleCodeBuildTag = []CodeSample{
20+
{[]string{`
21+
// +build tag
22+
package main
23+
24+
import "fmt"
25+
26+
func main() {
27+
fmt.Println("Hello world")
28+
}
29+
`}, 0, gosec.NewConfig()},
30+
}
31+
)

testutils/g501_samples.go

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,9 @@ package testutils
33
import "github.com/securego/gosec/v2"
44

55
// SampleCodeG501 - Blocklisted import MD5
6-
var SampleCodeG501 = []CodeSample{
7-
{[]string{`
6+
var (
7+
SampleCodeG501 = []CodeSample{
8+
{[]string{`
89
package main
910
1011
import (
@@ -19,4 +20,27 @@ func main() {
1920
}
2021
}
2122
`}, 1, gosec.NewConfig()},
23+
}
24+
25+
// SampleCodeG501BuildTag provides a reportable file if a build tag is
26+
// supplied.
27+
SampleCodeG501BuildTag = []CodeSample{
28+
{[]string{`
29+
//go:build tag
30+
31+
package main
32+
33+
import (
34+
"crypto/md5"
35+
"fmt"
36+
"os"
37+
)
38+
39+
func main() {
40+
for _, arg := range os.Args {
41+
fmt.Printf("%x - %s\n", md5.Sum([]byte(arg)), arg)
42+
}
2243
}
44+
`}, 2, gosec.NewConfig()},
45+
}
46+
)

testutils/g601_samples.go

Lines changed: 11 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,9 @@ package testutils
22

33
import "github.com/securego/gosec/v2"
44

5-
var (
6-
// SampleCodeG601 - Implicit aliasing over range statement
7-
SampleCodeG601 = []CodeSample{
8-
{[]string{`
5+
// SampleCodeG601 - Implicit aliasing over range statement
6+
var SampleCodeG601 = []CodeSample{
7+
{[]string{`
98
package main
109
1110
import "fmt"
@@ -40,7 +39,7 @@ func main() {
4039
fmt.Printf("%d %v %s", zero, c_star, c)
4140
}
4241
`}, 1, gosec.NewConfig()},
43-
{[]string{`
42+
{[]string{`
4443
// see: github.com/securego/gosec/issues/475
4544
package main
4645
@@ -56,7 +55,7 @@ func main() {
5655
}
5756
}
5857
`}, 0, gosec.NewConfig()},
59-
{[]string{`
58+
{[]string{`
6059
package main
6160
6261
import (
@@ -77,7 +76,7 @@ func main() {
7776
}
7877
}
7978
`}, 0, gosec.NewConfig()},
80-
{[]string{`
79+
{[]string{`
8180
package main
8281
8382
import (
@@ -98,7 +97,7 @@ func main() {
9897
}
9998
}
10099
`}, 1, gosec.NewConfig()},
101-
{[]string{`
100+
{[]string{`
102101
package main
103102
104103
import (
@@ -119,7 +118,7 @@ func main() {
119118
}
120119
}
121120
`}, 0, gosec.NewConfig()},
122-
{[]string{`
121+
{[]string{`
123122
package main
124123
125124
import (
@@ -140,7 +139,7 @@ func main() {
140139
}
141140
}
142141
`}, 1, gosec.NewConfig()},
143-
{[]string{`
142+
{[]string{`
144143
package main
145144
146145
import (
@@ -165,7 +164,7 @@ func main() {
165164
}
166165
}
167166
`}, 1, gosec.NewConfig()},
168-
{[]string{`
167+
{[]string{`
169168
package main
170169
171170
import (
@@ -190,7 +189,7 @@ func main() {
190189
}
191190
}
192191
`}, 0, gosec.NewConfig()},
193-
{[]string{`
192+
{[]string{`
194193
package main
195194
196195
import (
@@ -205,17 +204,4 @@ func main() {
205204
}
206205
}
207206
`}, 1, gosec.NewConfig()},
208-
}
209-
210-
// SampleCodeBuildTag - G601 build tags
211-
SampleCodeBuildTag = []CodeSample{
212-
{[]string{`
213-
// +build tag
214-
package main
215-
216-
func main() {
217-
fmt.Println("no package imported error")
218207
}
219-
`}, 1, gosec.NewConfig()},
220-
}
221-
)

0 commit comments

Comments
 (0)