mirror of
https://github.com/securego/gosec.git
synced 2024-12-25 03:55:54 +00:00
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
This commit is contained in:
parent
14ed63d558
commit
f87af5fa72
8 changed files with 128 additions and 17 deletions
|
@ -41,7 +41,7 @@ type Context struct {
|
||||||
Pkg *types.Package
|
Pkg *types.Package
|
||||||
PkgFiles []*ast.File
|
PkgFiles []*ast.File
|
||||||
Root *ast.File
|
Root *ast.File
|
||||||
Config map[string]interface{}
|
Config Config
|
||||||
Imports *ImportTracker
|
Imports *ImportTracker
|
||||||
Ignores []map[string]bool
|
Ignores []map[string]bool
|
||||||
}
|
}
|
||||||
|
@ -69,8 +69,8 @@ type Analyzer struct {
|
||||||
// NewAnalyzer builds a new analyzer.
|
// NewAnalyzer builds a new analyzer.
|
||||||
func NewAnalyzer(conf Config, logger *log.Logger) *Analyzer {
|
func NewAnalyzer(conf Config, logger *log.Logger) *Analyzer {
|
||||||
ignoreNoSec := false
|
ignoreNoSec := false
|
||||||
if setting, err := conf.GetGlobal("nosec"); err == nil {
|
if enabled, err := conf.IsGlobalEnabled(Nosec); err == nil {
|
||||||
ignoreNoSec = setting == "true" || setting == "enabled"
|
ignoreNoSec = enabled
|
||||||
}
|
}
|
||||||
if logger == nil {
|
if logger == nil {
|
||||||
logger = log.New(os.Stderr, "[gosec]", log.LstdFlags)
|
logger = log.New(os.Stderr, "[gosec]", log.LstdFlags)
|
||||||
|
|
|
@ -201,7 +201,7 @@ var _ = Describe("Analyzer", func() {
|
||||||
|
|
||||||
// overwrite nosec option
|
// overwrite nosec option
|
||||||
nosecIgnoreConfig := gosec.NewConfig()
|
nosecIgnoreConfig := gosec.NewConfig()
|
||||||
nosecIgnoreConfig.SetGlobal("nosec", "true")
|
nosecIgnoreConfig.SetGlobal(gosec.Nosec, "true")
|
||||||
customAnalyzer := gosec.NewAnalyzer(nosecIgnoreConfig, logger)
|
customAnalyzer := gosec.NewAnalyzer(nosecIgnoreConfig, logger)
|
||||||
customAnalyzer.LoadRules(rules.Generate(rules.NewRuleFilter(false, "G401")).Builders())
|
customAnalyzer.LoadRules(rules.Generate(rules.NewRuleFilter(false, "G401")).Builders())
|
||||||
|
|
||||||
|
|
|
@ -137,7 +137,7 @@ func loadConfig(configFile string) (gosec.Config, error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if *flagIgnoreNoSec {
|
if *flagIgnoreNoSec {
|
||||||
config.SetGlobal("nosec", "true")
|
config.SetGlobal(gosec.Nosec, "true")
|
||||||
}
|
}
|
||||||
return config, nil
|
return config, nil
|
||||||
}
|
}
|
||||||
|
|
29
config.go
29
config.go
|
@ -14,6 +14,16 @@ const (
|
||||||
Globals = "global"
|
Globals = "global"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// GlobalOption defines the name of the global options
|
||||||
|
type GlobalOption string
|
||||||
|
|
||||||
|
const (
|
||||||
|
// Nosec global option for #nosec directive
|
||||||
|
Nosec GlobalOption = "nosec"
|
||||||
|
// Audit global option which indicates that gosec runs in audit mode
|
||||||
|
Audit GlobalOption = "audit"
|
||||||
|
)
|
||||||
|
|
||||||
// Config is used to provide configuration and customization to each of the rules.
|
// Config is used to provide configuration and customization to each of the rules.
|
||||||
type Config map[string]interface{}
|
type Config map[string]interface{}
|
||||||
|
|
||||||
|
@ -22,7 +32,7 @@ type Config map[string]interface{}
|
||||||
// or from a *os.File.
|
// or from a *os.File.
|
||||||
func NewConfig() Config {
|
func NewConfig() Config {
|
||||||
cfg := make(Config)
|
cfg := make(Config)
|
||||||
cfg[Globals] = make(map[string]string)
|
cfg[Globals] = make(map[GlobalOption]string)
|
||||||
return cfg
|
return cfg
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -65,9 +75,9 @@ func (c Config) Set(section string, value interface{}) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetGlobal returns value associated with global configuration option
|
// GetGlobal returns value associated with global configuration option
|
||||||
func (c Config) GetGlobal(option string) (string, error) {
|
func (c Config) GetGlobal(option GlobalOption) (string, error) {
|
||||||
if globals, ok := c[Globals]; ok {
|
if globals, ok := c[Globals]; ok {
|
||||||
if settings, ok := globals.(map[string]string); ok {
|
if settings, ok := globals.(map[GlobalOption]string); ok {
|
||||||
if value, ok := settings[option]; ok {
|
if value, ok := settings[option]; ok {
|
||||||
return value, nil
|
return value, nil
|
||||||
}
|
}
|
||||||
|
@ -79,10 +89,19 @@ func (c Config) GetGlobal(option string) (string, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetGlobal associates a value with a global configuration option
|
// SetGlobal associates a value with a global configuration option
|
||||||
func (c Config) SetGlobal(option, value string) {
|
func (c Config) SetGlobal(option GlobalOption, value string) {
|
||||||
if globals, ok := c[Globals]; ok {
|
if globals, ok := c[Globals]; ok {
|
||||||
if settings, ok := globals.(map[string]string); ok {
|
if settings, ok := globals.(map[GlobalOption]string); ok {
|
||||||
settings[option] = value
|
settings[option] = value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsGlobalEnabled checks if a global option is enabled
|
||||||
|
func (c Config) IsGlobalEnabled(option GlobalOption) (bool, error) {
|
||||||
|
value, err := c.GetGlobal(option)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
return (value == "true" || value == "enabled"), nil
|
||||||
|
}
|
||||||
|
|
|
@ -81,23 +81,31 @@ var _ = Describe("Configuration", func() {
|
||||||
It("should have a default global section", func() {
|
It("should have a default global section", func() {
|
||||||
settings, err := configuration.Get("global")
|
settings, err := configuration.Get("global")
|
||||||
Expect(err).Should(BeNil())
|
Expect(err).Should(BeNil())
|
||||||
expectedType := make(map[string]string)
|
expectedType := make(map[gosec.GlobalOption]string)
|
||||||
Expect(settings).Should(BeAssignableToTypeOf(expectedType))
|
Expect(settings).Should(BeAssignableToTypeOf(expectedType))
|
||||||
})
|
})
|
||||||
|
|
||||||
It("should save global settings to correct section", func() {
|
It("should save global settings to correct section", func() {
|
||||||
configuration.SetGlobal("nosec", "enabled")
|
configuration.SetGlobal(gosec.Nosec, "enabled")
|
||||||
settings, err := configuration.Get("global")
|
settings, err := configuration.Get("global")
|
||||||
Expect(err).Should(BeNil())
|
Expect(err).Should(BeNil())
|
||||||
if globals, ok := settings.(map[string]string); ok {
|
if globals, ok := settings.(map[gosec.GlobalOption]string); ok {
|
||||||
Expect(globals["nosec"]).Should(MatchRegexp("enabled"))
|
Expect(globals["nosec"]).Should(MatchRegexp("enabled"))
|
||||||
} else {
|
} else {
|
||||||
Fail("globals are not defined as map")
|
Fail("globals are not defined as map")
|
||||||
}
|
}
|
||||||
|
|
||||||
setValue, err := configuration.GetGlobal("nosec")
|
setValue, err := configuration.GetGlobal(gosec.Nosec)
|
||||||
Expect(err).Should(BeNil())
|
Expect(err).Should(BeNil())
|
||||||
Expect(setValue).Should(MatchRegexp("enabled"))
|
Expect(setValue).Should(MatchRegexp("enabled"))
|
||||||
})
|
})
|
||||||
|
|
||||||
|
It("should find global settings which are enabled", func() {
|
||||||
|
configuration.SetGlobal(gosec.Nosec, "enabled")
|
||||||
|
enabled, err := configuration.IsGlobalEnabled(gosec.Nosec)
|
||||||
|
Expect(err).Should(BeNil())
|
||||||
|
Expect(enabled).Should(BeTrue())
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
|
@ -51,6 +51,21 @@ func returnsError(callExpr *ast.CallExpr, ctx *gosec.Context) int {
|
||||||
|
|
||||||
func (r *noErrorCheck) Match(n ast.Node, ctx *gosec.Context) (*gosec.Issue, error) {
|
func (r *noErrorCheck) Match(n ast.Node, ctx *gosec.Context) (*gosec.Issue, error) {
|
||||||
switch stmt := n.(type) {
|
switch stmt := n.(type) {
|
||||||
|
case *ast.AssignStmt:
|
||||||
|
cfg := ctx.Config
|
||||||
|
if enabled, err := cfg.IsGlobalEnabled(gosec.Audit); err == nil && enabled {
|
||||||
|
for _, expr := range stmt.Rhs {
|
||||||
|
if callExpr, ok := expr.(*ast.CallExpr); ok && r.whitelist.ContainsCallExpr(expr, ctx, false) == nil {
|
||||||
|
pos := returnsError(callExpr, ctx)
|
||||||
|
if pos < 0 || pos >= len(stmt.Lhs) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
if id, ok := stmt.Lhs[pos].(*ast.Ident); ok && id.Name == "_" {
|
||||||
|
return gosec.NewIssue(ctx, n, r.ID(), r.What, r.Severity, r.Confidence), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
case *ast.ExprStmt:
|
case *ast.ExprStmt:
|
||||||
if callExpr, ok := stmt.X.(*ast.CallExpr); ok && r.whitelist.ContainsCallExpr(stmt.X, ctx, false) == nil {
|
if callExpr, ok := stmt.X.(*ast.CallExpr); ok && r.whitelist.ContainsCallExpr(stmt.X, ctx, false) == nil {
|
||||||
pos := returnsError(callExpr, ctx)
|
pos := returnsError(callExpr, ctx)
|
||||||
|
|
|
@ -12,13 +12,18 @@ import (
|
||||||
"github.com/securego/gosec/testutils"
|
"github.com/securego/gosec/testutils"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type option struct {
|
||||||
|
name gosec.GlobalOption
|
||||||
|
value string
|
||||||
|
}
|
||||||
|
|
||||||
var _ = Describe("gosec rules", func() {
|
var _ = Describe("gosec rules", func() {
|
||||||
|
|
||||||
var (
|
var (
|
||||||
logger *log.Logger
|
logger *log.Logger
|
||||||
config gosec.Config
|
config gosec.Config
|
||||||
analyzer *gosec.Analyzer
|
analyzer *gosec.Analyzer
|
||||||
runner func(string, []testutils.CodeSample)
|
runner func(string, []testutils.CodeSample, ...option)
|
||||||
buildTags []string
|
buildTags []string
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -26,7 +31,10 @@ var _ = Describe("gosec rules", func() {
|
||||||
logger, _ = testutils.NewLogger()
|
logger, _ = testutils.NewLogger()
|
||||||
config = gosec.NewConfig()
|
config = gosec.NewConfig()
|
||||||
analyzer = gosec.NewAnalyzer(config, logger)
|
analyzer = gosec.NewAnalyzer(config, logger)
|
||||||
runner = func(rule string, samples []testutils.CodeSample) {
|
runner = func(rule string, samples []testutils.CodeSample, options ...option) {
|
||||||
|
for _, o := range options {
|
||||||
|
config.SetGlobal(o.name, o.value)
|
||||||
|
}
|
||||||
analyzer.LoadRules(rules.Generate(rules.NewRuleFilter(false, rule)).Builders())
|
analyzer.LoadRules(rules.Generate(rules.NewRuleFilter(false, rule)).Builders())
|
||||||
for n, sample := range samples {
|
for n, sample := range samples {
|
||||||
analyzer.Reset()
|
analyzer.Reset()
|
||||||
|
@ -61,10 +69,14 @@ var _ = Describe("gosec rules", func() {
|
||||||
runner("G103", testutils.SampleCodeG103)
|
runner("G103", testutils.SampleCodeG103)
|
||||||
})
|
})
|
||||||
|
|
||||||
It("should errors not being checked", func() {
|
It("should detect errors not being checked", func() {
|
||||||
runner("G104", testutils.SampleCodeG104)
|
runner("G104", testutils.SampleCodeG104)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
It("should detect errors not being checked in audit mode", func() {
|
||||||
|
runner("G104", testutils.SampleCodeG104Audit, option{name: gosec.Audit, value: "enabled"})
|
||||||
|
})
|
||||||
|
|
||||||
It("should detect of big.Exp function", func() {
|
It("should detect of big.Exp function", func() {
|
||||||
runner("G105", testutils.SampleCodeG105)
|
runner("G105", testutils.SampleCodeG105)
|
||||||
})
|
})
|
||||||
|
|
|
@ -231,6 +231,63 @@ package main
|
||||||
func dummy(){}
|
func dummy(){}
|
||||||
`}, 0}}
|
`}, 0}}
|
||||||
|
|
||||||
|
// SampleCodeG104Audit finds errors that aren't being handled in audit mode
|
||||||
|
SampleCodeG104Audit = []CodeSample{
|
||||||
|
{[]string{`
|
||||||
|
package main
|
||||||
|
import "fmt"
|
||||||
|
func test() (int,error) {
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
func main() {
|
||||||
|
v, _ := test()
|
||||||
|
fmt.Println(v)
|
||||||
|
}`}, 1}, {[]string{`
|
||||||
|
package main
|
||||||
|
import (
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
func a() error {
|
||||||
|
return fmt.Errorf("This is an error")
|
||||||
|
}
|
||||||
|
func b() {
|
||||||
|
fmt.Println("b")
|
||||||
|
ioutil.WriteFile("foo.txt", []byte("bar"), os.ModeExclusive)
|
||||||
|
}
|
||||||
|
func c() string {
|
||||||
|
return fmt.Sprintf("This isn't anything")
|
||||||
|
}
|
||||||
|
func main() {
|
||||||
|
_ = a()
|
||||||
|
a()
|
||||||
|
b()
|
||||||
|
c()
|
||||||
|
}`}, 3}, {[]string{`
|
||||||
|
package main
|
||||||
|
import "fmt"
|
||||||
|
func test() error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
func main() {
|
||||||
|
e := test()
|
||||||
|
fmt.Println(e)
|
||||||
|
}`}, 0}, {[]string{`
|
||||||
|
// +build go1.10
|
||||||
|
|
||||||
|
package main
|
||||||
|
import "strings"
|
||||||
|
func main() {
|
||||||
|
var buf strings.Builder
|
||||||
|
_, err := buf.WriteString("test string")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}`, `
|
||||||
|
package main
|
||||||
|
func dummy(){}
|
||||||
|
`}, 0}}
|
||||||
// SampleCodeG105 - bignum overflow
|
// SampleCodeG105 - bignum overflow
|
||||||
SampleCodeG105 = []CodeSample{{[]string{`
|
SampleCodeG105 = []CodeSample{{[]string{`
|
||||||
package main
|
package main
|
||||||
|
|
Loading…
Reference in a new issue