mirror of
https://github.com/securego/gosec.git
synced 2024-11-05 11:35:51 +00:00
Fix hardcoded_credentials rule to only match on more specific patterns (#1009)
* Fix hardcoded_credentials rule to only match on more specific patterns Signed-off-by: Cosmin Cojocar <gcojocar@adobe.com> * Fix lint warnings Signed-off-by: Cosmin Cojocar <gcojocar@adobe.com> * Fix double escape in regexps Signed-off-by: Cosmin Cojocar <gcojocar@adobe.com> --------- Signed-off-by: Cosmin Cojocar <gcojocar@adobe.com>
This commit is contained in:
parent
325eb19a54
commit
6c93653a29
2 changed files with 198 additions and 93 deletions
|
@ -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,
|
||||
|
|
|
@ -273,45 +273,8 @@ package main
|
|||
import "fmt"
|
||||
|
||||
func main() {
|
||||
username := "admin"
|
||||
key := "472bb6c8c1871887cc117742ead362d688707d0442de930f7588db9d5ba091cc"
|
||||
fmt.Println("Logging in with: ", username, key)
|
||||
}`}, 1, gosec.NewConfig()},
|
||||
{[]string{`
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
const (
|
||||
b = "Bearer: c0df7a0f9b4a6a336029689b5df0712459a4f396c609ab05fd21a9097b4264f7"
|
||||
)
|
||||
|
||||
func main() {
|
||||
fmt.Println(b)
|
||||
}`}, 1, gosec.NewConfig()},
|
||||
{[]string{`
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
const (
|
||||
tooLongConst = "key: c0df7a0f9b4a6a336029689b5df0712459a4f396c609ab05fd21a9097b4264f71294129"
|
||||
)
|
||||
|
||||
func main() {
|
||||
fmt.Println(tooLongConst)
|
||||
}`}, 0, gosec.NewConfig()},
|
||||
{[]string{`
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
const (
|
||||
tooShortConst = "key: c0df7a0f9b4a6a336029689b5df0712459a4f396c609ab05fd21a9097b4264f71294"
|
||||
)
|
||||
|
||||
func main() {
|
||||
fmt.Println(tooShortConst)
|
||||
customerNameEnvKey := "FOO_CUSTOMER_NAME"
|
||||
fmt.Println(customerNameEnvKey)
|
||||
}`}, 0, gosec.NewConfig()},
|
||||
{[]string{`
|
||||
package main
|
||||
|
@ -319,10 +282,17 @@ package main
|
|||
import "fmt"
|
||||
|
||||
func main() {
|
||||
compareStr := "test"
|
||||
if compareStr == "b7997caa846af0c50c095d63d212be2fbaffd35c22c735a905ddba87d85618fd" {
|
||||
fmt.Println(compareStr)
|
||||
}
|
||||
txnID := "3637cfcc1eec55a50f78a7c435914583ccbc75a21dec9a0e94dfa077647146d7"
|
||||
fmt.Println(txnID)
|
||||
}`}, 0, gosec.NewConfig()},
|
||||
{[]string{`
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func main() {
|
||||
urlSecret := "https://username:abcdef0123456789abcdef0123456789abcdef01@contoso.com/"
|
||||
fmt.Println(urlSecret)
|
||||
}`}, 1, gosec.NewConfig()},
|
||||
{[]string{`
|
||||
package main
|
||||
|
@ -330,22 +300,18 @@ package main
|
|||
import "fmt"
|
||||
|
||||
func main() {
|
||||
compareTooShort := "test"
|
||||
if compareTooShort == "b7997caa846af0c50c095d63d212be2fbaffd35c22c735a905ddba87d85618d" {
|
||||
fmt.Println(compareTooShort)
|
||||
}
|
||||
}`}, 0, gosec.NewConfig()},
|
||||
githubToken := "ghp_iR54dhCYg9Tfmoywi9xLmmKZrrnAw438BYh3"
|
||||
fmt.Println(githubToken)
|
||||
}`}, 1, gosec.NewConfig()},
|
||||
{[]string{`
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func main() {
|
||||
compareTooLong := "test"
|
||||
if compareTooLong == "b7997caa846af0c50c095d63d212be2fbaffd35c22c735a905ddba87d85618fd11" {
|
||||
fmt.Println(compareTooLong)
|
||||
}
|
||||
}`}, 0, gosec.NewConfig()},
|
||||
awsAccessKeyID := "AKIAI44QH8DHBEXAMPLE"
|
||||
fmt.Println(awsAccessKeyID)
|
||||
}`}, 1, gosec.NewConfig()},
|
||||
{[]string{`
|
||||
package main
|
||||
|
||||
|
@ -356,28 +322,6 @@ func main() {
|
|||
if compareGoogleAPI == "AIzajtGS_aJGkoiAmSbXzu9I-1eytAi9Lrlh-vT" {
|
||||
fmt.Println(compareGoogleAPI)
|
||||
}
|
||||
}`}, 1, gosec.NewConfig()},
|
||||
{[]string{`
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
const (
|
||||
githubPAT = "key: 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"
|
||||
)
|
||||
|
||||
func main() {
|
||||
fmt.Println(githubPAT)
|
||||
}`}, 1, gosec.NewConfig()},
|
||||
{[]string{`
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func main() {
|
||||
username := "admin"
|
||||
googOAuthSec := "uibYYslvAUKn2ORRJ7EaZtMs"
|
||||
fmt.Println("Logging in with: ", username, googOAuthSec)
|
||||
}`}, 1, gosec.NewConfig()},
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue