Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(secret): skip regular strings contain secret patterns #7182

Merged
merged 14 commits into from
Jul 25, 2024
155 changes: 83 additions & 72 deletions pkg/fanal/secret/builtin-rules.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,10 +74,10 @@ var (

// Reusable regex patterns
const (
quote = `["']?`
connect = `\s*(:|=>|=)?\s*`
startSecret = `(^|\s+)`
endSecret = `[.,]?(\s+|$)`
quote = `["']?`
connect = `\s*(:|=>|=)?\s*`
endSecret = `[.,]?(\s+|$)`
startWord = "([^0-9a-zA-Z]|^)"

aws = `aws_?`
)
Expand All @@ -98,7 +98,7 @@ var builtinRules = []Rule{
Category: CategoryAWS,
Severity: "CRITICAL",
Title: "AWS Access Key ID",
Regex: MustCompile(fmt.Sprintf(`%s(?P<secret>(A3T[A-Z0-9]|AKIA|AGPA|AIDA|AROA|AIPA|ANPA|ANVA|ASIA)[A-Z0-9]{16})%s%s`, quote, quote, endSecret)),
Regex: MustCompileWithoutWordPrefix(fmt.Sprintf(`(?P<secret>(A3T[A-Z0-9]|AKIA|AGPA|AIDA|AROA|AIPA|ANPA|ANVA|ASIA)[A-Z0-9]{16})%s%s`, quote, endSecret)),
SecretGroupName: "secret",
Keywords: []string{"AKIA", "AGPA", "AIDA", "AROA", "AIPA", "ANPA", "ANVA", "ASIA"},
},
Expand All @@ -107,41 +107,45 @@ var builtinRules = []Rule{
Category: CategoryAWS,
Severity: "CRITICAL",
Title: "AWS Secret Access Key",
Regex: MustCompile(fmt.Sprintf(`(?i)%s%s%s(sec(ret)?)?_?(access)?_?key%s%s%s(?P<secret>[A-Za-z0-9\/\+=]{40})%s%s`, startSecret, quote, aws, quote, connect, quote, quote, endSecret)),
Regex: MustCompile(fmt.Sprintf(`(?i)%s%s(sec(ret)?)?_?(access)?_?key%s%s%s(?P<secret>[A-Za-z0-9\/\+=]{40})%s%s`, quote, aws, quote, connect, quote, quote, endSecret)),
SecretGroupName: "secret",
Keywords: []string{"key"},
},
{
ID: "github-pat",
Category: CategoryGitHub,
Title: "GitHub Personal Access Token",
Severity: "CRITICAL",
Regex: MustCompile(`ghp_[0-9a-zA-Z]{36}`),
Keywords: []string{"ghp_"},
ID: "github-pat",
Category: CategoryGitHub,
Title: "GitHub Personal Access Token",
Severity: "CRITICAL",
Regex: MustCompileWithoutWordPrefix(`?P<secret>ghp_[0-9a-zA-Z]{36}`),
SecretGroupName: "secret",
Keywords: []string{"ghp_"},
},
{
ID: "github-oauth",
Category: CategoryGitHub,
Title: "GitHub OAuth Access Token",
Severity: "CRITICAL",
Regex: MustCompile(`gho_[0-9a-zA-Z]{36}`),
Keywords: []string{"gho_"},
ID: "github-oauth",
Category: CategoryGitHub,
Title: "GitHub OAuth Access Token",
Severity: "CRITICAL",
Regex: MustCompileWithoutWordPrefix(`?P<secret>gho_[0-9a-zA-Z]{36}`),
SecretGroupName: "secret",
Keywords: []string{"gho_"},
},
{
ID: "github-app-token",
Category: CategoryGitHub,
Title: "GitHub App Token",
Severity: "CRITICAL",
Regex: MustCompile(`(ghu|ghs)_[0-9a-zA-Z]{36}`),
Keywords: []string{"ghu_", "ghs_"},
ID: "github-app-token",
Category: CategoryGitHub,
Title: "GitHub App Token",
Severity: "CRITICAL",
Regex: MustCompileWithoutWordPrefix(`?P<secret>(ghu|ghs)_[0-9a-zA-Z]{36}`),
SecretGroupName: "secret",
Keywords: []string{"ghu_", "ghs_"},
},
{
ID: "github-refresh-token",
Category: CategoryGitHub,
Title: "GitHub Refresh Token",
Severity: "CRITICAL",
Regex: MustCompile(`ghr_[0-9a-zA-Z]{76}`),
Keywords: []string{"ghr_"},
ID: "github-refresh-token",
Category: CategoryGitHub,
Title: "GitHub Refresh Token",
Severity: "CRITICAL",
Regex: MustCompileWithoutWordPrefix(`?P<secret>ghr_[0-9a-zA-Z]{76}`),
SecretGroupName: "secret",
Keywords: []string{"ghr_"},
},
{
ID: "github-fine-grained-pat",
Expand All @@ -152,21 +156,23 @@ var builtinRules = []Rule{
Keywords: []string{"github_pat_"},
},
{
ID: "gitlab-pat",
Category: CategoryGitLab,
Title: "GitLab Personal Access Token",
Severity: "CRITICAL",
Regex: MustCompile(`glpat-[0-9a-zA-Z\-\_]{20}`),
Keywords: []string{"glpat-"},
ID: "gitlab-pat",
Category: CategoryGitLab,
Title: "GitLab Personal Access Token",
Severity: "CRITICAL",
Regex: MustCompileWithoutWordPrefix(`?P<secret>glpat-[0-9a-zA-Z\-\_]{20}`),
SecretGroupName: "secret",
Keywords: []string{"glpat-"},
},
{
// cf. https://huggingface.co/docs/hub/en/security-tokens
ID: "hugging-face-access-token",
Category: CategoryHuggingFace,
Severity: "CRITICAL",
Title: "Hugging Face Access Token",
Regex: MustCompile(`hf_[A-Za-z0-9]{34,40}`),
Keywords: []string{"hf_"},
ID: "hugging-face-access-token",
Category: CategoryHuggingFace,
Severity: "CRITICAL",
Title: "Hugging Face Access Token",
Regex: MustCompileWithoutWordPrefix(`?P<secret>hf_[A-Za-z0-9]{34,40}`),
SecretGroupName: "secret",
Keywords: []string{"hf_"},
},
{
ID: "private-key",
Expand All @@ -186,28 +192,31 @@ var builtinRules = []Rule{
Keywords: []string{"shpss_", "shpat_", "shpca_", "shppa_"},
},
{
ID: "slack-access-token",
Category: CategorySlack,
Title: "Slack token",
Severity: "HIGH",
Regex: MustCompile(`xox[baprs]-([0-9a-zA-Z]{10,48})`),
Keywords: []string{"xoxb-", "xoxa-", "xoxp-", "xoxr-", "xoxs-"},
ID: "slack-access-token",
Category: CategorySlack,
Title: "Slack token",
Severity: "HIGH",
Regex: MustCompileWithoutWordPrefix(`?P<secret>xox[baprs]-([0-9a-zA-Z]{10,48})`),
SecretGroupName: "secret",
Keywords: []string{"xoxb-", "xoxa-", "xoxp-", "xoxr-", "xoxs-"},
},
{
ID: "stripe-publishable-token",
Category: CategoryStripe,
Title: "Stripe Publishable Key",
Severity: "LOW",
Regex: MustCompile(`(?i)pk_(test|live)_[0-9a-z]{10,32}`),
Keywords: []string{"pk_test_", "pk_live_"},
ID: "stripe-publishable-token",
Category: CategoryStripe,
Title: "Stripe Publishable Key",
Severity: "LOW",
Regex: MustCompileWithoutWordPrefix(`?P<secret>(?i)pk_(test|live)_[0-9a-z]{10,32}`),
SecretGroupName: "secret",
Keywords: []string{"pk_test_", "pk_live_"},
},
{
ID: "stripe-secret-token",
Category: CategoryStripe,
Title: "Stripe Secret Key",
Severity: "CRITICAL",
Regex: MustCompile(`(?i)sk_(test|live)_[0-9a-z]{10,32}`),
Keywords: []string{"sk_test_", "sk_live_"},
ID: "stripe-secret-token",
Category: CategoryStripe,
Title: "Stripe Secret Key",
Severity: "CRITICAL",
Regex: MustCompileWithoutWordPrefix(`?P<secret>(?i)sk_(test|live)_[0-9a-z]{10,32}`),
SecretGroupName: "secret",
Keywords: []string{"sk_test_", "sk_live_"},
},
{
ID: "pypi-upload-token",
Expand Down Expand Up @@ -501,20 +510,22 @@ var builtinRules = []Rule{
Keywords: []string{"finicity"},
},
{
ID: "flutterwave-public-key",
Category: CategoryFlutterwave,
Title: "Flutterwave public/secret key",
Severity: "MEDIUM",
Regex: MustCompile(`FLW(PUB|SEC)K_TEST-(?i)[a-h0-9]{32}-X`),
Keywords: []string{"FLWSECK_TEST-", "FLWPUBK_TEST-"},
ID: "flutterwave-public-key",
Category: CategoryFlutterwave,
Title: "Flutterwave public/secret key",
Severity: "MEDIUM",
Regex: MustCompileWithoutWordPrefix(`?P<secret>FLW(PUB|SEC)K_TEST-(?i)[a-h0-9]{32}-X`),
SecretGroupName: "secret",
Keywords: []string{"FLWSECK_TEST-", "FLWPUBK_TEST-"},
},
{
ID: "flutterwave-enc-key",
Category: CategoryFlutterwave,
Title: "Flutterwave encrypted key",
Severity: "MEDIUM",
Regex: MustCompile(`FLWSECK_TEST[a-h0-9]{12}`),
Keywords: []string{"FLWSECK_TEST"},
ID: "flutterwave-enc-key",
Category: CategoryFlutterwave,
Title: "Flutterwave encrypted key",
Severity: "MEDIUM",
Regex: MustCompileWithoutWordPrefix(`?P<secret>FLWSECK_TEST[a-h0-9]{12}`),
SecretGroupName: "secret",
Keywords: []string{"FLWSECK_TEST"},
},
{
ID: "frameio-api-token",
Expand Down
5 changes: 5 additions & 0 deletions pkg/fanal/secret/scanner.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package secret
import (
"bytes"
"errors"
"fmt"
"os"
"regexp"
"slices"
Expand Down Expand Up @@ -62,6 +63,10 @@ type Regexp struct {
*regexp.Regexp
}

func MustCompileWithoutWordPrefix(str string) *Regexp {
return MustCompile(fmt.Sprintf("%s(%s)", startWord, str))
}

func MustCompile(str string) *Regexp {
return &Regexp{regexp.MustCompile(str)}
}
Expand Down
72 changes: 72 additions & 0 deletions pkg/fanal/secret/scanner_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -315,6 +315,59 @@ func TestSecretScanner(t *testing.T) {
},
},
}
wantFindingMyAwsAccessKey := types.SecretFinding{
RuleID: "aws-secret-access-key",
Category: secret.CategoryAWS,
Title: "AWS Secret Access Key",
Severity: "CRITICAL",
StartLine: 1,
EndLine: 1,
Match: `MyAWS_secret_KEY="****************************************"`,
Code: types.Code{
Lines: []types.Line{
{
Number: 1,
Content: "MyAWS_secret_KEY=\"****************************************\"",
Highlighted: "MyAWS_secret_KEY=\"****************************************\"",
IsCause: true,
FirstCause: true,
LastCause: true,
},
{
Number: 2,
Content: "our*********************************************************************************************",
Highlighted: "our*********************************************************************************************",
},
},
},
}

wantFindingMyGitHubPAT := types.SecretFinding{
RuleID: "github-fine-grained-pat",
Category: secret.CategoryGitHub,
Title: "GitHub Fine-grained personal access tokens",
Severity: "CRITICAL",
StartLine: 2,
EndLine: 2,
Match: "our*********************************************************************************************",
Code: types.Code{
Lines: []types.Line{
{
Number: 1,
Content: "MyAWS_secret_KEY=\"****************************************\"",
Highlighted: "MyAWS_secret_KEY=\"****************************************\"",
},
{
Number: 2,
Content: "our*********************************************************************************************",
Highlighted: "our*********************************************************************************************",
IsCause: true,
FirstCause: true,
LastCause: true,
},
},
},
}
wantFindingGHButDisableAWS := types.SecretFinding{
RuleID: "github-pat",
Category: secret.CategoryGitHub,
Expand Down Expand Up @@ -419,6 +472,7 @@ func TestSecretScanner(t *testing.T) {
},
},
}

wantFinding10 := types.SecretFinding{
RuleID: "aws-secret-access-key",
Category: secret.CategoryAWS,
Expand Down Expand Up @@ -979,6 +1033,24 @@ func TestSecretScanner(t *testing.T) {
inputFilePath: filepath.Join("testdata", "invalid-aws-secrets.txt"),
want: types.Secret{},
},
{
name: "secret inside another word",
configPath: filepath.Join("testdata", "skip-test.yaml"),
inputFilePath: filepath.Join("testdata", "wrapped-secrets.txt"),
want: types.Secret{},
},
{
name: "sensitive secret inside another word",
configPath: filepath.Join("testdata", "skip-test.yaml"),
inputFilePath: filepath.Join("testdata", "wrapped-secrets-sensitive.txt"),
want: types.Secret{
FilePath: filepath.Join("testdata", "wrapped-secrets-sensitive.txt"),
Findings: []types.SecretFinding{
wantFindingMyAwsAccessKey,
wantFindingMyGitHubPAT,
},
},
},
{
name: "asymmetric file",
configPath: filepath.Join("testdata", "skip-test.yaml"),
Expand Down
2 changes: 2 additions & 0 deletions pkg/fanal/secret/testdata/wrapped-secrets-sensitive.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
MyAWS_secret_KEY="12ASD34qwe56CXZ78tyH10Tna543VBokN85RHCas"
our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
11 changes: 11 additions & 0 deletions pkg/fanal/secret/testdata/wrapped-secrets.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
DISPID_ICANVASRENDERINGCONTEXT2D_CANVAS DISPID_CANVASRENDERCONTEXT2D
ABCDFLWSECK_TEST-CANVASRENDERCONTEXT2DCANVASRENDA
ABCFLWSECK_TEST123456789012
Rought_ICANVASRENDERINGVIACONTEXT2D3D5D7D8D
Sogho_ICANVASRENDERINGVIACONTEXT2D3D5D7D8D
Soghu_ICANVASRENDERINGVIACONTEXT2D3D5D7D8D
Bighr_ICANVASRENDERINGVIACONTEXT2D3D5D7D8DICANVASRENDERINGVIACONTEXT2D3D5D7D8D9D22
Surhf_ICANVASRENDERINGVIACONTEXT2D3D5D6D7D8D9
abcdexoxb-1234567890
APK_TEST_1234567890
ask_live_superlive1