Merge branch 'dev' into 0xalpharush-patch-1

pull/1083/head
Josselin 3 years ago
commit 4b188a0e17
  1. 3
      .github/workflows/black.yml
  2. 3
      .github/workflows/linter.yml
  3. 3
      .github/workflows/pylint.yml
  4. 2
      README.md
  5. 5
      slither/__main__.py
  6. 80
      slither/analyses/data_dependency/data_dependency.py
  7. 1
      slither/core/declarations/solidity_variables.py
  8. 16
      slither/core/expressions/binary_operation.py
  9. 2
      slither/core/expressions/expression.py
  10. 2
      slither/core/expressions/expression_typed.py
  11. 5
      slither/core/scope/scope.py
  12. 4
      slither/core/variables/top_level_variable.py
  13. 2
      slither/detectors/abstract_detector.py
  14. 3
      slither/detectors/functions/dead_code.py
  15. 2
      slither/detectors/operations/missing_zero_address_validation.py
  16. 11
      slither/detectors/reentrancy/reentrancy.py
  17. 70
      slither/detectors/statements/unprotected_upgradeable.py
  18. 22
      slither/printers/abstract_printer.py
  19. 1
      slither/printers/guidance/echidna.py
  20. 2
      slither/printers/summary/modifier_calls.py
  21. 12
      slither/slithir/convert.py
  22. 2
      slither/slithir/operations/member.py
  23. 2
      slither/slithir/utils/ssa.py
  24. 2
      slither/slithir/utils/utils.py
  25. 18
      slither/solc_parsing/expressions/find_variable.py
  26. 10
      slither/solc_parsing/slither_compilation_unit_solc.py
  27. 11
      slither/solc_parsing/solidity_types/type_parsing.py
  28. 32
      slither/solc_parsing/variables/top_level_variable.py
  29. 50
      slither/solc_parsing/yul/parse_yul.py
  30. 3
      slither/tools/properties/properties/erc20.py
  31. 12
      slither/utils/command_line.py
  32. BIN
      tests/ast-parsing/compile/assembly-0.8.12-compact.zip
  33. BIN
      tests/ast-parsing/compile/assignment-0.8.12-compact.zip
  34. BIN
      tests/ast-parsing/compile/binaryoperation-0.8.12-compact.zip
  35. BIN
      tests/ast-parsing/compile/break-0.8.12-compact.zip
  36. BIN
      tests/ast-parsing/compile/call_to_variable-0.8.12-compact.zip
  37. BIN
      tests/ast-parsing/compile/comment-0.8.12-compact.zip
  38. BIN
      tests/ast-parsing/compile/conditional-0.8.12-compact.zip
  39. BIN
      tests/ast-parsing/compile/continue-0.8.12-compact.zip
  40. BIN
      tests/ast-parsing/compile/contract-0.8.12-compact.zip
  41. BIN
      tests/ast-parsing/compile/custom_error-0.8.12-compact.zip
  42. BIN
      tests/ast-parsing/compile/dowhile-0.8.12-compact.zip
  43. BIN
      tests/ast-parsing/compile/emit-0.8.12-compact.zip
  44. BIN
      tests/ast-parsing/compile/enum-0.8.12-compact.zip
  45. BIN
      tests/ast-parsing/compile/event-0.8.12-compact.zip
  46. BIN
      tests/ast-parsing/compile/for-0.8.12-compact.zip
  47. BIN
      tests/ast-parsing/compile/function-0.8.12-compact.zip
  48. BIN
      tests/ast-parsing/compile/functioncall-0.8.12-compact.zip
  49. BIN
      tests/ast-parsing/compile/if-0.8.12-compact.zip
  50. BIN
      tests/ast-parsing/compile/import_interface_with_struct_from_top_level-0.4.0-legacy.zip
  51. BIN
      tests/ast-parsing/compile/import_interface_with_struct_from_top_level-0.4.1-legacy.zip
  52. BIN
      tests/ast-parsing/compile/import_interface_with_struct_from_top_level-0.4.10-legacy.zip
  53. BIN
      tests/ast-parsing/compile/import_interface_with_struct_from_top_level-0.4.11-legacy.zip
  54. BIN
      tests/ast-parsing/compile/import_interface_with_struct_from_top_level-0.4.12-compact.zip
  55. BIN
      tests/ast-parsing/compile/import_interface_with_struct_from_top_level-0.4.12-legacy.zip
  56. BIN
      tests/ast-parsing/compile/import_interface_with_struct_from_top_level-0.4.13-compact.zip
  57. BIN
      tests/ast-parsing/compile/import_interface_with_struct_from_top_level-0.4.13-legacy.zip
  58. BIN
      tests/ast-parsing/compile/import_interface_with_struct_from_top_level-0.4.14-compact.zip
  59. BIN
      tests/ast-parsing/compile/import_interface_with_struct_from_top_level-0.4.14-legacy.zip
  60. BIN
      tests/ast-parsing/compile/import_interface_with_struct_from_top_level-0.4.15-compact.zip
  61. BIN
      tests/ast-parsing/compile/import_interface_with_struct_from_top_level-0.4.15-legacy.zip
  62. BIN
      tests/ast-parsing/compile/import_interface_with_struct_from_top_level-0.4.16-compact.zip
  63. BIN
      tests/ast-parsing/compile/import_interface_with_struct_from_top_level-0.4.16-legacy.zip
  64. BIN
      tests/ast-parsing/compile/import_interface_with_struct_from_top_level-0.4.17-compact.zip
  65. BIN
      tests/ast-parsing/compile/import_interface_with_struct_from_top_level-0.4.17-legacy.zip
  66. BIN
      tests/ast-parsing/compile/import_interface_with_struct_from_top_level-0.4.18-compact.zip
  67. BIN
      tests/ast-parsing/compile/import_interface_with_struct_from_top_level-0.4.18-legacy.zip
  68. BIN
      tests/ast-parsing/compile/import_interface_with_struct_from_top_level-0.4.19-compact.zip
  69. BIN
      tests/ast-parsing/compile/import_interface_with_struct_from_top_level-0.4.19-legacy.zip
  70. BIN
      tests/ast-parsing/compile/import_interface_with_struct_from_top_level-0.4.2-legacy.zip
  71. BIN
      tests/ast-parsing/compile/import_interface_with_struct_from_top_level-0.4.20-compact.zip
  72. BIN
      tests/ast-parsing/compile/import_interface_with_struct_from_top_level-0.4.20-legacy.zip
  73. BIN
      tests/ast-parsing/compile/import_interface_with_struct_from_top_level-0.4.21-compact.zip
  74. BIN
      tests/ast-parsing/compile/import_interface_with_struct_from_top_level-0.4.21-legacy.zip
  75. BIN
      tests/ast-parsing/compile/import_interface_with_struct_from_top_level-0.4.22-compact.zip
  76. BIN
      tests/ast-parsing/compile/import_interface_with_struct_from_top_level-0.4.22-legacy.zip
  77. BIN
      tests/ast-parsing/compile/import_interface_with_struct_from_top_level-0.4.23-compact.zip
  78. BIN
      tests/ast-parsing/compile/import_interface_with_struct_from_top_level-0.4.23-legacy.zip
  79. BIN
      tests/ast-parsing/compile/import_interface_with_struct_from_top_level-0.4.24-compact.zip
  80. BIN
      tests/ast-parsing/compile/import_interface_with_struct_from_top_level-0.4.24-legacy.zip
  81. BIN
      tests/ast-parsing/compile/import_interface_with_struct_from_top_level-0.4.25-compact.zip
  82. BIN
      tests/ast-parsing/compile/import_interface_with_struct_from_top_level-0.4.25-legacy.zip
  83. BIN
      tests/ast-parsing/compile/import_interface_with_struct_from_top_level-0.4.26-compact.zip
  84. BIN
      tests/ast-parsing/compile/import_interface_with_struct_from_top_level-0.4.26-legacy.zip
  85. BIN
      tests/ast-parsing/compile/import_interface_with_struct_from_top_level-0.4.3-legacy.zip
  86. BIN
      tests/ast-parsing/compile/import_interface_with_struct_from_top_level-0.4.4-legacy.zip
  87. BIN
      tests/ast-parsing/compile/import_interface_with_struct_from_top_level-0.4.5-legacy.zip
  88. BIN
      tests/ast-parsing/compile/import_interface_with_struct_from_top_level-0.4.6-legacy.zip
  89. BIN
      tests/ast-parsing/compile/import_interface_with_struct_from_top_level-0.4.7-legacy.zip
  90. BIN
      tests/ast-parsing/compile/import_interface_with_struct_from_top_level-0.4.8-legacy.zip
  91. BIN
      tests/ast-parsing/compile/import_interface_with_struct_from_top_level-0.4.9-legacy.zip
  92. BIN
      tests/ast-parsing/compile/import_interface_with_struct_from_top_level-0.5.0-compact.zip
  93. BIN
      tests/ast-parsing/compile/import_interface_with_struct_from_top_level-0.5.0-legacy.zip
  94. BIN
      tests/ast-parsing/compile/import_interface_with_struct_from_top_level-0.5.1-compact.zip
  95. BIN
      tests/ast-parsing/compile/import_interface_with_struct_from_top_level-0.5.1-legacy.zip
  96. BIN
      tests/ast-parsing/compile/import_interface_with_struct_from_top_level-0.5.10-compact.zip
  97. BIN
      tests/ast-parsing/compile/import_interface_with_struct_from_top_level-0.5.10-legacy.zip
  98. BIN
      tests/ast-parsing/compile/import_interface_with_struct_from_top_level-0.5.11-compact.zip
  99. BIN
      tests/ast-parsing/compile/import_interface_with_struct_from_top_level-0.5.11-legacy.zip
  100. BIN
      tests/ast-parsing/compile/import_interface_with_struct_from_top_level-0.5.12-compact.zip
  101. Some files were not shown because too many files have changed in this diff Show More

