44//! to identify vulnerabilities and generate contextually appropriate fix suggestions.
55
66use crate :: utils:: audit:: { AuditFinding , CodeSnippet } ;
7+ use crate :: utils:: debug_logger:: VerbosityLevel ;
8+ use crate :: { debug_print, debug_warn} ;
79use anyhow:: { Context , Result } ;
810use proc_macro2:: TokenStream ;
911use quote:: ToTokens ;
@@ -84,6 +86,23 @@ pub struct ImprovementSuggestion {
8486 pub explanation : String ,
8587}
8688
89+ /// Signer validation analysis result
90+ #[ derive( Debug , Clone , Default ) ]
91+ pub struct SignerValidationResult {
92+ pub has_signer_check : bool ,
93+ pub has_require_signer : bool ,
94+ pub has_anchor_signer_constraint : bool ,
95+ pub has_conditional_signer_check : bool ,
96+ }
97+
98+ /// Owner validation analysis result
99+ #[ derive( Debug , Clone , Default ) ]
100+ pub struct OwnerValidationResult {
101+ pub has_owner_check : bool ,
102+ pub has_program_id_check : bool ,
103+ pub has_anchor_owner_constraint : bool ,
104+ }
105+
87106/// Visitor for analyzing Solana/Anchor program patterns
88107struct SolanaSecurityVisitor {
89108 context : AnalysisContext ,
@@ -289,6 +308,11 @@ impl AstAnalyzer {
289308
290309 /// Extract the variable name that holds an account reference using AST parsing
291310 fn extract_account_variable_name ( & self , content : & str ) -> Option < String > {
311+ debug_print ! (
312+ VerbosityLevel :: Detailed ,
313+ "Extracting account variable name from content"
314+ ) ;
315+
292316 // Try to parse the content as statements to properly extract variable names
293317 if let Ok ( parsed) = syn:: parse_str :: < syn:: Block > ( & format ! ( "{{{}}}" , content) ) {
294318 for stmt in & parsed. stmts {
@@ -299,6 +323,11 @@ impl AstAnalyzer {
299323 if content. contains ( & format ! ( "{}.balance" , var_name) )
300324 || content. contains ( & format ! ( "{}." , var_name) )
301325 {
326+ debug_print ! (
327+ VerbosityLevel :: Detailed ,
328+ "Found account variable: {}" ,
329+ var_name
330+ ) ;
302331 return Some ( var_name) ;
303332 }
304333 }
@@ -314,34 +343,135 @@ impl AstAnalyzer {
314343 if content. contains ( & format ! ( "{}.balance" , var_name) )
315344 || content. contains ( & format ! ( "{}." , var_name) )
316345 {
346+ debug_print ! (
347+ VerbosityLevel :: Detailed ,
348+ "Found account variable (fallback): {}" ,
349+ var_name
350+ ) ;
317351 return Some ( var_name. to_string ( ) ) ;
318352 }
319353 }
320354 }
355+
356+ debug_warn ! ( "Could not extract account variable name" ) ;
321357 None
322358 }
323359
324- /// Apply pattern-based fixes as fallback
360+ /// Advanced signer validation analysis using control flow
361+ fn analyze_signer_validation ( & self , content : & str ) -> SignerValidationResult {
362+ debug_print ! (
363+ VerbosityLevel :: Verbose ,
364+ "Analyzing signer validation patterns"
365+ ) ;
366+
367+ let mut result = SignerValidationResult :: default ( ) ;
368+
369+ // Check for explicit is_signer checks
370+ if content. contains ( "is_signer" ) {
371+ result. has_signer_check = true ;
372+ debug_print ! ( VerbosityLevel :: Detailed , "Found explicit is_signer check" ) ;
373+ }
374+
375+ // Check for require! macros with signer validation
376+ if content. contains ( "require!" ) && content. contains ( "is_signer" ) {
377+ result. has_require_signer = true ;
378+ debug_print ! (
379+ VerbosityLevel :: Detailed ,
380+ "Found require! with signer validation"
381+ ) ;
382+ }
383+
384+ // Check for anchor constraints
385+ if content. contains ( "#[account(signer)]" ) || content. contains ( "Signer<" ) {
386+ result. has_anchor_signer_constraint = true ;
387+ debug_print ! ( VerbosityLevel :: Detailed , "Found Anchor signer constraint" ) ;
388+ }
389+
390+ // Analyze control flow for conditional signer checks
391+ if let Ok ( parsed) = syn:: parse_str :: < syn:: Block > ( & format ! ( "{{{}}}" , content) ) {
392+ for stmt in & parsed. stmts {
393+ self . analyze_statement_for_signer_validation ( stmt, & mut result) ;
394+ }
395+ }
396+
397+ result
398+ }
399+
400+ /// Advanced owner validation analysis using control flow
401+ fn analyze_owner_validation ( & self , content : & str ) -> OwnerValidationResult {
402+ debug_print ! (
403+ VerbosityLevel :: Verbose ,
404+ "Analyzing owner validation patterns"
405+ ) ;
406+
407+ let mut result = OwnerValidationResult :: default ( ) ;
408+
409+ // Check for explicit owner checks
410+ if content. contains ( ".owner" ) && ( content. contains ( "==" ) || content. contains ( "require!" ) ) {
411+ result. has_owner_check = true ;
412+ debug_print ! ( VerbosityLevel :: Detailed , "Found explicit owner check" ) ;
413+ }
414+
415+ // Check for program ID validation
416+ if content. contains ( "program_id" ) && content. contains ( "==" ) {
417+ result. has_program_id_check = true ;
418+ debug_print ! ( VerbosityLevel :: Detailed , "Found program ID validation" ) ;
419+ }
420+
421+ // Check for anchor constraints
422+ if content. contains ( "#[account(owner" ) {
423+ result. has_anchor_owner_constraint = true ;
424+ debug_print ! ( VerbosityLevel :: Detailed , "Found Anchor owner constraint " ) ;
425+ }
426+
427+ result
428+ }
429+
430+ /// Analyze a statement for signer validation patterns
431+ fn analyze_statement_for_signer_validation (
432+ & self ,
433+ stmt : & syn:: Stmt ,
434+ result : & mut SignerValidationResult ,
435+ ) {
436+ match stmt {
437+ syn:: Stmt :: Expr ( syn:: Expr :: If ( if_expr) , _) => {
438+ // Check if condition involves signer validation
439+ let cond_str = quote:: quote!( #if_expr. cond) . to_string ( ) ;
440+ if cond_str. contains ( "is_signer" ) {
441+ result. has_conditional_signer_check = true ;
442+ debug_print ! ( VerbosityLevel :: Verbose , "Found conditional signer check " ) ;
443+ }
444+ }
445+ syn:: Stmt :: Expr ( syn:: Expr :: Macro ( macro_expr) , _) => {
446+ let macro_str = quote:: quote!( #macro_expr) . to_string ( ) ;
447+ if macro_str. contains ( "require!" ) && macro_str. contains ( "is_signer" ) {
448+ result. has_require_signer = true ;
449+ debug_print ! (
450+ VerbosityLevel :: Verbose ,
451+ "Found require macro with signer validation "
452+ ) ;
453+ }
454+ }
455+ _ => { }
456+ }
457+ }
458+
459+ /// Apply pattern-based fixes with AST-aware insertion
325460 fn apply_pattern_based_fixes ( & self , content : & str , finding : & AuditFinding ) -> String {
461+ debug_print ! (
462+ VerbosityLevel :: Detailed ,
463+ "Applying pattern-based fixes for category: {}" ,
464+ finding. category
465+ ) ;
466+
326467 let mut fixed_content = content. to_string ( ) ;
327468
328469 match finding. category . as_str ( ) {
329470 "Authentication & Authorization" => {
330471 if !content. contains ( "is_signer" )
331472 && ( content. contains ( "AccountInfo" ) || content. contains ( "ctx.accounts" ) )
332473 {
333- // Extract account variable name from the content
334- if let Some ( account_var) = self . extract_account_variable_name ( content) {
335- fixed_content = format ! (
336- "// Added signer validation\n require!({}.is_signer, ErrorCode::MissingSignature);\n {}" ,
337- account_var, fixed_content
338- ) ;
339- } else {
340- fixed_content = format ! (
341- "// Added signer validation\n require!(account.is_signer, ErrorCode::MissingSignature);\n {}" ,
342- fixed_content
343- ) ;
344- }
474+ fixed_content = self . insert_signer_validation_fix ( content, finding) ;
345475 }
346476 }
347477 "Solana Security" => {
@@ -350,10 +480,7 @@ impl AstAnalyzer {
350480 . replace ( "unwrap()" , "map_err(|e| ErrorCode::UnexpectedError)?" ) ;
351481 }
352482 if content. contains ( "user_account" ) && !content. contains ( "key()" ) {
353- fixed_content = format ! (
354- "// Added account key validation\n require!(user_account.key() == expected_user_key, ErrorCode::InvalidUser);\n {}" ,
355- fixed_content
356- ) ;
483+ fixed_content = self . insert_owner_validation_fix ( content, finding) ;
357484 }
358485 }
359486 "Trading Security" => {
@@ -364,12 +491,68 @@ impl AstAnalyzer {
364491 ) ;
365492 }
366493 }
367- _ => { }
494+ _ => {
495+ debug_warn ! ( "Unknown finding category for fixes: {}" , finding. category) ;
496+ }
368497 }
369498
370499 fixed_content
371500 }
372501
502+ /// Insert signer validation fix using AST-aware positioning
503+ fn insert_signer_validation_fix ( & self , content : & str , finding : & AuditFinding ) -> String {
504+ debug_print ! ( VerbosityLevel :: Verbose , "Inserting signer validation fix" ) ;
505+
506+ // Extract account variable name from the content
507+ if let Some ( account_var) = self . extract_account_variable_name ( content) {
508+ // Try to find the exact position after variable binding using AST
509+ if let Ok ( parsed) = syn:: parse_str :: < syn:: Block > ( & format ! ( "{{{}}}" , content) ) {
510+ for ( i, stmt) in parsed. stmts . iter ( ) . enumerate ( ) {
511+ if let syn:: Stmt :: Local ( local) = stmt {
512+ if let syn:: Pat :: Ident ( pat_ident) = & local. pat {
513+ if pat_ident. ident == account_var {
514+ // Insert the fix after this statement
515+ let lines: Vec < & str > = content. lines ( ) . collect ( ) ;
516+ if i < lines. len ( ) {
517+ let mut result = Vec :: new ( ) ;
518+ result. extend_from_slice ( & lines[ 0 ..=i] ) ;
519+ result. push ( & format ! (
520+ " require!({}.is_signer, ErrorCode::MissingSignature);" ,
521+ account_var
522+ ) ) ;
523+ result. extend_from_slice ( & lines[ i + 1 ..] ) ;
524+ return result. join ( "\n " ) ;
525+ }
526+ }
527+ }
528+ }
529+ }
530+ }
531+
532+ // Fallback to simple prepend
533+ format ! (
534+ "// Added signer validation\n require!({}.is_signer, ErrorCode::MissingSignature);\n {}" ,
535+ account_var, content
536+ )
537+ } else {
538+ format ! (
539+ "// Added signer validation\n require!(account.is_signer, ErrorCode::MissingSignature);\n {}" ,
540+ content
541+ )
542+ }
543+ }
544+
545+ /// Insert owner validation fix using AST-aware positioning
546+ fn insert_owner_validation_fix ( & self , content : & str , finding : & AuditFinding ) -> String {
547+ debug_print ! ( VerbosityLevel :: Verbose , "Inserting owner validation fix" ) ;
548+
549+ // Similar AST-aware insertion for owner validation
550+ format ! (
551+ "// Added account key validation\n require!(user_account.key() == expected_user_key, ErrorCode::InvalidUser);\n {}" ,
552+ content
553+ )
554+ }
555+
373556 /// Extract detailed vulnerability information using AST analysis
374557 pub fn extract_vulnerability_details (
375558 & self ,
0 commit comments