Compare commits

...

1 Commits

Author SHA1 Message Date
HanShaoshuai-k
1c4a1e0188 fix: reduce public content credential fixture false positives 2026-07-09 17:39:07 +08:00
2 changed files with 248 additions and 6 deletions

View File

@@ -134,6 +134,9 @@ func isCredentialAssignmentMatch(match string) bool {
if isBenignTokenField(name) && !credentialShapedValue(value) {
return false
}
if isCredentialIdentifierField(name) && !credentialShapedValue(value) {
return false
}
if isWeakTokenCredentialKey(name) && !weakTokenValueLooksCredentialLike(value) {
return false
}
@@ -225,6 +228,17 @@ func isTokenMetadataField(key string) bool {
}
}
func isCredentialIdentifierField(key string) bool {
switch {
case key == "api_key_id":
return true
case strings.HasSuffix(key, "_api_key_id"):
return true
default:
return false
}
}
func isPaginationOrSyncTokenField(key string) bool {
switch key {
case "page_token",
@@ -493,12 +507,18 @@ func isBenignCodeCredentialExpression(file, line, match, value string) bool {
if strings.HasPrefix(normalized, "regexp.MustCompile(") {
return true
}
if !sourceCodeFile(file) || credentialShapedValue(value) {
return false
if !sourceCodeFile(file) {
return testFixtureFile(file) && testFixtureCredentialLiteral(normalized)
}
if rhs, ok := sourceCodeTypedCredentialRHS(line, match); ok {
return isBenignTypedCredentialRHS(rhs)
}
if testFixtureFile(file) && testFixtureCredentialLiteral(normalized) {
return true
}
if credentialShapedValue(value) {
return false
}
rawValueQuoted := credentialAssignmentRawValueQuoted(match)
if sourceCodeLiteralLooksNonSecret(normalized, !rawValueQuoted) {
return true
@@ -568,7 +588,7 @@ func credentialAssignmentRawValueQuoted(match string) bool {
func sourceCodeFile(file string) bool {
switch filepath.Ext(file) {
case ".go", ".js", ".jsx", ".py", ".ts", ".tsx":
case ".go", ".js", ".jsx", ".py", ".sh", ".ts", ".tsx":
return true
default:
return false
@@ -593,6 +613,9 @@ func sourceCodeLiteralLooksNonSecret(value string, allowNumeric bool) bool {
sourceCodeFakeOrPlaceholderLiteral(literal) ||
sourceCodeCredentialTermLiteral(literal) ||
sourceCodeCredentialPrefixLiteral(literal) ||
sourceCodeLowEvidenceFixtureLiteral(literal) ||
sourceCodeStringExpressionLiteral(literal) ||
sourceCodeSyntheticIdentifierLiteral(literal) ||
sourceCodeVocabularyLiteral(literal) ||
sourceCodeSchemaTypeLiteral(literal) ||
benignCredentialStatusLiteral(literal)
@@ -685,6 +708,143 @@ func sourceCodeCredentialPrefixLiteral(value string) bool {
}
}
func sourceCodeLowEvidenceFixtureLiteral(value string) bool {
normalized := normalizeFixtureCredentialLiteral(value)
return simpleFixtureCredentialLiteral(normalized)
}
func sourceCodeStringExpressionLiteral(value string) bool {
normalized := strings.TrimSpace(value)
if normalized == "" ||
credentialShapedIdentifier(strings.ToLower(normalized)) ||
highEntropyCredentialValue(strings.ToLower(normalized)) {
return false
}
return strings.Contains(normalized, "${") ||
strings.Contains(normalized, "$(") ||
(strings.Contains(normalized, `\b`) && strings.ContainsAny(normalized, "|[]{}()+*?"))
}
func sourceCodeSyntheticIdentifierLiteral(value string) bool {
normalized := strings.ToLower(strings.TrimSpace(value))
if normalized == "" ||
strings.HasPrefix(normalized, "real_") ||
strings.HasPrefix(normalized, "real-") ||
credentialShapedIdentifier(normalized) ||
highEntropyCredentialValue(normalized) ||
!delimitedPlaceholderIdentifier(normalized) ||
!strings.ContainsAny(normalized, "_-") {
return false
}
var parts int
for _, part := range strings.FieldsFunc(normalized, func(r rune) bool {
return r == '_' || r == '-'
}) {
if part != "" {
parts++
}
}
return parts >= 2
}
func testFixtureCredentialLiteral(value string) bool {
normalized := normalizeFixtureCredentialLiteral(value)
if simpleFixtureCredentialLiteral(normalized) {
return true
}
switch normalized {
case "real-token",
"real-tenant-access-token":
return true
default:
return false
}
}
func normalizeFixtureCredentialLiteral(value string) string {
normalized := strings.ToLower(strings.TrimSpace(strings.Trim(value, `"'`)))
if before, _, ok := strings.Cut(normalized, `\n`); ok {
normalized = before
}
if before, _, ok := strings.Cut(normalized, `\r`); ok {
normalized = before
}
if before, _, ok := strings.Cut(normalized, "\n"); ok {
normalized = before
}
if before, _, ok := strings.Cut(normalized, "\r"); ok {
normalized = before
}
for {
next := strings.TrimSuffix(normalized, `\n`)
next = strings.TrimSuffix(next, `\r`)
next = strings.TrimSuffix(next, "\n")
next = strings.TrimSuffix(next, "\r")
if next == normalized {
break
}
normalized = strings.TrimSpace(next)
}
normalized = strings.TrimSpace(normalized)
normalized = strings.TrimPrefix(normalized, `\"`)
normalized = strings.TrimSuffix(normalized, `\"`)
return strings.TrimSpace(strings.Trim(normalized, `"'`))
}
func simpleFixtureCredentialLiteral(value string) bool {
if value == "" || credentialShapedIdentifier(value) || highEntropyCredentialValue(value) {
return false
}
if len(value) <= 3 && delimitedPlaceholderIdentifier(value) {
return true
}
switch value {
case "pass",
"pat-token",
"password",
"pat_abc",
"pw",
"s3cret",
"secret",
"secret_only",
"test":
return true
default:
return allXPlaceholder(value) || humanReadableFixtureCredentialLiteral(value)
}
}
func humanReadableFixtureCredentialLiteral(value string) bool {
if !delimitedPlaceholderIdentifier(value) {
return false
}
normalized := strings.ReplaceAll(value, "-", "_")
for _, marker := range []string{
"auto",
"dummy",
"example",
"fake",
"fixture",
"hermes",
"new",
"replace",
"restored",
"sample",
"super",
"test",
"valid",
} {
if normalized == marker ||
strings.HasPrefix(normalized, marker) ||
strings.HasSuffix(normalized, marker) ||
strings.Contains(normalized, marker+"_") ||
strings.Contains(normalized, "_"+marker) {
return true
}
}
return false
}
func sourceCodeVocabularyLiteral(value string) bool {
switch strings.ToLower(value) {
case "bot", "tenant", "user":
@@ -753,7 +913,7 @@ func codeIdentifier(value string) bool {
func isNonSecretLiteralValue(value string) bool {
switch strings.ToLower(strings.TrimSpace(strings.Trim(value, `"'`))) {
case "true", "false", "null", "nil", "{", "[":
case "true", "false", "null", "nil", "{", "[", `\`:
return true
default:
return false
@@ -980,6 +1140,7 @@ func credentialURLPasswordFixture(password string) bool {
normalized := strings.ToLower(strings.Trim(password, `"'`))
switch normalized {
case "p",
"p%40ss",
"pass",
"password",
"pat_abc",
@@ -1002,6 +1163,20 @@ func sourceOrTestFixtureFile(file string) bool {
strings.Contains(normalized, "/fixtures/")
}
func testFixtureFile(file string) bool {
normalized := filepath.ToSlash(file)
base := filepath.Base(normalized)
return strings.Contains(base, "_test.") ||
strings.Contains(base, ".test.") ||
strings.Contains(base, "sample") ||
strings.HasPrefix(normalized, "tests/") ||
strings.HasPrefix(normalized, "testdata/") ||
strings.HasPrefix(normalized, "fixtures/") ||
strings.Contains(normalized, "/tests/") ||
strings.Contains(normalized, "/testdata/") ||
strings.Contains(normalized, "/fixtures/")
}
func warnForPrivateIPv4(file string) bool {
normalized := filepath.ToSlash(file)
if sourceOrTestFixtureFile(normalized) {

View File

@@ -648,6 +648,7 @@ func TestScanFileAllowsCredentialURLPlaceholders(t *testing.T) {
func TestScanFileAllowsCredentialURLFixtures(t *testing.T) {
got := ScanFile("fixtures/network_test.go", []byte(strings.Join([]string{
`proxy := "http://user:pass@proxy:8080"`,
`proxy := "http://user:p%40ss@proxy:8080/path"`,
`repo := "https://u:t@h/r.git"`,
`target := "https://attacker:pw@open.feishu.cn"`,
`proxy := "http://admin:s3cret@127.0.0.1:3128"`,
@@ -840,7 +841,24 @@ func TestScanFileDetectsStrongAuthTokenKeysWithFixtureLikeValues(t *testing.T) {
}
func TestScanFileAllowsTestFixtureSecretValues(t *testing.T) {
got := ScanFile("fixtures/calendar_meeting_test.go", []byte(`AppID: "test-app", AppSecret: "test-secret", Brand: core.BrandFeishu,`+"\n"))
got := ScanFile("fixtures/calendar_meeting_test.go", []byte(strings.Join([]string{
`AppID: "test-app", AppSecret: "test-secret", Brand: core.BrandFeishu,`,
`cfg := &core.CliConfig{AppID: "a", AppSecret: "s"}`,
`os.WriteFile(path, []byte("FEISHU_APP_ID=cli_abc\nFEISHU_APP_SECRET=secret\n"), 0600)`,
`rt := &stubRoundTripper{respBody: ` + "`" + `{"access_token":"t","token_type":"Bearer"}` + "`" + `}`,
`cred := credential.NewCredentialProvider([]extcred.Provider{&fakeExtProvider{token: "real-token"}}, nil, nil, nil)`,
`const realToken = "real-tenant-access-token"`,
`envContent := "FEISHU_APP_ID=cli_hermes_abc\nFEISHU_APP_SECRET=hermes_secret_123\nFEISHU_DOMAIN=lark\n"`,
`content := "# Hermes config\nFEISHU_APP_ID=cli_abc123\nFEISHU_APP_SECRET=supersecret\n"`,
`os.WriteFile(path, []byte("FEISHU_APP_ID=cli_auto\nFEISHU_APP_SECRET=auto_secret\n"), 0600)`,
`os.WriteFile(path, []byte("FEISHU_APP_ID=cli_new_app\nFEISHU_APP_SECRET=new_secret\n"), 0600)`,
`if got := out.String(); got != "username=x-access-token\npassword=valid-pat\n\n" {`,
`if got := out.String(); got != "username=x-access-token\npassword=restored-pat\n\n" {`,
`if got := stdout.String(); got != "username=x-access-token\npassword=pat-token\n\n" {`,
`return &core.CliConfig{AppID: "dummy", AppSecret: "dummy"}`,
`os.WriteFile(path, []byte("API_KEY=replace-me\n"), 0600)`,
`body := "APP_ID=\"cli_xxxxx\"\nAPP_SECRET=\"xxxxx\"\n"`,
}, "\n")+"\n"))
for _, item := range got {
if item.Rule == "public_content_generic_credential" {
t.Fatalf("test fixture secret should not be credential finding: %#v", got)
@@ -848,8 +866,41 @@ func TestScanFileAllowsTestFixtureSecretValues(t *testing.T) {
}
}
func TestScanFileAllowsCredentialIdentifierFields(t *testing.T) {
got := ScanFile("fixtures/openapi_key_test.go", []byte(strings.Join([]string{
`"api_key_id": "k1",`,
`"secret_id": "s1",`,
`"token_id": "t1",`,
`"private_key_id": "pk1",`,
}, "\n")+"\n"))
for _, item := range got {
if item.Rule == "public_content_generic_credential" {
t.Fatalf("credential identifier fields should not be credential findings: %#v", got)
}
}
}
func TestScanFileDetectsCredentialShapedIdentifierFieldValues(t *testing.T) {
got := ScanFile("fixtures/openapi_key_test.go", []byte(strings.Join([]string{
`"api_key_id": "real-api-key-id",`,
`"token_id": "ghp_1234567890abcdef1234567890abcdef1234",`,
}, "\n")+"\n"))
var count int
for _, item := range got {
if item.Rule == "public_content_generic_credential" {
count++
}
}
if count != 2 {
t.Fatalf("credential-shaped identifier field findings = %d, want 2: %#v", count, got)
}
}
func TestScanFileAllowsRegexpTokenValidators(t *testing.T) {
got := ScanFile("fixtures/minutes_detail.go", []byte("var validMinuteTokenDetail = regexp.MustCompile(`^[a-z0-9]+$`)\n"))
got := ScanFile("fixtures/minutes_detail.go", []byte(strings.Join([]string{
"var validMinuteTokenDetail = regexp.MustCompile(`^[a-z0-9]+$`)",
"REALISTIC_TOKEN_RE=\"\\\"${TOKEN_BODY}\\\"|\\`${TOKEN_BODY}\\`|\\\\b${TOKEN_BODY}\\\\b\"",
}, "\n")+"\n"))
for _, item := range got {
if item.Rule == "public_content_generic_credential" {
t.Fatalf("regexp token validator should not be credential finding: %#v", got)
@@ -927,6 +978,22 @@ func TestScanFileAllowsSourceCodeCredentialNonSecretLiterals(t *testing.T) {
}
}
func TestScanFileAllowsSourceCodeSyntheticCredentialIdentifiers(t *testing.T) {
got := ScanFile("fixtures/sheets_media.go", []byte(strings.Join([]string{
`const fakeOfficeTokenPrefix = "fake_office_"`,
`const localOfficeTokenPrefix = "local_office_"`,
`const imageLiveSecretMarker = "img_live_secret"`,
`const imageProdKeyMarker = "img_prod_key"`,
`if strings.HasPrefix(spreadsheetToken, fakeOfficeTokenPrefix) {`,
`if strings.HasPrefix(spreadsheetToken, localOfficeTokenPrefix) {`,
}, "\n")+"\n"))
for _, item := range got {
if item.Rule == "public_content_generic_credential" {
t.Fatalf("source code token prefix references should not be credential findings: %#v", got)
}
}
}
func TestScanFileAllowsCredentialLikePublicPlaceholders(t *testing.T) {
got := ScanFile("fixtures/placeholders.md", []byte(strings.Join([]string{
`app_secret=***`,