@ -36,7 +36,7 @@ jobs:
cp pyproject.toml .github/linters cp pyproject.toml .github/linters
- name: Black - name: Black
uses: docker://github/super-linter:v4 uses: github/super-linter/slim@v4.8.7
if: always() if: always()
env: env:
# run linter on everything to catch preexisting problems # run linter on everything to catch preexisting problems
@ -46,3 +46,4 @@ jobs:
# Run only black # Run only black
VALIDATE_PYTHON_BLACK: true VALIDATE_PYTHON_BLACK: true
PYTHON_BLACK_CONFIG_FILE: pyproject.toml PYTHON_BLACK_CONFIG_FILE: pyproject.toml
FILTER_REGEX_EXCLUDE: .*tests/.*.(json|zip|sol)

@ -36,7 +36,7 @@ jobs:
cp pyproject.toml .github/linters cp pyproject.toml .github/linters
- name: Lint everything else - name: Lint everything else
uses: docker://github/super-linter:v4 uses: github/super-linter/slim@v4
if: always() if: always()
env: env:
# run linter on everything to catch preexisting problems # run linter on everything to catch preexisting problems
@ -58,3 +58,4 @@ jobs:
VALIDATE_JSCPD: false VALIDATE_JSCPD: false
VALIDATE_PYTHON_MYPY: false VALIDATE_PYTHON_MYPY: false
SHELLCHECK_OPTS: "-e SC1090" SHELLCHECK_OPTS: "-e SC1090"
FILTER_REGEX_EXCLUDE: .*tests/.*.(json|zip|sol)

@ -36,7 +36,7 @@ jobs:
cp pyproject.toml .github/linters cp pyproject.toml .github/linters
- name: Pylint - name: Pylint
uses: docker://github/super-linter:v4 uses: github/super-linter/slim@v4
if: always() if: always()
env: env:
# run linter on everything to catch preexisting problems # run linter on everything to catch preexisting problems
@ -47,3 +47,4 @@ jobs:
VALIDATE_PYTHON: true VALIDATE_PYTHON: true
VALIDATE_PYTHON_PYLINT: true VALIDATE_PYTHON_PYLINT: true
PYTHON_PYLINT_CONFIG_FILE: pyproject.toml PYTHON_PYLINT_CONFIG_FILE: pyproject.toml
FILTER_REGEX_EXCLUDE: .*tests/.*.(json|zip|sol)

