diff options
| author | Taras Madan <tarasmadan@google.com> | 2023-12-05 15:10:03 +0100 |
|---|---|---|
| committer | Taras Madan <tarasmadan@google.com> | 2023-12-06 11:31:44 +0000 |
| commit | 2ab72b4feef2c97f22f90cfbf9e45a6cfcd08bda (patch) | |
| tree | a6d19b94b6399fcc00a6cfa430885cd349dd1533 /vendor/github.com/securego/gosec/v2/rules | |
| parent | e08e8f492d31d672cc245944c185f8aadf2ee695 (diff) | |
vendor: updates
Diffstat (limited to 'vendor/github.com/securego/gosec/v2/rules')
6 files changed, 243 insertions, 439 deletions
diff --git a/vendor/github.com/securego/gosec/v2/rules/fileperms.go b/vendor/github.com/securego/gosec/v2/rules/fileperms.go index 0376b6a03..5311f74c6 100644 --- a/vendor/github.com/securego/gosec/v2/rules/fileperms.go +++ b/vendor/github.com/securego/gosec/v2/rules/fileperms.go @@ -30,6 +30,7 @@ type filePermissions struct { calls []string } +// ID returns the ID of the rule. func (r *filePermissions) ID() string { return r.MetaData.ID } @@ -55,6 +56,7 @@ func modeIsSubset(subset int64, superset int64) bool { return (subset | superset) == superset } +// Match checks if the rule is matched. func (r *filePermissions) Match(n ast.Node, c *gosec.Context) (*issue.Issue, error) { for _, pkg := range r.pkgs { if callexpr, matched := gosec.MatchCallByPackage(n, c, pkg, r.calls...); matched { @@ -116,3 +118,47 @@ func NewMkdirPerms(id string, conf gosec.Config) (gosec.Rule, []ast.Node) { }, }, []ast.Node{(*ast.CallExpr)(nil)} } + +type osCreatePermissions struct { + issue.MetaData + mode int64 + pkgs []string + calls []string +} + +const defaultOsCreateMode = 0o666 + +// ID returns the ID of the rule. +func (r *osCreatePermissions) ID() string { + return r.MetaData.ID +} + +// Match checks if the rule is matched. +func (r *osCreatePermissions) Match(n ast.Node, c *gosec.Context) (*issue.Issue, error) { + for _, pkg := range r.pkgs { + if _, matched := gosec.MatchCallByPackage(n, c, pkg, r.calls...); matched { + if !modeIsSubset(defaultOsCreateMode, r.mode) { + return c.NewIssue(n, r.ID(), r.What, r.Severity, r.Confidence), nil + } + } + } + return nil, nil +} + +// NewOsCreatePerms reates a rule to detect file creation with a more permissive than configured +// permission mask. +func NewOsCreatePerms(id string, conf gosec.Config) (gosec.Rule, []ast.Node) { + mode := getConfiguredMode(conf, id, 0o666) + return &osCreatePermissions{ + mode: mode, + pkgs: []string{"os"}, + calls: []string{"Create"}, + MetaData: issue.MetaData{ + ID: id, + Severity: issue.Medium, + Confidence: issue.High, + What: fmt.Sprintf("Expect file permissions to be %#o or less but os.Create used with default permissions %#o", + mode, defaultOsCreateMode), + }, + }, []ast.Node{(*ast.CallExpr)(nil)} +} diff --git a/vendor/github.com/securego/gosec/v2/rules/hardcoded_credentials.go b/vendor/github.com/securego/gosec/v2/rules/hardcoded_credentials.go index ea8386084..ed1fb947d 100644 --- a/vendor/github.com/securego/gosec/v2/rules/hardcoded_credentials.go +++ b/vendor/github.com/securego/gosec/v2/rules/hardcoded_credentials.go @@ -15,6 +15,7 @@ package rules import ( + "fmt" "go/ast" "go/token" "regexp" @@ -26,10 +27,169 @@ import ( "github.com/securego/gosec/v2/issue" ) +type secretPattern struct { + name string + regexp *regexp.Regexp +} + +var secretsPatterns = [...]secretPattern{ + { + name: "RSA private key", + regexp: regexp.MustCompile(`-----BEGIN RSA PRIVATE KEY-----`), + }, + { + name: "SSH (DSA) private key", + regexp: regexp.MustCompile(`-----BEGIN DSA PRIVATE KEY-----`), + }, + { + name: "SSH (EC) private key", + regexp: regexp.MustCompile(`-----BEGIN EC PRIVATE KEY-----`), + }, + { + name: "PGP private key block", + regexp: regexp.MustCompile(`-----BEGIN PGP PRIVATE KEY BLOCK-----`), + }, + { + name: "Slack Token", + regexp: regexp.MustCompile(`xox[pborsa]-[0-9]{12}-[0-9]{12}-[0-9]{12}-[a-z0-9]{32}`), + }, + { + name: "AWS API Key", + regexp: regexp.MustCompile(`AKIA[0-9A-Z]{16}`), + }, + { + name: "Amazon MWS Auth Token", + regexp: regexp.MustCompile(`amzn\.mws\.[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}`), + }, + { + name: "AWS AppSync GraphQL Key", + regexp: regexp.MustCompile(`da2-[a-z0-9]{26}`), + }, + { + name: "GitHub personal access token", + regexp: regexp.MustCompile(`ghp_[a-zA-Z0-9]{36}`), + }, + { + name: "GitHub fine-grained access token", + regexp: regexp.MustCompile(`github_pat_[a-zA-Z0-9]{22}_[a-zA-Z0-9]{59}`), + }, + { + name: "GitHub action temporary token", + regexp: regexp.MustCompile(`ghs_[a-zA-Z0-9]{36}`), + }, + { + name: "Google API Key", + regexp: regexp.MustCompile(`AIza[0-9A-Za-z\-_]{35}`), + }, + { + name: "Google Cloud Platform API Key", + regexp: regexp.MustCompile(`AIza[0-9A-Za-z\-_]{35}`), + }, + { + name: "Google Cloud Platform OAuth", + regexp: regexp.MustCompile(`[0-9]+-[0-9A-Za-z_]{32}\.apps\.googleusercontent\.com`), + }, + { + name: "Google Drive API Key", + regexp: regexp.MustCompile(`AIza[0-9A-Za-z\-_]{35}`), + }, + { + name: "Google Drive OAuth", + regexp: regexp.MustCompile(`[0-9]+-[0-9A-Za-z_]{32}\.apps\.googleusercontent\.com`), + }, + { + name: "Google (GCP) Service-account", + regexp: regexp.MustCompile(`"type": "service_account"`), + }, + { + name: "Google Gmail API Key", + regexp: regexp.MustCompile(`AIza[0-9A-Za-z\-_]{35}`), + }, + { + name: "Google Gmail OAuth", + regexp: regexp.MustCompile(`[0-9]+-[0-9A-Za-z_]{32}\.apps\.googleusercontent\.com`), + }, + { + name: "Google OAuth Access Token", + regexp: regexp.MustCompile(`ya29\.[0-9A-Za-z\-_]+`), + }, + { + name: "Google YouTube API Key", + regexp: regexp.MustCompile(`AIza[0-9A-Za-z\-_]{35}`), + }, + { + name: "Google YouTube OAuth", + regexp: regexp.MustCompile(`[0-9]+-[0-9A-Za-z_]{32}\.apps\.googleusercontent\.com`), + }, + { + name: "Generic API Key", + regexp: regexp.MustCompile(`[aA][pP][iI]_?[kK][eE][yY].*[''|"][0-9a-zA-Z]{32,45}[''|"]`), + }, + { + name: "Generic Secret", + regexp: regexp.MustCompile(`[sS][eE][cC][rR][eE][tT].*[''|"][0-9a-zA-Z]{32,45}[''|"]`), + }, + { + name: "Heroku API Key", + regexp: regexp.MustCompile(`[hH][eE][rR][oO][kK][uU].*[0-9A-F]{8}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{12}`), + }, + { + name: "MailChimp API Key", + regexp: regexp.MustCompile(`[0-9a-f]{32}-us[0-9]{1,2}`), + }, + { + name: "Mailgun API Key", + regexp: regexp.MustCompile(`key-[0-9a-zA-Z]{32}`), + }, + { + name: "Password in URL", + regexp: regexp.MustCompile(`[a-zA-Z]{3,10}://[^/\\s:@]{3,20}:[^/\\s:@]{3,20}@.{1,100}["'\\s]`), + }, + { + name: "Slack Webhook", + regexp: regexp.MustCompile(`https://hooks\.slack\.com/services/T[a-zA-Z0-9_]{8}/B[a-zA-Z0-9_]{8}/[a-zA-Z0-9_]{24}`), + }, + { + name: "Stripe API Key", + regexp: regexp.MustCompile(`sk_live_[0-9a-zA-Z]{24}`), + }, + { + name: "Stripe API Key", + regexp: regexp.MustCompile(`sk_live_[0-9a-zA-Z]{24}`), + }, + { + name: "Stripe Restricted API Key", + regexp: regexp.MustCompile(`rk_live_[0-9a-zA-Z]{24}`), + }, + { + name: "Square Access Token", + regexp: regexp.MustCompile(`sq0atp-[0-9A-Za-z\-_]{22}`), + }, + { + name: "Square OAuth Secret", + regexp: regexp.MustCompile(`sq0csp-[0-9A-Za-z\-_]{43}`), + }, + { + name: "Telegram Bot API Key", + regexp: regexp.MustCompile(`[0-9]+:AA[0-9A-Za-z\-_]{33}`), + }, + { + name: "Twilio API Key", + regexp: regexp.MustCompile(`SK[0-9a-fA-F]{32}`), + }, + { + name: "Twitter Access Token", + regexp: regexp.MustCompile(`[tT][wW][iI][tT][tT][eE][rR].*[1-9][0-9]+-[0-9a-zA-Z]{40}`), + }, + { + name: "Twitter OAuth", + regexp: regexp.MustCompile(`[tT][wW][iI][tT][tT][eE][rR].*[''|"][0-9a-zA-Z]{35,44}[''|"]`), + }, +} + type credentials struct { issue.MetaData pattern *regexp.Regexp - patternValue *regexp.Regexp // Pattern for matching string values (LHS on assign statements) entropyThreshold float64 perCharThreshold float64 truncate int @@ -56,6 +216,15 @@ func (r *credentials) isHighEntropyString(str string) bool { entropyPerChar >= r.perCharThreshold)) } +func (r *credentials) isSecretPattern(str string) (bool, string) { + for _, pattern := range secretsPatterns { + if pattern.regexp.MatchString(str) { + return true, pattern.name + } + } + return false, "" +} + func (r *credentials) Match(n ast.Node, ctx *gosec.Context) (*issue.Issue, error) { switch node := n.(type) { case *ast.AssignStmt: @@ -89,9 +258,9 @@ func (r *credentials) matchAssign(assign *ast.AssignStmt, ctx *gosec.Context) (* continue } - if r.patternValue.MatchString(val) { - if r.ignoreEntropy || r.isHighEntropyString(val) { - return ctx.NewIssue(assign, r.ID(), r.What, r.Severity, r.Confidence), nil + if r.ignoreEntropy || r.isHighEntropyString(val) { + if ok, patternName := r.isSecretPattern(val); ok { + return ctx.NewIssue(assign, r.ID(), fmt.Sprintf("%s: %s", r.What, patternName), r.Severity, r.Confidence), nil } } } @@ -120,9 +289,9 @@ func (r *credentials) matchValueSpec(valueSpec *ast.ValueSpec, ctx *gosec.Contex // Now that no variable names have been matched, match the actual values to find any creds for _, ident := range valueSpec.Values { if val, err := gosec.GetString(ident); err == nil { - if r.patternValue.MatchString(val) { - if r.ignoreEntropy || r.isHighEntropyString(val) { - return ctx.NewIssue(valueSpec, r.ID(), r.What, r.Severity, r.Confidence), nil + if r.ignoreEntropy || r.isHighEntropyString(val) { + if ok, patternName := r.isSecretPattern(val); ok { + return ctx.NewIssue(valueSpec, r.ID(), fmt.Sprintf("%s: %s", r.What, patternName), r.Severity, r.Confidence), nil } } } @@ -159,9 +328,9 @@ func (r *credentials) matchEqualityCheck(binaryExpr *ast.BinaryExpr, ctx *gosec. if ok && identStrConst.Kind == token.STRING { s, _ := gosec.GetString(identStrConst) - if r.patternValue.MatchString(s) { - if r.ignoreEntropy || r.isHighEntropyString(s) { - return ctx.NewIssue(binaryExpr, r.ID(), r.What, r.Severity, r.Confidence), nil + if r.ignoreEntropy || r.isHighEntropyString(s) { + if ok, patternName := r.isSecretPattern(s); ok { + return ctx.NewIssue(binaryExpr, r.ID(), fmt.Sprintf("%s: %s", r.What, patternName), r.Severity, r.Confidence), nil } } } @@ -173,7 +342,6 @@ func (r *credentials) matchEqualityCheck(binaryExpr *ast.BinaryExpr, ctx *gosec. // assigned to variables that appear to be related to credentials. func NewHardcodedCredentials(id string, conf gosec.Config) (gosec.Rule, []ast.Node) { pattern := `(?i)passwd|pass|password|pwd|secret|token|pw|apiKey|bearer|cred` - patternValue := "(?i)(^(.*[:;,](\\s)*)?[a-f0-9]{64}$)|(AIza[0-9A-Za-z-_]{35})|(^(.*[:;,](\\s)*)?github_pat_[a-zA-Z0-9]{22}_[a-zA-Z0-9]{59}$)|(^(.*[:;,](\\s)*)?[0-9a-zA-Z-_]{24}$)" entropyThreshold := 80.0 perCharThreshold := 3.0 ignoreEntropy := false @@ -186,12 +354,6 @@ func NewHardcodedCredentials(id string, conf gosec.Config) (gosec.Rule, []ast.No } } - if configPatternValue, ok := conf["patternValue"]; ok { - if cfgPatternValue, ok := configPatternValue.(string); ok { - patternValue = cfgPatternValue - } - } - if configIgnoreEntropy, ok := conf["ignore_entropy"]; ok { if cfgIgnoreEntropy, ok := configIgnoreEntropy.(bool); ok { ignoreEntropy = cfgIgnoreEntropy @@ -222,7 +384,6 @@ func NewHardcodedCredentials(id string, conf gosec.Config) (gosec.Rule, []ast.No return &credentials{ pattern: regexp.MustCompile(pattern), - patternValue: regexp.MustCompile(patternValue), entropyThreshold: entropyThreshold, perCharThreshold: perCharThreshold, ignoreEntropy: ignoreEntropy, diff --git a/vendor/github.com/securego/gosec/v2/rules/implicit_aliasing.go b/vendor/github.com/securego/gosec/v2/rules/implicit_aliasing.go index 32e2fd205..a7eabb20b 100644 --- a/vendor/github.com/securego/gosec/v2/rules/implicit_aliasing.go +++ b/vendor/github.com/securego/gosec/v2/rules/implicit_aliasing.go @@ -3,6 +3,7 @@ package rules import ( "go/ast" "go/token" + "go/types" "github.com/securego/gosec/v2" "github.com/securego/gosec/v2/issue" @@ -28,23 +29,20 @@ func containsUnary(exprs []*ast.UnaryExpr, expr *ast.UnaryExpr) bool { return false } -func getIdentExpr(expr ast.Expr) *ast.Ident { +func getIdentExpr(expr ast.Expr) (*ast.Ident, bool) { + return doGetIdentExpr(expr, false) +} + +func doGetIdentExpr(expr ast.Expr, hasSelector bool) (*ast.Ident, bool) { switch node := expr.(type) { case *ast.Ident: - return node + return node, hasSelector case *ast.SelectorExpr: - return getIdentExpr(node.X) + return doGetIdentExpr(node.X, true) case *ast.UnaryExpr: - switch e := node.X.(type) { - case *ast.Ident: - return e - case *ast.SelectorExpr: - return getIdentExpr(e.X) - default: - return nil - } + return doGetIdentExpr(node.X, hasSelector) default: - return nil + return nil, false } } @@ -92,9 +90,13 @@ func (r *implicitAliasing) Match(n ast.Node, c *gosec.Context) (*issue.Issue, er } // If we find a unary op of & (reference) of an object within r.aliases, complain. - if identExpr := getIdentExpr(node); identExpr != nil && node.Op.String() == "&" { + if identExpr, hasSelector := getIdentExpr(node); identExpr != nil && node.Op.String() == "&" { if _, contains := r.aliases[identExpr.Obj]; contains { - return c.NewIssue(n, r.ID(), r.What, r.Severity, r.Confidence), nil + _, isPointer := c.Info.TypeOf(identExpr).(*types.Pointer) + + if !hasSelector || !isPointer { + return c.NewIssue(n, r.ID(), r.What, r.Severity, r.Confidence), nil + } } } case *ast.ReturnStmt: diff --git a/vendor/github.com/securego/gosec/v2/rules/rulelist.go b/vendor/github.com/securego/gosec/v2/rules/rulelist.go index 316691f61..f9ca4f52c 100644 --- a/vendor/github.com/securego/gosec/v2/rules/rulelist.go +++ b/vendor/github.com/securego/gosec/v2/rules/rulelist.go @@ -91,6 +91,7 @@ func Generate(trackSuppressions bool, filters ...RuleFilter) RuleList { {"G304", "File path provided as taint input", NewReadFile}, {"G305", "File path traversal when extracting zip archive", NewArchive}, {"G306", "Poor file permissions used when writing to a file", NewWritePerms}, + {"G307", "Poor file permissions used when creating a file with os.Create", NewOsCreatePerms}, // crypto {"G401", "Detect the usage of DES, RC4, MD5 or SHA1", NewUsesWeakCryptography}, @@ -107,7 +108,6 @@ func Generate(trackSuppressions bool, filters ...RuleFilter) RuleList { // memory safety {"G601", "Implicit memory aliasing in RangeStmt", NewImplicitAliasing}, - {"G602", "Slice access out of bounds", NewSliceBoundCheck}, } ruleMap := make(map[string]RuleDefinition) diff --git a/vendor/github.com/securego/gosec/v2/rules/slice_bounds.go b/vendor/github.com/securego/gosec/v2/rules/slice_bounds.go deleted file mode 100644 index 04811bb50..000000000 --- a/vendor/github.com/securego/gosec/v2/rules/slice_bounds.go +++ /dev/null @@ -1,405 +0,0 @@ -package rules - -import ( - "fmt" - "go/ast" - "go/types" - - "github.com/securego/gosec/v2" - "github.com/securego/gosec/v2/issue" -) - -// sliceOutOfBounds is a rule which checks for slices which are accessed outside their capacity, -// either through indexing it out of bounds or through slice expressions whose low or high index -// are out of bounds. -type sliceOutOfBounds struct { - sliceCaps map[*ast.CallExpr]map[string]*int64 // Capacities of slices. Maps function call -> var name -> value. - currentScope *types.Scope // Current scope. Map is cleared when scope changes. - currentFuncName string // Current function. - funcCallArgs map[string][]*int64 // Caps to load once a func declaration is scanned. - issue.MetaData // Metadata for this rule. -} - -// ID returns the rule ID for sliceOutOfBounds: G602. -func (s *sliceOutOfBounds) ID() string { - return s.MetaData.ID -} - -func (s *sliceOutOfBounds) Match(node ast.Node, ctx *gosec.Context) (*issue.Issue, error) { - if s.currentScope == nil { - s.currentScope = ctx.Pkg.Scope() - } else if s.currentScope != ctx.Pkg.Scope() { - s.currentScope = ctx.Pkg.Scope() - - // Clear slice map, since we are in a new scope - sliceMapNil := make(map[string]*int64) - sliceCaps := make(map[*ast.CallExpr]map[string]*int64) - sliceCaps[nil] = sliceMapNil - s.sliceCaps = sliceCaps - } - - switch node := node.(type) { - case *ast.AssignStmt: - return s.matchAssign(node, ctx) - case *ast.SliceExpr: - return s.matchSliceExpr(node, ctx) - case *ast.IndexExpr: - return s.matchIndexExpr(node, ctx) - case *ast.FuncDecl: - s.currentFuncName = node.Name.Name - s.loadArgCaps(node) - case *ast.CallExpr: - if _, ok := node.Fun.(*ast.FuncLit); ok { - // Do nothing with func literals for now. - break - } - - sliceMap := make(map[string]*int64) - s.sliceCaps[node] = sliceMap - s.setupCallArgCaps(node, ctx) - } - return nil, nil -} - -// updateSliceCaps takes in a variable name and a map of calls we are updating the variables for to the updated values -// and will add it to the sliceCaps map. -func (s *sliceOutOfBounds) updateSliceCaps(varName string, caps map[*ast.CallExpr]*int64) { - for callExpr, cap := range caps { - s.sliceCaps[callExpr][varName] = cap - } -} - -// getAllCalls returns all CallExprs that are calls to the given function. -func (s *sliceOutOfBounds) getAllCalls(funcName string, ctx *gosec.Context) []*ast.CallExpr { - calls := []*ast.CallExpr{} - - for callExpr := range s.sliceCaps { - if callExpr != nil { - // Compare the names of the function the code is scanning with the current call we are iterating over - _, callFuncName, err := gosec.GetCallInfo(callExpr, ctx) - if err != nil { - continue - } - - if callFuncName == funcName { - calls = append(calls, callExpr) - } - } - } - return calls -} - -// getSliceCapsForFunc gets all the capacities for slice with given name that are stored for each call to the passed function. -func (s *sliceOutOfBounds) getSliceCapsForFunc(funcName string, varName string, ctx *gosec.Context) map[*ast.CallExpr]*int64 { - caps := make(map[*ast.CallExpr]*int64) - - calls := s.getAllCalls(funcName, ctx) - for _, call := range calls { - if callCaps, ok := s.sliceCaps[call]; ok { - caps[call] = callCaps[varName] - } - } - - return caps -} - -// setupCallArgCaps evaluates and saves the caps for any slices in the args so they can be validated when the function is scanned. -func (s *sliceOutOfBounds) setupCallArgCaps(callExpr *ast.CallExpr, ctx *gosec.Context) { - // Array of caps to be loaded once the function declaration is scanned - funcCallArgs := []*int64{} - - // Get function name - _, funcName, err := gosec.GetCallInfo(callExpr, ctx) - if err != nil { - return - } - - for _, arg := range callExpr.Args { - switch node := arg.(type) { - case *ast.SliceExpr: - caps := s.evaluateSliceExpr(node, ctx) - - // Simplifying assumption: use the lowest capacity. Storing all possible capacities for slices passed - // to a function call would catch the most issues, but would require a data structure like a stack and a - // reworking of the code for scanning itself. Use the lowest capacity, as this would be more likely to - // raise an issue for being out of bounds. - var lowestCap *int64 - for _, cap := range caps { - if cap == nil { - continue - } - - if lowestCap == nil { - lowestCap = cap - } else if *lowestCap > *cap { - lowestCap = cap - } - } - - if lowestCap == nil { - funcCallArgs = append(funcCallArgs, nil) - continue - } - - // Now create a map of just this value to add it to the sliceCaps - funcCallArgs = append(funcCallArgs, lowestCap) - case *ast.Ident: - ident := arg.(*ast.Ident) - caps := s.getSliceCapsForFunc(s.currentFuncName, ident.Name, ctx) - - var lowestCap *int64 - for _, cap := range caps { - if cap == nil { - continue - } - - if lowestCap == nil { - lowestCap = cap - } else if *lowestCap > *cap { - lowestCap = cap - } - } - - if lowestCap == nil { - funcCallArgs = append(funcCallArgs, nil) - continue - } - - // Now create a map of just this value to add it to the sliceCaps - funcCallArgs = append(funcCallArgs, lowestCap) - default: - funcCallArgs = append(funcCallArgs, nil) - } - } - s.funcCallArgs[funcName] = funcCallArgs -} - -// loadArgCaps loads caps that were saved for a call to this function. -func (s *sliceOutOfBounds) loadArgCaps(funcDecl *ast.FuncDecl) { - sliceMap := make(map[string]*int64) - funcName := funcDecl.Name.Name - - // Create a dummmy call expr for the new function. This is so we can still store args for - // functions which are not explicitly called in the code by other functions (specifically, main). - ident := ast.NewIdent(funcName) - dummyCallExpr := ast.CallExpr{ - Fun: ident, - } - - argCaps, ok := s.funcCallArgs[funcName] - if !ok || len(argCaps) == 0 { - s.sliceCaps[&dummyCallExpr] = sliceMap - return - } - - params := funcDecl.Type.Params.List - if len(params) > len(argCaps) { - return // Length of params and args doesn't match, so don't do anything with this. - } - - for it := range params { - capacity := argCaps[it] - if capacity == nil { - continue - } - - if len(params[it].Names) == 0 { - continue - } - - if paramName := params[it].Names[0]; paramName != nil { - sliceMap[paramName.Name] = capacity - } - } - - s.sliceCaps[&dummyCallExpr] = sliceMap -} - -// matchSliceMake matches calls to make() and stores the capacity of the new slice in the map to compare against future slice usage. -func (s *sliceOutOfBounds) matchSliceMake(funcCall *ast.CallExpr, sliceName string, ctx *gosec.Context) (*issue.Issue, error) { - _, funcName, err := gosec.GetCallInfo(funcCall, ctx) - if err != nil || funcName != "make" { - return nil, nil - } - - var capacityArg int - if len(funcCall.Args) < 2 { - return nil, nil // No size passed - } else if len(funcCall.Args) == 2 { - capacityArg = 1 - } else if len(funcCall.Args) == 3 { - capacityArg = 2 - } else { - return nil, nil // Unexpected, args should always be 2 or 3 - } - - // Check and get the capacity of the slice passed to make. It must be a literal value, since we aren't evaluating the expression. - sliceCapLit, ok := funcCall.Args[capacityArg].(*ast.BasicLit) - if !ok { - return nil, nil - } - - capacity, err := gosec.GetInt(sliceCapLit) - if err != nil { - return nil, nil - } - - caps := s.getSliceCapsForFunc(s.currentFuncName, sliceName, ctx) - for callExpr := range caps { - caps[callExpr] = &capacity - } - - s.updateSliceCaps(sliceName, caps) - return nil, nil -} - -// evaluateSliceExpr takes a slice expression and evaluates what the capacity of said slice is for each of the -// calls to the current function. Returns map of the call expressions of each call to the current function to -// the evaluated capacities. -func (s *sliceOutOfBounds) evaluateSliceExpr(node *ast.SliceExpr, ctx *gosec.Context) map[*ast.CallExpr]*int64 { - // Get ident to get name - ident, ok := node.X.(*ast.Ident) - if !ok { - return nil - } - - // Get cap of old slice to calculate this new slice's cap - caps := s.getSliceCapsForFunc(s.currentFuncName, ident.Name, ctx) - for callExpr, oldCap := range caps { - if oldCap == nil { - continue - } - - // Get and check low value - lowIdent, ok := node.Low.(*ast.BasicLit) - if ok && lowIdent != nil { - low, _ := gosec.GetInt(lowIdent) - - newCap := *oldCap - low - caps[callExpr] = &newCap - } else if lowIdent == nil { // If no lower bound, capacity will be same - continue - } - } - - return caps -} - -// matchSliceAssignment matches slice assignments, calculates capacity of slice if possible to store it in map. -func (s *sliceOutOfBounds) matchSliceAssignment(node *ast.SliceExpr, sliceName string, ctx *gosec.Context) (*issue.Issue, error) { - // First do the normal match that verifies the slice expr is not out of bounds - if i, err := s.matchSliceExpr(node, ctx); err != nil { - return i, fmt.Errorf("There was an error while matching a slice expression to check slice bounds for %s: %w", sliceName, err) - } - - // Now that the assignment is (presumably) successfully, we can calculate the capacity and add this new slice to the map - caps := s.evaluateSliceExpr(node, ctx) - s.updateSliceCaps(sliceName, caps) - - return nil, nil -} - -// matchAssign matches checks if an assignment statement is making a slice, or if it is assigning a slice. -func (s *sliceOutOfBounds) matchAssign(node *ast.AssignStmt, ctx *gosec.Context) (*issue.Issue, error) { - // Check RHS for calls to make() so we can get the actual size of the slice - for it, i := range node.Rhs { - // Get the slice name so we can associate the cap with the slice in the map - sliceIdent, ok := node.Lhs[it].(*ast.Ident) - if !ok { - return nil, nil - } - sliceName := sliceIdent.Name - - switch expr := i.(type) { - case *ast.CallExpr: // Check for and handle call to make() - return s.matchSliceMake(expr, sliceName, ctx) - case *ast.SliceExpr: // Handle assignments to a slice - return s.matchSliceAssignment(expr, sliceName, ctx) - } - } - return nil, nil -} - -// matchSliceExpr validates that a given slice expression (eg, slice[10:30]) is not out of bounds. -func (s *sliceOutOfBounds) matchSliceExpr(node *ast.SliceExpr, ctx *gosec.Context) (*issue.Issue, error) { - // First get the slice name so we can check the size in our map - ident, ok := node.X.(*ast.Ident) - if !ok { - return nil, nil - } - - // Get slice cap from the map to compare it against high and low - caps := s.getSliceCapsForFunc(s.currentFuncName, ident.Name, ctx) - - for _, cap := range caps { - if cap == nil { - continue - } - - // Get and check high value - highIdent, ok := node.High.(*ast.BasicLit) - if ok && highIdent != nil { - high, _ := gosec.GetInt(highIdent) - if high > *cap { - return ctx.NewIssue(node, s.ID(), s.What, s.Severity, s.Confidence), nil - } - } - - // Get and check low value - lowIdent, ok := node.Low.(*ast.BasicLit) - if ok && lowIdent != nil { - low, _ := gosec.GetInt(lowIdent) - if low > *cap { - return ctx.NewIssue(node, s.ID(), s.What, s.Severity, s.Confidence), nil - } - } - } - - return nil, nil -} - -// matchIndexExpr validates that an index into a slice is not out of bounds. -func (s *sliceOutOfBounds) matchIndexExpr(node *ast.IndexExpr, ctx *gosec.Context) (*issue.Issue, error) { - // First get the slice name so we can check the size in our map - ident, ok := node.X.(*ast.Ident) - if !ok { - return nil, nil - } - - // Get slice cap from the map to compare it against high and low - caps := s.getSliceCapsForFunc(s.currentFuncName, ident.Name, ctx) - - for _, cap := range caps { - if cap == nil { - continue - } - // Get the index literal - indexIdent, ok := node.Index.(*ast.BasicLit) - if ok && indexIdent != nil { - index, _ := gosec.GetInt(indexIdent) - if index >= *cap { - return ctx.NewIssue(node, s.ID(), s.What, s.Severity, s.Confidence), nil - } - } - } - - return nil, nil -} - -// NewSliceBoundCheck attempts to find any slices being accessed out of bounds -// by reslicing or by being indexed. -func NewSliceBoundCheck(id string, _ gosec.Config) (gosec.Rule, []ast.Node) { - sliceMap := make(map[*ast.CallExpr]map[string]*int64) - - return &sliceOutOfBounds{ - sliceCaps: sliceMap, - currentFuncName: "", - funcCallArgs: make(map[string][]*int64), - MetaData: issue.MetaData{ - ID: id, - Severity: issue.Medium, - Confidence: issue.Medium, - What: "Potentially accessing slice out of bounds", - }, - }, []ast.Node{(*ast.CallExpr)(nil), (*ast.FuncDecl)(nil), (*ast.AssignStmt)(nil), (*ast.SliceExpr)(nil), (*ast.IndexExpr)(nil)} -} diff --git a/vendor/github.com/securego/gosec/v2/rules/unsafe.go b/vendor/github.com/securego/gosec/v2/rules/unsafe.go index e1e8d0231..2e2adca7c 100644 --- a/vendor/github.com/securego/gosec/v2/rules/unsafe.go +++ b/vendor/github.com/securego/gosec/v2/rules/unsafe.go @@ -43,7 +43,7 @@ func (r *usingUnsafe) Match(n ast.Node, c *gosec.Context) (gi *issue.Issue, err func NewUsingUnsafe(id string, _ gosec.Config) (gosec.Rule, []ast.Node) { return &usingUnsafe{ pkg: "unsafe", - calls: []string{"Alignof", "Offsetof", "Sizeof", "Pointer"}, + calls: []string{"Pointer", "String", "StringData", "Slice", "SliceData"}, MetaData: issue.MetaData{ ID: id, What: "Use of unsafe calls should be audited", |
