gosec/rules/bad_defer.go

97 lines
1.9 KiB
Go
Raw Permalink Normal View History

package rules
import (
"fmt"
"go/ast"
"strings"
"github.com/securego/gosec/v2"
)
type deferType struct {
typ string
methods []string
}
type badDefer struct {
gosec.MetaData
types []deferType
}
func (r *badDefer) ID() string {
return r.MetaData.ID
}
func normalize(typ string) string {
return strings.TrimPrefix(typ, "*")
}
func contains(methods []string, method string) bool {
for _, m := range methods {
if m == method {
return true
}
}
return false
}
func (r *badDefer) Match(n ast.Node, c *gosec.Context) (*gosec.Issue, error) {
if deferStmt, ok := n.(*ast.DeferStmt); ok {
for _, deferTyp := range r.types {
if typ, method, err := gosec.GetCallInfo(deferStmt.Call, c); err == nil {
if normalize(typ) == deferTyp.typ && contains(deferTyp.methods, method) {
return gosec.NewIssue(c, n, r.ID(), fmt.Sprintf(r.What, method, typ), r.Severity, r.Confidence), nil
}
}
}
}
return nil, nil
}
// NewDeferredClosing detects unsafe defer of error returning methods
func NewDeferredClosing(id string, conf gosec.Config) (gosec.Rule, []ast.Node) {
return &badDefer{
types: []deferType{
{
typ: "os.File",
methods: []string{"Close"},
},
{
typ: "io.ReadCloser",
methods: []string{"Close"},
},
{
typ: "io.WriteCloser",
methods: []string{"Close"},
},
{
typ: "io.ReadWriteCloser",
methods: []string{"Close"},
},
{
typ: "io.ReadSeekCloser",
methods: []string{"Close"},
},
{
typ: "io.Closer",
methods: []string{"Close"},
},
{
typ: "net.Conn",
methods: []string{"Close"},
},
{
typ: "net.Listener",
methods: []string{"Close"},
},
},
MetaData: gosec.MetaData{
ID: id,
Severity: gosec.Medium,
Confidence: gosec.High,
What: "Deferring unsafe method %q on type %q",
},
}, []ast.Node{(*ast.DeferStmt)(nil)}
}