Skip to content

Commit 77f8e96

Browse files
Copilot0xrinegade
andcommitted
Enhance Solana key detection with confidence-based analysis and reduce false positives
Co-authored-by: 0xrinegade <[email protected]>
1 parent 91fec97 commit 77f8e96

File tree

1 file changed

+118
-41
lines changed

1 file changed

+118
-41
lines changed

src/utils/audit_modular.rs

Lines changed: 118 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)