@@ -24,7 +24,10 @@ mod constants {
2424 pub const FALSE_POSITIVE_INDICATORS : & [ & str ] = & [
2525 "test" , "mock" , "example" , "dummy" , "placeholder" ,
2626 "lorem" , "ipsum" , "comment" , "doc" , "readme" ,
27- "license" , "copyright" , "author"
27+ "license" , "copyright" , "author" , "guid" , "uuid" ,
28+ "id64" , "hash" , "checksum" , "base64" , "encoded" ,
29+ "string" , "sample" , "fake" , "default" , "null" ,
30+ "zero" , "empty" , "temp" , "benchmark" , "perf" , "stress"
2831 ] ;
2932}
3033
@@ -602,47 +605,97 @@ impl SolanaSecurityCheck {
602605 }
603606 }
604607
605- /// Determine if a base58 string is likely a Solana key based on context
606- fn is_likely_solana_key ( & self , value : & str , context : & str ) -> bool {
607- // Skip common false positives
608+ /// Determine confidence level and likelihood of Solana key with enhanced analysis
609+ fn analyze_solana_key_confidence ( & self , value : & str , context : & str ) -> ( bool , f32 , AuditSeverity ) {
608610 let context_lower = context. to_lowercase ( ) ;
611+
612+ // Skip common false positives
609613 for & indicator in constants:: FALSE_POSITIVE_INDICATORS {
610614 if context_lower. contains ( indicator) {
611- return false ;
615+ return ( false , 0.0 , AuditSeverity :: Info ) ;
616+ }
617+ }
618+
619+ let mut confidence = 0.0 ;
620+
621+ // Check if it matches known Solana program IDs (highest confidence)
622+ if self . is_known_solana_program_id ( value) {
623+ confidence += 0.9 ;
624+ }
625+
626+ // Strong Solana-specific indicators
627+ let strong_indicators = [
628+ ( "pubkey" , 0.8 ) , ( "program_id" , 0.9 ) , ( "account" , 0.6 ) ,
629+ ( "signer" , 0.7 ) , ( "authority" , 0.7 ) , ( "mint" , 0.8 ) ,
630+ ( "pda" , 0.9 ) , ( "system_program" , 0.9 ) , ( "spl_token" , 0.8 ) ,
631+ ( "metaplex" , 0.8 ) , ( "anchor" , 0.7 ) , ( "solana_sdk" , 0.9 ) ,
632+ ( "solana_program" , 0.9 ) , ( "anchor_lang" , 0.8 )
633+ ] ;
634+
635+ for ( indicator, weight) in & strong_indicators {
636+ if context_lower. contains ( indicator) {
637+ confidence += weight;
638+ break ; // Only count the highest match
639+ }
640+ }
641+
642+ // Variable naming patterns
643+ let naming_patterns = [
644+ ( "_pubkey" , 0.7 ) , ( "_program" , 0.6 ) , ( "_account" , 0.5 ) ,
645+ ( "pubkey_" , 0.7 ) , ( "program_" , 0.6 ) , ( "solana_" , 0.6 )
646+ ] ;
647+
648+ for ( pattern, weight) in & naming_patterns {
649+ if context_lower. contains ( pattern) {
650+ confidence += weight * 0.8 ; // Slightly reduce naming pattern confidence
651+ break ;
612652 }
613653 }
614654
615- // Look for Solana-specific context clues
616- let solana_indicators = [
617- "pubkey" ,
618- "program_id" ,
619- "account" ,
620- "signer" ,
621- "authority" ,
622- "mint" ,
623- "token" ,
624- "pda" ,
625- "system_program" ,
626- "spl_token" ,
627- "metaplex" ,
628- "anchor" ,
629- "lamports" ,
630- "rent" ,
631- "solana" ,
632- "devnet" ,
633- "mainnet" ,
634- "testnet" ,
655+ // Crypto/blockchain context (lower confidence)
656+ let crypto_indicators = [
657+ ( "crypto" , 0.3 ) , ( "blockchain" , 0.3 ) , ( "defi" , 0.4 ) ,
658+ ( "web3" , 0.4 ) , ( "transaction" , 0.2 ) , ( "wallet" , 0.3 )
635659 ] ;
636660
637- for indicator in & solana_indicators {
661+ for ( indicator, weight ) in & crypto_indicators {
638662 if context_lower. contains ( indicator) {
639- return true ;
663+ confidence += weight;
664+ break ;
640665 }
641666 }
642667
643- // If no specific context, assume it might be a key (conservative approach)
644- // but lower severity if no clear context
645- true
668+ // Determine if it's likely a key and appropriate severity
669+ let is_likely = confidence > 0.4 ;
670+ let severity = if confidence > 0.8 {
671+ AuditSeverity :: Medium
672+ } else if confidence > 0.6 {
673+ AuditSeverity :: Low
674+ } else if confidence > 0.4 {
675+ AuditSeverity :: Info
676+ } else {
677+ AuditSeverity :: Info
678+ } ;
679+
680+ // Cap confidence at 1.0
681+ ( is_likely, confidence. min ( 1.0 ) , severity)
682+ }
683+
684+ /// Check if a public key matches known Solana program IDs
685+ fn is_known_solana_program_id ( & self , value : & str ) -> bool {
686+ // List of well-known Solana program IDs that should definitely be flagged
687+ let known_program_ids = [
688+ "11111111111111111111111111111112" , // System Program
689+ "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" , // SPL Token Program
690+ "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL" , // Associated Token Program
691+ "metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s" , // Metaplex Token Metadata
692+ "p1exdMJcjVao65QdewkaZRUnU6VPSXhus9n2GzWfh98" , // Serum DEX v1
693+ "9WzDXwBbmkg8ZTbNMqUxvQRAyrZzDsGYdLVL9zYtAWWM" , // Serum DEX v2
694+ "DjVE6JNiYqPL2QXyCUUh8rNjHrbz9hXHNYt99MQ59qw1" , // Orca DEX
695+ "JUP2jxvXaqu7NQY1GmNF4m1vodw12LVXYxbFL2uJvfo" , // Jupiter Aggregator
696+ ] ;
697+
698+ known_program_ids. contains ( & value)
646699 }
647700
648701 /// Check for additional Solana security patterns using regex cache
@@ -652,28 +705,52 @@ impl SolanaSecurityCheck {
652705 file_path : & str ,
653706 findings : & mut Vec < AuditFinding > ,
654707 ) {
655- // Check for hardcoded program IDs or keys in string literals with context awareness
708+ // Check for hardcoded program IDs or keys in string literals with enhanced context awareness
656709 for string_lit in & analysis. string_literals {
657710 // Check for potential base58 encoded Solana public keys
658711 if Self :: is_valid_base58_pubkey ( & string_lit. value ) {
659- // Reduce false positives by checking context
660- let is_likely_key =
661- self . is_likely_solana_key ( & string_lit. value , & string_lit. context ) ;
712+ // Use confidence-based analysis to reduce false positives
713+ let ( is_likely_key, confidence , severity ) =
714+ self . analyze_solana_key_confidence ( & string_lit. value , & string_lit. context ) ;
662715
663716 if is_likely_key {
717+ let confidence_desc = if confidence > 0.8 {
718+ "high confidence"
719+ } else if confidence > 0.6 {
720+ "medium confidence"
721+ } else {
722+ "low confidence"
723+ } ;
724+
664725 findings. push ( AuditFinding {
665726 id : FindingIdAllocator :: next_category_id ( "solana" ) ,
666- title : "Potential hardcoded Solana public key" . to_string ( ) ,
727+ title : format ! ( "Potential hardcoded Solana public key ({})" , confidence_desc ) ,
667728 description : format ! (
668- "File {} contains what appears to be a hardcoded base58-encoded public key at line {}: '{}'" ,
669- file_path, string_lit. line, & string_lit. value[ ..12 ] // Show only first 12 chars
729+ "File {} contains what appears to be a hardcoded base58-encoded public key at line {} (confidence: {:.1}%): '{}'{}" ,
730+ file_path,
731+ string_lit. line,
732+ confidence * 100.0 ,
733+ & string_lit. value[ ..12 ] , // Show only first 12 chars
734+ if confidence > 0.8 { "" } else { " - manual review recommended" }
670735 ) ,
671- severity : AuditSeverity :: Medium ,
736+ severity,
672737 category : "Solana Security" . to_string ( ) ,
673738 cwe_id : Some ( "CWE-798" . to_string ( ) ) ,
674- cvss_score : Some ( 5.0 ) ,
675- impact : "Hardcoded keys reduce flexibility and may expose sensitive information" . to_string ( ) ,
676- recommendation : "Use environment variables or configuration for public keys, or use the Pubkey::from_str() function with constants" . to_string ( ) ,
739+ cvss_score : Some ( 3.0 + ( confidence * 4.0 ) ) , // Scale CVSS with confidence
740+ impact : if confidence > 0.8 {
741+ "Hardcoded keys reduce flexibility and may expose sensitive information" . to_string ( )
742+ } else {
743+ "Potential hardcoded key detected - requires manual verification" . to_string ( )
744+ } ,
745+ recommendation : format ! (
746+ "{}. {}" ,
747+ if confidence > 0.8 {
748+ "Use environment variables or configuration for public keys"
749+ } else {
750+ "Verify if this is actually a Solana public key"
751+ } ,
752+ "Consider using the Pubkey::from_str() function with constants if this is a legitimate program ID"
753+ ) ,
677754 code_location : Some ( format ! ( "{}:{}" , file_path, string_lit. line) ) ,
678755 references : vec ! [
679756 "https://docs.solana.com/developing/programming-model/accounts" . to_string( ) ,
0 commit comments