@ -40,7 +40,7 @@ Run Slither on a single file:
slither tests/uninitialized.sol slither tests/uninitialized.sol
``` ```
For additional configuration, see the [usage](https://github.com/trailofbits/slither/wiki/Usage) documentation. For GitHub action integration, see [slither-action](https://github.com/marketplace/actions/slither-action). For additional configuration, see the [usage](https://github.com/trailofbits/slither/wiki/Usage) documentation.
Use [solc-select](https://github.com/crytic/solc-select) if your contracts require older versions of solc. Use [solc-select](https://github.com/crytic/solc-select) if your contracts require older versions of solc.

@ -64,6 +64,9 @@ def process_single(target, args, detector_classes, printer_classes):
ast = "--ast-compact-json" ast = "--ast-compact-json"
if args.legacy_ast: if args.legacy_ast:
ast = "--ast-json" ast = "--ast-json"
if args.checklist:
args.show_ignored_findings = True
slither = Slither(target, ast_format=ast, **vars(args)) slither = Slither(target, ast_format=ast, **vars(args))
return _process(slither, detector_classes, printer_classes) return _process(slither, detector_classes, printer_classes)
@ -462,7 +465,7 @@ def parse_args(detector_classes, printer_classes): # pylint: disable=too-many-s
help="Provide a config file (default: slither.config.json)", help="Provide a config file (default: slither.config.json)",
action="store", action="store",
dest="config_file", dest="config_file",
default="slither.config.json", default=None,
) )
group_misc.add_argument( group_misc.add_argument(

@ -14,8 +14,9 @@ from slither.core.declarations import (
Structure, Structure,
) )
from slither.core.declarations.solidity_import_placeholder import SolidityImportPlaceHolder from slither.core.declarations.solidity_import_placeholder import SolidityImportPlaceHolder
from slither.core.variables.top_level_variable import TopLevelVariable
from slither.core.variables.variable import Variable from slither.core.variables.variable import Variable
from slither.slithir.operations import Index, OperationWithLValue, InternalCall from slither.slithir.operations import Index, OperationWithLValue, InternalCall, Operation
from slither.slithir.variables import ( from slither.slithir.variables import (
Constant, Constant,
LocalIRVariable, LocalIRVariable,
@ -38,10 +39,14 @@ if TYPE_CHECKING:
################################################################################### ###################################################################################
Variable_types = Union[Variable, SolidityVariable]
Context_types = Union[Contract, Function]
def is_dependent( def is_dependent(
variable: Variable, variable: Variable_types,
source: Variable, source: Variable_types,
context: Union[Contract, Function], context: Context_types,
only_unprotected: bool = False, only_unprotected: bool = False,
) -> bool: ) -> bool:
""" """
@ -69,9 +74,9 @@ def is_dependent(
def is_dependent_ssa( def is_dependent_ssa(
variable: Variable, variable: Variable_types,
source: Variable, source: Variable_types,
context: Union[Contract, Function], context: Context_types,
only_unprotected: bool = False, only_unprotected: bool = False,
) -> bool: ) -> bool:
""" """
@ -105,7 +110,12 @@ GENERIC_TAINT = {
} }
def is_tainted(variable, context, only_unprotected=False, ignore_generic_taint=False): def is_tainted(
variable: Variable_types,
context: Context_types,
only_unprotected: bool = False,
ignore_generic_taint: bool = False,
) -> bool:
""" """
Args: Args:
variable variable
@ -127,7 +137,12 @@ def is_tainted(variable, context, only_unprotected=False, ignore_generic_taint=F
) )
def is_tainted_ssa(variable, context, only_unprotected=False, ignore_generic_taint=False): def is_tainted_ssa(
variable: Variable_types,
context: Context_types,
only_unprotected: bool = False,
ignore_generic_taint: bool = False,
):
""" """
Args: Args:
variable variable
@ -150,8 +165,8 @@ def is_tainted_ssa(variable, context, only_unprotected=False, ignore_generic_tai
def get_dependencies( def get_dependencies(
variable: Variable, variable: Variable_types,
context: Union[Contract, Function], context: Context_types,
only_unprotected: bool = False, only_unprotected: bool = False,
) -> Set[Variable]: ) -> Set[Variable]:
""" """
@ -170,7 +185,7 @@ def get_dependencies(
def get_all_dependencies( def get_all_dependencies(
context: Union[Contract, Function], only_unprotected: bool = False context: Context_types, only_unprotected: bool = False
) -> Dict[Variable, Set[Variable]]: ) -> Dict[Variable, Set[Variable]]:
""" """
Return the dictionary of dependencies. Return the dictionary of dependencies.
@ -187,8 +202,8 @@ def get_all_dependencies(
def get_dependencies_ssa( def get_dependencies_ssa(
variable: Variable, variable: Variable_types,
context: Union[Contract, Function], context: Context_types,
only_unprotected: bool = False, only_unprotected: bool = False,
) -> Set[Variable]: ) -> Set[Variable]:
""" """
@ -207,7 +222,7 @@ def get_dependencies_ssa(
def get_all_dependencies_ssa( def get_all_dependencies_ssa(
context: Union[Contract, Function], only_unprotected: bool = False context: Context_types, only_unprotected: bool = False
) -> Dict[Variable, Set[Variable]]: ) -> Dict[Variable, Set[Variable]]:
""" """
Return the dictionary of dependencies. Return the dictionary of dependencies.
@ -249,9 +264,9 @@ KEY_INPUT_SSA = "DATA_DEPENDENCY_INPUT_SSA"
################################################################################### ###################################################################################
def pprint_dependency(context): def pprint_dependency(caller_context: Context_types) -> None:
print("#### SSA ####") print("#### SSA ####")
context = context.context context = caller_context.context
for k, values in context[KEY_SSA].items(): for k, values in context[KEY_SSA].items():
print("{} ({}):".format(k, id(k))) print("{} ({}):".format(k, id(k)))
for v in values: for v in values:
@ -272,7 +287,7 @@ def pprint_dependency(context):
################################################################################### ###################################################################################
def compute_dependency(compilation_unit: "SlitherCompilationUnit"): def compute_dependency(compilation_unit: "SlitherCompilationUnit") -> None:
compilation_unit.context[KEY_INPUT] = set() compilation_unit.context[KEY_INPUT] = set()
compilation_unit.context[KEY_INPUT_SSA] = set() compilation_unit.context[KEY_INPUT_SSA] = set()
@ -280,14 +295,16 @@ def compute_dependency(compilation_unit: "SlitherCompilationUnit"):
compute_dependency_contract(contract, compilation_unit) compute_dependency_contract(contract, compilation_unit)
def compute_dependency_contract(contract, compilation_unit: "SlitherCompilationUnit"): def compute_dependency_contract(
contract: Contract, compilation_unit: "SlitherCompilationUnit"
) -> None:
if KEY_SSA in contract.context: if KEY_SSA in contract.context:
return return
contract.context[KEY_SSA] = {} contract.context[KEY_SSA] = {}
contract.context[KEY_SSA_UNPROTECTED] = {} contract.context[KEY_SSA_UNPROTECTED] = {}
for function in contract.functions + contract.modifiers: for function in contract.functions + list(contract.modifiers):
compute_dependency_function(function) compute_dependency_function(function)
propagate_function(contract, function, KEY_SSA, KEY_NON_SSA) propagate_function(contract, function, KEY_SSA, KEY_NON_SSA)
@ -302,7 +319,9 @@ def compute_dependency_contract(contract, compilation_unit: "SlitherCompilationU
propagate_contract(contract, KEY_SSA_UNPROTECTED, KEY_NON_SSA_UNPROTECTED) propagate_contract(contract, KEY_SSA_UNPROTECTED, KEY_NON_SSA_UNPROTECTED)
def propagate_function(contract, function, context_key, context_key_non_ssa): def propagate_function(
contract: Contract, function: Function, context_key: str, context_key_non_ssa: str
) -> None:
transitive_close_dependencies(function, context_key, context_key_non_ssa) transitive_close_dependencies(function, context_key, context_key_non_ssa)
# Propage data dependency # Propage data dependency
data_depencencies = function.context[context_key] data_depencencies = function.context[context_key]
@ -313,7 +332,9 @@ def propagate_function(contract, function, context_key, context_key_non_ssa):
contract.context[context_key][key].union(values) contract.context[context_key][key].union(values)
def transitive_close_dependencies(context, context_key, context_key_non_ssa): def transitive_close_dependencies(
context: Context_types, context_key: str, context_key_non_ssa: str
) -> None:
# transitive closure # transitive closure
changed = True changed = True
keys = context.context[context_key].keys() keys = context.context[context_key].keys()
@ -336,11 +357,11 @@ def transitive_close_dependencies(context, context_key, context_key_non_ssa):
context.context[context_key_non_ssa] = convert_to_non_ssa(context.context[context_key]) context.context[context_key_non_ssa] = convert_to_non_ssa(context.context[context_key])
def propagate_contract(contract, context_key, context_key_non_ssa): def propagate_contract(contract: Contract, context_key: str, context_key_non_ssa: str) -> None:
transitive_close_dependencies(contract, context_key, context_key_non_ssa) transitive_close_dependencies(contract, context_key, context_key_non_ssa)
def add_dependency(lvalue, function, ir, is_protected): def add_dependency(lvalue: Variable, function: Function, ir: Operation, is_protected: bool) -> None:
if not lvalue in function.context[KEY_SSA]: if not lvalue in function.context[KEY_SSA]:
function.context[KEY_SSA][lvalue] = set() function.context[KEY_SSA][lvalue] = set()
if not is_protected: if not is_protected:
@ -361,7 +382,7 @@ def add_dependency(lvalue, function, ir, is_protected):
] ]
def compute_dependency_function(function): def compute_dependency_function(function: Function) -> None:
if KEY_SSA in function.context: if KEY_SSA in function.context:
return return
@ -386,7 +407,7 @@ def compute_dependency_function(function):
) )
def convert_variable_to_non_ssa(v): def convert_variable_to_non_ssa(v: Variable_types) -> Variable_types:
if isinstance( if isinstance(
v, v,
( (
@ -410,14 +431,17 @@ def convert_variable_to_non_ssa(v):
Function, Function,
Type, Type,
SolidityImportPlaceHolder, SolidityImportPlaceHolder,
TopLevelVariable,
), ),
) )
return v return v
def convert_to_non_ssa(data_depencies): def convert_to_non_ssa(
data_depencies: Dict[Variable_types, Set[Variable_types]]
) -> Dict[Variable_types, Set[Variable_types]]:
# Need to create new set() as its changed during iteration # Need to create new set() as its changed during iteration
ret = {} ret: Dict[Variable_types, Set[Variable_types]] = {}
for (k, values) in data_depencies.items(): for (k, values) in data_depencies.items():
var = convert_variable_to_non_ssa(k) var = convert_variable_to_non_ssa(k)
if not var in ret: if not var in ret:

@ -71,6 +71,7 @@ SOLIDITY_FUNCTIONS: Dict[str, List[str]] = {
"abi.encodeWithSelector()": ["bytes"], "abi.encodeWithSelector()": ["bytes"],
"abi.encodeWithSignature()": ["bytes"], "abi.encodeWithSignature()": ["bytes"],
"bytes.concat()": ["bytes"], "bytes.concat()": ["bytes"],
"string.concat()": ["string"],
# abi.decode returns an a list arbitrary types # abi.decode returns an a list arbitrary types
"abi.decode()": [], "abi.decode()": [],
"type(address)": [], "type(address)": [],

@ -40,8 +40,11 @@ class BinaryOperationType(Enum):
GREATER_SIGNED = 22 GREATER_SIGNED = 22
RIGHT_SHIFT_ARITHMETIC = 23 RIGHT_SHIFT_ARITHMETIC = 23
# pylint: disable=too-many-branches
@staticmethod @staticmethod
def get_type(operation_type: "BinaryOperation"): # pylint: disable=too-many-branches def get_type(
operation_type: "BinaryOperation",
) -> "BinaryOperationType":
if operation_type == "**": if operation_type == "**":
return BinaryOperationType.POWER return BinaryOperationType.POWER
if operation_type == "*": if operation_type == "*":
@ -93,7 +96,7 @@ class BinaryOperationType(Enum):
raise SlitherCoreError("get_type: Unknown operation type {})".format(operation_type)) raise SlitherCoreError("get_type: Unknown operation type {})".format(operation_type))
def __str__(self): # pylint: disable=too-many-branches def __str__(self) -> str: # pylint: disable=too-many-branches
if self == BinaryOperationType.POWER: if self == BinaryOperationType.POWER:
return "**" return "**"
if self == BinaryOperationType.MULTIPLICATION: if self == BinaryOperationType.MULTIPLICATION:
@ -146,7 +149,12 @@ class BinaryOperationType(Enum):
class BinaryOperation(ExpressionTyped): class BinaryOperation(ExpressionTyped):
def __init__(self, left_expression, right_expression, expression_type): def __init__(
self,
left_expression: Expression,
right_expression: Expression,
expression_type: BinaryOperationType,
) -> None:
assert isinstance(left_expression, Expression) assert isinstance(left_expression, Expression)
assert isinstance(right_expression, Expression) assert isinstance(right_expression, Expression)
super().__init__() super().__init__()
@ -169,5 +177,5 @@ class BinaryOperation(ExpressionTyped):
def type(self) -> BinaryOperationType: def type(self) -> BinaryOperationType:
return self._type return self._type
def __str__(self): def __str__(self) -> str:
return str(self.expression_left) + " " + str(self.type) + " " + str(self.expression_right) return str(self.expression_left) + " " + str(self.type) + " " + str(self.expression_right)

@ -2,7 +2,7 @@ from slither.core.source_mapping.source_mapping import SourceMapping
class Expression(SourceMapping): class Expression(SourceMapping):
def __init__(self): def __init__(self) -> None:
super().__init__() super().__init__()
self._is_lvalue = False self._is_lvalue = False

@ -7,7 +7,7 @@ if TYPE_CHECKING:
class ExpressionTyped(Expression): # pylint: disable=too-few-public-methods class ExpressionTyped(Expression): # pylint: disable=too-few-public-methods
def __init__(self): def __init__(self) -> None:
super().__init__() super().__init__()
self._type: Optional["Type"] = None self._type: Optional["Type"] = None

@ -6,6 +6,7 @@ from slither.core.declarations.custom_error_top_level import CustomErrorTopLevel
from slither.core.declarations.enum_top_level import EnumTopLevel from slither.core.declarations.enum_top_level import EnumTopLevel
from slither.core.declarations.function_top_level import FunctionTopLevel from slither.core.declarations.function_top_level import FunctionTopLevel
from slither.core.declarations.structure_top_level import StructureTopLevel from slither.core.declarations.structure_top_level import StructureTopLevel
from slither.core.variables.top_level_variable import TopLevelVariable
from slither.slithir.variables import Constant from slither.slithir.variables import Constant
@ -36,6 +37,7 @@ class FileScope:
self.imports: Set[Import] = set() self.imports: Set[Import] = set()
self.pragmas: Set[Pragma] = set() self.pragmas: Set[Pragma] = set()
self.structures: Dict[str, StructureTopLevel] = {} self.structures: Dict[str, StructureTopLevel] = {}
self.variables: Dict[str, TopLevelVariable] = {}
def add_accesible_scopes(self) -> bool: def add_accesible_scopes(self) -> bool:
""" """
@ -69,6 +71,9 @@ class FileScope:
if not _dict_contain(new_scope.structures, self.structures): if not _dict_contain(new_scope.structures, self.structures):
self.structures.update(new_scope.structures) self.structures.update(new_scope.structures)
learn_something = True learn_something = True
if not _dict_contain(new_scope.variables, self.variables):
self.variables.update(new_scope.variables)
learn_something = True
return learn_something return learn_something

@ -5,12 +5,14 @@ from slither.core.variables.variable import Variable
if TYPE_CHECKING: if TYPE_CHECKING:
from slither.core.cfg.node import Node from slither.core.cfg.node import Node
from slither.core.scope.scope import FileScope
class TopLevelVariable(TopLevel, Variable): class TopLevelVariable(TopLevel, Variable):
def __init__(self): def __init__(self, scope: "FileScope"):
super().__init__() super().__init__()
self._node_initialization: Optional["Node"] = None self._node_initialization: Optional["Node"] = None
self.file_scope = scope
# endregion # endregion
################################################################################### ###################################################################################

@ -208,6 +208,8 @@ class AbstractDetector(metaclass=abc.ABCMeta):
return [r for (idx, r) in enumerate(results) if idx not in indexes_converted] return [r for (idx, r) in enumerate(results) if idx not in indexes_converted]
except ValueError: except ValueError:
self.logger.error(yellow("Malformed input. Example of valid input: 0,1,2,3")) self.logger.error(yellow("Malformed input. Example of valid input: 0,1,2,3"))
results = sorted(results, key=lambda x: x["id"])
return results return results
@property @property

@ -68,6 +68,9 @@ contract Contract{
function.contract_declarer.is_from_dependency() function.contract_declarer.is_from_dependency()
): ):
continue continue
# Continue if the functon is not implemented because it means the contract is abstract
if not function.is_implemented:
continue
info = [function, " is never used and should be removed\n"] info = [function, " is never used and should be removed\n"]
res = self.generate_result(info) res = self.generate_result(info)
results.append(res) results.append(res)

@ -40,7 +40,7 @@ contract C {
} }
} }
``` ```
Bob calls `updateOwner` without specifying the `newOwner`, soBob loses ownership of the contract. Bob calls `updateOwner` without specifying the `newOwner`, so Bob loses ownership of the contract.
""" """
# endregion wiki_exploit_scenario # endregion wiki_exploit_scenario

