Merge branch 'dev' into dev-fix-parsing-test

pull/682/head
Josselin 4 years ago
commit 87844f0f86
  1. 1
      .github/workflows/linter.yml
  2. 1
      CONTRIBUTING.md
  3. 4
      plugin_example/setup.py
  4. 3
      pyproject.toml
  5. 10
      slither/__main__.py
  6. 19
      slither/analyses/data_dependency/data_dependency.py
  7. 5
      slither/analyses/write/are_variables_written.py
  8. 8
      slither/core/cfg/node.py
  9. 17
      slither/core/declarations/function.py
  10. 4
      slither/core/solidity_types/function_type.py
  11. 3
      slither/detectors/attributes/locked_ether.py
  12. 8
      slither/detectors/functions/arbitrary_send.py
  13. 18
      slither/detectors/naming_convention/naming_convention.py
  14. 4
      slither/detectors/operations/block_timestamp.py
  15. 3
      slither/detectors/reentrancy/reentrancy.py
  16. 12
      slither/detectors/reentrancy/reentrancy_benign.py
  17. 10
      slither/detectors/reentrancy/reentrancy_eth.py
  18. 12
      slither/detectors/reentrancy/reentrancy_events.py
  19. 18
      slither/detectors/reentrancy/reentrancy_no_gas.py
  20. 7
      slither/detectors/reentrancy/reentrancy_read_before_write.py
  21. 7
      slither/formatters/attributes/constant_pragma.py
  22. 7
      slither/formatters/naming_convention/naming_convention.py
  23. 58
      slither/printers/call/call_graph.py
  24. 6
      slither/printers/functions/authorization.py
  25. 2
      slither/printers/guidance/echidna.py
  26. 6
      slither/printers/summary/evm.py
  27. 19
      slither/printers/summary/function.py
  28. 9
      slither/printers/summary/human_summary.py
  29. 5
      slither/printers/summary/require_calls.py
  30. 26
      slither/slithir/convert.py
  31. 6
      slither/slithir/operations/binary.py
  32. 6
      slither/slithir/operations/library_call.py
  33. 9
      slither/slithir/tmp_operations/tmp_call.py
  34. 18
      slither/slithir/utils/ssa.py
  35. 9
      slither/slithir/utils/utils.py
  36. 9
      slither/solc_parsing/declarations/contract.py
  37. 8
      slither/solc_parsing/declarations/function.py
  38. 9
      slither/solc_parsing/expressions/expression_parsing.py
  39. 12
      slither/solc_parsing/slitherSolc.py
  40. 9
      slither/solc_parsing/solidity_types/type_parsing.py
  41. 11
      slither/solc_parsing/yul/parse_yul.py
  42. 3
      slither/tools/erc_conformance/__main__.py
  43. 13
      slither/tools/flattening/__main__.py
  44. 4
      slither/tools/flattening/export/export.py
  45. 15
      slither/tools/flattening/flattening.py
  46. 11
      slither/tools/kspec_coverage/__main__.py
  47. 3
      slither/tools/possible_paths/__main__.py
  48. 3
      slither/tools/possible_paths/possible_paths.py
  49. 4
      slither/tools/properties/__main__.py
  50. 6
      slither/tools/properties/properties/erc20.py
  51. 11
      slither/tools/properties/properties/ercs/erc20/unit_tests/truffle.py
  52. 2
      slither/tools/properties/properties/properties.py
  53. 5
      slither/tools/similarity/__main__.py
  54. 11
      slither/tools/slither_format/__main__.py
  55. 5
      slither/tools/slither_format/slither_format.py
  56. 5
      slither/tools/upgradeability/__main__.py
  57. 43
      slither/utils/erc.py
  58. 12
      slither/utils/expression_manipulations.py
  59. 4
      slither/utils/inheritance_analysis.py
  60. 44
      slither/utils/output.py
  61. 10
      tests/test_ast_parsing.py

@ -72,6 +72,7 @@ jobs:
VALIDATE_PYTHON: false
VALIDATE_PYTHON_PYLINT: false
VALIDATE_PYTHON_BLACK: false
VALIDATE_PYTHON_ISORT: false
# Always false
VALIDATE_JSON: false
VALIDATE_JAVASCRIPT_STANDARD: false

@ -33,6 +33,7 @@ To run them locally:
- `pylint slither --rconfig pyproject.toml`
- `black slither --config pyproject.toml`
We use black `19.10b0`.
## Detectors regression tests
For each new detector, at least one regression tests must be present.

@ -9,7 +9,5 @@ setup(
packages=find_packages(),
python_requires=">=3.6",
install_requires=["slither-analyzer==0.1"],
entry_points={
"slither_analyzer.plugin": "slither my-plugin=slither_my_plugin:make_plugin",
},
entry_points={"slither_analyzer.plugin": "slither my-plugin=slither_my_plugin:make_plugin",},
)

