aboutsummaryrefslogtreecommitdiffstats
path: root/vendor/github.com/securego/gosec/v2/rules
diff options
context:
space:
mode:
authorTaras Madan <tarasmadan@google.com>2023-12-05 15:10:03 +0100
committerTaras Madan <tarasmadan@google.com>2023-12-06 11:31:44 +0000
commit2ab72b4feef2c97f22f90cfbf9e45a6cfcd08bda (patch)
treea6d19b94b6399fcc00a6cfa430885cd349dd1533 /vendor/github.com/securego/gosec/v2/rules
parente08e8f492d31d672cc245944c185f8aadf2ee695 (diff)
vendor: updates
Diffstat (limited to 'vendor/github.com/securego/gosec/v2/rules')
-rw-r--r--vendor/github.com/securego/gosec/v2/rules/fileperms.go46
-rw-r--r--vendor/github.com/securego/gosec/v2/rules/hardcoded_credentials.go197
-rw-r--r--vendor/github.com/securego/gosec/v2/rules/implicit_aliasing.go30
-rw-r--r--vendor/github.com/securego/gosec/v2/rules/rulelist.go2
-rw-r--r--vendor/github.com/securego/gosec/v2/rules/slice_bounds.go405
-rw-r--r--vendor/github.com/securego/gosec/v2/rules/unsafe.go2
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",