@ -283,11 +283,12 @@ class Reentrancy(AbstractDetector):
def detect_reentrancy(self, contract): def detect_reentrancy(self, contract):
for function in contract.functions_and_modifiers_declared: for function in contract.functions_and_modifiers_declared:
if function.is_implemented: if not function.is_constructor:
if self.KEY in function.context: if function.is_implemented:
continue if self.KEY in function.context:
self._explore(function.entry_point, []) continue
function.context[self.KEY] = True self._explore(function.entry_point, [])
function.context[self.KEY] = True
def _detect(self): def _detect(self):
"""""" """"""

@ -21,6 +21,14 @@ def _can_be_destroyed(contract) -> List[Function]:
return targets return targets
def _has_initializer_modifier(functions: List[Function]) -> bool:
for f in functions:
for m in f.modifiers:
if m.name == "initializer":
return True
return False
class UnprotectedUpgradeable(AbstractDetector): class UnprotectedUpgradeable(AbstractDetector):
ARGUMENT = "unprotected-upgrade" ARGUMENT = "unprotected-upgrade"
@ -61,34 +69,38 @@ class UnprotectedUpgradeable(AbstractDetector):
for contract in self.compilation_unit.contracts_derived: for contract in self.compilation_unit.contracts_derived:
if contract.is_upgradeable: if contract.is_upgradeable:
functions_that_can_destroy = _can_be_destroyed(contract) if not _has_initializer_modifier(contract.constructors):
if functions_that_can_destroy: functions_that_can_destroy = _can_be_destroyed(contract)
initiliaze_functions = [f for f in contract.functions if f.name == "initialize"] if functions_that_can_destroy:
vars_init_ = [ initiliaze_functions = [
init.all_state_variables_written() for init in initiliaze_functions f for f in contract.functions if f.name == "initialize"
] ]
vars_init = [item for sublist in vars_init_ for item in sublist]
vars_init_ = [
vars_init_in_constructors_ = [ init.all_state_variables_written() for init in initiliaze_functions
f.all_state_variables_written() for f in contract.constructors ]
] vars_init = [item for sublist in vars_init_ for item in sublist]
vars_init_in_constructors = [
item for sublist in vars_init_in_constructors_ for item in sublist vars_init_in_constructors_ = [
] f.all_state_variables_written() for f in contract.constructors
if vars_init and (set(vars_init) - set(vars_init_in_constructors)): ]
info = ( vars_init_in_constructors = [
[ item for sublist in vars_init_in_constructors_ for item in sublist
contract, ]
" is an upgradeable contract that does not protect its initiliaze functions: ", if vars_init and (set(vars_init) - set(vars_init_in_constructors)):
] info = (
+ initiliaze_functions [
+ [ contract,
". Anyone can delete the contract with: ", " is an upgradeable contract that does not protect its initiliaze functions: ",
] ]
+ functions_that_can_destroy + initiliaze_functions
) + [
". Anyone can delete the contract with: ",
res = self.generate_result(info) ]
results.append(res) + functions_that_can_destroy
)
res = self.generate_result(info)
results.append(res)
return results return results

@ -1,6 +1,13 @@
import abc import abc
from logging import Logger
from typing import TYPE_CHECKING, Union, List, Optional, Dict
from slither.utils import output from slither.utils import output
from slither.utils.output import SupportedOutput
if TYPE_CHECKING:
from slither import Slither
class IncorrectPrinterInitialization(Exception): class IncorrectPrinterInitialization(Exception):
@ -13,7 +20,7 @@ class AbstractPrinter(metaclass=abc.ABCMeta):
WIKI = "" WIKI = ""
def __init__(self, slither, logger): def __init__(self, slither: "Slither", logger: Logger) -> None:
self.slither = slither self.slither = slither
self.contracts = slither.contracts self.contracts = slither.contracts
self.filename = slither.filename self.filename = slither.filename
@ -34,11 +41,15 @@ class AbstractPrinter(metaclass=abc.ABCMeta):
f"WIKI is not initialized {self.__class__.__name__}" f"WIKI is not initialized {self.__class__.__name__}"
) )
def info(self, info): def info(self, info: str) -> None:
if self.logger: if self.logger:
self.logger.info(info) self.logger.info(info)
def generate_output(self, info, additional_fields=None): def generate_output(
self,
info: Union[str, List[Union[str, SupportedOutput]]],
additional_fields: Optional[Dict] = None,
) -> output.Output:
if additional_fields is None: if additional_fields is None:
additional_fields = {} additional_fields = {}
printer_output = output.Output(info, additional_fields) printer_output = output.Output(info, additional_fields)
@ -47,6 +58,5 @@ class AbstractPrinter(metaclass=abc.ABCMeta):
return printer_output return printer_output
@abc.abstractmethod @abc.abstractmethod
def output(self, filename): def output(self, filename: str) -> output.Output:
"""TODO Documentation""" pass
return