@ -17,5 +17,6 @@ too-many-ancestors,
logging-fstring-interpolation,
logging-not-lazy,
duplicate-code,
import-error
import-error,
unsubscriptable-object
"""

@ -402,10 +402,7 @@ def parse_args(detector_classes, printer_classes):
)
group_misc.add_argument(
"--markdown-root",
help="URL for markdown generation",
action="store",
default="",
"--markdown-root", help="URL for markdown generation", action="store", default="",
)
group_misc.add_argument(
@ -440,10 +437,7 @@ def parse_args(detector_classes, printer_classes):
)
group_misc.add_argument(
"--solc-ast",
help="Provide the contract as a json AST",
action="store_true",
default=False,
"--solc-ast", help="Provide the contract as a json AST", action="store_true", default=False,
)
group_misc.add_argument(

@ -135,9 +135,7 @@ def is_tainted_ssa(variable, context, only_unprotected=False, ignore_generic_tai
def get_dependencies(
variable: Variable,
context: Union[Contract, Function],
only_unprotected: bool = False,
variable: Variable, context: Union[Contract, Function], only_unprotected: bool = False,
) -> Set[Variable]:
"""
Return the variables for which `variable` depends on.
@ -172,9 +170,7 @@ def get_all_dependencies(
def get_dependencies_ssa(
variable: Variable,
context: Union[Contract, Function],
only_unprotected: bool = False,
variable: Variable, context: Union[Contract, Function], only_unprotected: bool = False,
) -> Set[Variable]:
"""
Return the variables for which `variable` depends on (SSA version).
@ -380,16 +376,7 @@ def convert_variable_to_non_ssa(v):
return v.non_ssa_version
assert isinstance(
v,
(
Constant,
SolidityVariable,
Contract,
Enum,
SolidityFunction,
Structure,
Function,
Type,
),
(Constant, SolidityVariable, Contract, Enum, SolidityFunction, Structure, Function, Type,),
)
return v

@ -35,10 +35,7 @@ class State: # pylint: disable=too-few-public-methods
# pylint: disable=too-many-branches
def _visit(
node: Node,
state: State,
variables_written: Set[Variable],
variables_to_write: List[Variable],
node: Node, state: State, variables_written: Set[Variable], variables_to_write: List[Variable],
):
"""
Explore all the nodes to look for values not written when the node's function return

@ -800,15 +800,11 @@ class Node(SourceMapping, ChildFunction): # pylint: disable=too-many-public-met
###################################################################################
@property
def phi_origins_local_variables(
self,
) -> Dict[str, Tuple[LocalVariable, Set["Node"]]]:
def phi_origins_local_variables(self,) -> Dict[str, Tuple[LocalVariable, Set["Node"]]]:
return self._phi_origins_local_variables
@property
def phi_origins_state_variables(
self,
) -> Dict[str, Tuple[StateVariable, Set["Node"]]]:
def phi_origins_state_variables(self,) -> Dict[str, Tuple[StateVariable, Set["Node"]]]:
return self._phi_origins_state_variables
# @property

@ -53,10 +53,7 @@ ReacheableNode = namedtuple("ReacheableNode", ["node", "ir"])
class ModifierStatements:
def __init__(
self,
modifier: Union["Contract", "Function"],
entry_point: "Node",
nodes: List["Node"],
self, modifier: Union["Contract", "Function"], entry_point: "Node", nodes: List["Node"],
):
self._modifier = modifier
self._entry_point = entry_point
@ -1144,9 +1141,7 @@ class Function(
@staticmethod
def _explore_func_conditional(
func: "Function",
f: Callable[["Node"], List[SolidityVariable]],
include_loop: bool,
func: "Function", f: Callable[["Node"], List[SolidityVariable]], include_loop: bool,
):
ret = [f(n) for n in func.nodes if n.is_conditional(include_loop)]
return [item for sublist in ret for item in sublist]
@ -1602,14 +1597,10 @@ class Function(
return ret
def get_last_ssa_state_variables_instances(
self,
) -> Dict[str, Set["SlithIRVariable"]]:
def get_last_ssa_state_variables_instances(self,) -> Dict[str, Set["SlithIRVariable"]]:
return self._get_last_ssa_variable_instances(target_state=True, target_local=False)
def get_last_ssa_local_variables_instances(
self,
) -> Dict[str, Set["SlithIRVariable"]]:
def get_last_ssa_local_variables_instances(self,) -> Dict[str, Set["SlithIRVariable"]]:
return self._get_last_ssa_variable_instances(target_state=False, target_local=True)
@staticmethod

@ -6,9 +6,7 @@ from slither.core.variables.function_type_variable import FunctionTypeVariable
class FunctionType(Type):
def __init__(
self,
params: List[FunctionTypeVariable],
return_values: List[FunctionTypeVariable],
self, params: List[FunctionTypeVariable], return_values: List[FunctionTypeVariable],
):
assert all(isinstance(x, FunctionTypeVariable) for x in params)
assert all(isinstance(x, FunctionTypeVariable) for x in return_values)

@ -53,8 +53,7 @@ Every Ether sent to `Locked` will be lost."""
for node in function.nodes:
for ir in node.irs:
if isinstance(
ir,
(Send, Transfer, HighLevelCall, LowLevelCall, NewContract),
ir, (Send, Transfer, HighLevelCall, LowLevelCall, NewContract),
):
if ir.call_value and ir.call_value != 0:
return False

@ -41,9 +41,7 @@ def arbitrary_send(func):
if ir.variable_right == SolidityVariableComposed("msg.sender"):
return False
if is_dependent(
ir.variable_right,
SolidityVariableComposed("msg.sender"),
func.contract,
ir.variable_right, SolidityVariableComposed("msg.sender"), func.contract,
):
return False
if isinstance(ir, (HighLevelCall, LowLevelCall, Transfer, Send)):
@ -56,9 +54,7 @@ def arbitrary_send(func):
if ir.call_value == SolidityVariableComposed("msg.value"):
continue
if is_dependent(
ir.call_value,
SolidityVariableComposed("msg.value"),
func.contract,
ir.call_value, SolidityVariableComposed("msg.value"), func.contract,
):
continue

@ -86,14 +86,10 @@ Solidity defines a [naming convention](https://solidity.readthedocs.io/en/v0.4.2
if func.is_constructor:
continue
if not self.is_mixed_case(func.name):
if (
func.visibility
in [
"internal",
"private",
]
and self.is_mixed_case_with_underscore(func.name)
):
if func.visibility in [
"internal",
"private",
] and self.is_mixed_case_with_underscore(func.name):
continue
if func.name.startswith("echidna_") or func.name.startswith("crytic_"):
continue
@ -129,11 +125,7 @@ Solidity defines a [naming convention](https://solidity.readthedocs.io/en/v0.4.2
res = self.generate_result(info)
res.add(
var,
{
"target": "variable",
"convention": "l_O_I_should_not_be_used",
},
var, {"target": "variable", "convention": "l_O_I_should_not_be_used",},
)
results.append(res)

@ -36,9 +36,7 @@ def _timestamp(func: Function) -> List[Node]:
return sorted(list(ret), key=lambda x: x.node_id)
def _detect_dangerous_timestamp(
contract: Contract,
) -> List[Tuple[Function, List[Node]]]:
def _detect_dangerous_timestamp(contract: Contract,) -> List[Tuple[Function, List[Node]]]:
"""
Args:
contract (Contract)

@ -127,8 +127,7 @@ class AbstractState:
)
self._reads = union_dict(self._reads, father.context[detector.KEY].reads)
self._reads_prior_calls = union_dict(
self.reads_prior_calls,
father.context[detector.KEY].reads_prior_calls,
self.reads_prior_calls, father.context[detector.KEY].reads_prior_calls,
)
def analyze_node(self, node, detector):

@ -63,11 +63,7 @@ Only report reentrancy that acts as a double call (see `reentrancy-eth`, `reentr
if v in node.context[self.KEY].reads_prior_calls[c]
]
not_read_then_written = {
FindingValue(
v,
node,
tuple(sorted(nodes, key=lambda x: x.node_id)),
)
FindingValue(v, node, tuple(sorted(nodes, key=lambda x: x.node_id)),)
for (v, nodes) in node.context[self.KEY].written.items()
if v not in read_then_written
}
@ -130,8 +126,7 @@ Only report reentrancy that acts as a double call (see `reentrancy-eth`, `reentr
for call_list_info in calls_list:
if call_list_info != call_info:
res.add(
call_list_info,
{"underlying_type": "external_calls_sending_eth"},
call_list_info, {"underlying_type": "external_calls_sending_eth"},
)
#
@ -143,8 +138,7 @@ Only report reentrancy that acts as a double call (see `reentrancy-eth`, `reentr
for call_list_info in calls_list:
if call_list_info != call_info:
res.add(
call_list_info,
{"underlying_type": "external_calls_sending_eth"},
call_list_info, {"underlying_type": "external_calls_sending_eth"},
)
# Add all variables written via nodes which write them.

@ -63,9 +63,7 @@ Bob uses the re-entrancy bug to call `withdrawBalance` two times, and withdraw m
continue
read_then_written |= {
FindingValue(
v,
node,
tuple(sorted(nodes, key=lambda x: x.node_id)),
v, node, tuple(sorted(nodes, key=lambda x: x.node_id)),
)
for (v, nodes) in node.context[self.KEY].written.items()
if v in node.context[self.KEY].reads_prior_calls[c]
@ -130,8 +128,7 @@ Bob uses the re-entrancy bug to call `withdrawBalance` two times, and withdraw m
for call_list_info in calls_list:
if call_list_info != call_info:
res.add(
call_list_info,
{"underlying_type": "external_calls_sending_eth"},
call_list_info, {"underlying_type": "external_calls_sending_eth"},
)
# If the calls are not the same ones that send eth, add the eth sending nodes.
@ -141,8 +138,7 @@ Bob uses the re-entrancy bug to call `withdrawBalance` two times, and withdraw m
for call_list_info in calls_list:
if call_list_info != call_info:
res.add(
call_list_info,
{"underlying_type": "external_calls_sending_eth"},
call_list_info, {"underlying_type": "external_calls_sending_eth"},
)
# Add all variables written via nodes which write them.

@ -61,11 +61,7 @@ If `d.()` re-enters, the `Counter` events will be shown in an incorrect order, w
send_eth=to_hashable(node.context[self.KEY].send_eth),
)
finding_vars = {
FindingValue(
e,
e.node,
tuple(sorted(nodes, key=lambda x: x.node_id)),
)
FindingValue(e, e.node, tuple(sorted(nodes, key=lambda x: x.node_id)),)
for (e, nodes) in node.context[self.KEY].events.items()
}
if finding_vars:
@ -119,8 +115,7 @@ If `d.()` re-enters, the `Counter` events will be shown in an incorrect order, w
for call_list_info in calls_list:
if call_list_info != call_info:
res.add(
call_list_info,
{"underlying_type": "external_calls_sending_eth"},
call_list_info, {"underlying_type": "external_calls_sending_eth"},
)
#
@ -132,8 +127,7 @@ If `d.()` re-enters, the `Counter` events will be shown in an incorrect order, w
for call_list_info in calls_list:
if call_list_info != call_info:
res.add(
call_list_info,
{"underlying_type": "external_calls_sending_eth"},
call_list_info, {"underlying_type": "external_calls_sending_eth"},
)
for finding_value in events:

@ -72,19 +72,11 @@ Only report reentrancy that is based on `transfer` or `send`."""
send_eth=to_hashable(node.context[self.KEY].send_eth),
)
finding_vars = {
FindingValue(
v,
node,
tuple(sorted(nodes, key=lambda x: x.node_id)),
)
FindingValue(v, node, tuple(sorted(nodes, key=lambda x: x.node_id)),)
for (v, nodes) in node.context[self.KEY].written.items()
}
finding_vars |= {
FindingValue(
e,
e.node,
tuple(sorted(nodes, key=lambda x: x.node_id)),
)
FindingValue(e, e.node, tuple(sorted(nodes, key=lambda x: x.node_id)),)
for (e, nodes) in node.context[self.KEY].events.items()
}
if finding_vars:
@ -159,8 +151,7 @@ Only report reentrancy that is based on `transfer` or `send`."""
for call_list_info in calls_list:
if call_list_info != call_info:
res.add(
call_list_info,
{"underlying_type": "external_calls_sending_eth"},
call_list_info, {"underlying_type": "external_calls_sending_eth"},
)
#
@ -172,8 +163,7 @@ Only report reentrancy that is based on `transfer` or `send`."""
for call_list_info in calls_list:
if call_list_info != call_info:
res.add(
call_list_info,
{"underlying_type": "external_calls_sending_eth"},
call_list_info, {"underlying_type": "external_calls_sending_eth"},
)
# Add all variables written via nodes which write them.

@ -58,9 +58,7 @@ Do not report reentrancies that involve Ether (see `reentrancy-eth`)."""
continue
read_then_written |= {
FindingValue(
v,
node,
tuple(sorted(nodes, key=lambda x: x.node_id)),
v, node, tuple(sorted(nodes, key=lambda x: x.node_id)),
)
for (v, nodes) in node.context[self.KEY].written.items()
if v in node.context[self.KEY].reads_prior_calls[c]
@ -116,8 +114,7 @@ Do not report reentrancies that involve Ether (see `reentrancy-eth`)."""
for call_list_info in calls_list:
if call_list_info != call_info:
res.add(
call_list_info,
{"underlying_type": "external_calls_sending_eth"},
call_list_info, {"underlying_type": "external_calls_sending_eth"},
)
# Add all variables written via nodes which write them.

@ -70,10 +70,5 @@ def _patch(
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,
result, in_file, int(modify_loc_start), int(modify_loc_end), old_str_of_interest, pragma,
)

@ -575,12 +575,7 @@ def _explore_irs(slither, irs, result, target, convert):
loc_end = loc_start + len(old_str)
create_patch(
result,
filename_source_code,
loc_start,
loc_end,
old_str,
new_str,
result, filename_source_code, loc_start, loc_end, old_str, new_str,
)

@ -33,39 +33,21 @@ def _edge(from_node, to_node):
# return dot language string to add graph node (with optional label)
def _node(node, label=None):
return " ".join(
(
f'"{node}"',
f'[label="{label}"]' if label is not None else "",
)
)
return " ".join((f'"{node}"', f'[label="{label}"]' if label is not None else "",))
# pylint: disable=too-many-arguments
def _process_internal_call(
contract,
function,
internal_call,
contract_calls,
solidity_functions,
solidity_calls,
contract, function, internal_call, contract_calls, solidity_functions, solidity_calls,
):
if isinstance(internal_call, (Function)):
contract_calls[contract].add(
_edge(
_function_node(contract, function),
_function_node(contract, internal_call),
)
_edge(_function_node(contract, function), _function_node(contract, internal_call),)
)
elif isinstance(internal_call, (SolidityFunction)):
solidity_functions.add(
_node(_solidity_function_node(internal_call)),
)
solidity_functions.add(_node(_solidity_function_node(internal_call)),)
solidity_calls.add(
_edge(
_function_node(contract, function),
_solidity_function_node(internal_call),
)
_edge(_function_node(contract, function), _solidity_function_node(internal_call),)
)
@ -102,12 +84,7 @@ def _render_solidity_calls(solidity_functions, solidity_calls):
def _process_external_call(
contract,
function,
external_call,
contract_functions,
external_calls,
all_contracts,
contract, function, external_call, contract_functions, external_calls, all_contracts,
):
external_contract, external_function = external_call
@ -117,10 +94,7 @@ def _process_external_call(
# add variable as node to respective contract
if isinstance(external_function, (Variable)):
contract_functions[external_contract].add(
_node(
_function_node(external_contract, external_function),
external_function.name,
)
_node(_function_node(external_contract, external_function), external_function.name,)
)
external_calls.add(
@ -142,27 +116,15 @@ def _process_function(
external_calls,
all_contracts,
):
contract_functions[contract].add(
_node(_function_node(contract, function), function.name),
)
contract_functions[contract].add(_node(_function_node(contract, function), function.name),)
for internal_call in function.internal_calls:
_process_internal_call(
contract,
function,
internal_call,
contract_calls,
solidity_functions,
solidity_calls,
contract, function, internal_call, contract_calls, solidity_functions, solidity_calls,
)
for external_call in function.high_level_calls:
_process_external_call(
contract,
function,
external_call,
contract_functions,
external_calls,
all_contracts,
contract, function, external_call, contract_functions, external_calls, all_contracts,
)

@ -52,11 +52,7 @@ class PrinterWrittenVariablesAndAuthorization(AbstractPrinter):
state_variables_written = [v.name for v in function.all_state_variables_written()]
msg_sender_condition = self.get_msg_sender_checks(function)
table.add_row(
[
function.name,
str(state_variables_written),
str(msg_sender_condition),
]
[function.name, str(state_variables_written), str(msg_sender_condition),]
)
all_tables.append((contract.name, table))
txt += str(table) + "\n"

@ -158,7 +158,7 @@ def json_serializable(cls):
@json_serializable
class ConstantValue(NamedTuple):
class ConstantValue(NamedTuple): # pylint: disable=inherit-non-class,too-few-public-methods
# Here value should be Union[str, int, bool]
# But the json lib in Echidna does not handle large integer in json
# So we convert everything to string

@ -101,8 +101,7 @@ class PrinterEVM(AbstractPrinter):
)
txt += green(
"\t\tSource line {}: {}\n".format(
node_source_line,
contract_file_lines[node_source_line - 1].rstrip(),
node_source_line, contract_file_lines[node_source_line - 1].rstrip(),
)
)
txt += magenta("\t\tEVM Instructions:\n")
@ -124,8 +123,7 @@ class PrinterEVM(AbstractPrinter):
)
txt += green(
"\t\tSource line {}: {}\n".format(
node_source_line,
contract_file_lines[node_source_line - 1].rstrip(),
node_source_line, contract_file_lines[node_source_line - 1].rstrip(),
)
)
txt += magenta("\t\tEVM Instructions:\n")

@ -65,26 +65,11 @@ class FunctionSummary(AbstractPrinter):
internal_calls = self._convert(internal_calls)
external_calls = self._convert(external_calls)
table.add_row(
[
f_name,
visi,
modifiers,
read,
write,
internal_calls,
external_calls,
]
[f_name, visi, modifiers, read, write, internal_calls, external_calls,]
)
txt += "\n \n" + str(table)
table = MyPrettyTable(
[
"Modifiers",
"Visibility",
"Read",
"Write",
"Internal Calls",
"External Calls",
]
["Modifiers", "Visibility", "Read", "Write", "Internal Calls", "External Calls",]
)
for (
_c_name,

@ -379,14 +379,7 @@ class PrinterHumanSummary(AbstractPrinter):
)
table.add_row(
[
contract.name,
number_functions,
ercs,
erc20_info,
is_complex,
features,
]
[contract.name, number_functions, ercs, erc20_info, is_complex, features,]
)
self.info(txt + "\n" + str(table))

@ -46,10 +46,7 @@ class RequireOrAssert(AbstractPrinter):
]
require = [ir.node for ir in require]
table.add_row(
[
function.name,
self._convert([str(m.expression) for m in set(require)]),
]
[function.name, self._convert([str(m.expression) for m in set(require)]),]
)
txt += "\n" + str(table)
self.info(txt)

@ -206,8 +206,7 @@ def convert_arguments(arguments):
def is_temporary(ins):
return isinstance(
ins,
(Argument, TmpNewElementaryType, TmpNewContract, TmpNewArray, TmpNewStructure),
ins, (Argument, TmpNewElementaryType, TmpNewContract, TmpNewArray, TmpNewStructure),
)
@ -646,8 +645,7 @@ def propagate_types(ir, node): # pylint: disable=too-many-locals
# We dont need to check for function collision, as solc prevents the use of selector
# if there are multiple functions with the same name
f = next(
(f for f in type_t.functions if f.name == ir.variable_right),
None,
(f for f in type_t.functions if f.name == ir.variable_right), None,
)
if f:
ir.lvalue.set_type(f)
@ -854,10 +852,7 @@ def extract_tmp_call(ins, contract): # pylint: disable=too-many-locals
ins.called = SolidityFunction("blockhash(uint256)")
elif str(ins.called) == "this.balance":
s = SolidityCall(
SolidityFunction("this.balance()"),
ins.nbr_arguments,
ins.lvalue,
ins.type_call,
SolidityFunction("this.balance()"), ins.nbr_arguments, ins.lvalue, ins.type_call,
)
s.set_expression(ins.expression)
return s
@ -1148,11 +1143,7 @@ def look_for_library(contract, ir, using_for, t):
lib_contract = contract.slither.get_contract_from_name(str(destination))
if lib_contract:
lib_call = LibraryCall(
lib_contract,
ir.function_name,
ir.nbr_arguments,
ir.lvalue,
ir.type_call,
lib_contract, ir.function_name, ir.nbr_arguments, ir.lvalue, ir.type_call,
)
lib_call.set_expression(ir.expression)
lib_call.set_node(ir.node)
@ -1385,14 +1376,7 @@ def remove_temporary(result):
ins
for ins in result
if not isinstance(
ins,
(
Argument,
TmpNewElementaryType,
TmpNewContract,
TmpNewArray,
TmpNewStructure,
),
ins, (Argument, TmpNewElementaryType, TmpNewContract, TmpNewArray, TmpNewStructure,),
)
]

@ -175,11 +175,7 @@ class Binary(OperationWithLValue):
while isinstance(points, ReferenceVariable):
points = points.points_to
return "{}(-> {}) = {} {} {}".format(
str(self.lvalue),
points,
self.variable_left,
self.type_str,
self.variable_right,
str(self.lvalue), points, self.variable_left, self.type_str, self.variable_right,
)
return "{}({}) = {} {} {}".format(
str(self.lvalue),

@ -38,9 +38,5 @@ class LibraryCall(HighLevelCall):
lvalue = "{}({}) = ".format(self.lvalue, self.lvalue.type)
txt = "{}LIBRARY_CALL, dest:{}, function:{}, arguments:{} {}"
return txt.format(
lvalue,
self.destination,
self.function_name,
[str(x) for x in arguments],
gas,
lvalue, self.destination, self.function_name, [str(x) for x in arguments], gas,
)

@ -13,14 +13,7 @@ class TmpCall(OperationWithLValue): # pylint: disable=too-many-instance-attribu
def __init__(self, called, nbr_arguments, result, type_call):
assert isinstance(
called,
(
Contract,
Variable,
SolidityVariableComposed,
SolidityFunction,
Structure,
Event,
),
(Contract, Variable, SolidityVariableComposed, SolidityFunction, Structure, Event,),
)
super().__init__()
self._called = called

@ -173,9 +173,7 @@ def add_ssa_ir(function, all_state_variables_instances):
init_state_variables_instances = dict(all_state_variables_instances)
initiate_all_local_variables_instances(
function.nodes,
init_local_variables_instances,
all_init_local_variables_instances,
function.nodes, init_local_variables_instances, all_init_local_variables_instances,
)
generate_ssa_irs(
@ -508,8 +506,7 @@ def add_phi_origins(node, local_variables_definition, state_variables_definition
# We keep the instance as we want to avoid to add __hash__ on v.name in Variable
# That might work for this used, but could create collision for other uses
local_variables_definition = dict(
local_variables_definition,
**{v.name: (v, node) for v in node.local_variables_written},
local_variables_definition, **{v.name: (v, node) for v in node.local_variables_written},
)
state_variables_definition = dict(
state_variables_definition,
@ -601,16 +598,7 @@ def get(
return tuple_variables_instances[variable.index]
assert isinstance(
variable,
(
Constant,
SolidityVariable,
Contract,
Enum,
SolidityFunction,
Structure,
Function,
Type,
),
(Constant, SolidityVariable, Contract, Enum, SolidityFunction, Structure, Function, Type,),
) # type for abi.decode(.., t)
return variable

@ -25,12 +25,5 @@ def is_valid_rvalue(v):
def is_valid_lvalue(v):
return isinstance(
v,
(
StateVariable,
LocalVariable,
TemporaryVariable,
ReferenceVariable,
TupleVariable,
),
v, (StateVariable, LocalVariable, TemporaryVariable, ReferenceVariable, TupleVariable,),
)

@ -441,15 +441,10 @@ class ContractSolc:
elem.set_contract(self._contract)
elem.set_contract_declarer(element_parser.underlying_function.contract_declarer)
elem.set_offset(
element_parser.function_not_parsed["src"],
self._contract.slither,
element_parser.function_not_parsed["src"], self._contract.slither,
)
elem_parser = Cls_parser(
elem,
element_parser.function_not_parsed,
self,
)
elem_parser = Cls_parser(elem, element_parser.function_not_parsed, self,)
elem_parser.analyze_params()
if isinstance(elem, Modifier):
self._contract.slither.add_modifier(elem)

@ -48,10 +48,7 @@ class FunctionSolc:
# elems = [(type, name)]
def __init__(
self,
function: Function,
function_data: Dict,
contract_parser: "ContractSolc",
self, function: Function, function_data: Dict, contract_parser: "ContractSolc",
):
self._slither_parser: "SlitherSolc" = contract_parser.slither_parser
self._contract_parser = contract_parser
@ -575,8 +572,7 @@ class FunctionSolc:
link_underlying_nodes(node_startDoWhile, node_condition)
else:
link_nodes(
node_startDoWhile.underlying_node,
node_condition.underlying_node.sons[0],
node_startDoWhile.underlying_node, node_condition.underlying_node.sons[0],
)
link_underlying_nodes(statement, node_condition)
link_underlying_nodes(node_condition, node_endDoWhile)

@ -84,14 +84,7 @@ def find_variable( # pylint: disable=too-many-locals,too-many-statements
referenced_declaration: Optional[int] = None,
is_super=False,
) -> Union[
Variable,
Function,
Contract,
SolidityVariable,
SolidityFunction,
Event,
Enum,
Structure,
Variable, Function, Contract, SolidityVariable, SolidityFunction, Event, Enum, Structure,
]:
from slither.solc_parsing.declarations.contract import ContractSolc
from slither.solc_parsing.declarations.function import FunctionSolc

@ -379,9 +379,7 @@ Please rename it, this name is reserved for Slither's internals"""
contracts_to_be_analyzed += [contract]
def _analyze_first_part(
self,
contracts_to_be_analyzed: List[ContractSolc],
libraries: List[ContractSolc],
self, contracts_to_be_analyzed: List[ContractSolc], libraries: List[ContractSolc],
):
for lib in libraries:
self._parse_struct_var_modifiers_functions(lib)
@ -406,9 +404,7 @@ Please rename it, this name is reserved for Slither's internals"""
contracts_to_be_analyzed += [contract]
def _analyze_second_part(
self,
contracts_to_be_analyzed: List[ContractSolc],
libraries: List[ContractSolc],
self, contracts_to_be_analyzed: List[ContractSolc], libraries: List[ContractSolc],
):
for lib in libraries:
self._analyze_struct_events(lib)
@ -433,9 +429,7 @@ Please rename it, this name is reserved for Slither's internals"""
contracts_to_be_analyzed += [contract]
def _analyze_third_part(
self,
contracts_to_be_analyzed: List[ContractSolc],
libraries: List[ContractSolc],
self, contracts_to_be_analyzed: List[ContractSolc], libraries: List[ContractSolc],
):
for lib in libraries:
self._analyze_variables_modifiers_functions(lib)

@ -134,8 +134,7 @@ def _find_from_type_name( # pylint: disable=too-many-locals,too-many-branches,t
found = re.findall("mapping\(([a-zA-Z0-9\.]*) => ([a-zA-Z0-9\.\[\]]*)\)", name)
else:
found = re.findall(
"mapping\(([a-zA-Z0-9\.]*) => (mapping\([=> a-zA-Z0-9\.\[\]]*\))\)",
name,
"mapping\(([a-zA-Z0-9\.]*) => (mapping\([=> a-zA-Z0-9\.\[\]]*\))\)", name,
)
assert len(found) == 1
from_ = found[0][0]
@ -191,11 +190,7 @@ def parse_type(t: Union[Dict, UnknownType], caller_context):
if t[key] == "UserDefinedTypeName":
if is_compact_ast:
return _find_from_type_name(
t["typeDescriptions"]["typeString"],
contract,
contracts,
structures,
enums,
t["typeDescriptions"]["typeString"], contract, contracts, structures, enums,
)
# Determine if we have a type node (otherwise we use the name node, as some older solc did not have 'type').

@ -162,10 +162,7 @@ class YulScope(metaclass=abc.ABCMeta):
self._yul_local_functions.append(func)
def get_yul_local_function_from_name(self, func_name):
return next(
(v for v in self._yul_local_functions if v.underlying.name == func_name),
None,
)
return next((v for v in self._yul_local_functions if v.underlying.name == func_name), None,)
class YulLocalVariable: # pylint: disable=too-few-public-methods
@ -456,11 +453,7 @@ def convert_yul_switch(root: YulScope, parent: YulNode, ast: Dict) -> YulNode:
"name": "eq",
},
"arguments": [
{
"nodeType": "YulIdentifier",
"src": case_ast["src"],
"name": switch_expr_var,
},
{"nodeType": "YulIdentifier", "src": case_ast["src"], "name": switch_expr_var,},
value_ast,
],
},

@ -31,8 +31,7 @@ def parse_args():
:return: Returns the arguments for the program.
"""
parser = argparse.ArgumentParser(
description="Check the ERC 20 conformance",
usage="slither-check-erc project contractName",
description="Check the ERC 20 conformance", usage="slither-check-erc project contractName",
)
parser.add_argument("project", help="The codebase to be tested.")

@ -41,9 +41,7 @@ def parse_args():
group_export = parser.add_argument_group("Export options")
group_export.add_argument(
"--dir",
help=f"Export directory (default: {DEFAULT_EXPORT_PATH}).",
default=None,
"--dir", help=f"Export directory (default: {DEFAULT_EXPORT_PATH}).", default=None,
)
group_export.add_argument(
@ -54,10 +52,7 @@ def parse_args():
)
parser.add_argument(
"--zip",
help="Export all the files to a zip file",
action="store",
default=None,
"--zip", help="Export all the files to a zip file", action="store", default=None,
)
parser.add_argument(
@ -74,9 +69,7 @@ def parse_args():
)
group_patching.add_argument(
"--convert-private",
help="Convert private variables to internal.",
action="store_true",
"--convert-private", help="Convert private variables to internal.", action="store_true",
)
group_patching.add_argument(

@ -24,9 +24,7 @@ def save_to_zip(files: List[Export], zip_filename: str, zip_type: str = "lzma"):
"""
logger.info(f"Export {zip_filename}")
with zipfile.ZipFile(
zip_filename,
"w",
compression=ZIP_TYPES_ACCEPTED.get(zip_type, zipfile.ZIP_LZMA),
zip_filename, "w", compression=ZIP_TYPES_ACCEPTED.get(zip_type, zipfile.ZIP_LZMA),
) as file_desc:
for f in files:
file_desc.writestr(str(f.filename), f.content)

@ -108,10 +108,7 @@ class Flattening:
regex = re.search(r"((\sexternal)\s+)|(\sexternal)$|(\)external)$", attributes)
if regex:
to_patch.append(
Patch(
attributes_start + regex.span()[0] + 1,
"public_to_external",
)
Patch(attributes_start + regex.span()[0] + 1, "public_to_external",)
)
else:
raise SlitherException(f"External keyword not found {f.name} {attributes}")
@ -122,10 +119,7 @@ class Flattening:
calldata_end = calldata_start + var.source_mapping["length"]
calldata_idx = content[calldata_start:calldata_end].find(" calldata ")
to_patch.append(
Patch(
calldata_start + calldata_idx + 1,
"calldata_to_memory",
)
Patch(calldata_start + calldata_idx + 1, "calldata_to_memory",)
)
if self._private_to_internal:
@ -139,10 +133,7 @@ class Flattening:
regex = re.search(r" private ", attributes)
if regex:
to_patch.append(
Patch(
attributes_start + regex.span()[0] + 1,
"private_to_internal",
)
Patch(attributes_start + regex.span()[0] + 1, "private_to_internal",)
)
else:
raise SlitherException(

@ -22,23 +22,18 @@ def parse_args():
:return: Returns the arguments for the program.
"""
parser = argparse.ArgumentParser(
description="slither-kspec-coverage",
usage="slither-kspec-coverage contract.sol kspec.md",
description="slither-kspec-coverage", usage="slither-kspec-coverage contract.sol kspec.md",
)
parser.add_argument(
"contract", help="The filename of the contract or truffle directory to analyze."
)
parser.add_argument(
"kspec",
help="The filename of the Klab spec markdown for the analyzed contract(s)",
"kspec", help="The filename of the Klab spec markdown for the analyzed contract(s)",
)
parser.add_argument(
"--version",
help="displays the current version",
version="0.1.0",
action="version",
"--version", help="displays the current version", version="0.1.0", action="version",
)
parser.add_argument(
"--json",

@ -21,8 +21,7 @@ def parse_args():
:return: Returns the arguments for the program.
"""
parser = argparse.ArgumentParser(
description="PossiblePaths",
usage="possible_paths.py filename [contract.function targets]",
description="PossiblePaths", usage="possible_paths.py filename [contract.function targets]",
)
parser.add_argument(

@ -18,8 +18,7 @@ def resolve_function(slither, contract_name, function_name):
# Obtain the target function
target_function = next(
(function for function in contract.functions if function.name == function_name),
None,
(function for function in contract.functions if function.name == function_name), None,
)
# Verify we have resolved the function specified.

@ -105,9 +105,7 @@ def parse_args():
)
parser.add_argument(
"--address-attacker",
help=f"Attacker address. Default {ATTACKER_ADDRESS}",
default=None,
"--address-attacker", help=f"Attacker address. Default {ATTACKER_ADDRESS}", default=None,
)
# Add default arguments from crytic-compile

@ -107,11 +107,7 @@ def generate_erc20(
# Generate the Test contract
initialization_recommendation = _initialization_recommendation(type_property)
contract_filename, contract_name = generate_test_contract(
contract,
type_property,
output_dir,
property_file,
initialization_recommendation,
contract, type_property, output_dir, property_file, initialization_recommendation,
)
# Generate Echidna config file

@ -15,10 +15,7 @@ logger = logging.getLogger("Slither")
def generate_truffle_test(
contract: Contract,
type_property: str,
unit_tests: List[Property],
addresses: Addresses,
contract: Contract, type_property: str, unit_tests: List[Property], addresses: Addresses,
) -> str:
test_contract = f"Test{contract.name}{type_property}"
filename_init = f"Initialization{test_contract}.js"
@ -38,11 +35,7 @@ def generate_truffle_test(
)
generate_unit_test(
test_contract,
filename,
unit_tests,
output_dir,
addresses,
test_contract, filename, unit_tests, output_dir, addresses,
)
log_info = "\n"

@ -24,7 +24,7 @@ class PropertyCaller(Enum):
ANY = 5 # If the caller does not matter
class Property(NamedTuple):
class Property(NamedTuple): # pylint: disable=inherit-non-class,too-few-public-methods
name: str
content: str
type: PropertyType

@ -54,10 +54,7 @@ def parse_args():
)
parser.add_argument(
"--version",
help="displays the current version",
version="0.0",
action="version",
"--version", help="displays the current version", version="0.0", action="version",
)
cryticparser.init(parser)

@ -41,17 +41,10 @@ def parse_args():
default=False,
)
parser.add_argument(
"--verbose-json",
"-j",
help="verbose json output",
action="store_true",
default=False,
"--verbose-json", "-j", help="verbose json output", action="store_true", default=False,
)
parser.add_argument(
"--version",
help="displays the current version",
version="0.1.0",
action="version",
"--version", help="displays the current version", version="0.1.0", action="version",
)
parser.add_argument(

@ -65,10 +65,7 @@ def slither_format(slither, **kwargs): # pylint: disable=too-many-locals
logger.info(f"Issue: {one_line_description}")
logger.info(f"Generated: ({export_result})")
for (
_,
diff,
) in result["patches_diff"].items():
for (_, diff,) in result["patches_diff"].items():
filename = f"fix_{counter}.patch"
path = Path(export_result, filename)
logger.info(f"\t- {filename}")

@ -57,10 +57,7 @@ def parse_args():
)
parser.add_argument(
"--markdown-root",
help="URL for markdown generation",
action="store",
default="",
"--markdown-root", help="URL for markdown generation", action="store", default="",
)
parser.add_argument(

@ -63,14 +63,7 @@ ERC223 = [
ERC("totalSupply", [], "uint256", True, True, []),
ERC("balanceOf", ["address"], "uint256", True, True, []),
ERC("transfer", ["address", "uint256"], "bool", False, True, [ERC223_transfer_event]),
ERC(
"transfer",
["address", "uint256", "bytes"],
"bool",
False,
True,
[ERC223_transfer_event],
),
ERC("transfer", ["address", "uint256", "bytes"], "bool", False, True, [ERC223_transfer_event],),
ERC(
"transfer",
["address", "uint256", "bytes", "string"],
@ -125,22 +118,10 @@ ERC721 = [
[ERC721_transfer_event],
),
ERC(
"transferFrom",
["address", "address", "uint256"],
"",
False,
True,
[ERC721_transfer_event],
"transferFrom", ["address", "address", "uint256"], "", False, True, [ERC721_transfer_event],
),
ERC("approve", ["address", "uint256"], "", False, True, [ERC721_approval_event]),
ERC(
"setApprovalForAll",
["address", "bool"],
"",
False,
True,
[ERC721_approvalforall_event],
),
ERC("setApprovalForAll", ["address", "bool"], "", False, True, [ERC721_approvalforall_event],),
ERC("getApproved", ["uint256"], "address", True, True, []),
ERC("isApprovedForAll", ["address", "address"], "bool", True, True, []),
] + ERC165
@ -159,14 +140,7 @@ ERC721_signatures = erc_to_signatures(ERC721)
# https://eips.ethereum.org/EIPS/eip-1820
ERC1820_EVENTS: List = []
ERC1820 = [
ERC(
"canImplementInterfaceForAddress",
["bytes32", "address"],
"bytes32",
True,
True,
[],
)
ERC("canImplementInterfaceForAddress", ["bytes32", "address"], "bytes32", True, True, [],)
]
ERC1820_signatures = erc_to_signatures(ERC1820)
@ -207,14 +181,7 @@ ERC777 = [
ERC("granularity", [], "uint256", True, True, []),
ERC("defaultOperators", [], "address[]", True, True, []),
ERC("isOperatorFor", ["address", "address"], "bool", True, True, []),
ERC(
"authorizeOperator",
["address"],
"",
False,
True,
[ERC777_authorizedOperator_event],
),
ERC("authorizeOperator", ["address"], "", False, True, [ERC777_authorizedOperator_event],),
ERC("revokeOperator", ["address"], "", False, True, [ERC777_revokedoperator_event]),
ERC("send", ["address", "uint256", "bytes"], "", False, True, [ERC777_sent_event]),
ERC(

@ -110,27 +110,21 @@ class SplitTernaryExpression:
if self.apply_copy(next_expr, true_expression, false_expression, f_call):
# always on last arguments added
self.copy_expression(
next_expr,
true_expression.arguments[-1],
false_expression.arguments[-1],
next_expr, true_expression.arguments[-1], false_expression.arguments[-1],
)
elif isinstance(expression, TypeConversion):
next_expr = expression.expression
if self.apply_copy(next_expr, true_expression, false_expression, f_expression):
self.copy_expression(
expression.expression,
true_expression.expression,
false_expression.expression,
expression.expression, true_expression.expression, false_expression.expression,
)
elif isinstance(expression, UnaryOperation):
next_expr = expression.expression
if self.apply_copy(next_expr, true_expression, false_expression, f_expression):
self.copy_expression(
expression.expression,
true_expression.expression,
false_expression.expression,
expression.expression, true_expression.expression, false_expression.expression,
)
else:

@ -10,9 +10,7 @@ if TYPE_CHECKING:
from slither.core.variables.state_variable import StateVariable
def detect_c3_function_shadowing(
contract: "Contract",
) -> Dict["Function", Set["Function"]]:
def detect_c3_function_shadowing(contract: "Contract",) -> Dict["Function", Set["Function"]]:
"""
Detects and obtains functions which are indirectly shadowed via multiple inheritance by C3 linearization
properties, despite not directly inheriting from each other.

@ -78,9 +78,7 @@ def output_to_zip(filename: str, error: Optional[str], results: Dict, zip_type:
logger.info(yellow(f"{filename} exists already, the overwrite is prevented"))
else:
with ZipFile(
filename,
"w",
compression=ZIP_TYPES_ACCEPTED.get(zip_type, zipfile.ZIP_LZMA),
filename, "w", compression=ZIP_TYPES_ACCEPTED.get(zip_type, zipfile.ZIP_LZMA),
) as file_desc:
file_desc.writestr("slither_results.json", json.dumps(json_result).encode("utf8"))
@ -357,11 +355,7 @@ class Output:
additional_fields = {}
type_specific_fields = {"parent": _create_parent_element(enum)}
element = _create_base_element(
"enum",
enum.name,
enum.source_mapping,
type_specific_fields,
additional_fields,
"enum", enum.name, enum.source_mapping, type_specific_fields, additional_fields,
)
self._data["elements"].append(element)
@ -377,11 +371,7 @@ class Output:
additional_fields = {}
type_specific_fields = {"parent": _create_parent_element(struct)}
element = _create_base_element(
"struct",
struct.name,
struct.source_mapping,
type_specific_fields,
additional_fields,
"struct", struct.name, struct.source_mapping, type_specific_fields, additional_fields,
)
self._data["elements"].append(element)
@ -400,11 +390,7 @@ class Output:
"signature": event.full_name,
}
element = _create_base_element(
"event",
event.name,
event.source_mapping,
type_specific_fields,
additional_fields,
"event", event.name, event.source_mapping, type_specific_fields, additional_fields,
)
self._data["elements"].append(element)
@ -424,11 +410,7 @@ class Output:
}
node_name = str(node.expression) if node.expression else ""
element = _create_base_element(
"node",
node_name,
node.source_mapping,
type_specific_fields,
additional_fields,
"node", node_name, node.source_mapping, type_specific_fields, additional_fields,
)
self._data["elements"].append(element)
@ -479,10 +461,7 @@ class Output:
###################################################################################
def add_pretty_table(
self,
content: MyPrettyTable,
name: str,
additional_fields: Optional[Dict] = None,
self, content: MyPrettyTable, name: str, additional_fields: Optional[Dict] = None,
):
if additional_fields is None:
additional_fields = {}
@ -499,11 +478,7 @@ class Output:
###################################################################################
def add_other(
self,
name: str,
source_mapping,
slither,
additional_fields: Optional[Dict] = None,
self, name: str, source_mapping, slither, additional_fields: Optional[Dict] = None,
):
# If this a tuple with (filename, start, end), convert it to a source mapping.
if additional_fields is None:
@ -514,10 +489,7 @@ class Output:
source_id = next(
(
source_unit_id
for (
source_unit_id,
source_unit_filename,
) in slither.source_units.items()
for (source_unit_id, source_unit_filename,) in slither.source_units.items()
if source_unit_filename == filename
),
-1,

@ -444,15 +444,7 @@ def get_tests(solc_versions) -> Dict[str, List[str]]:
return tests
Item = namedtuple(
"TestItem",
[
"test_id",
"base_ver",
"solc_ver",
"is_legacy",
],
)
Item = namedtuple("TestItem", ["test_id", "base_ver", "solc_ver", "is_legacy",],)
def get_all_test() -> List[Item]:

Loading…
Cancel
Save