@@ -139,20 +139,6 @@ func TestAPIKeyScanner_CredentialFileContentNotInFindings(t *testing.T) {
139139 assertResource (t , result .Findings , credFile )
140140}
141141
142- func TestAPIKeyScanner_NoCredentialFileNoFinding (t * testing.T ) {
143- clearAllEnv (t )
144-
145- s := newScannerWithHome (t .TempDir ())
146- result := s .Scan ()
147-
148- if result .Skipped {
149- t .Error ("APIKeyScanner must never return skipped=true" )
150- }
151- if len (result .Findings ) != 0 {
152- t .Errorf ("expected 0 findings in empty home dir, got %d: %v" , len (result .Findings ), resourceSet (result .Findings ))
153- }
154- }
155-
156142func TestAPIKeyScanner_GCPCredentialsDirDetected (t * testing.T ) {
157143 home := t .TempDir ()
158144 gcloudDir := filepath .Join (home , ".config" , "gcloud" )
@@ -427,28 +413,61 @@ func TestAPIKeyScanner_ValuePatterns(t *testing.T) {
427413 }
428414}
429415
430- func TestAPIKeyScanner_NameRegex_FLY_Anchored (t * testing.T ) {
431- clearHighRiskEnv (t )
432- t .Setenv ("FLY_API_TOKEN" , "real-token" )
433- t .Setenv ("MY_FLY_TOKEN" , "also-real-token" )
434- t .Setenv ("BUTTERFLY_KEY" , "not-a-fly-token" )
435- t .Setenv ("FLYWEIGHT_INDEX" , "not-a-token" )
416+ func TestAPIKeyScanner_NameRegex_AnchoredPatterns (t * testing.T ) {
417+ cases := []struct {
418+ name string
419+ shouldMatch map [string ]string
420+ shouldNotMatch map [string ]string
421+ }{
422+ {
423+ name : "FLY_" ,
424+ shouldMatch : map [string ]string {"FLY_API_TOKEN" : "real-token" , "MY_FLY_TOKEN" : "also-real" },
425+ shouldNotMatch : map [string ]string {"BUTTERFLY_KEY" : "v" , "FLYWEIGHT_INDEX" : "v" },
426+ },
427+ {
428+ name : "NEON_" ,
429+ shouldMatch : map [string ]string {"NEON_API_KEY" : "real-neon-key" , "MY_NEON_KEY" : "also-real" },
430+ shouldNotMatch : map [string ]string {"ANEMONE_CONFIG" : "v" , "NEONLIGHTS_COLOR" : "v" },
431+ },
432+ {
433+ name : "LINEAR_" ,
434+ shouldMatch : map [string ]string {"LINEAR_API_KEY" : "real-linear-key" , "MY_LINEAR_TOKEN" : "also-real" },
435+ shouldNotMatch : map [string ]string {"BILINEAR_FILTER" : "v" },
436+ },
437+ {
438+ name : "PALM_" ,
439+ shouldMatch : map [string ]string {"PALM_API_KEY" : "real-palm-key" , "MY_PALM_KEY" : "also-real" },
440+ shouldNotMatch : map [string ]string {"NAPALM_MODE" : "v" },
441+ },
442+ {
443+ name : "XAI_" ,
444+ shouldMatch : map [string ]string {"XAI_API_KEY" : "real-xai-key" , "MY_XAI_KEY" : "also-real" },
445+ shouldNotMatch : map [string ]string {"PROXAI_ENDPOINT" : "https://api.proxai.com" , "RELAXAI_MODE" : "true" },
446+ },
447+ }
436448
437- s := newScannerWithHome (t .TempDir ())
438- result := s .Scan ()
449+ for _ , tc := range cases {
450+ t .Run (tc .name , func (t * testing.T ) {
451+ clearAllEnv (t )
452+ for k , v := range tc .shouldMatch {
453+ t .Setenv (k , v )
454+ }
455+ for k , v := range tc .shouldNotMatch {
456+ t .Setenv (k , v )
457+ }
439458
440- // FLY_API_TOKEN and MY_FLY_TOKEN must both be flagged.
441- assertResource (t , result .Findings , "FLY_API_TOKEN" )
442- assertResource (t , result .Findings , "MY_FLY_TOKEN" )
459+ s := newScannerWithHome (t .TempDir ())
460+ result := s .Scan ()
443461
444- // BUTTERFLY_KEY and FLYWEIGHT_INDEX must NOT be flagged.
445- for _ , f := range result .Findings {
446- if f .Resource == "BUTTERFLY_KEY" {
447- t .Error ("BUTTERFLY_KEY should not be flagged by FLY_ pattern" )
448- }
449- if f .Resource == "FLYWEIGHT_INDEX" {
450- t .Error ("FLYWEIGHT_INDEX should not be flagged by FLY_ pattern" )
451- }
462+ for k := range tc .shouldMatch {
463+ assertResource (t , result .Findings , k )
464+ }
465+ for k := range tc .shouldNotMatch {
466+ if contains (result .Findings , k ) {
467+ t .Errorf ("%s should not be flagged by %s pattern" , k , tc .name )
468+ }
469+ }
470+ })
452471 }
453472}
454473
@@ -458,18 +477,33 @@ func TestAPIKeyScanner_NameRegex_NewProviders(t *testing.T) {
458477 envVar string
459478 value string
460479 }{
480+ // Google / cloud AI
461481 {"MY_GEMINI_KEY" , "gemini-key-value" },
462482 {"VERTEX_API_KEY" , "vertex-key-value" },
463483 {"BEDROCK_ACCESS_KEY" , "bedrock-key-value" },
464484 {"AZURE_OPENAI_KEY" , "azure-openai-key" },
485+ // Communication
465486 {"RESEND_API_KEY" , "resend-key-value" },
466487 {"POSTMARK_TOKEN" , "postmark-key-value" },
488+ // Productivity / project tools
467489 {"MY_LINEAR_TOKEN" , "linear-key-value" },
468490 {"NOTION_API_KEY" , "notion-key-value" },
469491 {"AIRTABLE_KEY" , "airtable-key-value" },
492+ // Database-as-a-service
470493 {"SUPABASE_KEY" , "supabase-key-value" },
471494 {"NEON_API_KEY" , "neon-key-value" },
472495 {"PLANETSCALE_TOKEN" , "ps-key-value" },
496+ // Newer AI providers
497+ {"OPENROUTER_API_KEY" , "or-key-value" },
498+ {"FIREWORKS_API_KEY" , "fw-key-value" },
499+ {"DEEPSEEK_API_KEY" , "ds-key-value" },
500+ {"PERPLEXITY_API_KEY" , "pplx-key-value" },
501+ {"CEREBRAS_API_KEY" , "cb-key-value" },
502+ {"DOPPLER_TOKEN" , "dp-token-value" },
503+ {"XAI_API_KEY" , "xai-key-value" },
504+ {"ASSEMBLYAI_API_KEY" , "aai-key-value" },
505+ {"AI21_API_KEY" , "ai21-key-value" },
506+ {"NVIDIA_NIM_API_KEY" , "nim-key-value" },
473507 }
474508
475509 for _ , tc := range cases {
@@ -499,28 +533,6 @@ func TestAPIKeyScanner_ValuePattern_NoMatchWrongLength(t *testing.T) {
499533 }
500534}
501535
502- func TestAPIKeyScanner_ValuePattern_TwilioSID (t * testing.T ) {
503- value := "SK" + strings .Repeat ("f" , 32 ) // total 34 chars
504- t .Setenv ("CRED_SID" , value )
505- clearHighRiskEnv (t )
506-
507- s := newScannerWithHome (t .TempDir ())
508- result := s .Scan ()
509-
510- assertResource (t , result .Findings , "CRED_SID" )
511- for _ , f := range result .Findings {
512- if f .Resource == "CRED_SID" {
513- if f .Severity != "UNCERTAIN" {
514- t .Errorf ("expected UNCERTAIN severity for Twilio SID (broad SK prefix), got %q" , f .Severity )
515- }
516- if ! strings .Contains (f .Description , "Twilio" ) {
517- t .Errorf ("expected description to contain %q, got %q" , "Twilio" , f .Description )
518- }
519- }
520- }
521- assertNoSecretValue (t , result .Findings , value )
522- }
523-
524536func TestAPIKeyScanner_CrossPassDedup_NameRegexWins (t * testing.T ) {
525537 // CUSTOM_STRIPE_KEY matches the STRIPE name-regex.
526538 // sk_live_ + 47 chars matches the Stripe live secret value pattern.
@@ -551,118 +563,6 @@ func TestAPIKeyScanner_CrossPassDedup_NameRegexWins(t *testing.T) {
551563 }
552564}
553565
554- func TestAPIKeyScanner_NameRegex_NEON_NarrowedPattern (t * testing.T ) {
555- clearHighRiskEnv (t )
556- // These should NOT be flagged.
557- t .Setenv ("ANEMONE_CONFIG" , "some-value" )
558- t .Setenv ("NEONLIGHTS_COLOR" , "blue" )
559- // These SHOULD be flagged.
560- t .Setenv ("NEON_API_KEY" , "real-neon-key" )
561- t .Setenv ("MY_NEON_KEY" , "also-real-neon-key" )
562-
563- s := newScannerWithHome (t .TempDir ())
564- result := s .Scan ()
565-
566- assertResource (t , result .Findings , "NEON_API_KEY" )
567- assertResource (t , result .Findings , "MY_NEON_KEY" )
568- for _ , f := range result .Findings {
569- if f .Resource == "ANEMONE_CONFIG" {
570- t .Error ("ANEMONE_CONFIG should not be flagged by NEON_ pattern" )
571- }
572- if f .Resource == "NEONLIGHTS_COLOR" {
573- t .Error ("NEONLIGHTS_COLOR should not be flagged by NEON_ pattern" )
574- }
575- }
576- }
577-
578- func TestAPIKeyScanner_NameRegex_LINEAR_NarrowedPattern (t * testing.T ) {
579- clearHighRiskEnv (t )
580- t .Setenv ("BILINEAR_FILTER" , "some-value" )
581- t .Setenv ("LINEAR_API_KEY" , "real-linear-key" )
582-
583- s := newScannerWithHome (t .TempDir ())
584- result := s .Scan ()
585-
586- assertResource (t , result .Findings , "LINEAR_API_KEY" )
587- for _ , f := range result .Findings {
588- if f .Resource == "BILINEAR_FILTER" {
589- t .Error ("BILINEAR_FILTER should not be flagged by LINEAR_ pattern" )
590- }
591- }
592- }
593-
594- func TestAPIKeyScanner_NameRegex_PALM_NarrowedPattern (t * testing.T ) {
595- clearHighRiskEnv (t )
596- t .Setenv ("NAPALM_MODE" , "some-value" )
597- // These SHOULD be flagged.
598- t .Setenv ("PALM_API_KEY" , "real-palm-key" )
599- t .Setenv ("MY_PALM_KEY" , "also-real-palm-key" )
600-
601- s := newScannerWithHome (t .TempDir ())
602- result := s .Scan ()
603-
604- assertResource (t , result .Findings , "PALM_API_KEY" )
605- assertResource (t , result .Findings , "MY_PALM_KEY" )
606- for _ , f := range result .Findings {
607- if f .Resource == "NAPALM_MODE" {
608- t .Error ("NAPALM_MODE should not be flagged by PALM_ pattern" )
609- }
610- }
611- }
612-
613- func TestAPIKeyScanner_NameRegex_NewAIProviders (t * testing.T ) {
614- clearHighRiskEnv (t )
615- cases := []struct {
616- envVar string
617- value string
618- }{
619- {"OPENROUTER_API_KEY" , "or-key-value" },
620- {"FIREWORKS_API_KEY" , "fw-key-value" },
621- {"DEEPSEEK_API_KEY" , "ds-key-value" },
622- {"PERPLEXITY_API_KEY" , "pplx-key-value" },
623- {"CEREBRAS_API_KEY" , "cb-key-value" },
624- {"DOPPLER_TOKEN" , "dp-token-value" },
625- {"XAI_API_KEY" , "xai-key-value" },
626- {"ASSEMBLYAI_API_KEY" , "aai-key-value" },
627- {"AI21_API_KEY" , "ai21-key-value" },
628- {"NVIDIA_NIM_API_KEY" , "nim-key-value" },
629- }
630- for _ , tc := range cases {
631- t .Setenv (tc .envVar , tc .value )
632- }
633-
634- s := newScannerWithHome (t .TempDir ())
635- result := s .Scan ()
636-
637- for _ , tc := range cases {
638- assertResource (t , result .Findings , tc .envVar )
639- }
640- }
641-
642- func TestAPIKeyScanner_NameRegex_XAI_Anchored (t * testing.T ) {
643- clearAllEnv (t )
644- // XAI embedded mid-word with no credential suffix — should NOT be flagged.
645- t .Setenv ("PROXAI_ENDPOINT" , "https://api.proxai.com" )
646- t .Setenv ("RELAXAI_MODE" , "true" )
647- // These SHOULD be flagged.
648- t .Setenv ("XAI_API_KEY" , "real-xai-key" )
649- t .Setenv ("MY_XAI_KEY" , "also-real-xai-key" )
650-
651- s := newScannerWithHome (t .TempDir ())
652- result := s .Scan ()
653-
654- assertResource (t , result .Findings , "XAI_API_KEY" )
655- assertResource (t , result .Findings , "MY_XAI_KEY" )
656- for _ , f := range result .Findings {
657- if f .Resource == "PROXAI_ENDPOINT" {
658- t .Error ("PROXAI_ENDPOINT should not be flagged by XAI pattern" )
659- }
660- if f .Resource == "RELAXAI_MODE" {
661- t .Error ("RELAXAI_MODE should not be flagged by XAI pattern" )
662- }
663- }
664- }
665-
666566func TestAPIKeyScanner_ExtraEnvKeys_NoDuplicateWithNameRegex (t * testing.T ) {
667567 const key = "MY_OPENAI_KEY" // matches OPENAI nameRegexPattern AND is in ExtraEnvKeys
668568 t .Setenv (key , "sk-test-value" )
0 commit comments