Skip to content

Commit f87af5f

Browse files
Cosmin Cojocargcmurphy
Cosmin Cojocar
authored andcommitted
Detect the unhandled errors even though they are explicitly ignored if the 'audit: enabled' setting is defined in the global configuration (#274)
* Define more explicit the global options in the configuration * Detect in audit mode the unhandled errors even thought they are explicitly ignored
1 parent 14ed63d commit f87af5f

File tree

8 files changed

+128
-17
lines changed

8 files changed

+128
-17
lines changed

analyzer.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ type Context struct {
4141
Pkg *types.Package
4242
PkgFiles []*ast.File
4343
Root *ast.File
44-
Config map[string]interface{}
44+
Config Config
4545
Imports *ImportTracker
4646
Ignores []map[string]bool
4747
}
@@ -69,8 +69,8 @@ type Analyzer struct {
6969
// NewAnalyzer builds a new analyzer.
7070
func NewAnalyzer(conf Config, logger *log.Logger) *Analyzer {
7171
ignoreNoSec := false
72-
if setting, err := conf.GetGlobal("nosec"); err == nil {
73-
ignoreNoSec = setting == "true" || setting == "enabled"
72+
if enabled, err := conf.IsGlobalEnabled(Nosec); err == nil {
73+
ignoreNoSec = enabled
7474
}
7575
if logger == nil {
7676
logger = log.New(os.Stderr, "[gosec]", log.LstdFlags)

analyzer_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,7 @@ var _ = Describe("Analyzer", func() {
201201

202202
// overwrite nosec option
203203
nosecIgnoreConfig := gosec.NewConfig()
204-
nosecIgnoreConfig.SetGlobal("nosec", "true")
204+
nosecIgnoreConfig.SetGlobal(gosec.Nosec, "true")
205205
customAnalyzer := gosec.NewAnalyzer(nosecIgnoreConfig, logger)
206206
customAnalyzer.LoadRules(rules.Generate(rules.NewRuleFilter(false, "G401")).Builders())
207207

cmd/gosec/main.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ func loadConfig(configFile string) (gosec.Config, error) {
137137
}
138138
}
139139
if *flagIgnoreNoSec {
140-
config.SetGlobal("nosec", "true")
140+
config.SetGlobal(gosec.Nosec, "true")
141141
}
142142
return config, nil
143143
}

config.go

+24-5
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,16 @@ const (
1414
Globals = "global"
1515
)
1616

17+
// GlobalOption defines the name of the global options
18+
type GlobalOption string
19+
20+
const (
21+
// Nosec global option for #nosec directive
22+
Nosec GlobalOption = "nosec"
23+
// Audit global option which indicates that gosec runs in audit mode
24+
Audit GlobalOption = "audit"
25+
)
26+
1727
// Config is used to provide configuration and customization to each of the rules.
1828
type Config map[string]interface{}
1929

@@ -22,7 +32,7 @@ type Config map[string]interface{}
2232
// or from a *os.File.
2333
func NewConfig() Config {
2434
cfg := make(Config)
25-
cfg[Globals] = make(map[string]string)
35+
cfg[Globals] = make(map[GlobalOption]string)
2636
return cfg
2737
}
2838

@@ -65,9 +75,9 @@ func (c Config) Set(section string, value interface{}) {
6575
}
6676

6777
// GetGlobal returns value associated with global configuration option
68-
func (c Config) GetGlobal(option string) (string, error) {
78+
func (c Config) GetGlobal(option GlobalOption) (string, error) {
6979
if globals, ok := c[Globals]; ok {
70-
if settings, ok := globals.(map[string]string); ok {
80+
if settings, ok := globals.(map[GlobalOption]string); ok {
7181
if value, ok := settings[option]; ok {
7282
return value, nil
7383
}
@@ -79,10 +89,19 @@ func (c Config) GetGlobal(option string) (string, error) {
7989
}
8090

8191
// SetGlobal associates a value with a global configuration option
82-
func (c Config) SetGlobal(option, value string) {
92+
func (c Config) SetGlobal(option GlobalOption, value string) {
8393
if globals, ok := c[Globals]; ok {
84-
if settings, ok := globals.(map[string]string); ok {
94+
if settings, ok := globals.(map[GlobalOption]string); ok {
8595
settings[option] = value
8696
}
8797
}
8898
}
99+
100+
// IsGlobalEnabled checks if a global option is enabled
101+
func (c Config) IsGlobalEnabled(option GlobalOption) (bool, error) {
102+
value, err := c.GetGlobal(option)
103+
if err != nil {
104+
return false, err
105+
}
106+
return (value == "true" || value == "enabled"), nil
107+
}

config_test.go

+12-4
Original file line numberDiff line numberDiff line change
@@ -81,23 +81,31 @@ var _ = Describe("Configuration", func() {
8181
It("should have a default global section", func() {
8282
settings, err := configuration.Get("global")
8383
Expect(err).Should(BeNil())
84-
expectedType := make(map[string]string)
84+
expectedType := make(map[gosec.GlobalOption]string)
8585
Expect(settings).Should(BeAssignableToTypeOf(expectedType))
8686
})
8787

8888
It("should save global settings to correct section", func() {
89-
configuration.SetGlobal("nosec", "enabled")
89+
configuration.SetGlobal(gosec.Nosec, "enabled")
9090
settings, err := configuration.Get("global")
9191
Expect(err).Should(BeNil())
92-
if globals, ok := settings.(map[string]string); ok {
92+
if globals, ok := settings.(map[gosec.GlobalOption]string); ok {
9393
Expect(globals["nosec"]).Should(MatchRegexp("enabled"))
9494
} else {
9595
Fail("globals are not defined as map")
9696
}
9797

98-
setValue, err := configuration.GetGlobal("nosec")
98+
setValue, err := configuration.GetGlobal(gosec.Nosec)
9999
Expect(err).Should(BeNil())
100100
Expect(setValue).Should(MatchRegexp("enabled"))
101101
})
102+
103+
It("should find global settings which are enabled", func() {
104+
configuration.SetGlobal(gosec.Nosec, "enabled")
105+
enabled, err := configuration.IsGlobalEnabled(gosec.Nosec)
106+
Expect(err).Should(BeNil())
107+
Expect(enabled).Should(BeTrue())
108+
})
102109
})
110+
103111
})

rules/errors.go

+15
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,21 @@ func returnsError(callExpr *ast.CallExpr, ctx *gosec.Context) int {
5151

5252
func (r *noErrorCheck) Match(n ast.Node, ctx *gosec.Context) (*gosec.Issue, error) {
5353
switch stmt := n.(type) {
54+
case *ast.AssignStmt:
55+
cfg := ctx.Config
56+
if enabled, err := cfg.IsGlobalEnabled(gosec.Audit); err == nil && enabled {
57+
for _, expr := range stmt.Rhs {
58+
if callExpr, ok := expr.(*ast.CallExpr); ok && r.whitelist.ContainsCallExpr(expr, ctx, false) == nil {
59+
pos := returnsError(callExpr, ctx)
60+
if pos < 0 || pos >= len(stmt.Lhs) {
61+
return nil, nil
62+
}
63+
if id, ok := stmt.Lhs[pos].(*ast.Ident); ok && id.Name == "_" {
64+
return gosec.NewIssue(ctx, n, r.ID(), r.What, r.Severity, r.Confidence), nil
65+
}
66+
}
67+
}
68+
}
5469
case *ast.ExprStmt:
5570
if callExpr, ok := stmt.X.(*ast.CallExpr); ok && r.whitelist.ContainsCallExpr(stmt.X, ctx, false) == nil {
5671
pos := returnsError(callExpr, ctx)

rules/rules_test.go

+15-3
Original file line numberDiff line numberDiff line change
@@ -12,21 +12,29 @@ import (
1212
"github.com/securego/gosec/testutils"
1313
)
1414

15+
type option struct {
16+
name gosec.GlobalOption
17+
value string
18+
}
19+
1520
var _ = Describe("gosec rules", func() {
1621

1722
var (
1823
logger *log.Logger
1924
config gosec.Config
2025
analyzer *gosec.Analyzer
21-
runner func(string, []testutils.CodeSample)
26+
runner func(string, []testutils.CodeSample, ...option)
2227
buildTags []string
2328
)
2429

2530
BeforeEach(func() {
2631
logger, _ = testutils.NewLogger()
2732
config = gosec.NewConfig()
2833
analyzer = gosec.NewAnalyzer(config, logger)
29-
runner = func(rule string, samples []testutils.CodeSample) {
34+
runner = func(rule string, samples []testutils.CodeSample, options ...option) {
35+
for _, o := range options {
36+
config.SetGlobal(o.name, o.value)
37+
}
3038
analyzer.LoadRules(rules.Generate(rules.NewRuleFilter(false, rule)).Builders())
3139
for n, sample := range samples {
3240
analyzer.Reset()
@@ -61,10 +69,14 @@ var _ = Describe("gosec rules", func() {
6169
runner("G103", testutils.SampleCodeG103)
6270
})
6371

64-
It("should errors not being checked", func() {
72+
It("should detect errors not being checked", func() {
6573
runner("G104", testutils.SampleCodeG104)
6674
})
6775

76+
It("should detect errors not being checked in audit mode", func() {
77+
runner("G104", testutils.SampleCodeG104Audit, option{name: gosec.Audit, value: "enabled"})
78+
})
79+
6880
It("should detect of big.Exp function", func() {
6981
runner("G105", testutils.SampleCodeG105)
7082
})

testutils/source.go

+57
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,63 @@ package main
231231
func dummy(){}
232232
`}, 0}}
233233

234+
// SampleCodeG104Audit finds errors that aren't being handled in audit mode
235+
SampleCodeG104Audit = []CodeSample{
236+
{[]string{`
237+
package main
238+
import "fmt"
239+
func test() (int,error) {
240+
return 0, nil
241+
}
242+
func main() {
243+
v, _ := test()
244+
fmt.Println(v)
245+
}`}, 1}, {[]string{`
246+
package main
247+
import (
248+
"io/ioutil"
249+
"os"
250+
"fmt"
251+
)
252+
func a() error {
253+
return fmt.Errorf("This is an error")
254+
}
255+
func b() {
256+
fmt.Println("b")
257+
ioutil.WriteFile("foo.txt", []byte("bar"), os.ModeExclusive)
258+
}
259+
func c() string {
260+
return fmt.Sprintf("This isn't anything")
261+
}
262+
func main() {
263+
_ = a()
264+
a()
265+
b()
266+
c()
267+
}`}, 3}, {[]string{`
268+
package main
269+
import "fmt"
270+
func test() error {
271+
return nil
272+
}
273+
func main() {
274+
e := test()
275+
fmt.Println(e)
276+
}`}, 0}, {[]string{`
277+
// +build go1.10
278+
279+
package main
280+
import "strings"
281+
func main() {
282+
var buf strings.Builder
283+
_, err := buf.WriteString("test string")
284+
if err != nil {
285+
panic(err)
286+
}
287+
}`, `
288+
package main
289+
func dummy(){}
290+
`}, 0}}
234291
// SampleCodeG105 - bignum overflow
235292
SampleCodeG105 = []CodeSample{{[]string{`
236293
package main

0 commit comments

Comments
 (0)