mirror of
https://github.com/securego/gosec.git
synced 2024-12-25 12:05:52 +00:00
MatchCallByPackage updated to avoid GetCallObject
There seems to be an inconsistency in the way that the type.Info.Uses map is populated by the type checker in Go 1.5 and the latest release. It is possible to ascertain the package that relates to an object 1.7.x release but this does not work for earlier Go versions. To work around this limitation we now track imports, and monitor if they are aliased or initalization only imports.
This commit is contained in:
parent
d16326051f
commit
7a275fd0ad
3 changed files with 89 additions and 20 deletions
|
@ -27,6 +27,13 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// ImportInfo is used to track aliased and initialization only imports.
|
||||||
|
type ImportInfo struct {
|
||||||
|
Imported map[string]string
|
||||||
|
Aliased map[string]string
|
||||||
|
InitOnly map[string]bool
|
||||||
|
}
|
||||||
|
|
||||||
// The Context is populated with data parsed from the source code as it is scanned.
|
// The Context is populated with data parsed from the source code as it is scanned.
|
||||||
// It is passed through to all rule functions as they are called. Rules may use
|
// It is passed through to all rule functions as they are called. Rules may use
|
||||||
// this data in conjunction withe the encoutered AST node.
|
// this data in conjunction withe the encoutered AST node.
|
||||||
|
@ -37,6 +44,7 @@ type Context struct {
|
||||||
Pkg *types.Package
|
Pkg *types.Package
|
||||||
Root *ast.File
|
Root *ast.File
|
||||||
Config map[string]interface{}
|
Config map[string]interface{}
|
||||||
|
Imports *ImportInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
// The Rule interface used by all rules supported by GAS.
|
// The Rule interface used by all rules supported by GAS.
|
||||||
|
@ -77,8 +85,20 @@ func NewAnalyzer(conf map[string]interface{}, logger *log.Logger) Analyzer {
|
||||||
ignoreNosec: conf["ignoreNosec"].(bool),
|
ignoreNosec: conf["ignoreNosec"].(bool),
|
||||||
ruleset: make(RuleSet),
|
ruleset: make(RuleSet),
|
||||||
Issues: make([]Issue, 0),
|
Issues: make([]Issue, 0),
|
||||||
context: Context{token.NewFileSet(), nil, nil, nil, nil, nil},
|
context: Context{
|
||||||
logger: logger,
|
token.NewFileSet(),
|
||||||
|
nil,
|
||||||
|
nil,
|
||||||
|
nil,
|
||||||
|
nil,
|
||||||
|
nil,
|
||||||
|
&ImportInfo{
|
||||||
|
make(map[string]string),
|
||||||
|
make(map[string]string),
|
||||||
|
make(map[string]bool),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
logger: logger,
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(tkelsey): use the inc/exc lists
|
// TODO(tkelsey): use the inc/exc lists
|
||||||
|
@ -110,6 +130,10 @@ func (gas *Analyzer) process(filename string, source interface{}) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for _, pkg := range gas.context.Pkg.Imports() {
|
||||||
|
gas.context.Imports.Imported[pkg.Path()] = pkg.Name()
|
||||||
|
}
|
||||||
|
|
||||||
ast.Walk(gas, root)
|
ast.Walk(gas, root)
|
||||||
gas.Stats.NumFiles++
|
gas.Stats.NumFiles++
|
||||||
}
|
}
|
||||||
|
@ -169,6 +193,20 @@ func (gas *Analyzer) ignore(n ast.Node) bool {
|
||||||
// Rule methods added with AddRule will be invoked as necessary.
|
// Rule methods added with AddRule will be invoked as necessary.
|
||||||
func (gas *Analyzer) Visit(n ast.Node) ast.Visitor {
|
func (gas *Analyzer) Visit(n ast.Node) ast.Visitor {
|
||||||
if !gas.ignore(n) {
|
if !gas.ignore(n) {
|
||||||
|
|
||||||
|
// Track aliased and initialization imports
|
||||||
|
if imported, ok := n.(*ast.ImportSpec); ok {
|
||||||
|
if imported.Name != nil {
|
||||||
|
path := strings.Trim(imported.Path.Value, `"`)
|
||||||
|
if imported.Name.Name == "_" {
|
||||||
|
// Initialization import
|
||||||
|
gas.context.Imports.InitOnly[path] = true
|
||||||
|
} else {
|
||||||
|
// Aliased import
|
||||||
|
gas.context.Imports.Aliased[imported.Name.Name] = path
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
if val, ok := gas.ruleset[reflect.TypeOf(n)]; ok {
|
if val, ok := gas.ruleset[reflect.TypeOf(n)]; ok {
|
||||||
for _, rule := range val {
|
for _, rule := range val {
|
||||||
ret, err := rule.Match(n, &gas.context)
|
ret, err := rule.Match(n, &gas.context)
|
||||||
|
|
|
@ -47,19 +47,45 @@ func MatchCall(n ast.Node, r *regexp.Regexp) *ast.CallExpr {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// MatchCallByObject ses the type checker to resolve the associated object with a
|
// MatchCallByPackage ensures that the specified package is imported,
|
||||||
// particular *ast.CallExpr. This object is used to determine if the
|
// adjusts the name for any aliases and ignores cases that are
|
||||||
// package and identifier name matches the passed in parameters.
|
// initialization only imports.
|
||||||
//
|
//
|
||||||
// Usage:
|
// Usage:
|
||||||
// node, obj := MatchCall(n, ctx, "math/rand", "Read")
|
// node, matched := MatchCallByPackage(n, ctx, "math/rand", "Read")
|
||||||
//
|
//
|
||||||
func MatchCallByObject(n ast.Node, c *Context, pkg, name string) (*ast.CallExpr, types.Object) {
|
func MatchCallByPackage(n ast.Node, c *Context, pkg string, names ...string) (*ast.CallExpr, bool) {
|
||||||
call, obj := GetCallObject(n, c)
|
|
||||||
if obj != nil && obj.Pkg().Path() == pkg && obj.Name() == name {
|
importName, imported := c.Imports.Imported[pkg]
|
||||||
return call, obj
|
if !imported {
|
||||||
|
return nil, false
|
||||||
}
|
}
|
||||||
return nil, nil
|
|
||||||
|
if _, initonly := c.Imports.InitOnly[pkg]; initonly {
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
|
||||||
|
if alias, ok := c.Imports.Aliased[pkg]; ok {
|
||||||
|
importName = alias
|
||||||
|
}
|
||||||
|
|
||||||
|
switch node := n.(type) {
|
||||||
|
case *ast.CallExpr:
|
||||||
|
switch fn := node.Fun.(type) {
|
||||||
|
case *ast.SelectorExpr:
|
||||||
|
switch expr := fn.X.(type) {
|
||||||
|
case *ast.Ident:
|
||||||
|
if expr.Name == importName {
|
||||||
|
for _, name := range names {
|
||||||
|
if fn.Sel.Name == name {
|
||||||
|
return node, true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, false
|
||||||
}
|
}
|
||||||
|
|
||||||
// MatchCompLit will match an ast.CompositeLit if its string value obays the given regex.
|
// MatchCompLit will match an ast.CompositeLit if its string value obays the given regex.
|
||||||
|
|
|
@ -16,33 +16,38 @@ package rules
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"go/ast"
|
"go/ast"
|
||||||
"regexp"
|
|
||||||
|
|
||||||
gas "github.com/GoASTScanner/gas/core"
|
gas "github.com/GoASTScanner/gas/core"
|
||||||
)
|
)
|
||||||
|
|
||||||
type UsesWeakCryptography struct {
|
type UsesWeakCryptography struct {
|
||||||
gas.MetaData
|
gas.MetaData
|
||||||
pattern *regexp.Regexp
|
blacklist map[string][]string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *UsesWeakCryptography) Match(n ast.Node, c *gas.Context) (*gas.Issue, error) {
|
func (r *UsesWeakCryptography) Match(n ast.Node, c *gas.Context) (*gas.Issue, error) {
|
||||||
if node := gas.MatchCall(n, r.pattern); node != nil {
|
|
||||||
return gas.NewIssue(c, n, r.What, r.Severity, r.Confidence), nil
|
for pkg, funcs := range r.blacklist {
|
||||||
|
if _, matched := gas.MatchCallByPackage(n, c, pkg, funcs...); matched {
|
||||||
|
return gas.NewIssue(c, n, r.What, r.Severity, r.Confidence), nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Uses des.* md5.* or rc4.*
|
// Uses des.* md5.* or rc4.*
|
||||||
func NewUsesWeakCryptography(conf map[string]interface{}) (r gas.Rule, n ast.Node) {
|
func NewUsesWeakCryptography(conf map[string]interface{}) (gas.Rule, ast.Node) {
|
||||||
r = &UsesWeakCryptography{
|
calls := make(map[string][]string)
|
||||||
pattern: regexp.MustCompile(`des\.NewCipher|des\.NewTripleDESCipher|md5\.New|md5\.Sum|rc4\.NewCipher`),
|
calls["crypto/des"] = []string{"NewCipher", "NewTripleDESCipher"}
|
||||||
|
calls["crypto/md5"] = []string{"New", "Sum"}
|
||||||
|
calls["crypto/rc4"] = []string{"NewCipher"}
|
||||||
|
rule := &UsesWeakCryptography{
|
||||||
|
blacklist: calls,
|
||||||
MetaData: gas.MetaData{
|
MetaData: gas.MetaData{
|
||||||
Severity: gas.Medium,
|
Severity: gas.Medium,
|
||||||
Confidence: gas.High,
|
Confidence: gas.High,
|
||||||
What: "Use of weak cryptographic primitive",
|
What: "Use of weak cryptographic primitive",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
n = (*ast.CallExpr)(nil)
|
return rule, (*ast.CallExpr)(nil)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue