1package rules 2 3import ( 4 "fmt" 5 "go/ast" 6 "strings" 7 8 "github.com/securego/gosec/v2" 9) 10 11type deferType struct { 12 typ string 13 methods []string 14} 15 16type badDefer struct { 17 gosec.MetaData 18 types []deferType 19} 20 21func (r *badDefer) ID() string { 22 return r.MetaData.ID 23} 24 25func normalize(typ string) string { 26 return strings.TrimPrefix(typ, "*") 27} 28 29func contains(methods []string, method string) bool { 30 for _, m := range methods { 31 if m == method { 32 return true 33 } 34 } 35 return false 36} 37 38func (r *badDefer) Match(n ast.Node, c *gosec.Context) (*gosec.Issue, error) { 39 if deferStmt, ok := n.(*ast.DeferStmt); ok { 40 for _, deferTyp := range r.types { 41 if typ, method, err := gosec.GetCallInfo(deferStmt.Call, c); err == nil { 42 if normalize(typ) == deferTyp.typ && contains(deferTyp.methods, method) { 43 return gosec.NewIssue(c, n, r.ID(), fmt.Sprintf(r.What, method, typ), r.Severity, r.Confidence), nil 44 } 45 } 46 } 47 48 } 49 50 return nil, nil 51} 52 53// NewDeferredClosing detects unsafe defer of error returning methods 54func NewDeferredClosing(id string, conf gosec.Config) (gosec.Rule, []ast.Node) { 55 return &badDefer{ 56 types: []deferType{ 57 { 58 typ: "os.File", 59 methods: []string{"Close"}, 60 }, 61 }, 62 MetaData: gosec.MetaData{ 63 ID: id, 64 Severity: gosec.Medium, 65 Confidence: gosec.High, 66 What: "Deferring unsafe method %q on type %q", 67 }, 68 }, []ast.Node{(*ast.DeferStmt)(nil)} 69} 70