1
1
use std:: { collections:: BTreeMap , error:: Error } ;
2
2
3
- use crate :: ast:: { Expression , FunctionCall , NodeID , TypeName } ;
4
-
5
3
use crate :: {
4
+ ast:: { Expression , Identifier , NodeID } ,
6
5
capture,
7
- context:: workspace_context:: WorkspaceContext ,
8
- detect:: detector:: { IssueDetector , IssueDetectorNamePool , IssueSeverity } ,
6
+ context:: { browser:: ExtractFunctionCalls , workspace_context:: WorkspaceContext } ,
7
+ detect:: {
8
+ detector:: { IssueDetector , IssueDetectorNamePool , IssueSeverity } ,
9
+ helpers:: { get_implemented_external_and_public_functions, has_msg_sender_binary_operation} ,
10
+ } ,
9
11
} ;
10
12
use eyre:: Result ;
11
13
@@ -16,51 +18,69 @@ pub struct ArbitraryTransferFromDetector {
16
18
found_instances : BTreeMap < ( String , usize , String ) , NodeID > ,
17
19
}
18
20
19
- // Check if the first argument of the function call is valid
20
- // In function calls with 3 args, the first arg [0] is the `from` address
21
- // In function calls with 4 args, the second arg [1] is the `from` address
22
- fn check_argument_validity ( function_call : & FunctionCall ) -> bool {
23
- let arg_index = if function_call. arguments . len ( ) == 3 {
24
- 0
25
- } else if function_call. arguments . len ( ) == 4 {
26
- 1
27
- } else {
28
- return false ;
29
- } ;
30
-
31
- match & function_call. arguments [ arg_index] {
32
- Expression :: MemberAccess ( arg_member_access) => {
33
- !( arg_member_access. member_name == "sender"
34
- && matches ! ( & * arg_member_access. expression, Expression :: Identifier ( identifier) if identifier. name == "msg" ) )
35
- }
36
- Expression :: FunctionCall ( arg_function_call) => {
37
- !( matches ! ( & * arg_function_call. expression, Expression :: ElementaryTypeNameExpression ( arg_el_type_name_exp) if matches!( & arg_el_type_name_exp. type_name, TypeName :: ElementaryTypeName ( type_name) if type_name. name == "address" ) )
38
- && matches ! ( arg_function_call. arguments. first( ) , Some ( Expression :: Identifier ( arg_identifier) ) if arg_identifier. name == "this" ) )
39
- }
40
- _ => true ,
41
- }
42
- }
43
-
44
21
impl IssueDetector for ArbitraryTransferFromDetector {
45
22
fn detect ( & mut self , context : & WorkspaceContext ) -> Result < bool , Box < dyn Error > > {
46
- let transfer_from_function_calls =
47
- context. function_calls ( ) . into_iter ( ) . filter ( |& function_call| {
48
- // For each function call, check if the function call is a member access
49
- // and if the member name is "transferFrom" or "safeTransferFrom", then check if the
50
- // first argument is valid If the first argument is valid, add the
51
- // function call to found_instances
52
- if let Expression :: MemberAccess ( member_access) = & * function_call. expression {
53
- if member_access. member_name == "transferFrom"
54
- || member_access. member_name == "safeTransferFrom"
55
- {
56
- return check_argument_validity ( function_call) ;
57
- }
58
- }
59
- false
23
+ // Applying devtooligan's suggestion
24
+ // * Operate on public and external functions only
25
+ // * See that msg.sender is not checked
26
+ // * Check that the argument passed in is from the parameter list of the said function
27
+
28
+ let suspected_functions =
29
+ get_implemented_external_and_public_functions ( context) . filter ( |function_definition| {
30
+ !has_msg_sender_binary_operation ( & ( ( * function_definition) . into ( ) ) )
31
+ && function_definition. modifiers . is_empty ( ) // If there are modifiers, assume
32
+ // the function is safe because
33
+ // sometime modifiers' definition
34
+ // may not be in scope
60
35
} ) ;
61
36
62
- for item in transfer_from_function_calls {
63
- capture ! ( self , context, item) ;
37
+ for func in suspected_functions {
38
+ let func_parameters_ids =
39
+ & func. parameters . parameters . iter ( ) . map ( |f| f. id ) . collect :: < Vec < _ > > ( ) ;
40
+
41
+ let transfer_func_calls = ExtractFunctionCalls :: from ( func)
42
+ . extracted
43
+ . into_iter ( )
44
+ . filter ( |function_call| {
45
+ // For each function call, check if the function call is a member access
46
+ // and if the member name is "transferFrom" or "safeTransferFrom", then check if
47
+ // the first argument is valid If the first argument is
48
+ // valid, add the function call to found_instances
49
+ if let Expression :: MemberAccess ( member_access) = & * function_call. expression {
50
+ if member_access. member_name == "transferFrom"
51
+ || member_access. member_name == "safeTransferFrom"
52
+ {
53
+ return true ;
54
+ }
55
+ }
56
+ false
57
+ } )
58
+ . collect :: < Vec < _ > > ( ) ;
59
+
60
+ for func in transfer_func_calls {
61
+ // Check if the first argument of the function call is valid
62
+ // In function calls with 3 args, the first arg [0] is the `from` address
63
+ // In function calls with 4 args, the second arg [1] is the `from` address
64
+ let arg_index = if func. arguments . len ( ) == 3 {
65
+ 0
66
+ } else if func. arguments . len ( ) == 4 {
67
+ 1
68
+ } else {
69
+ continue ;
70
+ } ;
71
+
72
+ let arg = & func. arguments [ arg_index] ;
73
+
74
+ if let Expression :: Identifier ( Identifier {
75
+ referenced_declaration : Some ( referenced_id) ,
76
+ ..
77
+ } ) = arg
78
+ {
79
+ if func_parameters_ids. iter ( ) . any ( |r| r == referenced_id) {
80
+ capture ! ( self , context, func) ;
81
+ }
82
+ }
83
+ }
64
84
}
65
85
66
86
Ok ( !self . found_instances . is_empty ( ) )
@@ -107,7 +127,7 @@ mod arbitrary_transfer_from_tests {
107
127
// assert that the detector found an issue
108
128
assert ! ( found) ;
109
129
// assert that the detector found the correct number of instances
110
- assert_eq ! ( detector. instances( ) . len( ) , 4 ) ;
130
+ assert_eq ! ( detector. instances( ) . len( ) , 2 ) ;
111
131
// assert the severity is high
112
132
assert_eq ! ( detector. severity( ) , crate :: detect:: detector:: IssueSeverity :: High ) ;
113
133
// assert the title is correct
0 commit comments