@ -391,6 +391,7 @@ class Echidna(AbstractPrinter):
"have_external_calls": external_calls, "have_external_calls": external_calls,
"call_a_parameter": call_parameters, "call_a_parameter": call_parameters,
"use_balance": use_balance, "use_balance": use_balance,
"solc_versions": [unit.solc_version for unit in self.slither.compilation_units],
} }
self.info(json.dumps(d, indent=4)) self.info(json.dumps(d, indent=4))

@ -38,6 +38,8 @@ class Modifiers(AbstractPrinter):
table.add_row([function.name, [m.name for m in set(modifiers)]]) table.add_row([function.name, [m.name for m in set(modifiers)]])
txt += "\n" + str(table) txt += "\n" + str(table)
self.info(txt) self.info(txt)
all_txt += txt
all_tables.append((contract.name, table))
res = self.generate_output(all_txt) res = self.generate_output(all_txt)
for name, table in all_tables: for name, table in all_tables:

@ -944,6 +944,18 @@ def extract_tmp_call(ins: TmpCall, contract: Optional[Contract]): # pylint: dis
s.set_expression(ins.expression) s.set_expression(ins.expression)
return s return s
if ins.ori.variable_left == ElementaryType("string") and ins.ori.variable_right == Constant(
"concat"
):
s = SolidityCall(
SolidityFunction("string.concat()"),
ins.nbr_arguments,
ins.lvalue,
ins.type_call,
)
s.set_expression(ins.expression)
return s
msgcall = HighLevelCall( msgcall = HighLevelCall(
ins.ori.variable_left, ins.ori.variable_left,
ins.ori.variable_right, ins.ori.variable_right,

@ -22,7 +22,7 @@ class Member(OperationWithLValue):
# f.h(1); # f.h(1);
# } # }
# } # }
# Can be an ElementaryType because of bytes.concat # Can be an ElementaryType because of bytes.concat, string.concat
assert is_valid_rvalue(variable_left) or isinstance( assert is_valid_rvalue(variable_left) or isinstance(
variable_left, variable_left,
(Contract, Enum, Function, CustomError, SolidityImportPlaceHolder, ElementaryType), (Contract, Enum, Function, CustomError, SolidityImportPlaceHolder, ElementaryType),

@ -13,6 +13,7 @@ from slither.core.declarations.solidity_import_placeholder import SolidityImport
from slither.core.solidity_types.type import Type from slither.core.solidity_types.type import Type
from slither.core.variables.local_variable import LocalVariable from slither.core.variables.local_variable import LocalVariable
from slither.core.variables.state_variable import StateVariable from slither.core.variables.state_variable import StateVariable
from slither.core.variables.top_level_variable import TopLevelVariable
from slither.slithir.operations import ( from slither.slithir.operations import (
Assignment, Assignment,
Binary, Binary,
@ -617,6 +618,7 @@ def get(
Function, Function,
Type, Type,
SolidityImportPlaceHolder, SolidityImportPlaceHolder,
TopLevelVariable,
), ),
) # type for abi.decode(.., t) ) # type for abi.decode(.., t)
return variable return variable

@ -2,6 +2,7 @@ from slither.core.variables.local_variable import LocalVariable
from slither.core.variables.state_variable import StateVariable from slither.core.variables.state_variable import StateVariable
from slither.core.declarations.solidity_variables import SolidityVariable from slither.core.declarations.solidity_variables import SolidityVariable
from slither.core.variables.top_level_variable import TopLevelVariable
from slither.slithir.variables.temporary import TemporaryVariable from slither.slithir.variables.temporary import TemporaryVariable
from slither.slithir.variables.constant import Constant from slither.slithir.variables.constant import Constant
@ -15,6 +16,7 @@ def is_valid_rvalue(v):
( (
StateVariable, StateVariable,
LocalVariable, LocalVariable,
TopLevelVariable,
TemporaryVariable, TemporaryVariable,
Constant, Constant,
SolidityVariable, SolidityVariable,

@ -19,6 +19,7 @@ from slither.core.solidity_types import (
FunctionType, FunctionType,
MappingType, MappingType,
) )
from slither.core.variables.top_level_variable import TopLevelVariable
from slither.core.variables.variable import Variable from slither.core.variables.variable import Variable
from slither.exceptions import SlitherError from slither.exceptions import SlitherError
from slither.solc_parsing.declarations.caller_context import CallerContextExpression from slither.solc_parsing.declarations.caller_context import CallerContextExpression
@ -98,7 +99,9 @@ def _find_variable_in_function_parser(
def _find_top_level( def _find_top_level(
var_name: str, scope: "FileScope" var_name: str, scope: "FileScope"
) -> Tuple[Optional[Union[Enum, Structure, SolidityImportPlaceHolder, CustomError]], bool]: ) -> Tuple[
Optional[Union[Enum, Structure, SolidityImportPlaceHolder, CustomError, TopLevelVariable]], bool
]:
""" """
Return the top level variable use, and a boolean indicating if the variable returning was cretead Return the top level variable use, and a boolean indicating if the variable returning was cretead
If the variable was created, it has no source_mapping If the variable was created, it has no source_mapping
@ -126,6 +129,9 @@ def _find_top_level(
if custom_error.solidity_signature == var_name: if custom_error.solidity_signature == var_name:
return custom_error, False return custom_error, False
if var_name in scope.variables:
return scope.variables[var_name], False
return None, False return None, False
@ -210,6 +216,8 @@ def _find_variable_init(
) -> Tuple[List[Contract], List["Function"], FileScope,]: ) -> Tuple[List[Contract], List["Function"], FileScope,]:
from slither.solc_parsing.declarations.contract import ContractSolc from slither.solc_parsing.declarations.contract import ContractSolc
from slither.solc_parsing.declarations.function import FunctionSolc from slither.solc_parsing.declarations.function import FunctionSolc
from slither.solc_parsing.declarations.structure_top_level import StructureTopLevelSolc
from slither.solc_parsing.variables.top_level_variable import TopLevelVariableSolc
direct_contracts: List[Contract] direct_contracts: List[Contract]
direct_functions_parser: List[Function] direct_functions_parser: List[Function]
@ -244,6 +252,14 @@ def _find_variable_init(
else: else:
assert isinstance(underlying_function, FunctionContract) assert isinstance(underlying_function, FunctionContract)
scope = underlying_function.contract.file_scope scope = underlying_function.contract.file_scope
elif isinstance(caller_context, StructureTopLevelSolc):
direct_contracts = []
direct_functions_parser = []
scope = caller_context.underlying_structure.file_scope
elif isinstance(caller_context, TopLevelVariableSolc):
direct_contracts = []
direct_functions_parser = []
scope = caller_context.underlying_variable.file_scope
else: else:
raise SlitherError( raise SlitherError(
f"{type(caller_context)} ({caller_context} is not valid for find_variable" f"{type(caller_context)} ({caller_context} is not valid for find_variable"

@ -239,12 +239,13 @@ class SlitherCompilationUnitSolc:
self._parse_enum(top_level_data, filename) self._parse_enum(top_level_data, filename)
elif top_level_data[self.get_key()] == "VariableDeclaration": elif top_level_data[self.get_key()] == "VariableDeclaration":
var = TopLevelVariable() var = TopLevelVariable(scope)
var_parser = TopLevelVariableSolc(var, top_level_data) var_parser = TopLevelVariableSolc(var, top_level_data, self)
var.set_offset(top_level_data["src"], self._compilation_unit) var.set_offset(top_level_data["src"], self._compilation_unit)
self._compilation_unit.variables_top_level.append(var) self._compilation_unit.variables_top_level.append(var)
self._variables_top_level_parser.append(var_parser) self._variables_top_level_parser.append(var_parser)
scope.variables[var.name] = var
elif top_level_data[self.get_key()] == "FunctionDefinition": elif top_level_data[self.get_key()] == "FunctionDefinition":
scope = self.compilation_unit.get_scope(filename) scope = self.compilation_unit.get_scope(filename)
func = FunctionTopLevel(self._compilation_unit, scope) func = FunctionTopLevel(self._compilation_unit, scope)
@ -495,6 +496,7 @@ Please rename it, this name is reserved for Slither's internals"""
for lib in libraries: for lib in libraries:
self._analyze_struct_events(lib) self._analyze_struct_events(lib)
self._analyze_top_level_variables()
self._analyze_top_level_structures() self._analyze_top_level_structures()
# Start with the contracts without inheritance # Start with the contracts without inheritance
@ -580,9 +582,9 @@ Please rename it, this name is reserved for Slither's internals"""
def _analyze_top_level_variables(self): def _analyze_top_level_variables(self):
try: try:
for var in self._variables_top_level_parser: for var in self._variables_top_level_parser:
var.analyze(self) var.analyze(var)
except (VariableNotFound, KeyError) as e: except (VariableNotFound, KeyError) as e:
raise SlitherException(f"Missing struct {e} during top level structure analyze") from e raise SlitherException(f"Missing {e} during variable analyze") from e
def _analyze_params_top_level_function(self): def _analyze_params_top_level_function(self):
for func_parser in self._functions_top_level_parser: for func_parser in self._functions_top_level_parser:

@ -220,6 +220,7 @@ def parse_type(
from slither.solc_parsing.declarations.custom_error import CustomErrorSolc from slither.solc_parsing.declarations.custom_error import CustomErrorSolc
from slither.solc_parsing.declarations.structure_top_level import StructureTopLevelSolc from slither.solc_parsing.declarations.structure_top_level import StructureTopLevelSolc
from slither.solc_parsing.slither_compilation_unit_solc import SlitherCompilationUnitSolc from slither.solc_parsing.slither_compilation_unit_solc import SlitherCompilationUnitSolc
from slither.solc_parsing.variables.top_level_variable import TopLevelVariableSolc
sl: "SlitherCompilationUnit" sl: "SlitherCompilationUnit"
# Note: for convenicence top level functions use the same parser than function in contract # Note: for convenicence top level functions use the same parser than function in contract
@ -245,9 +246,11 @@ def parse_type(
all_enums += enums_direct_access all_enums += enums_direct_access
contracts = sl.contracts contracts = sl.contracts
functions = [] functions = []
elif isinstance(caller_context, (StructureTopLevelSolc, CustomErrorSolc)): elif isinstance(caller_context, (StructureTopLevelSolc, CustomErrorSolc, TopLevelVariableSolc)):
if isinstance(caller_context, StructureTopLevelSolc): if isinstance(caller_context, StructureTopLevelSolc):
scope = caller_context.underlying_structure.file_scope scope = caller_context.underlying_structure.file_scope
elif isinstance(caller_context, TopLevelVariableSolc):
scope = caller_context.underlying_variable.file_scope
else: else:
assert isinstance(caller_context, CustomErrorSolc) assert isinstance(caller_context, CustomErrorSolc)
custom_error = caller_context.underlying_custom_error custom_error = caller_context.underlying_custom_error
@ -258,8 +261,10 @@ def parse_type(
scope = custom_error.contract.file_scope scope = custom_error.contract.file_scope
next_context = caller_context.slither_parser next_context = caller_context.slither_parser
structures_direct_access = [] structures_direct_access = list(scope.structures.values())
all_structures = scope.structures.values() all_structuress = [c.structures for c in scope.contracts.values()]
all_structures = [item for sublist in all_structuress for item in sublist]
all_structures += structures_direct_access
enums_direct_access = [] enums_direct_access = []
all_enums = scope.enums.values() all_enums = scope.enums.values()
contracts = scope.contracts.values() contracts = scope.contracts.values()

@ -1,12 +1,38 @@
from typing import Dict from typing import Dict, TYPE_CHECKING
from slither.core.variables.top_level_variable import TopLevelVariable from slither.core.variables.top_level_variable import TopLevelVariable
from slither.solc_parsing.variables.variable_declaration import VariableDeclarationSolc from slither.solc_parsing.variables.variable_declaration import VariableDeclarationSolc
from slither.solc_parsing.declarations.caller_context import CallerContextExpression
if TYPE_CHECKING:
from slither.solc_parsing.slither_compilation_unit_solc import SlitherCompilationUnitSolc
from slither.core.compilation_unit import SlitherCompilationUnit
class TopLevelVariableSolc(VariableDeclarationSolc):
def __init__(self, variable: TopLevelVariable, variable_data: Dict): class TopLevelVariableSolc(VariableDeclarationSolc, CallerContextExpression):
def __init__(
self,
variable: TopLevelVariable,
variable_data: Dict,
slither_parser: "SlitherCompilationUnitSolc",
):
super().__init__(variable, variable_data) super().__init__(variable, variable_data)
self._slither_parser = slither_parser
@property
def is_compact_ast(self) -> bool:
return self._slither_parser.is_compact_ast
@property
def compilation_unit(self) -> "SlitherCompilationUnit":
return self._slither_parser.compilation_unit
def get_key(self) -> str:
return self._slither_parser.get_key()
@property
def slither_parser(self) -> "SlitherCompilationUnitSolc":
return self._slither_parser
@property @property
def underlying_variable(self) -> TopLevelVariable: def underlying_variable(self) -> TopLevelVariable:

@ -730,6 +730,39 @@ def _check_for_state_variable_name(root: YulScope, potential_name: str) -> Optio
return None return None
def _parse_yul_magic_suffixes(name: str, root: YulScope) -> Optional[Expression]:
# check for magic suffixes
# TODO: the following leads to wrong IR
# Currently SlithIR doesnt support raw access to memory
# So things like .offset/.slot will return the variable
# Instaed of the actual offset/slot
if name.endswith("_slot") or name.endswith(".slot"):
potential_name = name[:-5]
variable_found = _check_for_state_variable_name(root, potential_name)
if variable_found:
return variable_found
var = root.function.get_local_variable_from_name(potential_name)
if var and var.is_storage:
return Identifier(var)
if name.endswith("_offset") or name.endswith(".offset"):
potential_name = name[:-7]
variable_found = _check_for_state_variable_name(root, potential_name)
if variable_found:
return variable_found
var = root.function.get_local_variable_from_name(potential_name)
if var and var.location == "calldata":
return Identifier(var)
if name.endswith(".length"):
# TODO: length should create a new IP operation LENGTH var
# The code below is an hotfix to allow slither to process length in yul
# Until we have a better support
potential_name = name[:-7]
var = root.function.get_local_variable_from_name(potential_name)
if var and var.location == "calldata":
return Identifier(var)
return None
def parse_yul_identifier(root: YulScope, _node: YulNode, ast: Dict) -> Optional[Expression]: def parse_yul_identifier(root: YulScope, _node: YulNode, ast: Dict) -> Optional[Expression]:
name = ast["name"] name = ast["name"]
@ -759,20 +792,9 @@ def parse_yul_identifier(root: YulScope, _node: YulNode, ast: Dict) -> Optional[
if func: if func:
return Identifier(func.underlying) return Identifier(func.underlying)
# check for magic suffixes magic_suffix = _parse_yul_magic_suffixes(name, root)
if name.endswith("_slot") or name.endswith(".slot"): if magic_suffix:
potential_name = name[:-5] return magic_suffix
variable_found = _check_for_state_variable_name(root, potential_name)
if variable_found:
return variable_found
var = root.function.get_local_variable_from_name(potential_name)
if var and var.is_storage:
return Identifier(var)
if name.endswith("_offset") or name.endswith(".offset"):
potential_name = name[:-7]
variable_found = _check_for_state_variable_name(root, potential_name)
if variable_found:
return variable_found
raise SlitherException(f"unresolved reference to identifier {name}") raise SlitherException(f"unresolved reference to identifier {name}")

@ -78,6 +78,7 @@ def generate_erc20(
if contract.compilation_unit.core.crytic_compile.type not in [ if contract.compilation_unit.core.crytic_compile.type not in [
PlatformType.TRUFFLE, PlatformType.TRUFFLE,
PlatformType.SOLC, PlatformType.SOLC,
PlatformType.BUILDER,
]: ]:
logging.error( logging.error(
f"{contract.compilation_unit.core.crytic_compile.type} not yet supported by slither-prop" f"{contract.compilation_unit.core.crytic_compile.type} not yet supported by slither-prop"
@ -162,7 +163,7 @@ def _initialization_recommendation(type_property: str) -> str:
# TODO: move this to crytic-compile # TODO: move this to crytic-compile
def _platform_to_output_dir(platform: AbstractPlatform) -> Path: def _platform_to_output_dir(platform: AbstractPlatform) -> Path:
if platform.TYPE == PlatformType.TRUFFLE: if platform.TYPE in [PlatformType.TRUFFLE, platform.TYPE == PlatformType.BUILDER]:
return Path(platform.target, "contracts", "crytic") return Path(platform.target, "contracts", "crytic")
if platform.TYPE == PlatformType.SOLC: if platform.TYPE == PlatformType.SOLC:
return Path(platform.target).parent return Path(platform.target).parent

@ -52,6 +52,15 @@ defaults_flag_in_config = {
def read_config_file(args): def read_config_file(args):
# No config file was provided as an argument
if args.config_file is None:
# Check wether the default config file is present
if os.path.exists("slither.config.json"):
# The default file exists, use it
args.config_file = "slither.config.json"
else:
return
if os.path.isfile(args.config_file): if os.path.isfile(args.config_file):
try: try:
with open(args.config_file, encoding="utf8") as f: with open(args.config_file, encoding="utf8") as f:
@ -70,6 +79,9 @@ def read_config_file(args):
logger.error( logger.error(
red("Impossible to read {}, please check the file {}".format(args.config_file, e)) red("Impossible to read {}, please check the file {}".format(args.config_file, e))
) )
else:
logger.error(red("File {} is not a file or does not exist".format(args.config_file)))
logger.error(yellow("Falling back to the default settings..."))
def output_to_markdown(detector_classes, printer_classes, filter_wiki): def output_to_markdown(detector_classes, printer_classes, filter_wiki):

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save