mirror of https://github.com/crytic/slither
commit
1029b5233f
@ -0,0 +1,24 @@ |
||||
# Contributing to Slither |
||||
First, thanks for your interest in contributing to Slither! We welcome and appreciate all contributions, including bug reports, feature suggestions, tutorials/blog posts, and code improvements. |
||||
|
||||
If you're unsure where to start, we recommend our [`good first issue`](https://github.com/crytic/slither/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22) and [`help wanted`](https://github.com/crytic/slither/issues?q=is%3Aissue+is%3Aopen+label%3A%22help+wanted%22) issue labels. |
||||
|
||||
# Bug reports and feature suggestions |
||||
Bug reports and feature suggestions can be submitted to our issue tracker. For bug reports, attaching the contract that caused the bug will help us in debugging and resolving the issue quickly. If you find a security vulnerability, do not open an issue; email opensource@trailofbits.com instead. |
||||
|
||||
# Questions |
||||
Questions can be submitted to the issue tracker, but you may get a faster response if you ask in our [chat room](https://empireslacking.herokuapp.com/) (in the #ethereum channel). |
||||
|
||||
# Code |
||||
Slither uses the pull request contribution model. Please make an account on Github, fork this repo, and submit code contributions via pull request. For more documentation, look [here](https://guides.github.com/activities/forking/). |
||||
|
||||
Some pull request guidelines: |
||||
|
||||
- Work from the [`dev`](https://github.com/crytic/slither/tree/dev) branch. We performed extensive tests prior to merging anything to `master`, working from `dev` will allow us to merge your work faster. |
||||
- Minimize irrelevant changes (formatting, whitespace, etc) to code that would otherwise not be touched by this patch. Save formatting or style corrections for a separate pull request that does not make any semantic changes. |
||||
- When possible, large changes should be split up into smaller focused pull requests. |
||||
- Fill out the pull request description with a summary of what your patch does, key changes that have been made, and any further points of discussion, if applicable. |
||||
- Title your pull request with a brief description of what it's changing. "Fixes #123" is a good comment to add to the description, but makes for an unclear title on its own. |
||||
|
||||
# Development Environment |
||||
Instructions for installing a development version of Slither can be found in our [wiki](https://github.com/crytic/slither/wiki/Developer-installation). |
@ -0,0 +1,26 @@ |
||||
pragma solidity >=0.4.22 <0.6.0; |
||||
contract test{ |
||||
uint a; |
||||
constructor()public{ |
||||
a =5; |
||||
} |
||||
|
||||
} |
||||
contract test2 is test{ |
||||
constructor()public{ |
||||
a=10; |
||||
} |
||||
} |
||||
contract test3 is test2{ |
||||
address owner; |
||||
bytes32 name; |
||||
constructor(bytes32 _name)public{ |
||||
owner = msg.sender; |
||||
name = _name; |
||||
a=20; |
||||
} |
||||
function print() public returns(uint b){ |
||||
b=a; |
||||
|
||||
} |
||||
} |
@ -0,0 +1,20 @@ |
||||
#!/usr/bin/env bash |
||||
|
||||
### Test slither-check-erc |
||||
|
||||
DIR_TESTS="tests/check-erc" |
||||
|
||||
slither-check-erc "$DIR_TESTS/erc20.sol" ERC20 --solc solc-0.5.0 > test_1.txt 2>&1 |
||||
DIFF=$(diff test_1.txt "$DIR_TESTS/test_1.txt") |
||||
if [ "$DIFF" != "" ] |
||||
then |
||||
echo "slither-check-erc 1 failed" |
||||
cat test_1.txt |
||||
echo "" |
||||
cat "$DIR_TESTS/test_1.txt" |
||||
exit -1 |
||||
fi |
||||
|
||||
|
||||
rm test_1.txt |
||||
|
@ -0,0 +1,16 @@ |
||||
#!/bin/bash |
||||
|
||||
DIR_TESTS="tests/check-kspec" |
||||
|
||||
slither-check-kspec "$DIR_TESTS/safeAdd/safeAdd.sol" "$DIR_TESTS/safeAdd/spec.md" --solc solc-0.5.0 > test_1.txt 2>&1 |
||||
DIFF=$(diff test_1.txt "$DIR_TESTS/test_1.txt") |
||||
if [ "$DIFF" != "" ] |
||||
then |
||||
echo "slither-check-upgradeability 1 failed" |
||||
cat test_1.txt |
||||
echo "" |
||||
cat "$DIR_TESTS/test_1.txt" |
||||
exit -1 |
||||
fi |
||||
|
||||
rm test_1.txt |
@ -0,0 +1,12 @@ |
||||
|
||||
class ChildExpression: |
||||
def __init__(self): |
||||
super(ChildExpression, self).__init__() |
||||
self._expression = None |
||||
|
||||
def set_expression(self, expression): |
||||
self._expression = expression |
||||
|
||||
@property |
||||
def expression(self): |
||||
return self._expression |
@ -0,0 +1,45 @@ |
||||
|
||||
from slither.detectors.abstract_detector import AbstractDetector, DetectorClassification |
||||
from slither.slithir.operations import Nop |
||||
|
||||
|
||||
class VoidConstructor(AbstractDetector): |
||||
|
||||
ARGUMENT = 'void-cst' |
||||
HELP = 'Constructor called not implemented' |
||||
IMPACT = DetectorClassification.LOW |
||||
CONFIDENCE = DetectorClassification.HIGH |
||||
|
||||
WIKI = 'https://github.com/crytic/slither/wiki/Detector-Documentation#void-constructor' |
||||
|
||||
WIKI_TITLE = 'Void Constructor' |
||||
WIKI_DESCRIPTION = 'Detect the call to a constructor not implemented' |
||||
WIKI_RECOMMENDATION = 'Remove the constructor call.' |
||||
WIKI_EXPLOIT_SCENARIO = ''' |
||||
```solidity |
||||
contract A{} |
||||
contract B is A{ |
||||
constructor() public A(){} |
||||
} |
||||
``` |
||||
By reading B's constructor definition, the reader might assume that `A()` initiate the contract, while no code is executed.''' |
||||
|
||||
|
||||
def _detect(self): |
||||
""" |
||||
""" |
||||
results = [] |
||||
for c in self.contracts: |
||||
cst = c.constructor |
||||
if cst: |
||||
|
||||
for constructor_call in cst.explicit_base_constructor_calls_statements: |
||||
for node in constructor_call.nodes: |
||||
if any(isinstance(ir, Nop) for ir in node.irs): |
||||
info = ["Void constructor called in ", cst, ":\n"] |
||||
info += ["\t- ", node, "\n"] |
||||
|
||||
res = self.generate_result(info) |
||||
|
||||
results.append(res) |
||||
return results |
@ -0,0 +1,36 @@ |
||||
import re |
||||
from slither.formatters.exceptions import FormatError |
||||
from slither.formatters.utils.patches import create_patch |
||||
|
||||
def format(slither, result): |
||||
elements = result['elements'] |
||||
for element in elements: |
||||
if element['type'] != "function": |
||||
# Skip variable elements |
||||
continue |
||||
target_contract = slither.get_contract_from_name(element['type_specific_fields']['parent']['name']) |
||||
if target_contract: |
||||
function = target_contract.get_function_from_signature(element['type_specific_fields']['signature']) |
||||
if function: |
||||
_patch(slither, |
||||
result, |
||||
element['source_mapping']['filename_absolute'], |
||||
int(function.parameters_src.source_mapping['start'] + |
||||
function.parameters_src.source_mapping['length']), |
||||
int(function.returns_src.source_mapping['start'])) |
||||
|
||||
|
||||
def _patch(slither, result, in_file, modify_loc_start, modify_loc_end): |
||||
in_file_str = slither.source_code[in_file].encode('utf8') |
||||
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')) |
||||
if m: |
||||
create_patch(result, |
||||
in_file, |
||||
modify_loc_start + m.span()[0], |
||||
modify_loc_start + m.span()[1], |
||||
m.groups(0)[0], # this is view|pure|constant |
||||
"") |
||||
else: |
||||
raise FormatError("No view/pure/constant specifier exists. Regex failed to remove specifier!") |
@ -0,0 +1,69 @@ |
||||
import re |
||||
from slither.formatters.exceptions import FormatImpossible |
||||
from slither.formatters.utils.patches import create_patch |
||||
|
||||
# Indicates the recommended versions for replacement |
||||
REPLACEMENT_VERSIONS = ["^0.4.25", "^0.5.3"] |
||||
|
||||
# group: |
||||
# 0: ^ > >= < <= (optional) |
||||
# 1: ' ' (optional) |
||||
# 2: version number |
||||
# 3: version number |
||||
# 4: version number |
||||
PATTERN = re.compile('(\^|>|>=|<|<=)?([ ]+)?(\d+)\.(\d+)\.(\d+)') |
||||
|
||||
|
||||
def format(slither, result): |
||||
elements = result['elements'] |
||||
versions_used = [] |
||||
for element in elements: |
||||
versions_used.append(''.join(element['type_specific_fields']['directive'][1:])) |
||||
solc_version_replace = _analyse_versions(versions_used) |
||||
for element in elements: |
||||
_patch(slither, result, element['source_mapping']['filename_absolute'], solc_version_replace, |
||||
element['source_mapping']['start'], |
||||
element['source_mapping']['start'] + element['source_mapping']['length']) |
||||
|
||||
|
||||
def _analyse_versions(used_solc_versions): |
||||
replace_solc_versions = list() |
||||
for version in used_solc_versions: |
||||
replace_solc_versions.append(_determine_solc_version_replacement(version)) |
||||
if not all(version == replace_solc_versions[0] for version in replace_solc_versions): |
||||
raise FormatImpossible("Multiple incompatible versions!") |
||||
else: |
||||
return replace_solc_versions[0] |
||||
|
||||
|
||||
def _determine_solc_version_replacement(used_solc_version): |
||||
versions = PATTERN.findall(used_solc_version) |
||||
if len(versions) == 1: |
||||
version = versions[0] |
||||
minor_version = '.'.join(version[2:])[2] |
||||
if minor_version == '4': |
||||
return "pragma solidity " + REPLACEMENT_VERSIONS[0] + ';' |
||||
elif minor_version == '5': |
||||
return "pragma solidity " + REPLACEMENT_VERSIONS[1] + ';' |
||||
else: |
||||
raise FormatImpossible("Unknown version!") |
||||
elif len(versions) == 2: |
||||
version_right = versions[1] |
||||
minor_version_right = '.'.join(version_right[2:])[2] |
||||
if minor_version_right == '4': |
||||
# Replace with 0.4.25 |
||||
return "pragma solidity " + REPLACEMENT_VERSIONS[0] + ';' |
||||
elif minor_version_right in ['5', '6']: |
||||
# Replace with 0.5.3 |
||||
return "pragma solidity " + REPLACEMENT_VERSIONS[1] + ';' |
||||
|
||||
|
||||
def _patch(slither, result, in_file, pragma, modify_loc_start, modify_loc_end): |
||||
in_file_str = slither.source_code[in_file].encode('utf8') |
||||
old_str_of_interest = in_file_str[modify_loc_start:modify_loc_end] |
||||
create_patch(result, |
||||
in_file, |
||||
int(modify_loc_start), |
||||
int(modify_loc_end), |
||||
old_str_of_interest, |
||||
pragma) |
@ -0,0 +1,59 @@ |
||||
import re |
||||
from slither.formatters.exceptions import FormatImpossible |
||||
from slither.formatters.utils.patches import create_patch |
||||
|
||||
|
||||
# Indicates the recommended versions for replacement |
||||
REPLACEMENT_VERSIONS = ["^0.4.25", "^0.5.3"] |
||||
|
||||
# group: |
||||
# 0: ^ > >= < <= (optional) |
||||
# 1: ' ' (optional) |
||||
# 2: version number |
||||
# 3: version number |
||||
# 4: version number |
||||
PATTERN = re.compile('(\^|>|>=|<|<=)?([ ]+)?(\d+)\.(\d+)\.(\d+)') |
||||
|
||||
def format(slither, result): |
||||
elements = result['elements'] |
||||
for element in elements: |
||||
solc_version_replace = _determine_solc_version_replacement( |
||||
''.join(element['type_specific_fields']['directive'][1:])) |
||||
|
||||
_patch(slither, result, element['source_mapping']['filename_absolute'], solc_version_replace, |
||||
element['source_mapping']['start'], element['source_mapping']['start'] + |
||||
element['source_mapping']['length']) |
||||
|
||||
def _determine_solc_version_replacement(used_solc_version): |
||||
versions = PATTERN.findall(used_solc_version) |
||||
if len(versions) == 1: |
||||
version = versions[0] |
||||
minor_version = '.'.join(version[2:])[2] |
||||
if minor_version == '4': |
||||
# Replace with 0.4.25 |
||||
return "pragma solidity " + REPLACEMENT_VERSIONS[0] + ';' |
||||
elif minor_version == '5': |
||||
# Replace with 0.5.3 |
||||
return "pragma solidity " + REPLACEMENT_VERSIONS[1] + ';' |
||||
else: |
||||
raise FormatImpossible(f"Unknown version {versions}") |
||||
elif len(versions) == 2: |
||||
version_right = versions[1] |
||||
minor_version_right = '.'.join(version_right[2:])[2] |
||||
if minor_version_right == '4': |
||||
# Replace with 0.4.25 |
||||
return "pragma solidity " + REPLACEMENT_VERSIONS[0] + ';' |
||||
elif minor_version_right in ['5','6']: |
||||
# Replace with 0.5.3 |
||||
return "pragma solidity " + REPLACEMENT_VERSIONS[1] + ';' |
||||
|
||||
|
||||
def _patch(slither, result, in_file, solc_version, modify_loc_start, modify_loc_end): |
||||
in_file_str = slither.source_code[in_file].encode('utf8') |
||||
old_str_of_interest = in_file_str[modify_loc_start:modify_loc_end] |
||||
create_patch(result, |
||||
in_file, |
||||
int(modify_loc_start), |
||||
int(modify_loc_end), |
||||
old_str_of_interest, |
||||
solc_version) |
@ -0,0 +1,5 @@ |
||||
from slither.exceptions import SlitherException |
||||
|
||||
class FormatImpossible(SlitherException): pass |
||||
|
||||
class FormatError(SlitherException): pass |
@ -0,0 +1,42 @@ |
||||
import re |
||||
from slither.formatters.utils.patches import create_patch |
||||
|
||||
def format(slither, result): |
||||
elements = result['elements'] |
||||
for element in elements: |
||||
target_contract = slither.get_contract_from_name(element['type_specific_fields']['parent']['name']) |
||||
if target_contract: |
||||
function = target_contract.get_function_from_signature(element['type_specific_fields']['signature']) |
||||
if function: |
||||
_patch(slither, |
||||
result, |
||||
element['source_mapping']['filename_absolute'], |
||||
int(function.parameters_src.source_mapping['start']), |
||||
int(function.returns_src.source_mapping['start'])) |
||||
|
||||
|
||||
def _patch(slither, result, in_file, modify_loc_start, modify_loc_end): |
||||
in_file_str = slither.source_code[in_file].encode('utf8') |
||||
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')) |
||||
if m is None: |
||||
# No visibility specifier exists; public by default. |
||||
create_patch(result, |
||||
in_file, |
||||
# start after the function definition's closing paranthesis |
||||
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, |
||||
"", |
||||
" external") # replace_text is `external` |
||||
else: |
||||
create_patch(result, |
||||
in_file, |
||||
# start at the keyword `public` |
||||
modify_loc_start + m.span()[0] + 1, |
||||
# end after the keyword `public` = start + len('public'') |
||||
modify_loc_start + m.span()[0] + 1 + len('public'), |
||||
"public", |
||||
"external") |
@ -0,0 +1,609 @@ |
||||
import re |
||||
import logging |
||||
from slither.slithir.operations import Send, Transfer, OperationWithLValue, HighLevelCall, LowLevelCall, \ |
||||
InternalCall, InternalDynamicCall |
||||
from slither.core.declarations import Modifier |
||||
from slither.core.solidity_types import UserDefinedType, MappingType |
||||
from slither.core.declarations import Enum, Contract, Structure, Function |
||||
from slither.core.solidity_types.elementary_type import ElementaryTypeName |
||||
from slither.core.variables.local_variable import LocalVariable |
||||
from slither.formatters.exceptions import FormatError, FormatImpossible |
||||
from slither.formatters.utils.patches import create_patch |
||||
|
||||
logging.basicConfig(level=logging.INFO) |
||||
logger = logging.getLogger('Slither.Format') |
||||
|
||||
def format(slither, result): |
||||
elements = result['elements'] |
||||
for element in elements: |
||||
target = element['additional_fields']['target'] |
||||
|
||||
convention = element['additional_fields']['convention'] |
||||
|
||||
if convention == "l_O_I_should_not_be_used": |
||||
# l_O_I_should_not_be_used cannot be automatically patched |
||||
logger.info(f'The following naming convention cannot be patched: \n{result["description"]}') |
||||
continue |
||||
|
||||
_patch(slither, result, element, target) |
||||
|
||||
# endregion |
||||
################################################################################### |
||||
################################################################################### |
||||
# region Conventions |
||||
################################################################################### |
||||
################################################################################### |
||||
|
||||
KEY = 'ALL_NAMES_USED' |
||||
|
||||
# https://solidity.readthedocs.io/en/v0.5.11/miscellaneous.html#reserved-keywords |
||||
SOLIDITY_KEYWORDS = ['abstract', 'after', 'alias', 'apply', 'auto', 'case', 'catch', 'copyof', 'default', 'define', |
||||
'final', 'immutable', 'implements', 'in', 'inline', 'let', 'macro', 'match', 'mutable', 'null', |
||||
'of', 'override', 'partial', 'promise', 'reference', 'relocatable', 'sealed', 'sizeof', 'static', |
||||
'supports', 'switch', 'try', 'typedef', 'typeof', 'unchecked'] |
||||
|
||||
# https://solidity.readthedocs.io/en/v0.5.11/miscellaneous.html#language-grammar |
||||
SOLIDITY_KEYWORDS += ['pragma', 'import', 'contract', 'library', 'contract', 'function', 'using', 'struct', 'enum', |
||||
'public', 'private', 'internal', 'external', 'calldata', 'memory', 'modifier', 'view', 'pure', |
||||
'constant', 'storage', 'for', 'if', 'while', 'break', 'return', 'throw', 'else', 'type'] |
||||
|
||||
SOLIDITY_KEYWORDS += ElementaryTypeName |
||||
|
||||
def _name_already_use(slither, name): |
||||
# Do not convert to a name used somewhere else |
||||
if not KEY in slither.context: |
||||
all_names = set() |
||||
for contract in slither.contracts_derived: |
||||
all_names = all_names.union(set([st.name for st in contract.structures])) |
||||
all_names = all_names.union(set([f.name for f in contract.functions_and_modifiers])) |
||||
all_names = all_names.union(set([e.name for e in contract.enums])) |
||||
all_names = all_names.union(set([s.name for s in contract.state_variables])) |
||||
|
||||
for function in contract.functions: |
||||
all_names = all_names.union(set([v.name for v in function.variables])) |
||||
|
||||
slither.context[KEY] = all_names |
||||
return name in slither.context[KEY] |
||||
|
||||
def _convert_CapWords(original_name, slither): |
||||
name = original_name.capitalize() |
||||
|
||||
while '_' in name: |
||||
offset = name.find('_') |
||||
if len(name) > offset: |
||||
name = name[0:offset] + name[offset+1].upper() + name[offset+1:] |
||||
|
||||
if _name_already_use(slither, name): |
||||
raise FormatImpossible(f'{original_name} cannot be converted to {name} (already used)') |
||||
|
||||
if name in SOLIDITY_KEYWORDS: |
||||
raise FormatImpossible(f'{original_name} cannot be converted to {name} (Solidity keyword)') |
||||
return name |
||||
|
||||
def _convert_mixedCase(original_name, slither): |
||||
name = original_name |
||||
if isinstance(name, bytes): |
||||
name = name.decode('utf8') |
||||
|
||||
while '_' in name: |
||||
offset = name.find('_') |
||||
if len(name) > offset: |
||||
name = name[0:offset] + name[offset + 1].upper() + name[offset + 2:] |
||||
|
||||
name = name[0].lower() + name[1:] |
||||
if _name_already_use(slither, name): |
||||
raise FormatImpossible(f'{original_name} cannot be converted to {name} (already used)') |
||||
if name in SOLIDITY_KEYWORDS: |
||||
raise FormatImpossible(f'{original_name} cannot be converted to {name} (Solidity keyword)') |
||||
return name |
||||
|
||||
def _convert_UPPER_CASE_WITH_UNDERSCORES(name, slither): |
||||
if _name_already_use(slither, name.upper()): |
||||
raise FormatImpossible(f'{name} cannot be converted to {name.upper()} (already used)') |
||||
if name.upper() in SOLIDITY_KEYWORDS: |
||||
raise FormatImpossible(f'{name} cannot be converted to {name.upper()} (Solidity keyword)') |
||||
return name.upper() |
||||
|
||||
conventions ={ |
||||
"CapWords":_convert_CapWords, |
||||
"mixedCase":_convert_mixedCase, |
||||
"UPPER_CASE_WITH_UNDERSCORES":_convert_UPPER_CASE_WITH_UNDERSCORES |
||||
} |
||||
|
||||
|
||||
# endregion |
||||
################################################################################### |
||||
################################################################################### |
||||
# region Helpers |
||||
################################################################################### |
||||
################################################################################### |
||||
|
||||
def _get_from_contract(slither, element, name, getter): |
||||
contract_name = element['type_specific_fields']['parent']['name'] |
||||
contract = slither.get_contract_from_name(contract_name) |
||||
return getattr(contract, getter)(name) |
||||
|
||||
# endregion |
||||
################################################################################### |
||||
################################################################################### |
||||
# region Patch dispatcher |
||||
################################################################################### |
||||
################################################################################### |
||||
|
||||
def _patch(slither, result, element, _target): |
||||
|
||||
if _target == "contract": |
||||
target = slither.get_contract_from_name(element['name']) |
||||
|
||||
elif _target == "structure": |
||||
target = _get_from_contract(slither, element, element['name'], 'get_structure_from_name') |
||||
|
||||
elif _target == "event": |
||||
target = _get_from_contract(slither, element, element['name'], 'get_event_from_name') |
||||
|
||||
elif _target == "function": |
||||
# Avoid constructor (FP?) |
||||
if element['name'] != element['type_specific_fields']['parent']['name']: |
||||
function_sig = element['type_specific_fields']['signature'] |
||||
target = _get_from_contract(slither, element, function_sig, 'get_function_from_signature') |
||||
|
||||
elif _target == "modifier": |
||||
modifier_sig = element['type_specific_fields']['signature'] |
||||
target = _get_from_contract(slither, element, modifier_sig, 'get_modifier_from_signature') |
||||
|
||||
elif _target == "parameter": |
||||
contract_name = element['type_specific_fields']['parent']['type_specific_fields']['parent']['name'] |
||||
function_sig = element['type_specific_fields']['parent']['type_specific_fields']['signature'] |
||||
param_name = element['name'] |
||||
contract = slither.get_contract_from_name(contract_name) |
||||
function = contract.get_function_from_signature(function_sig) |
||||
target = function.get_local_variable_from_name(param_name) |
||||
|
||||
elif _target in ["variable", "variable_constant"]: |
||||
# Local variable |
||||
if element['type_specific_fields']['parent'] == 'function': |
||||
contract_name = element['type_specific_fields']['parent']['type_specific_fields']['parent']['name'] |
||||
function_sig = element['type_specific_fields']['parent']['type_specific_fields']['signature'] |
||||
var_name = element['name'] |
||||
contract = slither.get_contract_from_name(contract_name) |
||||
function = contract.get_function_from_signature(function_sig) |
||||
target = function.get_local_variable_from_name(var_name) |
||||
# State variable |
||||
else: |
||||
target = _get_from_contract(slither, element, element['name'], 'get_state_variable_from_name') |
||||
|
||||
elif _target == "enum": |
||||
target = _get_from_contract(slither, element, element['name'], 'get_enum_from_canonical_name') |
||||
|
||||
else: |
||||
raise FormatError("Unknown naming convention! " + _target) |
||||
|
||||
_explore(slither, |
||||
result, |
||||
target, |
||||
conventions[element['additional_fields']['convention']]) |
||||
|
||||
|
||||
# endregion |
||||
################################################################################### |
||||
################################################################################### |
||||
# region Explore functions |
||||
################################################################################### |
||||
################################################################################### |
||||
|
||||
# group 1: beginning of the from type |
||||
# group 2: beginning of the to type |
||||
# nested mapping are within the group 1 |
||||
#RE_MAPPING = '[ ]*mapping[ ]*\([ ]*([\=\>\(\) a-zA-Z0-9\._\[\]]*)[ ]*=>[ ]*([a-zA-Z0-9\._\[\]]*)\)' |
||||
RE_MAPPING_FROM = b'([a-zA-Z0-9\._\[\]]*)' |
||||
RE_MAPPING_TO = b'([\=\>\(\) a-zA-Z0-9\._\[\]\ ]*)' |
||||
RE_MAPPING = b'[ ]*mapping[ ]*\([ ]*' + RE_MAPPING_FROM + b'[ ]*' + b'=>' + b'[ ]*'+ RE_MAPPING_TO + b'\)' |
||||
|
||||
|
||||
def _is_var_declaration(slither, filename, start): |
||||
''' |
||||
Detect usage of 'var ' for Solidity < 0.5 |
||||
:param slither: |
||||
:param filename: |
||||
:param start: |
||||
:return: |
||||
''' |
||||
v = 'var ' |
||||
return slither.source_code[filename][start:start + len(v)] == v |
||||
|
||||
|
||||
def _explore_type(slither, result, target, convert, type, filename_source_code, start, end): |
||||
if isinstance(type, UserDefinedType): |
||||
# Patch type based on contract/enum |
||||
if isinstance(type.type, (Enum, Contract)): |
||||
if type.type == target: |
||||
old_str = type.type.name |
||||
new_str = convert(old_str, slither) |
||||
|
||||
loc_start = start |
||||
if _is_var_declaration(slither, filename_source_code, start): |
||||
loc_end = loc_start + len('var') |
||||
else: |
||||
loc_end = loc_start + len(old_str) |
||||
|
||||
create_patch(result, |
||||
filename_source_code, |
||||
loc_start, |
||||
loc_end, |
||||
old_str, |
||||
new_str) |
||||
|
||||
|
||||
else: |
||||
# Patch type based on structure |
||||
assert isinstance(type.type, Structure) |
||||
if type.type == target: |
||||
old_str = type.type.name |
||||
new_str = convert(old_str, slither) |
||||
|
||||
loc_start = start |
||||
if _is_var_declaration(slither, filename_source_code, start): |
||||
loc_end = loc_start + len('var') |
||||
else: |
||||
loc_end = loc_start + len(old_str) |
||||
|
||||
create_patch(result, |
||||
filename_source_code, |
||||
loc_start, |
||||
loc_end, |
||||
old_str, |
||||
new_str) |
||||
|
||||
# Structure contain a list of elements, that might need patching |
||||
# .elems return a list of VariableStructure |
||||
_explore_variables_declaration(slither, |
||||
type.type.elems.values(), |
||||
result, |
||||
target, |
||||
convert) |
||||
|
||||
if isinstance(type, MappingType): |
||||
# Mapping has three steps: |
||||
# Convert the "from" type |
||||
# Convert the "to" type |
||||
# Convert nested type in the "to" |
||||
# Ex: mapping (mapping (badName => uint) => uint) |
||||
|
||||
# Do the comparison twice, so we can factor together the re matching |
||||
# mapping can only have elementary type in type_from |
||||
if isinstance(type.type_to, (UserDefinedType, MappingType)) or target in [type.type_from, type.type_to]: |
||||
|
||||
full_txt_start = start |
||||
full_txt_end = end |
||||
full_txt = slither.source_code[filename_source_code].encode('utf8')[full_txt_start:full_txt_end] |
||||
re_match = re.match(RE_MAPPING, full_txt) |
||||
assert re_match |
||||
|
||||
if type.type_from == target: |
||||
old_str = type.type_from.name |
||||
new_str = convert(old_str, slither) |
||||
|
||||
loc_start = start + re_match.start(1) |
||||
loc_end = loc_start + len(old_str) |
||||
|
||||
create_patch(result, |
||||
filename_source_code, |
||||
loc_start, |
||||
loc_end, |
||||
old_str, |
||||
new_str) |
||||
|
||||
if type.type_to == target: |
||||
|
||||
old_str = type.type_to.name |
||||
new_str = convert(old_str, slither) |
||||
|
||||
loc_start = start + re_match.start(2) |
||||
loc_end = loc_start + len(old_str) |
||||
|
||||
create_patch(result, |
||||
filename_source_code, |
||||
loc_start, |
||||
loc_end, |
||||
old_str, |
||||
new_str) |
||||
|
||||
if isinstance(type.type_to, (UserDefinedType, MappingType)): |
||||
loc_start = start + re_match.start(2) |
||||
loc_end = start + re_match.end(2) |
||||
_explore_type(slither, |
||||
result, |
||||
target, |
||||
convert, |
||||
type.type_to, |
||||
filename_source_code, |
||||
loc_start, |
||||
loc_end) |
||||
|
||||
|
||||
|
||||
def _explore_variables_declaration(slither, variables, result, target, convert, patch_comment=False): |
||||
for variable in variables: |
||||
# First explore the type of the variable |
||||
filename_source_code = variable.source_mapping['filename_absolute'] |
||||
full_txt_start = variable.source_mapping['start'] |
||||
full_txt_end = full_txt_start + variable.source_mapping['length'] |
||||
full_txt = slither.source_code[filename_source_code].encode('utf8')[full_txt_start:full_txt_end] |
||||
|
||||
_explore_type(slither, |
||||
result, |
||||
target, |
||||
convert, |
||||
variable.type, |
||||
filename_source_code, |
||||
full_txt_start, |
||||
variable.source_mapping['start'] + variable.source_mapping['length']) |
||||
|
||||
# If the variable is the target |
||||
if variable == target: |
||||
old_str = variable.name |
||||
new_str = convert(old_str, slither) |
||||
|
||||
loc_start = full_txt_start + full_txt.find(old_str.encode('utf8')) |
||||
loc_end = loc_start + len(old_str) |
||||
|
||||
create_patch(result, |
||||
filename_source_code, |
||||
loc_start, |
||||
loc_end, |
||||
old_str, |
||||
new_str) |
||||
|
||||
# Patch comment only makes sense for local variable declaration in the parameter list |
||||
if patch_comment and isinstance(variable, LocalVariable): |
||||
if 'lines' in variable.source_mapping and variable.source_mapping['lines']: |
||||
func = variable.function |
||||
end_line = func.source_mapping['lines'][0] |
||||
if variable in func.parameters: |
||||
idx = len(func.parameters) - func.parameters.index(variable) + 1 |
||||
first_line = end_line - idx - 2 |
||||
|
||||
potential_comments = slither.source_code[filename_source_code].encode('utf8') |
||||
potential_comments = potential_comments.splitlines(keepends=True)[first_line:end_line-1] |
||||
|
||||
idx_beginning = func.source_mapping['start'] |
||||
idx_beginning += - func.source_mapping['starting_column'] + 1 |
||||
idx_beginning += - sum([len(c) for c in potential_comments]) |
||||
|
||||
old_comment = f'@param {old_str}'.encode('utf8') |
||||
|
||||
for line in potential_comments: |
||||
idx = line.find(old_comment) |
||||
if idx >=0: |
||||
loc_start = idx + idx_beginning |
||||
loc_end = loc_start + len(old_comment) |
||||
new_comment = f'@param {new_str}'.encode('utf8') |
||||
|
||||
create_patch(result, |
||||
filename_source_code, |
||||
loc_start, |
||||
loc_end, |
||||
old_comment, |
||||
new_comment) |
||||
|
||||
break |
||||
idx_beginning += len(line) |
||||
|
||||
|
||||
|
||||
|
||||
def _explore_modifiers_calls(slither, function, result, target, convert): |
||||
for modifier in function.modifiers_statements: |
||||
for node in modifier.nodes: |
||||
if node.irs: |
||||
_explore_irs(slither, node.irs, result, target, convert) |
||||
for modifier in function.explicit_base_constructor_calls_statements: |
||||
for node in modifier.nodes: |
||||
if node.irs: |
||||
_explore_irs(slither, node.irs, result, target, convert) |
||||
|
||||
def _explore_structures_declaration(slither, structures, result, target, convert): |
||||
for st in structures: |
||||
# Explore the variable declared within the structure (VariableStructure) |
||||
_explore_variables_declaration(slither, st.elems.values(), result, target, convert) |
||||
|
||||
# If the structure is the target |
||||
if st == target: |
||||
old_str = st.name |
||||
new_str = convert(old_str, slither) |
||||
|
||||
filename_source_code = st.source_mapping['filename_absolute'] |
||||
full_txt_start = st.source_mapping['start'] |
||||
full_txt_end = full_txt_start + st.source_mapping['length'] |
||||
full_txt = slither.source_code[filename_source_code].encode('utf8')[full_txt_start:full_txt_end] |
||||
|
||||
# The name is after the space |
||||
matches = re.finditer(b'struct[ ]*', full_txt) |
||||
# Look for the end offset of the largest list of ' ' |
||||
loc_start = full_txt_start + max(matches, key=lambda x: len(x.group())).end() |
||||
loc_end = loc_start + len(old_str) |
||||
|
||||
create_patch(result, |
||||
filename_source_code, |
||||
loc_start, |
||||
loc_end, |
||||
old_str, |
||||
new_str) |
||||
|
||||
|
||||
def _explore_events_declaration(slither, events, result, target, convert): |
||||
for event in events: |
||||
# Explore the parameters |
||||
_explore_variables_declaration(slither, event.elems, result, target, convert) |
||||
|
||||
# If the event is the target |
||||
if event == target: |
||||
filename_source_code = event.source_mapping['filename_absolute'] |
||||
|
||||
old_str = event.name |
||||
new_str = convert(old_str, slither) |
||||
|
||||
loc_start = event.source_mapping['start'] |
||||
loc_end = loc_start + len(old_str) |
||||
|
||||
create_patch(result, |
||||
filename_source_code, |
||||
loc_start, |
||||
loc_end, |
||||
old_str, |
||||
new_str) |
||||
|
||||
def get_ir_variables(ir): |
||||
vars = ir.read |
||||
|
||||
if isinstance(ir, (InternalCall, InternalDynamicCall, HighLevelCall)): |
||||
vars += [ir.function] |
||||
|
||||
if isinstance(ir, (HighLevelCall, Send, LowLevelCall, Transfer)): |
||||
vars += [ir.call_value] |
||||
|
||||
if isinstance(ir, (HighLevelCall, LowLevelCall)): |
||||
vars += [ir.call_gas] |
||||
|
||||
if isinstance(ir, OperationWithLValue): |
||||
vars += [ir.lvalue] |
||||
|
||||
return [v for v in vars if v] |
||||
|
||||
def _explore_irs(slither, irs, result, target, convert): |
||||
if irs is None: |
||||
return |
||||
for ir in irs: |
||||
for v in get_ir_variables(ir): |
||||
if target == v or ( |
||||
isinstance(target, Function) and isinstance(v, Function) and |
||||
v.canonical_name == target.canonical_name): |
||||
source_mapping = ir.expression.source_mapping |
||||
filename_source_code = source_mapping['filename_absolute'] |
||||
full_txt_start = source_mapping['start'] |
||||
full_txt_end = full_txt_start + source_mapping['length'] |
||||
full_txt = slither.source_code[filename_source_code].encode('utf8')[full_txt_start:full_txt_end] |
||||
|
||||
if not target.name.encode('utf8') in full_txt: |
||||
raise FormatError(f'{target} not found in {full_txt} ({source_mapping}') |
||||
|
||||
old_str = target.name.encode('utf8') |
||||
new_str = convert(old_str, slither) |
||||
|
||||
counter = 0 |
||||
# Can be found multiple time on the same IR |
||||
# We patch one by one |
||||
while old_str in full_txt: |
||||
|
||||
target_found_at = full_txt.find((old_str)) |
||||
|
||||
full_txt = full_txt[target_found_at+1:] |
||||
counter += target_found_at |
||||
|
||||
loc_start = full_txt_start + counter |
||||
loc_end = loc_start + len(old_str) |
||||
|
||||
create_patch(result, |
||||
filename_source_code, |
||||
loc_start, |
||||
loc_end, |
||||
old_str, |
||||
new_str) |
||||
|
||||
|
||||
def _explore_functions(slither, functions, result, target, convert): |
||||
for function in functions: |
||||
_explore_variables_declaration(slither, function.variables, result, target, convert, True) |
||||
_explore_modifiers_calls(slither, function, result, target, convert) |
||||
_explore_irs(slither, function.all_slithir_operations(), result, target, convert) |
||||
|
||||
if isinstance(target, Function) and function.canonical_name == target.canonical_name: |
||||
old_str = function.name |
||||
new_str = convert(old_str, slither) |
||||
|
||||
filename_source_code = function.source_mapping['filename_absolute'] |
||||
full_txt_start = function.source_mapping['start'] |
||||
full_txt_end = full_txt_start + function.source_mapping['length'] |
||||
full_txt = slither.source_code[filename_source_code].encode('utf8')[full_txt_start:full_txt_end] |
||||
|
||||
# The name is after the space |
||||
if isinstance(target, Modifier): |
||||
matches = re.finditer(b'modifier([ ]*)', full_txt) |
||||
else: |
||||
matches = re.finditer(b'function([ ]*)', full_txt) |
||||
# Look for the end offset of the largest list of ' ' |
||||
loc_start = full_txt_start + max(matches, key=lambda x: len(x.group())).end() |
||||
loc_end = loc_start + len(old_str) |
||||
|
||||
create_patch(result, |
||||
filename_source_code, |
||||
loc_start, |
||||
loc_end, |
||||
old_str, |
||||
new_str) |
||||
|
||||
def _explore_enums(slither, enums, result, target, convert): |
||||
for enum in enums: |
||||
if enum == target: |
||||
old_str = enum.name |
||||
new_str = convert(old_str, slither) |
||||
|
||||
filename_source_code = enum.source_mapping['filename_absolute'] |
||||
full_txt_start = enum.source_mapping['start'] |
||||
full_txt_end = full_txt_start + enum.source_mapping['length'] |
||||
full_txt = slither.source_code[filename_source_code].encode('utf8')[full_txt_start:full_txt_end] |
||||
|
||||
# The name is after the space |
||||
matches = re.finditer(b'enum([ ]*)', full_txt) |
||||
# Look for the end offset of the largest list of ' ' |
||||
loc_start = full_txt_start + max(matches, key=lambda x: len(x.group())).end() |
||||
loc_end = loc_start + len(old_str) |
||||
|
||||
create_patch(result, |
||||
filename_source_code, |
||||
loc_start, |
||||
loc_end, |
||||
old_str, |
||||
new_str) |
||||
|
||||
|
||||
def _explore_contract(slither, contract, result, target, convert): |
||||
_explore_variables_declaration(slither, contract.state_variables, result, target, convert) |
||||
_explore_structures_declaration(slither, contract.structures, result, target, convert) |
||||
_explore_functions(slither, contract.functions_and_modifiers, result, target, convert) |
||||
_explore_enums(slither, contract.enums, result, target, convert) |
||||
|
||||
if contract == target: |
||||
filename_source_code = contract.source_mapping['filename_absolute'] |
||||
full_txt_start = contract.source_mapping['start'] |
||||
full_txt_end = full_txt_start + contract.source_mapping['length'] |
||||
full_txt = slither.source_code[filename_source_code].encode('utf8')[full_txt_start:full_txt_end] |
||||
|
||||
old_str = contract.name |
||||
new_str = convert(old_str, slither) |
||||
|
||||
# The name is after the space |
||||
matches = re.finditer(b'contract[ ]*', full_txt) |
||||
# Look for the end offset of the largest list of ' ' |
||||
loc_start = full_txt_start + max(matches, key=lambda x: len(x.group())).end() |
||||
|
||||
loc_end = loc_start + len(old_str) |
||||
|
||||
create_patch(result, |
||||
filename_source_code, |
||||
loc_start, |
||||
loc_end, |
||||
old_str, |
||||
new_str) |
||||
|
||||
|
||||
def _explore(slither, result, target, convert): |
||||
for contract in slither.contracts_derived: |
||||
_explore_contract(slither, contract, result, target, convert) |
||||
|
||||
|
||||
|
||||
|
||||
# endregion |
||||
|
||||
|
@ -0,0 +1,43 @@ |
||||
import os |
||||
import difflib |
||||
from collections import defaultdict |
||||
|
||||
def create_patch(result, file, start, end, old_str, new_str): |
||||
if isinstance(old_str, bytes): |
||||
old_str = old_str.decode('utf8') |
||||
if isinstance(new_str, bytes): |
||||
new_str = new_str.decode('utf8') |
||||
p = {"start": start, |
||||
"end": end, |
||||
"old_string": old_str, |
||||
"new_string": new_str |
||||
} |
||||
if 'patches' not in result: |
||||
result['patches'] = defaultdict(list) |
||||
if p not in result['patches'][file]: |
||||
result['patches'][file].append(p) |
||||
|
||||
|
||||
def apply_patch(original_txt, patch, offset): |
||||
patched_txt = original_txt[:int(patch['start'] + offset)] |
||||
patched_txt += patch['new_string'].encode('utf8') |
||||
patched_txt += original_txt[int(patch['end'] + offset):] |
||||
|
||||
# Keep the diff of text added or sub, in case of multiple patches |
||||
patch_length_diff = len(patch['new_string']) - (patch['end'] - patch['start']) |
||||
return patched_txt, patch_length_diff + offset |
||||
|
||||
|
||||
def create_diff(slither, original_txt, patched_txt, filename): |
||||
if slither.crytic_compile: |
||||
relative_path = slither.crytic_compile.filename_lookup(filename).relative |
||||
relative_path = os.path.join('.', relative_path) |
||||
else: |
||||
relative_path = filename |
||||
diff = difflib.unified_diff(original_txt.decode('utf8').splitlines(False), |
||||
patched_txt.decode('utf8').splitlines(False), |
||||
fromfile=relative_path, |
||||
tofile=relative_path, |
||||
lineterm='') |
||||
|
||||
return '\n'.join(list(diff)) + '\n' |
@ -0,0 +1,38 @@ |
||||
import re |
||||
from slither.formatters.exceptions import FormatError, FormatImpossible |
||||
from slither.formatters.utils.patches import create_patch |
||||
|
||||
def format(slither, result): |
||||
elements = result['elements'] |
||||
for element in elements: |
||||
|
||||
# TODO: decide if this should be changed in the constant detector |
||||
contract_name = element['type_specific_fields']['parent']['name'] |
||||
contract = slither.get_contract_from_name(contract_name) |
||||
var = contract.get_state_variable_from_name(element['name']) |
||||
if not var.expression: |
||||
raise FormatImpossible(f'{var.name} is uninitialized and cannot become constant.') |
||||
|
||||
_patch(slither, result, element['source_mapping']['filename_absolute'], |
||||
element['name'], |
||||
"constant " + element['name'], |
||||
element['source_mapping']['start'], |
||||
element['source_mapping']['start'] + element['source_mapping']['length']) |
||||
|
||||
|
||||
def _patch(slither, result, in_file, match_text, replace_text, modify_loc_start, modify_loc_end): |
||||
in_file_str = slither.source_code[in_file].encode('utf8') |
||||
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) |
||||
if num_repl != 0: |
||||
create_patch(result, |
||||
in_file, |
||||
modify_loc_start, |
||||
modify_loc_end, |
||||
old_str_of_interest, |
||||
new_str_of_interest) |
||||
|
||||
else: |
||||
raise FormatError("State variable not found?!") |
||||
|
@ -0,0 +1,28 @@ |
||||
from slither.formatters.utils.patches import create_patch |
||||
|
||||
|
||||
def format(slither, result): |
||||
elements = result['elements'] |
||||
for element in elements: |
||||
if element['type'] == "variable": |
||||
_patch(slither, |
||||
result, |
||||
element['source_mapping']['filename_absolute'], |
||||
element['source_mapping']['start']) |
||||
|
||||
|
||||
def _patch(slither, result, in_file, modify_loc_start): |
||||
in_file_str = slither.source_code[in_file].encode('utf8') |
||||
old_str_of_interest = in_file_str[modify_loc_start:] |
||||
old_str = old_str_of_interest.decode('utf-8').partition(';')[0]\ |
||||
+ old_str_of_interest.decode('utf-8').partition(';')[1] |
||||
|
||||
create_patch(result, |
||||
in_file, |
||||
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), |
||||
old_str, |
||||
"") |
||||
|
||||
|
@ -0,0 +1,145 @@ |
||||
""" |
||||
""" |
||||
|
||||
import json |
||||
from collections import defaultdict |
||||
from slither.printers.abstract_printer import AbstractPrinter |
||||
from slither.core.declarations.solidity_variables import SolidityVariableComposed, SolidityFunction |
||||
from slither.slithir.operations.binary import Binary, BinaryType |
||||
from slither.core.variables.state_variable import StateVariable |
||||
from slither.slithir.variables import Constant |
||||
|
||||
|
||||
def _extract_payable(slither): |
||||
ret = {} |
||||
for contract in slither.contracts: |
||||
payable_functions = [f.full_name for f in contract.functions_entry_points if f.payable] |
||||
if payable_functions: |
||||
ret[contract.name] = payable_functions |
||||
return ret |
||||
|
||||
def _extract_solidity_variable_usage(slither, sol_var): |
||||
ret = {} |
||||
for contract in slither.contracts: |
||||
functions_using_sol_var = [] |
||||
for f in contract.functions_entry_points: |
||||
for v in f.all_solidity_variables_read(): |
||||
if v == sol_var: |
||||
functions_using_sol_var.append(f.full_name) |
||||
break |
||||
if functions_using_sol_var: |
||||
ret[contract.name] = functions_using_sol_var |
||||
return ret |
||||
|
||||
def _extract_constant_functions(slither): |
||||
ret = {} |
||||
for contract in slither.contracts: |
||||
cst_functions = [f.full_name for f in contract.functions_entry_points if f.view or f.pure] |
||||
cst_functions += [v.function_name for v in contract.state_variables if v.visibility in ['public']] |
||||
if cst_functions: |
||||
ret[contract.name] = cst_functions |
||||
return ret |
||||
|
||||
def _extract_assert(slither): |
||||
ret = {} |
||||
for contract in slither.contracts: |
||||
functions_using_assert = [] |
||||
for f in contract.functions_entry_points: |
||||
for v in f.all_solidity_calls(): |
||||
if v == SolidityFunction('assert(bool)'): |
||||
functions_using_assert.append(f.full_name) |
||||
break |
||||
if functions_using_assert: |
||||
ret[contract.name] = functions_using_assert |
||||
return ret |
||||
|
||||
def _extract_constants_from_irs(irs, all_cst_used, all_cst_used_in_binary, context_explored): |
||||
for ir in irs: |
||||
if isinstance(ir, Binary): |
||||
for r in ir.read: |
||||
if isinstance(r, Constant): |
||||
all_cst_used_in_binary[BinaryType.str(ir.type)].append(r.value) |
||||
for r in ir.read: |
||||
if isinstance(r, Constant): |
||||
all_cst_used.append(r.value) |
||||
if isinstance(r, StateVariable): |
||||
if r.node_initialization: |
||||
if r.node_initialization.irs: |
||||
if r.node_initialization in context_explored: |
||||
continue |
||||
else: |
||||
context_explored.add(r.node_initialization) |
||||
_extract_constants_from_irs(r.node_initialization.irs, |
||||
all_cst_used, |
||||
all_cst_used_in_binary, |
||||
context_explored) |
||||
|
||||
def _extract_constants(slither): |
||||
ret_cst_used = defaultdict(dict) |
||||
ret_cst_used_in_binary = defaultdict(dict) |
||||
for contract in slither.contracts: |
||||
for function in contract.functions_entry_points: |
||||
all_cst_used = [] |
||||
all_cst_used_in_binary = defaultdict(list) |
||||
|
||||
context_explored = set() |
||||
context_explored.add(function) |
||||
_extract_constants_from_irs(function.all_slithir_operations(), |
||||
all_cst_used, |
||||
all_cst_used_in_binary, |
||||
context_explored) |
||||
|
||||
if all_cst_used: |
||||
ret_cst_used[contract.name][function.full_name] = all_cst_used |
||||
if all_cst_used_in_binary: |
||||
ret_cst_used_in_binary[contract.name][function.full_name] = all_cst_used_in_binary |
||||
return (ret_cst_used, ret_cst_used_in_binary) |
||||
|
||||
|
||||
|
||||
|
||||
class Echidna(AbstractPrinter): |
||||
ARGUMENT = 'echidna' |
||||
HELP = 'Export Echidna guiding information' |
||||
|
||||
WIKI = 'https://github.com/trailofbits/slither/wiki/Printer-documentation#echidna' |
||||
|
||||
|
||||
def output(self, filename): |
||||
""" |
||||
Output the inheritance relation |
||||
|
||||
_filename is not used |
||||
Args: |
||||
_filename(string) |
||||
""" |
||||
|
||||
payable = _extract_payable(self.slither) |
||||
timestamp = _extract_solidity_variable_usage(self.slither, |
||||
SolidityVariableComposed('block.timestamp')) |
||||
block_number = _extract_solidity_variable_usage(self.slither, |
||||
SolidityVariableComposed('block.number')) |
||||
msg_sender = _extract_solidity_variable_usage(self.slither, |
||||
SolidityVariableComposed('msg.sender')) |
||||
msg_gas = _extract_solidity_variable_usage(self.slither, |
||||
SolidityVariableComposed('msg.gas')) |
||||
assert_usage = _extract_assert(self.slither) |
||||
cst_functions = _extract_constant_functions(self.slither) |
||||
(cst_used, cst_used_in_binary) = _extract_constants(self.slither) |
||||
|
||||
|
||||
d = {'payable': payable, |
||||
'timestamp': timestamp, |
||||
'block_number': block_number, |
||||
'msg_sender': msg_sender, |
||||
'msg_gas': msg_gas, |
||||
'assert': assert_usage, |
||||
'constant_functions': cst_functions, |
||||
'constants_used': cst_used, |
||||
'constants_used_in_binary': cst_used_in_binary} |
||||
|
||||
self.info(json.dumps(d, indent=4)) |
||||
|
||||
res = self.generate_output(json.dumps(d, indent=4)) |
||||
|
||||
return res |
@ -0,0 +1,52 @@ |
||||
""" |
||||
Module printing summary of the contract |
||||
""" |
||||
from slither.printers.abstract_printer import AbstractPrinter |
||||
from slither.utils import output |
||||
|
||||
|
||||
class ConstructorPrinter(AbstractPrinter): |
||||
WIKI = 'https://github.com/crytic/slither/wiki/Printer-documentation#constructor-calls' |
||||
ARGUMENT = 'constructor-calls' |
||||
HELP = 'Print the constructors executed' |
||||
|
||||
def _get_soruce_code(self, cst): |
||||
src_mapping = cst.source_mapping |
||||
content = self.slither.source_code[src_mapping['filename_absolute']] |
||||
start = src_mapping['start'] |
||||
end = src_mapping['start'] + src_mapping['length'] |
||||
initial_space = src_mapping['starting_column'] |
||||
return ' ' * initial_space + content[start:end] |
||||
|
||||
def output(self, _filename): |
||||
info = '' |
||||
for contract in self.contracts: |
||||
stack_name = [] |
||||
stack_definition = [] |
||||
info += "\n\nContact Name: " + contract.name |
||||
info += " Constructor Call Sequence: " |
||||
cst = contract.constructors_declared |
||||
if cst: |
||||
stack_name.append(contract.name) |
||||
stack_definition.append(self._get_soruce_code(cst)) |
||||
for inherited_contract in contract.inheritance: |
||||
cst = inherited_contract.constructors_declared |
||||
if cst: |
||||
stack_name.append(inherited_contract.name) |
||||
stack_definition.append(self._get_soruce_code(cst)) |
||||
if len(stack_name) > 0: |
||||
info += " " + ' '.join(stack_name[len(stack_name) - 1]) |
||||
count = len(stack_name) - 2 |
||||
while count >= 0: |
||||
info += "-->" + ' '.join(stack_name[count]) |
||||
count = count - 1 |
||||
info += "\n Constructor Definitions:" |
||||
count = len(stack_definition) - 1 |
||||
while count >= 0: |
||||
info += "\n Contract name:" + str(stack_name[count]) |
||||
info += "\n" + str(stack_definition[count]) |
||||
count = count - 1 |
||||
|
||||
self.info(info) |
||||
res = output.Output(info) |
||||
return res |
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue