Update to origin/dev-slither-format-tool-only-new

pull/238/head
Josselin 6 years ago
parent 0dacdddaa4
commit 52f09f95a3
  1. 1
      utils/slither_format/formatters/constable_states.py
  2. 3
      utils/slither_format/formatters/constant_function.py
  3. 15
      utils/slither_format/formatters/external_function.py
  4. 86
      utils/slither_format/formatters/naming_convention.py
  5. 2
      utils/slither_format/formatters/pragma.py
  6. 4
      utils/slither_format/formatters/solc_version.py
  7. 1
      utils/slither_format/formatters/unused_state.py
  8. 8
      utils/slither_format/slither_format.py

@ -15,6 +15,7 @@ def format(slither, patches, elements):
def _patch(slither, patches, in_file, in_file_relative, match_text, replace_text, modify_loc_start, modify_loc_end): def _patch(slither, patches, in_file, in_file_relative, match_text, replace_text, modify_loc_start, modify_loc_end):
in_file_str = slither.source_code[in_file].encode('utf-8') in_file_str = slither.source_code[in_file].encode('utf-8')
old_str_of_interest = in_file_str[modify_loc_start:modify_loc_end] old_str_of_interest = in_file_str[modify_loc_start:modify_loc_end]
# Add keyword `constant` before the variable name
(new_str_of_interest, num_repl) = re.subn(match_text, replace_text, old_str_of_interest.decode('utf-8'), 1) (new_str_of_interest, num_repl) = re.subn(match_text, replace_text, old_str_of_interest.decode('utf-8'), 1)
if num_repl != 0: if num_repl != 0:
create_patch( create_patch(

@ -22,6 +22,7 @@ def format(slither, patches, elements):
def _patch(slither, patches, in_file, in_file_relative, modify_loc_start, modify_loc_end): def _patch(slither, patches, in_file, in_file_relative, modify_loc_start, modify_loc_end):
in_file_str = slither.source_code[in_file].encode('utf-8') in_file_str = slither.source_code[in_file].encode('utf-8')
old_str_of_interest = in_file_str[modify_loc_start:modify_loc_end] old_str_of_interest = in_file_str[modify_loc_start:modify_loc_end]
# Find the keywords view|pure|constant and remove them
m = re.search("(view|pure|constant)", old_str_of_interest.decode('utf-8')) m = re.search("(view|pure|constant)", old_str_of_interest.decode('utf-8'))
if m: if m:
create_patch(patches, create_patch(patches,
@ -30,7 +31,7 @@ def _patch(slither, patches, in_file, in_file_relative, modify_loc_start, modify
in_file, in_file,
modify_loc_start + m.span()[0], modify_loc_start + m.span()[0],
modify_loc_start + m.span()[1], modify_loc_start + m.span()[1],
m.groups(0)[0], m.groups(0)[0], # this is view|pure|constant
"") "")
else: else:
raise SlitherException("No view/pure/constant specifier exists. Regex failed to remove specifier!") raise SlitherException("No view/pure/constant specifier exists. Regex failed to remove specifier!")

@ -10,15 +10,16 @@ def format(slither, patches, elements):
_patch(slither, patches, _patch(slither, patches,
element['source_mapping']['filename_absolute'], element['source_mapping']['filename_absolute'],
element['source_mapping']['filename_relative'], element['source_mapping']['filename_relative'],
"external",
int(function.parameters_src.source_mapping['start']), int(function.parameters_src.source_mapping['start']),
int(function.returns_src.source_mapping['start'])) int(function.returns_src.source_mapping['start']))
break break
def _patch(slither, patches, in_file, in_file_relative, replace_text, modify_loc_start, modify_loc_end): def _patch(slither, patches, in_file, in_file_relative, modify_loc_start, modify_loc_end):
in_file_str = slither.source_code[in_file].encode('utf-8') in_file_str = slither.source_code[in_file].encode('utf-8')
old_str_of_interest = in_file_str[modify_loc_start:modify_loc_end] old_str_of_interest = in_file_str[modify_loc_start:modify_loc_end]
# Search for 'public' keyword which is in-between the function name and modifier name (if present)
# regex: 'public' could have spaces around or be at the end of the line
m = re.search(r'((\spublic)\s+)|(\spublic)$|(\)public)$', old_str_of_interest.decode('utf-8')) m = re.search(r'((\spublic)\s+)|(\spublic)$|(\)public)$', old_str_of_interest.decode('utf-8'))
if m is None: if m is None:
# No visibility specifier exists; public by default. # No visibility specifier exists; public by default.
@ -26,16 +27,20 @@ def _patch(slither, patches, in_file, in_file_relative, replace_text, modify_loc
"external-function", "external-function",
in_file_relative, in_file_relative,
in_file, in_file,
# start after the function definition's closing paranthesis
modify_loc_start + len(old_str_of_interest.decode('utf-8').split(')')[0]) + 1, modify_loc_start + len(old_str_of_interest.decode('utf-8').split(')')[0]) + 1,
# end is same as start because we insert the keyword `external` at that location
modify_loc_start + len(old_str_of_interest.decode('utf-8').split(')')[0]) + 1, modify_loc_start + len(old_str_of_interest.decode('utf-8').split(')')[0]) + 1,
"", "",
" "+ replace_text) " external") # replace_text is `external`
else: else:
create_patch(patches, create_patch(patches,
"external-function", "external-function",
in_file_relative, in_file_relative,
in_file, in_file,
# start at the keyword `public`
modify_loc_start + m.span()[0] + 1, modify_loc_start + m.span()[0] + 1,
# end after the keyword `public` = start + len('public'')
modify_loc_start + m.span()[0] + 1 + 6, modify_loc_start + m.span()[0] + 1 + 6,
"", " public",
" " + replace_text) " external")

@ -117,11 +117,14 @@ def _patch(slither, patches, _target, name, function_name, contract_name, in_fil
def _create_patch_contract_definition(format_info): def _create_patch_contract_definition(format_info):
in_file_str = format_info.slither.source_code[format_info.in_file].encode('utf-8') in_file_str = format_info.slither.source_code[format_info.in_file].encode('utf-8')
old_str_of_interest = in_file_str[format_info.loc_start:format_info.loc_end] old_str_of_interest = in_file_str[format_info.loc_start:format_info.loc_end]
m = re.match(r'(.*)'+"contract"+r'(.*)'+format_info.name, old_str_of_interest.decode('utf-8')) # Locate the name following keywords `contract` | `interface` | `library`
m = re.match(r'(.*)' + "(contract|interface|library)" + r'(.*)' + format_info.name, old_str_of_interest.decode('utf-8'))
old_str_of_interest = in_file_str[format_info.loc_start:format_info.loc_start+m.span()[1]] old_str_of_interest = in_file_str[format_info.loc_start:format_info.loc_start+m.span()[1]]
(new_str_of_interest, num_repl) = re.subn(r'(.*)'+"contract"+r'(.*)'+format_info.name, # Capitalize the name
r'\1'+"contract"+r'\2'+format_info.name.capitalize(), (new_str_of_interest, num_repl) = re.subn(r'(.*)' + r'(contract|interface|library)' + r'(.*)' + format_info.name,
r'\1' + r'\2' + r'\3' + format_info.name.capitalize(),
old_str_of_interest.decode('utf-8'), 1) old_str_of_interest.decode('utf-8'), 1)
if num_repl != 0: if num_repl != 0:
create_patch(format_info.patches, create_patch(format_info.patches,
"naming-convention (contract definition)", "naming-convention (contract definition)",
@ -150,6 +153,7 @@ def _create_patch_contract_uses(format_info):
if (str(sv.type) == name): if (str(sv.type) == name):
old_str_of_interest = in_file_str[sv.source_mapping['start']:(sv.source_mapping['start'] + old_str_of_interest = in_file_str[sv.source_mapping['start']:(sv.source_mapping['start'] +
sv.source_mapping['length'])] sv.source_mapping['length'])]
# Get only the contract variable name even if it is initialised
(new_str_of_interest, num_repl) = re.subn(name, name.capitalize(), (new_str_of_interest, num_repl) = re.subn(name, name.capitalize(),
old_str_of_interest.decode('utf-8'), 1) old_str_of_interest.decode('utf-8'), 1)
create_patch(patches, create_patch(patches,
@ -187,15 +191,21 @@ def _create_patch_contract_uses(format_info):
if isinstance(ir, NewContract) and ir.contract_name == name: if isinstance(ir, NewContract) and ir.contract_name == name:
old_str_of_interest = in_file_str[node.source_mapping['start']:node.source_mapping['start'] + old_str_of_interest = in_file_str[node.source_mapping['start']:node.source_mapping['start'] +
node.source_mapping['length']] node.source_mapping['length']]
# Search for the name after the `new` keyword
m = re.search("new"+r'(.*)'+name, old_str_of_interest.decode('utf-8')) m = re.search("new"+r'(.*)'+name, old_str_of_interest.decode('utf-8'))
# Skip rare cases where re search fails. To-do: Investigate
if not m:
continue
old_str_of_interest = old_str_of_interest.decode('utf-8')[m.span()[0]:] old_str_of_interest = old_str_of_interest.decode('utf-8')[m.span()[0]:]
(new_str_of_interest, num_repl) = re.subn("new"+r'(.*)'+name, "new"+r'\1'+name[0].upper() + (new_str_of_interest, num_repl) = re.subn("new" + r'(.*)' + format_info.name,
name[1:], old_str_of_interest, 1) "new" + r'\1' + format_info.name.capitalize(),
format_info.name[1:], old_str_of_interest, 1)
if num_repl != 0: if num_repl != 0:
create_patch(patches, create_patch(patches,
"naming-convention (contract new object)", "naming-convention (contract new object)",
in_file_relative, in_file_relative,
in_file, in_file,
# start after the `new` keyword where the name begins
node.source_mapping['start'] + m.span()[0], node.source_mapping['start'] + m.span()[0],
node.source_mapping['start'] + m.span()[1], node.source_mapping['start'] + m.span()[1],
old_str_of_interest, old_str_of_interest,
@ -217,8 +227,10 @@ def _create_patch_modifier_definition(format_info):
if modifier.name == name: if modifier.name == name:
in_file_str = slither.source_code[in_file].encode('utf-8') in_file_str = slither.source_code[in_file].encode('utf-8')
old_str_of_interest = in_file_str[modify_loc_start:modify_loc_end] old_str_of_interest = in_file_str[modify_loc_start:modify_loc_end]
# Search for the modifier name after the `modifier` keyword
m = re.match(r'(.*)'+"modifier"+r'(.*)'+name, old_str_of_interest.decode('utf-8')) m = re.match(r'(.*)'+"modifier"+r'(.*)'+name, old_str_of_interest.decode('utf-8'))
old_str_of_interest = in_file_str[modify_loc_start:modify_loc_start+m.span()[1]] old_str_of_interest = in_file_str[modify_loc_start:modify_loc_start+m.span()[1]]
# Change the first letter of the modifier name to lowercase
(new_str_of_interest, num_repl) = re.subn(r'(.*)'+"modifier"+r'(.*)'+name, r'\1'+"modifier"+r'\2' + (new_str_of_interest, num_repl) = re.subn(r'(.*)'+"modifier"+r'(.*)'+name, r'\1'+"modifier"+r'\2' +
name[0].lower()+name[1:], old_str_of_interest.decode('utf-8'), 1) name[0].lower()+name[1:], old_str_of_interest.decode('utf-8'), 1)
if num_repl != 0: if num_repl != 0:
@ -247,8 +259,12 @@ def _create_patch_modifier_uses(format_info):
for m in function.modifiers: for m in function.modifiers:
if (m.name == name): if (m.name == name):
in_file_str = slither.source_code[in_file].encode('utf-8') in_file_str = slither.source_code[in_file].encode('utf-8')
# Get the text from function parameters until the return statement or function body beginning
# This text will include parameter declarations, any Solidity keywords and modifier call
# Parameter names cannot collide with modifier name per Solidity rules
old_str_of_interest = in_file_str[int(function.parameters_src.source_mapping['start']): old_str_of_interest = in_file_str[int(function.parameters_src.source_mapping['start']):
int(function.returns_src.source_mapping['start'])] int(function.returns_src.source_mapping['start'])]
# Change the first letter of the modifier name (if present) to lowercase
(new_str_of_interest, num_repl) = re.subn(name, name[0].lower()+name[1:], (new_str_of_interest, num_repl) = re.subn(name, name[0].lower()+name[1:],
old_str_of_interest.decode('utf-8'),1) old_str_of_interest.decode('utf-8'),1)
if num_repl != 0: if num_repl != 0:
@ -277,8 +293,10 @@ def _create_patch_function_definition(format_info):
if function.name == name: if function.name == name:
in_file_str = slither.source_code[in_file].encode('utf-8') in_file_str = slither.source_code[in_file].encode('utf-8')
old_str_of_interest = in_file_str[modify_loc_start:modify_loc_end] old_str_of_interest = in_file_str[modify_loc_start:modify_loc_end]
# Search for the function name after the `function` keyword
m = re.match(r'(.*)'+"function"+r'\s*'+name, old_str_of_interest.decode('utf-8')) m = re.match(r'(.*)'+"function"+r'\s*'+name, old_str_of_interest.decode('utf-8'))
old_str_of_interest = in_file_str[modify_loc_start:modify_loc_start+m.span()[1]] old_str_of_interest = in_file_str[modify_loc_start:modify_loc_start+m.span()[1]]
# Change the first letter of the function name to lowercase
(new_str_of_interest, num_repl) = re.subn(r'(.*)'+"function"+r'(.*)'+name, r'\1'+"function"+r'\2'+ (new_str_of_interest, num_repl) = re.subn(r'(.*)'+"function"+r'(.*)'+name, r'\1'+"function"+r'\2'+
name[0].lower()+name[1:], old_str_of_interest.decode('utf-8'), 1) name[0].lower()+name[1:], old_str_of_interest.decode('utf-8'), 1)
if num_repl != 0: if num_repl != 0:
@ -303,17 +321,22 @@ def _create_patch_function_calls(format_info):
for contract in slither.contracts: for contract in slither.contracts:
for function in contract.functions: for function in contract.functions:
for node in function.nodes: for node in function.nodes:
# Function call from another contract
for high_level_call in node.high_level_calls: for high_level_call in node.high_level_calls:
if (high_level_call[0].name == contract_name and high_level_call[1].name == name): if (high_level_call[0].name == contract_name and high_level_call[1].name == name):
for external_call in node.external_calls_as_expressions: for external_call in node.external_calls_as_expressions:
# Check the called function name
called_function = str(external_call.called).split('.')[-1] called_function = str(external_call.called).split('.')[-1]
if called_function == high_level_call[1].name: if called_function == high_level_call[1].name:
in_file_str = slither.source_code[in_file].encode('utf-8') in_file_str = slither.source_code[in_file].encode('utf-8')
old_str_of_interest = in_file_str[int(external_call.source_mapping['start']): old_str_of_interest = in_file_str[int(external_call.source_mapping['start']):
int(external_call.source_mapping['start']) + int(external_call.source_mapping['start']) +
int(external_call.source_mapping['length'])] int(external_call.source_mapping['length'])]
# Get the called function name. To-do: Check if we need to avoid parameters
called_function_name = old_str_of_interest.decode('utf-8').split('.')[-1] called_function_name = old_str_of_interest.decode('utf-8').split('.')[-1]
# Convert first letter of name to lowercase
fixed_function_name = called_function_name[0].lower() + called_function_name[1:] fixed_function_name = called_function_name[0].lower() + called_function_name[1:]
# Reconstruct the entire call
new_string = '.'.join(old_str_of_interest.decode('utf-8').split('.')[:-1]) + '.' + \ new_string = '.'.join(old_str_of_interest.decode('utf-8').split('.')[:-1]) + '.' + \
fixed_function_name fixed_function_name
create_patch(patches, create_patch(patches,
@ -325,12 +348,14 @@ def _create_patch_function_calls(format_info):
int(external_call.source_mapping['length']), int(external_call.source_mapping['length']),
old_str_of_interest.decode('utf-8'), old_str_of_interest.decode('utf-8'),
new_string) new_string)
# Function call from within same contract
for internal_call in node.internal_calls_as_expressions: for internal_call in node.internal_calls_as_expressions:
if (str(internal_call.called) == name): if (str(internal_call.called) == name):
in_file_str = slither.source_code[in_file].encode('utf-8') in_file_str = slither.source_code[in_file].encode('utf-8')
old_str_of_interest = in_file_str[int(internal_call.source_mapping['start']): old_str_of_interest = in_file_str[int(internal_call.source_mapping['start']):
int(internal_call.source_mapping['start']) + int(internal_call.source_mapping['start']) +
int(internal_call.source_mapping['length'])] int(internal_call.source_mapping['length'])]
# Get the called function name and avoid parameters
old_str_of_interest = old_str_of_interest.decode('utf-8').split('(')[0] old_str_of_interest = old_str_of_interest.decode('utf-8').split('(')[0]
# Avoid parameters # Avoid parameters
# TODO: (JF) review me # TODO: (JF) review me
@ -361,11 +386,13 @@ def _create_patch_event_definition(format_info):
raise SlitherException("Contract not found?!") raise SlitherException("Contract not found?!")
for event in target_contract.events: for event in target_contract.events:
if event.name == name: if event.name == name:
# Get only event name without parameters
event_name = name.split('(')[0] event_name = name.split('(')[0]
in_file_str = slither.source_code[in_file].encode('utf-8') in_file_str = slither.source_code[in_file].encode('utf-8')
old_str_of_interest = in_file_str[modify_loc_start:modify_loc_end] old_str_of_interest = in_file_str[modify_loc_start:modify_loc_end]
# Capitalize event name
(new_str_of_interest, num_repl) = re.subn(r'(.*)'+"event"+r'(.*)'+event_name, r'\1'+"event"+r'\2' + (new_str_of_interest, num_repl) = re.subn(r'(.*)'+"event"+r'(.*)'+event_name, r'\1'+"event"+r'\2' +
event_name[0].capitalize()+event_name[1:], event_name.capitalize(),
old_str_of_interest.decode('utf-8'), 1) old_str_of_interest.decode('utf-8'), 1)
if num_repl != 0: if num_repl != 0:
create_patch(patches, create_patch(patches,
@ -385,6 +412,7 @@ def _create_patch_event_calls(format_info):
in_file, in_file_relative = format_info.in_file, format_info.in_file_relative in_file, in_file_relative = format_info.in_file, format_info.in_file_relative
contract_name = format_info.contract_name contract_name = format_info.contract_name
# Get only event name without parameters
event_name = name.split('(')[0] event_name = name.split('(')[0]
target_contract = slither.get_contract_from_name(contract_name) target_contract = slither.get_contract_from_name(contract_name)
if not target_contract: if not target_contract:
@ -406,8 +434,8 @@ def _create_patch_event_calls(format_info):
call.source_mapping['start'], call.source_mapping['start'],
int(call.source_mapping['start']) + int(call.source_mapping['length']), int(call.source_mapping['start']) + int(call.source_mapping['length']),
old_str_of_interest.decode('utf-8'), old_str_of_interest.decode('utf-8'),
old_str_of_interest.decode('utf-8')[0].capitalize() + # Capitalize event name
old_str_of_interest.decode('utf-8')[1:]) old_str_of_interest.decode('utf-8').capitalize())
def _create_patch_parameter_declaration(format_info): def _create_patch_parameter_declaration(format_info):
@ -424,12 +452,15 @@ def _create_patch_parameter_declaration(format_info):
if function.name == function_name: if function.name == function_name:
in_file_str = slither.source_code[in_file].encode('utf-8') in_file_str = slither.source_code[in_file].encode('utf-8')
old_str_of_interest = in_file_str[modify_loc_start:modify_loc_end] old_str_of_interest = in_file_str[modify_loc_start:modify_loc_end]
# To-do: Change format logic below - how do we convert a name to mixedCase?
if(name[0] == '_'): if(name[0] == '_'):
# If parameter name begins with underscore, capitalize the letter after underscore
(new_str_of_interest, num_repl) = re.subn(r'(.*)'+name+r'(.*)', r'\1'+name[0]+name[1].upper() + (new_str_of_interest, num_repl) = re.subn(r'(.*)'+name+r'(.*)', r'\1'+name[0]+name[1].upper() +
name[2:]+r'\2', old_str_of_interest.decode('utf-8'), 1) name[2:]+r'\2', old_str_of_interest.decode('utf-8'), 1)
else: else:
(new_str_of_interest, num_repl) = re.subn(r'(.*)'+name+r'(.*)', r'\1'+'_'+name[0].upper() + # Add underscore and capitalize the first letter
name[1:]+r'\2', old_str_of_interest.decode('utf-8'), 1) (new_str_of_interest, num_repl) = re.subn(r'(.*)' + name + r'(.*)', r'\1' + '_' + name.capitalize() +
r'\2', old_str_of_interest.decode('utf-8'), 1)
if num_repl != 0: if num_repl != 0:
create_patch(patches, create_patch(patches,
"naming-convention (parameter declaration)", "naming-convention (parameter declaration)",
@ -460,17 +491,23 @@ def _create_patch_parameter_uses(format_info):
(node._local_vars_read + (node._local_vars_read +
node._local_vars_written) node._local_vars_written)
if str(lv) == name]: if str(lv) == name]:
# Skip rare cases where source_mapping is absent. To-do: Investigate
if not v.source_mapping:
continue
modify_loc_start = int(v.source_mapping['start']) modify_loc_start = int(v.source_mapping['start'])
modify_loc_end = int(v.source_mapping['start']) + int(v.source_mapping['length']) modify_loc_end = int(v.source_mapping['start']) + int(v.source_mapping['length'])
old_str_of_interest = in_file_str[modify_loc_start:modify_loc_end] old_str_of_interest = in_file_str[modify_loc_start:modify_loc_end]
# To-do: Change format logic below - how do we convert a name to mixedCase?
if(name[0] == '_'): if(name[0] == '_'):
# If parameter name begins with underscore, capitalize the letter after underscore
(new_str_of_interest, num_repl) = re.subn(r'(.*)'+name+r'(.*)', (new_str_of_interest, num_repl) = re.subn(r'(.*)'+name+r'(.*)',
r'\1'+name[0]+name[1].upper()+name[2:] + r'\1'+name[0]+name[1].upper()+name[2:] +
r'\2', old_str_of_interest.decode('utf-8'), r'\2', old_str_of_interest.decode('utf-8'),
1) 1)
else: else:
# Add underscore and capitalize the first letter
(new_str_of_interest, num_repl) = re.subn(r'(.*)'+name+r'(.*)', r'\1'+'_' + (new_str_of_interest, num_repl) = re.subn(r'(.*)'+name+r'(.*)', r'\1'+'_' +
name[0].upper()+name[1:]+r'\2', name.capitalize()+r'\2',
old_str_of_interest.decode('utf-8'), 1) old_str_of_interest.decode('utf-8'), 1)
if num_repl != 0: if num_repl != 0:
create_patch(patches, create_patch(patches,
@ -491,21 +528,26 @@ def _create_patch_parameter_uses(format_info):
old_str_of_interest = in_file_str[modifier.source_mapping['start']: old_str_of_interest = in_file_str[modifier.source_mapping['start']:
modifier.source_mapping['start'] + modifier.source_mapping['start'] +
modifier.source_mapping['length']] modifier.source_mapping['length']]
# Get text beyond modifier name which contains parameters
old_str_of_interest_beyond_modifier_name = old_str_of_interest.decode('utf-8')\ old_str_of_interest_beyond_modifier_name = old_str_of_interest.decode('utf-8')\
.split('(')[1] .split('(')[1]
# To-do: Change format logic below - how do we convert a name to mixedCase?
if(name[0] == '_'): if(name[0] == '_'):
# If parameter name begins with underscore, capitalize the letter after underscore
(new_str_of_interest, num_repl) = re.subn(r'(.*)'+name+r'(.*)', r'\1'+name[0]+ (new_str_of_interest, num_repl) = re.subn(r'(.*)'+name+r'(.*)', r'\1'+name[0]+
name[1].upper()+name[2:]+r'\2', name[1].upper()+name[2:]+r'\2',
old_str_of_interest_beyond_modifier_name, 1) old_str_of_interest_beyond_modifier_name, 1)
else: else:
# Add underscore and capitalize the first letter
(new_str_of_interest, num_repl) = re.subn(r'(.*)'+name+r'(.*)', r'\1'+'_'+ (new_str_of_interest, num_repl) = re.subn(r'(.*)'+name+r'(.*)', r'\1'+'_'+
name[0].upper()+name[1:]+r'\2', name.capitalize()+r'\2',
old_str_of_interest_beyond_modifier_name, 1) old_str_of_interest_beyond_modifier_name, 1)
if num_repl != 0: if num_repl != 0:
create_patch(patches, create_patch(patches,
"naming-convention (parameter uses)", "naming-convention (parameter uses)",
in_file_relative, in_file_relative,
in_file, in_file,
# Start beyond modifier name which contains parameters
modifier.source_mapping['start'] + modifier.source_mapping['start'] +
len(old_str_of_interest.decode('utf-8').split('(')[0]) + 1, len(old_str_of_interest.decode('utf-8').split('(')[0]) + 1,
modifier.source_mapping['start'] + modifier.source_mapping['length'], modifier.source_mapping['start'] + modifier.source_mapping['length'],
@ -533,8 +575,13 @@ def _create_patch_state_variable_declaration(format_info):
in_file_str = slither.source_code[in_file].encode('utf-8') in_file_str = slither.source_code[in_file].encode('utf-8')
old_str_of_interest = in_file_str[modify_loc_start:modify_loc_end] old_str_of_interest = in_file_str[modify_loc_start:modify_loc_end]
# Search for the state variable name and avoid the type
m = re.search(name, old_str_of_interest.decode('utf-8')) m = re.search(name, old_str_of_interest.decode('utf-8'))
# Skip rare cases where re search fails. To-do: Investigate
if not m:
return
if (_target == "variable_constant"): if (_target == "variable_constant"):
# Convert constant state variables to upper case
new_string = old_str_of_interest.decode('utf-8')[m.span()[0]:m.span()[1]].upper() new_string = old_str_of_interest.decode('utf-8')[m.span()[0]:m.span()[1]].upper()
else: else:
new_string = old_str_of_interest.decode('utf-8')[m.span()[0]:m.span()[1]] new_string = old_str_of_interest.decode('utf-8')[m.span()[0]:m.span()[1]]
@ -544,6 +591,7 @@ def _create_patch_state_variable_declaration(format_info):
"naming-convention (state variable declaration)", "naming-convention (state variable declaration)",
in_file_relative, in_file_relative,
in_file, in_file,
# Target only the state variable name and avoid the type
modify_loc_start+m.span()[0], modify_loc_start+m.span()[0],
modify_loc_start+m.span()[1], modify_loc_start+m.span()[1],
old_str_of_interest.decode('utf-8')[m.span()[0]:m.span()[1]], old_str_of_interest.decode('utf-8')[m.span()[0]:m.span()[1]],
@ -576,6 +624,7 @@ def _create_patch_state_variable_uses(format_info):
in_file_str = slither.source_code[in_file].encode('utf-8') in_file_str = slither.source_code[in_file].encode('utf-8')
old_str_of_interest = in_file_str[modify_loc_start:modify_loc_end] old_str_of_interest = in_file_str[modify_loc_start:modify_loc_end]
if (_target == "variable_constant"): if (_target == "variable_constant"):
# Convert constant state variables to upper case
new_str_of_interest = old_str_of_interest.decode('utf-8').upper() new_str_of_interest = old_str_of_interest.decode('utf-8').upper()
else: else:
new_str_of_interest = old_str_of_interest.decode('utf-8') new_str_of_interest = old_str_of_interest.decode('utf-8')
@ -609,6 +658,8 @@ def _create_patch_enum_definition(format_info):
in_file_str = slither.source_code[in_file].encode('utf-8') in_file_str = slither.source_code[in_file].encode('utf-8')
old_str_of_interest = in_file_str[modify_loc_start:modify_loc_end] old_str_of_interest = in_file_str[modify_loc_start:modify_loc_end]
# Search for the enum name after the `enum` keyword
# Capitalize enum name
(new_str_of_interest, num_repl) = re.subn(r'(.*)'+"enum"+r'(.*)'+name, r'\1'+"enum"+r'\2'+ (new_str_of_interest, num_repl) = re.subn(r'(.*)'+"enum"+r'(.*)'+name, r'\1'+"enum"+r'\2'+
name[0].capitalize()+name[1:], name[0].capitalize()+name[1:],
old_str_of_interest.decode('utf-8'), 1) old_str_of_interest.decode('utf-8'), 1)
@ -682,23 +733,29 @@ def _create_patch_enum_uses(format_info):
for ir in node.irs: for ir in node.irs:
if isinstance(ir, Member): if isinstance(ir, Member):
if str(ir.variable_left) == name: if str(ir.variable_left) == name:
# Skip past the assignment
old_str_of_interest = in_file_str[node.source_mapping['start']: old_str_of_interest = in_file_str[node.source_mapping['start']:
(node.source_mapping['start']+ (node.source_mapping['start']+
node.source_mapping['length'])].decode('utf-8')\ node.source_mapping['length'])].decode('utf-8')\
.split('=')[1] .split('=')[1]
m = re.search(r'(.*)'+name, old_str_of_interest) m = re.search(r'(.*)'+name, old_str_of_interest)
# Skip rare cases where re search fails. To-do: Investigate
if not m:
continue
old_str_of_interest = old_str_of_interest[m.span()[0]:] old_str_of_interest = old_str_of_interest[m.span()[0]:]
(new_str_of_interest, num_repl) = re.subn(r'(.*)'+name, r'\1'+name[0].upper()+name[1:], (new_str_of_interest, num_repl) = re.subn(r'(.*)'+name, r'\1'+name.capitalize(),
old_str_of_interest, 1) old_str_of_interest, 1)
if num_repl != 0: if num_repl != 0:
# TODO (JF): review me # TODO (JF): review me
# Start past the assignment
loc_start = node.source_mapping['start'] + \ loc_start = node.source_mapping['start'] + \
len(in_file_str[node.source_mapping['start']: len(in_file_str[node.source_mapping['start']:
(node.source_mapping['start']+ (node.source_mapping['start']+
node.source_mapping['length'])].decode('utf-8').split('=')[0]) + \ node.source_mapping['length'])].decode('utf-8').split('=')[0]) + \
1 + m.span()[0] 1 + m.span()[0]
# End accounts for the assignment from the start
loc_end = node.source_mapping['start'] +\ loc_end = node.source_mapping['start'] +\
len(in_file_str[node.source_mapping['start']:(node.source_mapping['start']+ len(in_file_str[node.source_mapping['start']:(node.source_mapping['start']+
node.source_mapping['length'])].\ node.source_mapping['length'])].\
@ -736,8 +793,9 @@ def _create_patch_struct_definition(format_info):
in_file_str = slither.source_code[in_file].encode('utf-8') in_file_str = slither.source_code[in_file].encode('utf-8')
old_str_of_interest = in_file_str[modify_loc_start:modify_loc_end] old_str_of_interest = in_file_str[modify_loc_start:modify_loc_end]
# Capitalize the struct name beyond the keyword `struct`
(new_str_of_interest, num_repl) = re.subn(r'(.*)'+"struct"+r'(.*)'+name, r'\1'+"struct"+r'\2'+ (new_str_of_interest, num_repl) = re.subn(r'(.*)'+"struct"+r'(.*)'+name, r'\1'+"struct"+r'\2'+
name[0].capitalize()+name[1:], name.capitalize(),
old_str_of_interest.decode('utf-8'), 1) old_str_of_interest.decode('utf-8'), 1)
if num_repl != 0: if num_repl != 0:
create_patch(patches, create_patch(patches,

@ -51,8 +51,10 @@ def _determine_solc_version_replacement(used_solc_version):
version_right = versions[1] version_right = versions[1]
minor_version_right = '.'.join(version_right[2:])[2] minor_version_right = '.'.join(version_right[2:])[2]
if minor_version_right == '4': if minor_version_right == '4':
# Replace with 0.4.25
return "pragma solidity " + REPLACEMENT_VERSIONS[0] + ';' return "pragma solidity " + REPLACEMENT_VERSIONS[0] + ';'
elif minor_version_right in ['5', '6']: elif minor_version_right in ['5', '6']:
# Replace with 0.5.3
return "pragma solidity " + REPLACEMENT_VERSIONS[1] + ';' return "pragma solidity " + REPLACEMENT_VERSIONS[1] + ';'

@ -29,8 +29,10 @@ def _determine_solc_version_replacement(used_solc_version):
version = versions[0] version = versions[0]
minor_version = '.'.join(version[2:])[2] minor_version = '.'.join(version[2:])[2]
if minor_version == '4': if minor_version == '4':
# Replace with 0.4.25
return "pragma solidity " + REPLACEMENT_VERSIONS[0] + ';' return "pragma solidity " + REPLACEMENT_VERSIONS[0] + ';'
elif minor_version == '5': elif minor_version == '5':
# Replace with 0.5.3
return "pragma solidity " + REPLACEMENT_VERSIONS[1] + ';' return "pragma solidity " + REPLACEMENT_VERSIONS[1] + ';'
else: else:
raise SlitherException("Unknown version!") raise SlitherException("Unknown version!")
@ -38,8 +40,10 @@ def _determine_solc_version_replacement(used_solc_version):
version_right = versions[1] version_right = versions[1]
minor_version_right = '.'.join(version_right[2:])[2] minor_version_right = '.'.join(version_right[2:])[2]
if minor_version_right == '4': if minor_version_right == '4':
# Replace with 0.4.25
return "pragma solidity " + REPLACEMENT_VERSIONS[0] + ';' return "pragma solidity " + REPLACEMENT_VERSIONS[0] + ';'
elif minor_version_right in ['5','6']: elif minor_version_right in ['5','6']:
# Replace with 0.5.3
return "pragma solidity " + REPLACEMENT_VERSIONS[1] + ';' return "pragma solidity " + REPLACEMENT_VERSIONS[1] + ';'

@ -21,6 +21,7 @@ def _patch(slither, patches, in_file, in_file_relative, modify_loc_start):
in_file_relative, in_file_relative,
in_file, in_file,
int(modify_loc_start), int(modify_loc_start),
# Remove the entire declaration until the semicolon
int(modify_loc_start + len(old_str_of_interest.decode('utf-8').partition(';')[0]) + 1), int(modify_loc_start + len(old_str_of_interest.decode('utf-8').partition(';')[0]) + 1),
old_str, old_str,
"") "")

@ -35,13 +35,21 @@ def slither_format(args, slither):
detector_results = [item for sublist in detector_results for item in sublist] # flatten detector_results = [item for sublist in detector_results for item in sublist] # flatten
results.extend(detector_results) results.extend(detector_results)
number_of_slither_results = get_number_of_slither_results(detector_results) number_of_slither_results = get_number_of_slither_results(detector_results)
# Apply slither detector results on contract files to generate patches
apply_detector_results(slither, patches, detector_results) apply_detector_results(slither, patches, detector_results)
# Sort the patches in ascending order of the source mapping i.e. from beginning of contract file to end.
# Multiple detectors can produce alerts on same code fragments e.g. unused-state and constable-states.
# The current approach makes a single pass on the contract file to apply patches.
# Therefore, overlapping patches are ignored for now. Neither is applied.
# To-do: Prioritise one detector over another (via user input or hardcoded) for overlapping patches.
sort_and_flag_overlapping_patches(patches) sort_and_flag_overlapping_patches(patches)
# Remove overlapping patches
prune_overlapping_patches(args, patches) prune_overlapping_patches(args, patches)
if args.verbose_json: if args.verbose_json:
print_patches_json(number_of_slither_results, patches) print_patches_json(number_of_slither_results, patches)
if args.verbose_test: if args.verbose_test:
print_patches(number_of_slither_results, patches) print_patches(number_of_slither_results, patches)
# Generate git-compatible patch files
generate_patch_files(slither, patches) generate_patch_files(slither, patches)
def sort_and_flag_overlapping_patches(patches): def sort_and_flag_overlapping_patches(patches):

Loading…
Cancel
Save