Merges with dev. Supports most recent changes to detector json output.

pull/238/head
rajeevgopalakrishna 6 years ago
commit fda131811e
  1. 1
      .travis.yml
  2. 2
      README.md
  3. 2
      examples/scripts/functions_called.py
  4. 8
      examples/scripts/possible_paths.py
  5. 2
      examples/scripts/slithIR.py
  6. 70
      scripts/tests_generate_expected_json_4.sh
  7. 31
      scripts/tests_generate_expected_json_5.sh
  8. 2
      scripts/travis_install.sh
  9. 1
      scripts/travis_test_4.sh
  10. 3
      scripts/travis_test_5.sh
  11. 2
      scripts/travis_test_etherscan.sh
  12. 21
      scripts/travis_test_simil.sh
  13. 17
      scripts/travis_test_upgradability.sh
  14. 7
      setup.py
  15. 20
      slither/__main__.py
  16. 2
      slither/all_exceptions.py
  17. 8
      slither/core/cfg/node.py
  18. 1
      slither/core/children/child_contract.py
  19. 13
      slither/core/children/child_inheritance.py
  20. 167
      slither/core/declarations/contract.py
  21. 8
      slither/core/declarations/enum.py
  22. 16
      slither/core/declarations/event.py
  23. 40
      slither/core/declarations/function.py
  24. 16
      slither/core/declarations/solidity_variables.py
  25. 10
      slither/core/declarations/structure.py
  26. 1
      slither/core/solidity_types/__init__.py
  27. 23
      slither/core/solidity_types/type_information.py
  28. 11
      slither/core/source_mapping/source_mapping.py
  29. 5
      slither/core/variables/local_variable.py
  30. 13
      slither/core/variables/state_variable.py
  31. 1
      slither/core/variables/variable.py
  32. 126
      slither/detectors/abstract_detector.py
  33. 13
      slither/detectors/attributes/const_functions.py
  34. 32
      slither/detectors/attributes/incorrect_solc.py
  35. 4
      slither/detectors/erc/incorrect_erc20_interface.py
  36. 2
      slither/detectors/erc/incorrect_erc721_interface.py
  37. 8
      slither/detectors/erc/unindexed_event_parameters.py
  38. 7
      slither/detectors/functions/arbitrary_send.py
  39. 5
      slither/detectors/functions/complex_function.py
  40. 9
      slither/detectors/functions/external_function.py
  41. 7
      slither/detectors/functions/suicidal.py
  42. 70
      slither/detectors/naming_convention/naming_convention.py
  43. 7
      slither/detectors/operations/block_timestamp.py
  44. 6
      slither/detectors/operations/low_level_calls.py
  45. 2
      slither/detectors/operations/unchecked_low_level_return_values.py
  46. 8
      slither/detectors/operations/unused_return_values.py
  47. 2
      slither/detectors/reentrancy/reentrancy.py
  48. 6
      slither/detectors/reentrancy/reentrancy_benign.py
  49. 6
      slither/detectors/reentrancy/reentrancy_eth.py
  50. 6
      slither/detectors/reentrancy/reentrancy_read_before_write.py
  51. 10
      slither/detectors/shadowing/abstract.py
  52. 12
      slither/detectors/shadowing/builtin_symbols.py
  53. 18
      slither/detectors/shadowing/local.py
  54. 10
      slither/detectors/shadowing/state.py
  55. 6
      slither/detectors/statements/assembly.py
  56. 7
      slither/detectors/statements/calls_in_loop.py
  57. 2
      slither/detectors/statements/controlled_delegatecall.py
  58. 10
      slither/detectors/statements/deprecated_calls.py
  59. 4
      slither/detectors/statements/incorrect_strict_equality.py
  60. 4
      slither/detectors/statements/tx_origin.py
  61. 4
      slither/detectors/variables/possible_const_state_variables.py
  62. 7
      slither/detectors/variables/uninitialized_local_variables.py
  63. 5
      slither/detectors/variables/uninitialized_state_variables.py
  64. 4
      slither/detectors/variables/uninitialized_storage_variables.py
  65. 4
      slither/detectors/variables/unused_state_variables.py
  66. 18
      slither/printers/inheritance/inheritance_graph.py
  67. 2
      slither/printers/summary/data_depenency.py
  68. 10
      slither/printers/summary/human_summary.py
  69. 6
      slither/printers/summary/slithir.py
  70. 6
      slither/printers/summary/slithir_ssa.py
  71. 90
      slither/slithir/convert.py
  72. 22
      slither/slithir/operations/internal_call.py
  73. 2
      slither/slithir/utils/ssa.py
  74. 2
      slither/solc_parsing/cfg/node.py
  75. 87
      slither/solc_parsing/declarations/contract.py
  76. 10
      slither/solc_parsing/declarations/function.py
  77. 2
      slither/solc_parsing/exceptions.py
  78. 73
      slither/solc_parsing/expressions/expression_parsing.py
  79. 10
      slither/solc_parsing/slitherSolc.py
  80. 6
      slither/solc_parsing/solidity_types/type_parsing.py
  81. 9
      slither/utils/inheritance_analysis.py
  82. 2
      slither/visitors/slithir/expression_to_slithir.py
  83. 4
      tests/check-upgradeability/test_5.txt
  84. 36
      tests/expected_json/arbitrary_send-0.5.1.arbitrary-send.json
  85. 4
      tests/expected_json/arbitrary_send-0.5.1.arbitrary-send.txt
  86. 36
      tests/expected_json/arbitrary_send.arbitrary-send.json
  87. 4
      tests/expected_json/arbitrary_send.arbitrary-send.txt
  88. 9
      tests/expected_json/backdoor.backdoor.json
  89. 11
      tests/expected_json/backdoor.suicidal.json
  90. 2
      tests/expected_json/backdoor.suicidal.txt
  91. 28
      tests/expected_json/const_state_variables.constable-states.json
  92. 11
      tests/expected_json/constant-0.5.1.constant-function.json
  93. 2
      tests/expected_json/constant-0.5.1.constant-function.txt
  94. 33
      tests/expected_json/constant.constant-function.json
  95. 6
      tests/expected_json/constant.constant-function.txt
  96. 32
      tests/expected_json/controlled_delegatecall.controlled-delegatecall.json
  97. 62
      tests/expected_json/deprecated_calls.deprecated-standards.json
  98. 48
      tests/expected_json/erc20_indexed.erc20-indexed.json
  99. 32
      tests/expected_json/external_function.external-function.json
  100. 8
      tests/expected_json/external_function.external-function.txt
  101. Some files were not shown because too many files have changed in this diff Show More

@ -18,6 +18,7 @@ env:
- TEST_SUITE=scripts/travis_test_cli.sh - TEST_SUITE=scripts/travis_test_cli.sh
- TEST_SUITE=scripts/travis_test_printers.sh - TEST_SUITE=scripts/travis_test_printers.sh
- TEST_SUITE=scripts/travis_test_slither_config.sh - TEST_SUITE=scripts/travis_test_slither_config.sh
- TEST_SUITE=scripts/travis_test_simil.sh
branches: branches:
only: only:
- master - master

@ -55,7 +55,7 @@ Num | Detector | What it Detects | Impact | Confidence
14 | `constant-function` | [Constant functions changing the state](https://github.com/crytic/slither/wiki/Detector-Documentation#constant-functions-changing-the-state) | Medium | Medium 14 | `constant-function` | [Constant functions changing the state](https://github.com/crytic/slither/wiki/Detector-Documentation#constant-functions-changing-the-state) | Medium | Medium
15 | `reentrancy-no-eth` | [Reentrancy vulnerabilities (no theft of ethers)](https://github.com/crytic/slither/wiki/Detector-Documentation#reentrancy-vulnerabilities-1) | Medium | Medium 15 | `reentrancy-no-eth` | [Reentrancy vulnerabilities (no theft of ethers)](https://github.com/crytic/slither/wiki/Detector-Documentation#reentrancy-vulnerabilities-1) | Medium | Medium
16 | `tx-origin` | [Dangerous usage of `tx.origin`](https://github.com/crytic/slither/wiki/Detector-Documentation#dangerous-usage-of-txorigin) | Medium | Medium 16 | `tx-origin` | [Dangerous usage of `tx.origin`](https://github.com/crytic/slither/wiki/Detector-Documentation#dangerous-usage-of-txorigin) | Medium | Medium
17 | `unchecked-lowlevel` | [Unchecked low-level calls](https://github.com/crytic/slither/wiki/Detector-Documentation#unchecked-low-level) | Medium | Medium 17 | `unchecked-lowlevel` | [Unchecked low-level calls](https://github.com/crytic/slither/wiki/Detector-Documentation#unchecked-low-level-calls) | Medium | Medium
18 | `unchecked-send` | [Unchecked send](https://github.com/crytic/slither/wiki/Detector-Documentation#unchecked-send) | Medium | Medium 18 | `unchecked-send` | [Unchecked send](https://github.com/crytic/slither/wiki/Detector-Documentation#unchecked-send) | Medium | Medium
19 | `uninitialized-local` | [Uninitialized local variables](https://github.com/crytic/slither/wiki/Detector-Documentation#uninitialized-local-variables) | Medium | Medium 19 | `uninitialized-local` | [Uninitialized local variables](https://github.com/crytic/slither/wiki/Detector-Documentation#uninitialized-local-variables) | Medium | Medium
20 | `unused-return` | [Unused return values](https://github.com/crytic/slither/wiki/Detector-Documentation#unused-return) | Medium | Medium 20 | `unused-return` | [Unused return values](https://github.com/crytic/slither/wiki/Detector-Documentation#unused-return) | Medium | Medium

@ -16,7 +16,7 @@ entry_point = contract.get_function_from_signature('entry_point()')
all_calls = entry_point.all_internal_calls() all_calls = entry_point.all_internal_calls()
all_calls_formated = [f.contract.name + '.' + f.name for f in all_calls] all_calls_formated = [f.canonical_name for f in all_calls]
# Print the result # Print the result
print('From entry_point the functions reached are {}'.format(all_calls_formated)) print('From entry_point the functions reached are {}'.format(all_calls_formated))

@ -69,7 +69,7 @@ def all_function_definitions(function):
:return: Returns a list composed of the provided function definition and any base definitions. :return: Returns a list composed of the provided function definition and any base definitions.
""" """
return [function] + [f for c in function.contract.inheritance return [function] + [f for c in function.contract.inheritance
for f in c.functions_and_modifiers_not_inherited for f in c.functions_and_modifiers_declared
if f.full_name == function.full_name] if f.full_name == function.full_name]
@ -86,7 +86,7 @@ def __find_target_paths(target_function, current_path=[]):
# Look through all functions # Look through all functions
for contract in slither.contracts: for contract in slither.contracts:
for function in contract.functions_and_modifiers_not_inherited: for function in contract.functions_and_modifiers_declared:
# If the function is already in our path, skip it. # If the function is already in our path, skip it.
if function in current_path: if function in current_path:
@ -179,12 +179,12 @@ reaching_functions = set([y for x in reaching_paths for y in x if y not in targe
# Print out all function names which can reach the targets. # Print out all function names which can reach the targets.
print(f"The following functions reach the specified targets:") print(f"The following functions reach the specified targets:")
for function_desc in sorted([f"{f.contract.name}.{f.full_name}" for f in reaching_functions]): for function_desc in sorted([f"{f.canonical_name}" for f in reaching_functions]):
print(f"-{function_desc}") print(f"-{function_desc}")
print("\n") print("\n")
# Format all function paths. # Format all function paths.
reaching_paths_str = [' -> '.join([f"{f.contract.name}.{f.full_name}" for f in reaching_path]) for reaching_path in reaching_paths] reaching_paths_str = [' -> '.join([f"{f.canonical_name}" for f in reaching_path]) for reaching_path in reaching_paths]
# Print a sorted list of all function paths which can reach the targets. # Print a sorted list of all function paths which can reach the targets.
print(f"The following paths reach the specified targets:") print(f"The following paths reach the specified targets:")

@ -15,7 +15,7 @@ for contract in slither.contracts:
for function in contract.functions: for function in contract.functions:
# Dont explore inherited functions # Dont explore inherited functions
if function.contract == contract: if function.contract_declarer == contract:
print('Function: {}'.format(function.name)) print('Function: {}'.format(function.name))

@ -21,38 +21,38 @@ generate_expected_json(){
} }
#generate_expected_json tests/deprecated_calls.sol "deprecated-standards" generate_expected_json tests/deprecated_calls.sol "deprecated-standards"
#generate_expected_json tests/erc20_indexed.sol "erc20-indexed" generate_expected_json tests/erc20_indexed.sol "erc20-indexed"
#generate_expected_json tests/incorrect_erc20_interface.sol "erc20-interface" generate_expected_json tests/incorrect_erc20_interface.sol "erc20-interface"
#generate_expected_json tests/incorrect_erc721_interface.sol "erc721-interface" generate_expected_json tests/incorrect_erc721_interface.sol "erc721-interface"
#generate_expected_json tests/uninitialized.sol "uninitialized-state" generate_expected_json tests/uninitialized.sol "uninitialized-state"
#generate_expected_json tests/backdoor.sol "backdoor" generate_expected_json tests/backdoor.sol "backdoor"
#generate_expected_json tests/backdoor.sol "suicidal" generate_expected_json tests/backdoor.sol "suicidal"
#generate_expected_json tests/pragma.0.4.24.sol "pragma" generate_expected_json tests/pragma.0.4.24.sol "pragma"
#generate_expected_json tests/old_solc.sol.json "solc-version" generate_expected_json tests/old_solc.sol.json "solc-version"
#generate_expected_json tests/reentrancy.sol "reentrancy-eth" generate_expected_json tests/reentrancy.sol "reentrancy-eth"
#generate_expected_json tests/uninitialized_storage_pointer.sol "uninitialized-storage" generate_expected_json tests/uninitialized_storage_pointer.sol "uninitialized-storage"
#generate_expected_json tests/tx_origin.sol "tx-origin" generate_expected_json tests/tx_origin.sol "tx-origin"
#generate_expected_json tests/unused_state.sol "unused-state" generate_expected_json tests/unused_state.sol "unused-state"
#generate_expected_json tests/locked_ether.sol "locked-ether" generate_expected_json tests/locked_ether.sol "locked-ether"
#generate_expected_json tests/arbitrary_send.sol "arbitrary-send" generate_expected_json tests/arbitrary_send.sol "arbitrary-send"
#generate_expected_json tests/inline_assembly_contract.sol "assembly" generate_expected_json tests/inline_assembly_contract.sol "assembly"
#generate_expected_json tests/inline_assembly_library.sol "assembly" generate_expected_json tests/inline_assembly_library.sol "assembly"
#generate_expected_json tests/low_level_calls.sol "low-level-calls" generate_expected_json tests/low_level_calls.sol "low-level-calls"
#generate_expected_json tests/const_state_variables.sol "constable-states" generate_expected_json tests/const_state_variables.sol "constable-states"
#generate_expected_json tests/external_function.sol "external-function" generate_expected_json tests/external_function.sol "external-function"
#generate_expected_json tests/external_function_2.sol "external-function" generate_expected_json tests/external_function_2.sol "external-function"
#generate_expected_json tests/naming_convention.sol "naming-convention" generate_expected_json tests/naming_convention.sol "naming-convention"
#generate_expected_json tests/uninitialized_local_variable.sol "uninitialized-local" generate_expected_json tests/uninitialized_local_variable.sol "uninitialized-local"
#generate_expected_json tests/controlled_delegatecall.sol "controlled-delegatecall" generate_expected_json tests/controlled_delegatecall.sol "controlled-delegatecall"
#generate_expected_json tests/constant.sol "constant-function" generate_expected_json tests/constant.sol "constant-function"
#generate_expected_json tests/unused_return.sol "unused-return" generate_expected_json tests/unused_return.sol "unused-return"
#generate_expected_json tests/shadowing_state_variable.sol "shadowing-state" generate_expected_json tests/shadowing_state_variable.sol "shadowing-state"
#generate_expected_json tests/shadowing_abstract.sol "shadowing-abstract" generate_expected_json tests/shadowing_abstract.sol "shadowing-abstract"
#generate_expected_json tests/timestamp.sol "timestamp" generate_expected_json tests/timestamp.sol "timestamp"
#generate_expected_json tests/multiple_calls_in_loop.sol "calls-loop" generate_expected_json tests/multiple_calls_in_loop.sol "calls-loop"
#generate_expected_json tests/shadowing_builtin_symbols.sol "shadowing-builtin" generate_expected_json tests/shadowing_builtin_symbols.sol "shadowing-builtin"
#generate_expected_json tests/shadowing_local_variable.sol "shadowing-local" generate_expected_json tests/shadowing_local_variable.sol "shadowing-local"
#generate_expected_json tests/solc_version_incorrect.sol "solc-version" generate_expected_json tests/solc_version_incorrect.sol "solc-version"
#generate_expected_json tests/right_to_left_override.sol "rtlo" generate_expected_json tests/right_to_left_override.sol "rtlo"
#generate_expected_json tests/unchecked_lowlevel.sol "unchecked-lowlevel" generate_expected_json tests/unchecked_lowlevel.sol "unchecked-lowlevel"

@ -20,19 +20,20 @@ generate_expected_json(){
sed "s|$CURRENT_PATH|$TRAVIS_PATH|g" "$output_filename_txt" -i sed "s|$CURRENT_PATH|$TRAVIS_PATH|g" "$output_filename_txt" -i
} }
#generate_expected_json tests/uninitialized-0.5.1.sol "uninitialized-state" generate_expected_json tests/solc_version_incorrect_05.ast.json "solc-version"
#generate_expected_json tests/backdoor.sol "backdoor" generate_expected_json tests/uninitialized-0.5.1.sol "uninitialized-state"
#generate_expected_json tests/backdoor.sol "suicidal" generate_expected_json tests/backdoor.sol "backdoor"
#generate_expected_json tests/old_solc.sol.json "solc-version" generate_expected_json tests/backdoor.sol "suicidal"
#generate_expected_json tests/reentrancy-0.5.1.sol "reentrancy-eth" generate_expected_json tests/old_solc.sol.json "solc-version"
#generate_expected_json tests/tx_origin-0.5.1.sol "tx-origin" generate_expected_json tests/reentrancy-0.5.1.sol "reentrancy-eth"
#generate_expected_json tests/locked_ether-0.5.1.sol "locked-ether" generate_expected_json tests/tx_origin-0.5.1.sol "tx-origin"
#generate_expected_json tests/arbitrary_send-0.5.1.sol "arbitrary-send" generate_expected_json tests/locked_ether-0.5.1.sol "locked-ether"
#generate_expected_json tests/inline_assembly_contract-0.5.1.sol "assembly" generate_expected_json tests/arbitrary_send-0.5.1.sol "arbitrary-send"
#generate_expected_json tests/inline_assembly_library-0.5.1.sol "assembly" generate_expected_json tests/inline_assembly_contract-0.5.1.sol "assembly"
#generate_expected_json tests/constant-0.5.1.sol "constant-function" generate_expected_json tests/inline_assembly_library-0.5.1.sol "assembly"
#generate_expected_json tests/incorrect_equality.sol "incorrect-equality" generate_expected_json tests/constant-0.5.1.sol "constant-function"
#generate_expected_json tests/too_many_digits.sol "too-many-digits" generate_expected_json tests/incorrect_equality.sol "incorrect-equality"
#generate_expected_json tests/unchecked_lowlevel-0.5.1.sol "unchecked-lowlevel" generate_expected_json tests/too_many_digits.sol "too-many-digits"
#generate_expected_json tests/unchecked_send-0.5.1.sol "unchecked-send" generate_expected_json tests/unchecked_lowlevel-0.5.1.sol "unchecked-lowlevel"
generate_expected_json tests/unchecked_send-0.5.1.sol "unchecked-send"

@ -24,5 +24,3 @@ function install_solc {
install_solc install_solc

@ -94,6 +94,7 @@ test_slither tests/external_function_2.sol "external-function"
test_slither tests/naming_convention.sol "naming-convention" test_slither tests/naming_convention.sol "naming-convention"
#test_slither tests/complex_func.sol "complex-function" #test_slither tests/complex_func.sol "complex-function"
test_slither tests/controlled_delegatecall.sol "controlled-delegatecall" test_slither tests/controlled_delegatecall.sol "controlled-delegatecall"
test_slither tests/uninitialized_local_variable.sol "uninitialized-local"
test_slither tests/constant.sol "constant-function" test_slither tests/constant.sol "constant-function"
test_slither tests/unused_return.sol "unused-return" test_slither tests/unused_return.sol "unused-return"
test_slither tests/shadowing_abstract.sol "shadowing-abstract" test_slither tests/shadowing_abstract.sol "shadowing-abstract"

@ -69,6 +69,7 @@ test_slither(){
} }
test_slither tests/solc_version_incorrect_05.ast.json "solc-version"
test_slither tests/unchecked_lowlevel-0.5.1.sol "unchecked-lowlevel" test_slither tests/unchecked_lowlevel-0.5.1.sol "unchecked-lowlevel"
test_slither tests/unchecked_send-0.5.1.sol "unchecked-send" test_slither tests/unchecked_send-0.5.1.sol "unchecked-send"
test_slither tests/uninitialized-0.5.1.sol "uninitialized-state" test_slither tests/uninitialized-0.5.1.sol "uninitialized-state"
@ -87,7 +88,7 @@ test_slither tests/const_state_variables.sol "constable-states"
test_slither tests/external_function.sol "external-function" test_slither tests/external_function.sol "external-function"
test_slither tests/external_function_2.sol "external-function" test_slither tests/external_function_2.sol "external-function"
test_slither tests/naming_convention.sol "naming-convention" test_slither tests/naming_convention.sol "naming-convention"
##test_slither tests/complex_func.sol "complex-function" #test_slither tests/complex_func.sol "complex-function"
test_slither tests/controlled_delegatecall.sol "controlled-delegatecall" test_slither tests/controlled_delegatecall.sol "controlled-delegatecall"
test_slither tests/constant-0.5.1.sol "constant-function" test_slither tests/constant-0.5.1.sol "constant-function"
test_slither tests/unused_return.sol "unused-return" test_slither tests/unused_return.sol "unused-return"

@ -10,7 +10,7 @@ chmod +x solc-0.4.25
slither 0x7F37f78cBD74481E593F9C737776F7113d76B315 --solc "./solc-0.4.25" slither 0x7F37f78cBD74481E593F9C737776F7113d76B315 --solc "./solc-0.4.25"
if [ $? -ne 5 ] if [ $? -ne 6 ]
then then
echo "Etherscan test failed" echo "Etherscan test failed"
exit -1 exit -1

@ -0,0 +1,21 @@
#!/usr/bin/env bash
### Install requisites
pip3.6 install pybind11
pip3.6 install https://github.com/facebookresearch/fastText/archive/0.2.0.zip
### Test slither-simil
DIR_TESTS="tests/simil"
slither-simil info "" --filename $DIR_TESTS/../complex_func.sol --fname Complex.complexExternalWrites --solc solc-0.4.25 > test_1.txt 2>&1
DIFF=$(diff test_1.txt "$DIR_TESTS/test_1.txt")
if [ "$DIFF" != "" ]
then
echo "slither-simil failed"
cat test_1.txt
cat "$DIR_TESTS/test_1.txt"
exit -1
fi
rm test_1.txt

@ -8,8 +8,9 @@ slither-check-upgradeability "$DIR_TESTS/proxy.sol" Proxy "$DIR_TESTS/contractV1
DIFF=$(diff test_1.txt "$DIR_TESTS/test_1.txt") DIFF=$(diff test_1.txt "$DIR_TESTS/test_1.txt")
if [ "$DIFF" != "" ] if [ "$DIFF" != "" ]
then then
echo "slither-check-upgradeability failed" echo "slither-check-upgradeability 1 failed"
cat test_1.txt cat test_1.txt
echo ""
cat "$DIR_TESTS/test_1.txt" cat "$DIR_TESTS/test_1.txt"
exit -1 exit -1
fi fi
@ -18,8 +19,9 @@ slither-check-upgradeability "$DIR_TESTS/proxy.sol" Proxy "$DIR_TESTS/contractV1
DIFF=$(diff test_2.txt "$DIR_TESTS/test_2.txt") DIFF=$(diff test_2.txt "$DIR_TESTS/test_2.txt")
if [ "$DIFF" != "" ] if [ "$DIFF" != "" ]
then then
echo "slither-check-upgradeability failed" echo "slither-check-upgradeability 2 failed"
cat test_2.txt cat test_2.txt
echo ""
cat "$DIR_TESTS/test_2.txt" cat "$DIR_TESTS/test_2.txt"
exit -1 exit -1
fi fi
@ -28,8 +30,9 @@ slither-check-upgradeability "$DIR_TESTS/proxy.sol" Proxy "$DIR_TESTS/contractV1
DIFF=$(diff test_3.txt "$DIR_TESTS/test_3.txt") DIFF=$(diff test_3.txt "$DIR_TESTS/test_3.txt")
if [ "$DIFF" != "" ] if [ "$DIFF" != "" ]
then then
echo "slither-check-upgradeability failed" echo "slither-check-upgradeability 3 failed"
cat test_3.txt cat test_3.txt
echo ""
cat "$DIR_TESTS/test_3.txt" cat "$DIR_TESTS/test_3.txt"
exit -1 exit -1
fi fi
@ -38,8 +41,9 @@ slither-check-upgradeability "$DIR_TESTS/proxy.sol" Proxy "$DIR_TESTS/contractV1
DIFF=$(diff test_4.txt "$DIR_TESTS/test_4.txt") DIFF=$(diff test_4.txt "$DIR_TESTS/test_4.txt")
if [ "$DIFF" != "" ] if [ "$DIFF" != "" ]
then then
echo "slither-check-upgradeability failed" echo "slither-check-upgradeability 4 failed"
cat test_4.txt cat test_4.txt
echo ""
cat "$DIR_TESTS/test_4.txt" cat "$DIR_TESTS/test_4.txt"
exit -1 exit -1
fi fi
@ -48,9 +52,12 @@ slither-check-upgradeability "$DIR_TESTS/proxy.sol" Proxy "$DIR_TESTS/contract_i
DIFF=$(diff test_5.txt "$DIR_TESTS/test_5.txt") DIFF=$(diff test_5.txt "$DIR_TESTS/test_5.txt")
if [ "$DIFF" != "" ] if [ "$DIFF" != "" ]
then then
echo "slither-check-upgradeability failed" echo "slither-check-upgradeability 5 failed"
cat test_5.txt cat test_5.txt
echo ""
cat "$DIR_TESTS/test_5.txt" cat "$DIR_TESTS/test_5.txt"
echo ""
echo "$DIFF"
exit -1 exit -1
fi fi

@ -5,17 +5,18 @@ setup(
description='Slither is a Solidity static analysis framework written in Python 3.', description='Slither is a Solidity static analysis framework written in Python 3.',
url='https://github.com/crytic/slither', url='https://github.com/crytic/slither',
author='Trail of Bits', author='Trail of Bits',
version='0.6.3', version='0.6.4',
packages=find_packages(), packages=find_packages(),
python_requires='>=3.6', python_requires='>=3.6',
install_requires=['prettytable>=0.7.2', 'pysha3>=1.0.2', 'crytic-compile>=0.1.0'], install_requires=['prettytable>=0.7.2', 'pysha3>=1.0.2', 'crytic-compile>=0.1.1'],
license='AGPL-3.0', license='AGPL-3.0',
long_description=open('README.md').read(), long_description=open('README.md').read(),
entry_points={ entry_points={
'console_scripts': [ 'console_scripts': [
'slither = slither.__main__:main', 'slither = slither.__main__:main',
'slither-check-upgradeability = utils.upgradeability.__main__:main', 'slither-check-upgradeability = utils.upgradeability.__main__:main',
'slither-find-paths = utils.possible_paths.__main__:main' 'slither-find-paths = utils.possible_paths.__main__:main',
'slither-simil = utils.similarity.__main__:main'
] ]
} }
) )

@ -100,16 +100,26 @@ def process_files(filenames, args, detector_classes, printer_classes):
################################################################################### ###################################################################################
def wrap_json_stdout(success, error_message, results=None): def wrap_json_detectors_results(success, error_message, results=None):
"""
Wrap the detector results.
:param success:
:param error_message:
:param results:
:return:
"""
results_json = {}
if results:
results_json['detectors'] = results
return { return {
"success": success, "success": success,
"error": error_message, "error": error_message,
"results": results "results": results_json
} }
def output_json(results, filename): def output_json(results, filename):
json_result = wrap_json_stdout(True, None, results) json_result = wrap_json_detectors_results(True, None, results)
if filename is None: if filename is None:
# Write json to console # Write json to console
print(json.dumps(json_result)) print(json.dumps(json_result))
@ -594,7 +604,7 @@ def main_impl(all_detector_classes, all_printer_classes):
except SlitherException as se: except SlitherException as se:
# Output our error accordingly, via JSON or logging. # Output our error accordingly, via JSON or logging.
if stdout_json: if stdout_json:
print(json.dumps(wrap_json_stdout(False, str(se), []))) print(json.dumps(wrap_json_detectors_results(False, str(se), [])))
else: else:
logging.error(red('Error:')) logging.error(red('Error:'))
logging.error(red(se)) logging.error(red(se))
@ -604,7 +614,7 @@ def main_impl(all_detector_classes, all_printer_classes):
except Exception: except Exception:
# Output our error accordingly, via JSON or logging. # Output our error accordingly, via JSON or logging.
if stdout_json: if stdout_json:
print(json.dumps(wrap_json_stdout(False, traceback.format_exc(), []))) print(json.dumps(wrap_json_detectors_results(False, traceback.format_exc(), [])))
else: else:
logging.error('Error in %s' % args.filename) logging.error('Error in %s' % args.filename)
logging.error(traceback.format_exc()) logging.error(traceback.format_exc())

@ -2,6 +2,6 @@
This module import all slither exceptions This module import all slither exceptions
""" """
from slither.slithir.exceptions import SlithIRError from slither.slithir.exceptions import SlithIRError
from slither.solc_parsing.exceptions import ParsingError, ParsingContractNotFound, ParsingNameReuse from slither.solc_parsing.exceptions import ParsingError, ParsingContractNotFound, ParsingNameReuse, VariableNotFound
from slither.core.exceptions import SlitherCoreError from slither.core.exceptions import SlitherCoreError
from slither.exceptions import SlitherException from slither.exceptions import SlitherException

@ -155,6 +155,7 @@ class Node(SourceMapping, ChildFunction):
self._library_calls = [] self._library_calls = []
self._low_level_calls = [] self._low_level_calls = []
self._external_calls_as_expressions = [] self._external_calls_as_expressions = []
self._internal_calls_as_expressions = []
self._irs = [] self._irs = []
self._irs_ssa = [] self._irs_ssa = []
@ -368,6 +369,13 @@ class Node(SourceMapping, ChildFunction):
""" """
return self._external_calls_as_expressions return self._external_calls_as_expressions
@property
def internal_calls_as_expressions(self):
"""
list(CallExpression): List of internal calls (that dont create a transaction)
"""
return self._internal_calls_as_expressions
@property @property
def calls_as_expression(self): def calls_as_expression(self):
return list(self._expression_calls) return list(self._expression_calls)

@ -11,3 +11,4 @@ class ChildContract:
@property @property
def contract(self): def contract(self):
return self._contract return self._contract

@ -0,0 +1,13 @@
class ChildInheritance:
def __init__(self):
super(ChildInheritance, self).__init__()
self._contract_declarer = None
def set_contract_declarer(self, contract):
self._contract_declarer = contract
@property
def contract_declarer(self):
return self._contract_declarer

@ -35,6 +35,7 @@ class Contract(ChildSlither, SourceMapping):
self._variables = {} self._variables = {}
self._modifiers = {} self._modifiers = {}
self._functions = {} self._functions = {}
self._using_for = {} self._using_for = {}
self._kind = None self._kind = None
@ -77,6 +78,20 @@ class Contract(ChildSlither, SourceMapping):
''' '''
return list(self._structures.values()) return list(self._structures.values())
@property
def structures_inherited(self):
'''
list(Structure): List of the inherited structures
'''
return [s for s in self.structures if s.contract != self]
@property
def structures_declared(self):
'''
list(Structues): List of the structures declared within the contract (not inherited)
'''
return [s for s in self.structures if s.contract == self]
def structures_as_dict(self): def structures_as_dict(self):
return self._structures return self._structures
@ -91,6 +106,20 @@ class Contract(ChildSlither, SourceMapping):
def enums(self): def enums(self):
return list(self._enums.values()) return list(self._enums.values())
@property
def enums_inherited(self):
'''
list(Enum): List of the inherited enums
'''
return [e for e in self.enums if e.contract != self]
@property
def enums_declared(self):
'''
list(Enum): List of the enums declared within the contract (not inherited)
'''
return [e for e in self.enums if e.contract == self]
def enums_as_dict(self): def enums_as_dict(self):
return self._enums return self._enums
@ -108,6 +137,20 @@ class Contract(ChildSlither, SourceMapping):
''' '''
return list(self._events.values()) return list(self._events.values())
@property
def events_inherited(self):
'''
list(Event): List of the inherited events
'''
return [e for e in self.events if e.contract != self]
@property
def events_declared(self):
'''
list(Event): List of the events declared within the contract (not inherited)
'''
return [e for e in self.events if e.contract == self]
def events_as_dict(self): def events_as_dict(self):
return self._events return self._events
@ -153,6 +196,20 @@ class Contract(ChildSlither, SourceMapping):
''' '''
return list(self._variables.values()) return list(self._variables.values())
@property
def state_variables_inherited(self):
'''
list(StateVariable): List of the inherited state variables
'''
return [s for s in self.state_variables if s.contract != self]
@property
def state_variables_declared(self):
'''
list(StateVariable): List of the state variables declared within the contract (not inherited)
'''
return [s for s in self.state_variables if s.contract == self]
@property @property
def slithir_variables(self): def slithir_variables(self):
''' '''
@ -177,18 +234,18 @@ class Contract(ChildSlither, SourceMapping):
executed, following the c3 linearization executed, following the c3 linearization
Return None if there is no constructor. Return None if there is no constructor.
''' '''
cst = self.constructor_not_inherited cst = self.constructors_declared
if cst: if cst:
return cst return cst
for inherited_contract in self.inheritance: for inherited_contract in self.inheritance:
cst = inherited_contract.constructor_not_inherited cst = inherited_contract.constructors_declared
if cst: if cst:
return cst return cst
return None return None
@property @property
def constructor_not_inherited(self): def constructors_declared(self):
return next((func for func in self.functions if func.is_constructor and func.contract == self), None) return next((func for func in self.functions if func.is_constructor and func.contract_declarer == self), None)
@property @property
def constructors(self): def constructors(self):
@ -238,29 +295,29 @@ class Contract(ChildSlither, SourceMapping):
''' '''
return list(self._functions.values()) return list(self._functions.values())
def functions_as_dict(self): def available_functions_as_dict(self):
return self._functions return {f.full_name: f for f in self._functions.values() if not f.is_shadowed}
@property @property
def functions_inherited(self): def functions_inherited(self):
''' '''
list(Function): List of the inherited functions list(Function): List of the inherited functions
''' '''
return [f for f in self.functions if f.contract != self] return [f for f in self.functions if f.contract_declarer != self]
@property @property
def functions_not_inherited(self): def functions_declared(self):
''' '''
list(Function): List of the functions defined within the contract (not inherited) list(Function): List of the functions defined within the contract (not inherited)
''' '''
return [f for f in self.functions if f.contract == self] return [f for f in self.functions if f.contract_declarer == self]
@property @property
def functions_entry_points(self): def functions_entry_points(self):
''' '''
list(Functions): List of public and external functions list(Functions): List of public and external functions
''' '''
return [f for f in self.functions if f.visibility in ['public', 'external']] return [f for f in self.functions if f.visibility in ['public', 'external'] and not f.is_shadowed]
@property @property
def modifiers(self): def modifiers(self):
@ -269,22 +326,22 @@ class Contract(ChildSlither, SourceMapping):
''' '''
return list(self._modifiers.values()) return list(self._modifiers.values())
def modifiers_as_dict(self): def available_modifiers_as_dict(self):
return self._modifiers return {m.full_name: m for m in self._modifiers.values() if not m.is_shadowed}
@property @property
def modifiers_inherited(self): def modifiers_inherited(self):
''' '''
list(Modifier): List of the inherited modifiers list(Modifier): List of the inherited modifiers
''' '''
return [m for m in self.modifiers if m.contract != self] return [m for m in self.modifiers if m.contract_declarer != self]
@property @property
def modifiers_not_inherited(self): def modifiers_declared(self):
''' '''
list(Modifier): List of the modifiers defined within the contract (not inherited) list(Modifier): List of the modifiers defined within the contract (not inherited)
''' '''
return [m for m in self.modifiers if m.contract == self] return [m for m in self.modifiers if m.contract_declarer == self]
@property @property
def functions_and_modifiers(self): def functions_and_modifiers(self):
@ -301,11 +358,36 @@ class Contract(ChildSlither, SourceMapping):
return self.functions_inherited + self.modifiers_inherited return self.functions_inherited + self.modifiers_inherited
@property @property
def functions_and_modifiers_not_inherited(self): def functions_and_modifiers_declared(self):
''' '''
list(Function|Modifier): List of the functions and modifiers defined within the contract (not inherited) list(Function|Modifier): List of the functions and modifiers defined within the contract (not inherited)
''' '''
return self.functions_not_inherited + self.modifiers_not_inherited return self.functions_declared + self.modifiers_declared
def available_elements_from_inheritances(self, elements, getter_available):
"""
:param elements: dict(canonical_name -> elements)
:param getter_available: fun x
:return:
"""
# keep track of the contracts visited
# to prevent an ovveride due to multiple inheritance of the same contract
# A is B, C, D is C, --> the second C was already seen
inherited_elements = {}
accessible_elements = {}
contracts_visited = []
for father in self.inheritance_reverse:
functions = {v.full_name: v for (_, v) in getter_available(father)
if not v.contract in contracts_visited}
contracts_visited.append(father)
inherited_elements.update(functions)
for element in inherited_elements.values():
accessible_elements[element.full_name] = elements[element.canonical_name]
return accessible_elements
# endregion # endregion
################################################################################### ###################################################################################
@ -367,45 +449,46 @@ class Contract(ChildSlither, SourceMapping):
''' '''
return [f for f in self.functions if f.is_writing(variable)] return [f for f in self.functions if f.is_writing(variable)]
def get_source_var_declaration(self, var): def get_function_from_signature(self, function_signature):
""" Return the source mapping where the variable is declared """
Return a function from a signature
Args: Args:
var (str): variable name function_signature (str): signature of the function (without return statement)
Returns: Returns:
(dict): sourceMapping Function
""" """
return next((x.source_mapping for x in self.variables if x.name == var)) return next((f for f in self.functions if f.full_name == function_signature and not f.is_shadowed), None)
def get_source_event_declaration(self, event):
""" Return the source mapping where the event is declared
def get_modifier_from_signature(self, modifier_signature):
"""
Return a modifier from a signature
Args: Args:
event (str): event name modifier_name (str): signature of the modifier
Returns: Returns:
(dict): sourceMapping Modifier
""" """
return next((x.source_mapping for x in self.events if x.name == event)) return next((m for m in self.modifiers if m.full_name == modifier_signature and not m.is_shadowed), None)
def get_function_from_signature(self, function_signature): def get_function_from_canonical_name(self, canonical_name):
""" """
Return a function from a signature Return a function from a a canonical name (contract.signature())
Args: Args:
function_signature (str): signature of the function (without return statement) canonical_name (str): canonical name of the function (without return statement)
Returns: Returns:
Function Function
""" """
return next((f for f in self.functions if f.full_name == function_signature), None) return next((f for f in self.functions if f.canonical_name == canonical_name), None)
def get_modifier_from_signature(self, modifier_signature): def get_modifier_from_canonical_name(self, canonical_name):
""" """
Return a modifier from a signature Return a modifier from a canonical name (contract.signature())
Args: Args:
modifier_name (str): signature of the modifier canonical_name (str): canonical name of the modifier
Returns: Returns:
Modifier Modifier
""" """
return next((m for m in self.modifiers if m.full_name == modifier_signature), None) return next((m for m in self.modifiers if m.canonical_name == canonical_name), None)
def get_state_variable_from_name(self, variable_name): def get_state_variable_from_name(self, variable_name):
""" """
@ -476,7 +559,7 @@ class Contract(ChildSlither, SourceMapping):
list(core.Function) list(core.Function)
''' '''
candidates = [c.functions_not_inherited for c in self.inheritance] candidates = [c.functions_declared for c in self.inheritance]
candidates = [candidate for sublist in candidates for candidate in sublist] candidates = [candidate for sublist in candidates for candidate in sublist]
return [f for f in candidates if f.full_name == function.full_name] return [f for f in candidates if f.full_name == function.full_name]
@ -490,10 +573,12 @@ class Contract(ChildSlither, SourceMapping):
@property @property
def all_functions_called(self): def all_functions_called(self):
''' '''
list(Function): List of functions reachable from the contract (include super) list(Function): List of functions reachable from the contract
Includes super, and private/internal functions not shadowed
''' '''
all_calls = [f.all_internal_calls() for f in self.functions + self.modifiers] + [self.functions + self.modifiers] all_calls = [f for f in self.functions + self.modifiers if not f.is_shadowed]
all_calls = [item for sublist in all_calls for item in sublist] + self.functions all_calls = [f.all_internal_calls() for f in all_calls] + [all_calls]
all_calls = [item for sublist in all_calls for item in sublist]
all_calls = list(set(all_calls)) all_calls = list(set(all_calls))
all_constructors = [c.constructor for c in self.inheritance] all_constructors = [c.constructor for c in self.inheritance]

@ -19,5 +19,13 @@ class Enum(ChildContract, SourceMapping):
def values(self): def values(self):
return self._values return self._values
def is_declared_by(self, contract):
"""
Check if the element is declared by the contract
:param contract:
:return:
"""
return self.contract == contract
def __str__(self): def __str__(self):
return self.name return self.name

@ -29,9 +29,25 @@ class Event(ChildContract, SourceMapping):
name, parameters = self.signature name, parameters = self.signature
return name+'('+','.join(parameters)+')' return name+'('+','.join(parameters)+')'
@property
def canonical_name(self):
''' Return the function signature as a str
Returns:
str: contract.func_name(type1,type2)
'''
return self.contract.name + self.full_name
@property @property
def elems(self): def elems(self):
return self._elems return self._elems
def is_declared_by(self, contract):
"""
Check if the element is declared by the contract
:param contract:
:return:
"""
return self.contract == contract
def __str__(self): def __str__(self):
return self.name return self.name

@ -6,6 +6,7 @@ from collections import namedtuple
from itertools import groupby from itertools import groupby
from slither.core.children.child_contract import ChildContract from slither.core.children.child_contract import ChildContract
from slither.core.children.child_inheritance import ChildInheritance
from slither.core.declarations.solidity_variables import (SolidityFunction, from slither.core.declarations.solidity_variables import (SolidityFunction,
SolidityVariable, SolidityVariable,
SolidityVariableComposed) SolidityVariableComposed)
@ -18,7 +19,7 @@ logger = logging.getLogger("Function")
ReacheableNode = namedtuple('ReacheableNode', ['node', 'ir']) ReacheableNode = namedtuple('ReacheableNode', ['node', 'ir'])
class Function(ChildContract, SourceMapping): class Function(ChildContract, ChildInheritance, SourceMapping):
""" """
Function class Function class
""" """
@ -39,8 +40,10 @@ class Function(ChildContract, SourceMapping):
self._slithir_variables = set() # slithir Temporary and references variables (but not SSA) self._slithir_variables = set() # slithir Temporary and references variables (but not SSA)
self._parameters = [] self._parameters = []
self._parameters_ssa = [] self._parameters_ssa = []
self._parameters_src = None
self._returns = [] self._returns = []
self._returns_ssa = [] self._returns_ssa = []
self._returns_src = None
self._return_values = None self._return_values = None
self._return_values_ssa = None self._return_values_ssa = None
self._vars_read = [] self._vars_read = []
@ -82,6 +85,8 @@ class Function(ChildContract, SourceMapping):
self._all_conditional_solidity_variables_read_with_loop = None self._all_conditional_solidity_variables_read_with_loop = None
self._all_solidity_variables_used_as_args = None self._all_solidity_variables_used_as_args = None
self._is_shadowed = False
# set(ReacheableNode) # set(ReacheableNode)
self._reachable_from_nodes = set() self._reachable_from_nodes = set()
self._reachable_from_functions = set() self._reachable_from_functions = set()
@ -114,12 +119,21 @@ class Function(ChildContract, SourceMapping):
name, parameters, _ = self.signature name, parameters, _ = self.signature
return name+'('+','.join(parameters)+')' return name+'('+','.join(parameters)+')'
@property
def canonical_name(self):
"""
str: contract.func_name(type1,type2)
Return the function signature without the return values
"""
name, parameters, _ = self.signature
return self.contract_declarer.name + '.' + name + '(' + ','.join(parameters) + ')'
@property @property
def is_constructor(self): def is_constructor(self):
""" """
bool: True if the function is the constructor bool: True if the function is the constructor
""" """
return self._is_constructor or self._name == self.contract.name return self._is_constructor or self._name == self.contract_declarer.name
@property @property
def contains_assembly(self): def contains_assembly(self):
@ -129,6 +143,14 @@ class Function(ChildContract, SourceMapping):
def slither(self): def slither(self):
return self.contract.slither return self.contract.slither
def is_declared_by(self, contract):
"""
Check if the element is declared by the contract
:param contract:
:return:
"""
return self.contract_declarer == contract
# endregion # endregion
################################################################################### ###################################################################################
################################################################################### ###################################################################################
@ -171,6 +193,14 @@ class Function(ChildContract, SourceMapping):
""" """
return self._pure return self._pure
@property
def is_shadowed(self):
return self._is_shadowed
@is_shadowed.setter
def is_shadowed(self, is_shadowed):
self._is_shadowed = is_shadowed
# endregion # endregion
################################################################################### ###################################################################################
################################################################################### ###################################################################################
@ -305,7 +335,7 @@ class Function(ChildContract, SourceMapping):
included. included.
""" """
# This is a list of contracts internally, so we convert it to a list of constructor functions. # This is a list of contracts internally, so we convert it to a list of constructor functions.
return [c.constructor_not_inherited for c in self._explicit_base_constructor_calls if c.constructor_not_inherited] return [c.constructors_declared for c in self._explicit_base_constructor_calls if c.constructors_declared]
# endregion # endregion
@ -558,7 +588,7 @@ class Function(ChildContract, SourceMapping):
list(core.Function) list(core.Function)
''' '''
candidates = [c.functions_not_inherited for c in self.contract.inheritance] candidates = [c.functions_declared for c in self.contract.inheritance]
candidates = [candidate for sublist in candidates for candidate in sublist] candidates = [candidate for sublist in candidates for candidate in sublist]
return [f for f in candidates if f.full_name == self.full_name] return [f for f in candidates if f.full_name == self.full_name]
@ -931,7 +961,7 @@ class Function(ChildContract, SourceMapping):
(str, str, str, list(str), list(str), listr(str), list(str), list(str); (str, str, str, list(str), list(str), listr(str), list(str), list(str);
contract_name, name, visibility, modifiers, vars read, vars written, internal_calls, external_calls_as_expressions contract_name, name, visibility, modifiers, vars read, vars written, internal_calls, external_calls_as_expressions
""" """
return (self.contract.name, self.full_name, self.visibility, return (self.contract_declarer.name, self.full_name, self.visibility,
[str(x) for x in self.modifiers], [str(x) for x in self.modifiers],
[str(x) for x in self.state_variables_read + self.solidity_variables_read], [str(x) for x in self.state_variables_read + self.solidity_variables_read],
[str(x) for x in self.state_variables_written], [str(x) for x in self.state_variables_written],

@ -1,6 +1,6 @@
# https://solidity.readthedocs.io/en/v0.4.24/units-and-global-variables.html # https://solidity.readthedocs.io/en/v0.4.24/units-and-global-variables.html
from slither.core.context.context import Context from slither.core.context.context import Context
from slither.core.solidity_types import ElementaryType from slither.core.solidity_types import ElementaryType, TypeInformation
SOLIDITY_VARIABLES = {"now":'uint256', SOLIDITY_VARIABLES = {"now":'uint256',
"this":'address', "this":'address',
@ -57,7 +57,8 @@ SOLIDITY_FUNCTIONS = {"gasleft()":['uint256'],
"abi.encodeWithSelector()":["bytes"], "abi.encodeWithSelector()":["bytes"],
"abi.encodeWithSignature()":["bytes"], "abi.encodeWithSignature()":["bytes"],
# abi.decode returns an a list arbitrary types # abi.decode returns an a list arbitrary types
"abi.decode()":[]} "abi.decode()":[],
"type(address)":[]}
def solidity_function_signature(name): def solidity_function_signature(name):
""" """
@ -125,10 +126,15 @@ class SolidityVariableComposed(SolidityVariable):
class SolidityFunction: class SolidityFunction:
# Non standard handling of type(address). This function returns an undefined object
# The type is dynamic
# https://solidity.readthedocs.io/en/latest/units-and-global-variables.html#type-information
# As a result, we set return_type during the Ir conversion
def __init__(self, name): def __init__(self, name):
assert name in SOLIDITY_FUNCTIONS assert name in SOLIDITY_FUNCTIONS
self._name = name self._name = name
self._return_type = [ElementaryType(x) for x in SOLIDITY_FUNCTIONS[self.name]]
@property @property
def name(self): def name(self):
@ -140,7 +146,11 @@ class SolidityFunction:
@property @property
def return_type(self): def return_type(self):
return [ElementaryType(x) for x in SOLIDITY_FUNCTIONS[self.name]] return self._return_type
@return_type.setter
def return_type(self, r):
self._return_type = r
def __str__(self): def __str__(self):
return self._name return self._name

@ -25,6 +25,15 @@ class Structure(ChildContract, SourceMapping):
def elems(self): def elems(self):
return self._elems return self._elems
def is_declared_by(self, contract):
"""
Check if the element is declared by the contract
:param contract:
:return:
"""
return self.contract == contract
@property @property
def elems_ordered(self): def elems_ordered(self):
ret = [] ret = []
@ -32,5 +41,6 @@ class Structure(ChildContract, SourceMapping):
ret.append(self._elems[e]) ret.append(self._elems[e])
return ret return ret
def __str__(self): def __str__(self):
return self.name return self.name

@ -3,3 +3,4 @@ from .elementary_type import ElementaryType
from .function_type import FunctionType from .function_type import FunctionType
from .mapping_type import MappingType from .mapping_type import MappingType
from .user_defined_type import UserDefinedType from .user_defined_type import UserDefinedType
from .type_information import TypeInformation

@ -0,0 +1,23 @@
from slither.core.solidity_types.type import Type
# Use to model the Type(X) function, which returns an undefined type
# https://solidity.readthedocs.io/en/latest/units-and-global-variables.html#type-information
class TypeInformation(Type):
def __init__(self, c):
from slither.core.declarations.contract import Contract
assert isinstance(c, (Contract))
super(TypeInformation, self).__init__()
self._type = c
@property
def type(self):
return self._type
def __str__(self):
return f'type({self.type.name})'
def __eq__(self, other):
if not isinstance(other, TypeInformation):
return False
return self.type == other.type

@ -125,16 +125,7 @@ class SourceMapping(Context):
@property @property
def source_mapping_str(self): def source_mapping_str(self):
# def relative_path(path): lines = self.source_mapping.get('lines', None)
# # Remove absolute path for printing
# # Truffle returns absolutePath
# splited_path = path.split(os.sep)
# if 'contracts' in splited_path:
# idx = splited_path.index('contracts')
# return os.sep.join(splited_path[idx-1:])
# return path
lines = self.source_mapping['lines']
if not lines: if not lines:
lines = '' lines = ''
elif len(lines) == 1: elif len(lines) == 1:

@ -50,3 +50,8 @@ class LocalVariable(ChildFunction, Variable):
return False return False
@property
def canonical_name(self):
return self.name

@ -4,6 +4,16 @@ from slither.utils.type import export_nested_types_from_variable
class StateVariable(ChildContract, Variable): class StateVariable(ChildContract, Variable):
def is_declared_by(self, contract):
"""
Check if the element is declared by the contract
:param contract:
:return:
"""
return self.contract == contract
################################################################################### ###################################################################################
################################################################################### ###################################################################################
# region Signature # region Signature
@ -36,7 +46,7 @@ class StateVariable(ChildContract, Variable):
@property @property
def canonical_name(self): def canonical_name(self):
return '{}:{}'.format(self.contract.name, self.name) return '{}.{}'.format(self.contract.name, self.name)
@property @property
def full_name(self): def full_name(self):
@ -51,3 +61,4 @@ class StateVariable(ChildContract, Variable):
# endregion # endregion
################################################################################### ###################################################################################
################################################################################### ###################################################################################

@ -78,6 +78,7 @@ class Variable(SourceMapping):
assert isinstance(t, (Type, list)) or t is None assert isinstance(t, (Type, list)) or t is None
self._type = t self._type = t
def __str__(self): def __str__(self):
return self._name return self._name

@ -146,27 +146,48 @@ class AbstractDetector(metaclass=abc.ABCMeta):
return d return d
@staticmethod @staticmethod
def _create_base_element(type, name, source_mapping, additional_fields={}): def _create_base_element(type, name, source_mapping, type_specific_fields={}, additional_fields={}):
element = {'type': type, element = {'type': type,
'name': name, 'name': name,
'source_mapping': source_mapping} 'source_mapping': source_mapping}
if type_specific_fields:
element['type_specific_fields'] = type_specific_fields
if additional_fields: if additional_fields:
element['additional_fields'] = additional_fields element['additional_fields'] = additional_fields
return element return element
@staticmethod @staticmethod
def add_variable_to_json(variable, d, additional_fields={}): def _create_parent_element(element):
from slither.core.variables.state_variable import StateVariable from slither.core.children.child_contract import ChildContract
from slither.core.variables.local_variable import LocalVariable from slither.core.children.child_function import ChildFunction
element = AbstractDetector._create_base_element('variable', variable.name, variable.source_mapping, additional_fields) from slither.core.children.child_inheritance import ChildInheritance
if isinstance(variable, StateVariable): if isinstance(element, ChildInheritance):
if element.contract_declarer:
contract = {'elements': []}
AbstractDetector.add_contract_to_json(element.contract_declarer, contract)
return contract['elements'][0]
elif isinstance(element, ChildContract):
if element.contract:
contract = {'elements': []} contract = {'elements': []}
AbstractDetector.add_contract_to_json(variable.contract, contract) AbstractDetector.add_contract_to_json(element.contract, contract)
element['contract'] = contract['elements'][0] return contract['elements'][0]
elif isinstance(variable, LocalVariable): elif isinstance(element, ChildFunction):
if element.function:
function = {'elements': []} function = {'elements': []}
AbstractDetector.add_function_to_json(variable.function, function) AbstractDetector.add_function_to_json(element.function, function)
element['function'] = function['elements'][0] return function['elements'][0]
return None
@staticmethod
def add_variable_to_json(variable, d, additional_fields={}):
type_specific_fields = {
'parent': AbstractDetector._create_parent_element(variable)
}
element = AbstractDetector._create_base_element('variable',
variable.name,
variable.source_mapping,
type_specific_fields,
additional_fields)
d['elements'].append(element) d['elements'].append(element)
@staticmethod @staticmethod
@ -176,56 +197,84 @@ class AbstractDetector(metaclass=abc.ABCMeta):
@staticmethod @staticmethod
def add_contract_to_json(contract, d, additional_fields={}): def add_contract_to_json(contract, d, additional_fields={}):
element = AbstractDetector._create_base_element('contract', contract.name, contract.source_mapping, additional_fields) element = AbstractDetector._create_base_element('contract',
contract.name,
contract.source_mapping,
{},
additional_fields)
d['elements'].append(element) d['elements'].append(element)
@staticmethod @staticmethod
def add_function_to_json(function, d, additional_fields={}): def add_function_to_json(function, d, additional_fields={}):
element = AbstractDetector._create_base_element('function', function.name, function.source_mapping, additional_fields) type_specific_fields = {
contract = {'elements':[]} 'parent': AbstractDetector._create_parent_element(function),
AbstractDetector.add_contract_to_json(function.contract, contract) 'signature': function.full_name
element['contract'] = contract['elements'][0] }
element = AbstractDetector._create_base_element('function',
function.name,
function.source_mapping,
type_specific_fields,
additional_fields)
d['elements'].append(element) d['elements'].append(element)
@staticmethod @staticmethod
def add_functions_to_json(functions, d): def add_functions_to_json(functions, d, additional_fields={}):
for function in sorted(functions, key=lambda x: x.name): for function in sorted(functions, key=lambda x: x.name):
AbstractDetector.add_function_to_json(function, d) AbstractDetector.add_function_to_json(function, d, additional_fields)
@staticmethod @staticmethod
def add_enum_to_json(enum, d, additional_fields={}): def add_enum_to_json(enum, d, additional_fields={}):
element = AbstractDetector._create_base_element('enum', enum.name, enum.source_mapping, additional_fields) type_specific_fields = {
contract = {'elements': []} 'parent': AbstractDetector._create_parent_element(enum)
AbstractDetector.add_contract_to_json(enum.contract, contract) }
element['contract'] = contract['elements'][0] element = AbstractDetector._create_base_element('enum',
enum.name,
enum.source_mapping,
type_specific_fields,
additional_fields)
d['elements'].append(element) d['elements'].append(element)
@staticmethod @staticmethod
def add_struct_to_json(struct, d, additional_fields={}): def add_struct_to_json(struct, d, additional_fields={}):
element = AbstractDetector._create_base_element('struct', struct.name, struct.source_mapping, additional_fields) type_specific_fields = {
contract = {'elements': []} 'parent': AbstractDetector._create_parent_element(struct)
AbstractDetector.add_contract_to_json(struct.contract, contract) }
element['contract'] = contract['elements'][0] element = AbstractDetector._create_base_element('struct',
struct.name,
struct.source_mapping,
type_specific_fields,
additional_fields)
d['elements'].append(element) d['elements'].append(element)
@staticmethod @staticmethod
def add_event_to_json(event, d, additional_fields={}): def add_event_to_json(event, d, additional_fields={}):
element = AbstractDetector._create_base_element('event', event.name, event.source_mapping, additional_fields) type_specific_fields = {
contract = {'elements':[]} 'parent': AbstractDetector._create_parent_element(event),
AbstractDetector.add_contract_to_json(event.contract, contract) 'signature': event.full_name
element['contract'] = contract['elements'][0] }
element = AbstractDetector._create_base_element('event',
event.name,
event.source_mapping,
type_specific_fields,
additional_fields)
d['elements'].append(element) d['elements'].append(element)
@staticmethod @staticmethod
def add_node_to_json(node, d, additional_fields={}): def add_node_to_json(node, d, additional_fields={}):
type_specific_fields = {
'parent': AbstractDetector._create_parent_element(node),
}
node_name = str(node.expression) if node.expression else "" node_name = str(node.expression) if node.expression else ""
element = AbstractDetector._create_base_element('node', node_name, node.source_mapping, additional_fields) element = AbstractDetector._create_base_element('node',
if node.function: node_name,
function = {'elements': []} node.source_mapping,
AbstractDetector.add_function_to_json(node.function, function) type_specific_fields,
element['function'] = function['elements'][0] additional_fields)
d['elements'].append(element) d['elements'].append(element)
@staticmethod @staticmethod
def add_nodes_to_json(nodes, d): def add_nodes_to_json(nodes, d):
for node in sorted(nodes, key=lambda x: x.node_id): for node in sorted(nodes, key=lambda x: x.node_id):
@ -233,10 +282,13 @@ class AbstractDetector(metaclass=abc.ABCMeta):
@staticmethod @staticmethod
def add_pragma_to_json(pragma, d, additional_fields={}): def add_pragma_to_json(pragma, d, additional_fields={}):
type_specific_fields = {
'directive': pragma.directive
}
element = AbstractDetector._create_base_element('pragma', element = AbstractDetector._create_base_element('pragma',
pragma.version, pragma.version,
pragma.source_mapping, pragma.source_mapping,
type_specific_fields,
additional_fields) additional_fields)
element['directive'] = pragma.directive
d['elements'].append(element) d['elements'].append(element)

@ -51,13 +51,13 @@ All the calls to `get` revert, breaking Bob's smart contract execution.'''
results = [] results = []
for c in self.contracts: for c in self.contracts:
for f in c.functions: for f in c.functions:
if f.contract != c: if f.contract_declarer != c:
continue continue
if f.view or f.pure: if f.view or f.pure:
if f.contains_assembly: if f.contains_assembly:
attr = 'view' if f.view else 'pure' attr = 'view' if f.view else 'pure'
info = '{}.{} ({}) is declared {} but contains assembly code\n' info = '{} ({}) is declared {} but contains assembly code\n'
info = info.format(f.contract.name, f.name, f.source_mapping_str, attr) info = info.format(f.canonical_name, f.source_mapping_str, attr)
json = self.generate_json_result(info, {'contains_assembly': True}) json = self.generate_json_result(info, {'contains_assembly': True})
self.add_function_to_json(f, json) self.add_function_to_json(f, json)
results.append(json) results.append(json)
@ -65,11 +65,10 @@ All the calls to `get` revert, breaking Bob's smart contract execution.'''
variables_written = f.all_state_variables_written() variables_written = f.all_state_variables_written()
if variables_written: if variables_written:
attr = 'view' if f.view else 'pure' attr = 'view' if f.view else 'pure'
info = '{}.{} ({}) is declared {} but changes state variables:\n' info = '{} ({}) is declared {} but changes state variables:\n'
info = info.format(f.contract.name, f.name, f.source_mapping_str, attr) info = info.format(f.canonical_name, f.source_mapping_str, attr)
for variable_written in variables_written: for variable_written in variables_written:
info += '\t- {}.{}\n'.format(variable_written.contract.name, info += '\t- {}\n'.format(variable_written.canonical_name)
variable_written.name)
json = self.generate_json_result(info, {'contains_assembly': False}) json = self.generate_json_result(info, {'contains_assembly': False})
self.add_function_to_json(f, json) self.add_function_to_json(f, json)

@ -23,31 +23,43 @@ class IncorrectSolc(AbstractDetector):
IMPACT = DetectorClassification.INFORMATIONAL IMPACT = DetectorClassification.INFORMATIONAL
CONFIDENCE = DetectorClassification.HIGH CONFIDENCE = DetectorClassification.HIGH
WIKI = 'https://github.com/crytic/slither/wiki/Detector-Documentation#incorrect-version-of-solidity' WIKI = 'https://github.com/crytic/slither/wiki/Detector-Documentation#incorrect-versions-of-solidity'
WIKI_TITLE = 'Incorrect versions of Solidity' WIKI_TITLE = 'Incorrect versions of Solidity'
WIKI_DESCRIPTION = ''' WIKI_DESCRIPTION = '''
Solc frequently releases new compiler versions. Using an old version prevents access to new Solidity security checks. Solc frequently releases new compiler versions. Using an old version prevents access to new Solidity security checks.
We recommend avoiding complex pragma statement.''' We recommend avoiding complex pragma statement.'''
WIKI_RECOMMENDATION = 'Use Solidity 0.4.25 or 0.5.2.' WIKI_RECOMMENDATION = '''
Use Solidity 0.4.25 or 0.5.3. Consider using the latest version of Solidity for testing the compilation, and a trusted version for deploying.'''
COMPLEX_PRAGMA = "is too complex" COMPLEX_PRAGMA_TXT = "is too complex"
OLD_VERSION = "allows old versions" OLD_VERSION_TXT = "allows old versions"
LESS_THAN = "uses lesser than" LESS_THAN_TXT = "uses lesser than"
TOO_RECENT_VERSION_TXT = "necessitates versions too recent to be trusted. Consider deploying with 0.5.3"
BUGGY_VERSION_TXT = "is known to contain severe issue (https://solidity.readthedocs.io/en/v0.5.8/bugs.html)"
# Indicates the allowed versions. # Indicates the allowed versions.
ALLOWED_VERSIONS = ["0.4.24", "0.4.25", "0.5.2", "0.5.3"] ALLOWED_VERSIONS = ["0.4.25", "0.4.26", "0.5.3"]
# Indicates the versions too recent.
TOO_RECENT_VERSIONS = ["0.5.4", "0.5.7", "0.5.8", "0.5.9", "0.5.10"]
# Indicates the versions that should not be used.
BUGGY_VERSIONS = ["0.4.22", "0.5.5", "0.5.6", "^0.4.22", "^0.5.5", "^0.5.6"]
def _check_version(self, version): def _check_version(self, version):
op = version[0] op = version[0]
if op and not op in ['>', '>=', '^']: if op and not op in ['>', '>=', '^']:
return self.LESS_THAN return self.LESS_THAN_TXT
version_number = '.'.join(version[2:]) version_number = '.'.join(version[2:])
if version_number not in self.ALLOWED_VERSIONS: if version_number not in self.ALLOWED_VERSIONS:
return self.OLD_VERSION if version_number in self.TOO_RECENT_VERSIONS:
return self.TOO_RECENT_VERSION_TXT
return self.OLD_VERSION_TXT
return None return None
def _check_pragma(self, version): def _check_pragma(self, version):
if version in self.BUGGY_VERSIONS:
return self.BUGGY_VERSION_TXT
versions = PATTERN.findall(version) versions = PATTERN.findall(version)
if len(versions) == 1: if len(versions) == 1:
version = versions[0] version = versions[0]
@ -58,10 +70,10 @@ We recommend avoiding complex pragma statement.'''
# Only allow two elements if the second one is # Only allow two elements if the second one is
# <0.5.0 or <0.6.0 # <0.5.0 or <0.6.0
if version_right not in [('<', '', '0', '5', '0'), ('<', '', '0', '6', '0')]: if version_right not in [('<', '', '0', '5', '0'), ('<', '', '0', '6', '0')]:
return self.COMPLEX_PRAGMA return self.COMPLEX_PRAGMA_TXT
return self._check_version(version_left) return self._check_version(version_left)
else: else:
return self.COMPLEX_PRAGMA return self.COMPLEX_PRAGMA_TXT
def _detect(self): def _detect(self):
""" """
Detects pragma statements that allow for outdated solc versions. Detects pragma statements that allow for outdated solc versions.

@ -61,6 +61,7 @@ contract Token{
Returns: Returns:
list(str) : list of incorrect function signatures list(str) : list of incorrect function signatures
""" """
# Verify this is an ERC20 contract. # Verify this is an ERC20 contract.
if not contract.is_possible_erc20(): if not contract.is_possible_erc20():
return [] return []
@ -72,6 +73,7 @@ contract Token{
funcs = contract.functions funcs = contract.functions
functions = [f for f in funcs if IncorrectERC20InterfaceDetection.incorrect_erc20_interface(f.signature)] functions = [f for f in funcs if IncorrectERC20InterfaceDetection.incorrect_erc20_interface(f.signature)]
return functions return functions
def _detect(self): def _detect(self):
@ -87,7 +89,7 @@ contract Token{
for function in functions: for function in functions:
info = "{} ({}) has incorrect ERC20 function interface: {} ({})\n".format(c.name, info = "{} ({}) has incorrect ERC20 function interface: {} ({})\n".format(c.name,
c.source_mapping_str, c.source_mapping_str,
function.name, function.full_name,
function.source_mapping_str) function.source_mapping_str)
json = self.generate_json_result(info) json = self.generate_json_result(info)
self.add_function_to_json(function, json) self.add_function_to_json(function, json)

@ -88,7 +88,7 @@ contract Token{
for function in functions: for function in functions:
info = "{} ({}) has incorrect ERC721 function interface: {} ({})\n".format(c.name, info = "{} ({}) has incorrect ERC721 function interface: {} ({})\n".format(c.name,
c.source_mapping_str, c.source_mapping_str,
function.name, function.full_name,
function.source_mapping_str) function.source_mapping_str)
json = self.generate_json_result(info) json = self.generate_json_result(info)
self.add_function_to_json(function, json) self.add_function_to_json(function, json)

@ -47,11 +47,7 @@ In this case, Transfer and Approval events should have the 'indexed' keyword on
return results return results
# Loop through all events to look for poor form. # Loop through all events to look for poor form.
for event in contract.events: for event in contract.events_declared:
# Only handle events which are declared in this contract.
if event.contract != contract:
continue
# If this is transfer/approval events, expect the first two parameters to be indexed. # If this is transfer/approval events, expect the first two parameters to be indexed.
if event.full_name in ["Transfer(address,address,uint256)", if event.full_name in ["Transfer(address,address,uint256)",
@ -74,6 +70,7 @@ In this case, Transfer and Approval events should have the 'indexed' keyword on
if unindexed_params: if unindexed_params:
# Add each problematic event definition to our result list # Add each problematic event definition to our result list
for (event, parameter) in unindexed_params: for (event, parameter) in unindexed_params:
info = "ERC20 event {}.{} ({}) does not index parameter '{}'\n".format(c.name, event.name, event.source_mapping_str, parameter.name) info = "ERC20 event {}.{} ({}) does not index parameter '{}'\n".format(c.name, event.name, event.source_mapping_str, parameter.name)
# Add the events to the JSON (note: we do not add the params/vars as they have no source mapping). # Add the events to the JSON (note: we do not add the params/vars as they have no source mapping).
@ -83,4 +80,5 @@ In this case, Transfer and Approval events should have the 'indexed' keyword on
}) })
results.append(json) results.append(json)
return results return results

@ -94,7 +94,7 @@ Bob calls `setDestination` and `withdraw`. As a result he withdraws the contract
list((Function), (list (Node))) list((Function), (list (Node)))
""" """
ret = [] ret = []
for f in [f for f in contract.functions if f.contract == contract]: for f in [f for f in contract.functions if f.contract_declarer == contract]:
nodes = self.arbitrary_send(f) nodes = self.arbitrary_send(f)
if nodes: if nodes:
ret.append((f, nodes)) ret.append((f, nodes))
@ -109,9 +109,8 @@ Bob calls `setDestination` and `withdraw`. As a result he withdraws the contract
arbitrary_send = self.detect_arbitrary_send(c) arbitrary_send = self.detect_arbitrary_send(c)
for (func, nodes) in arbitrary_send: for (func, nodes) in arbitrary_send:
info = "{}.{} ({}) sends eth to arbitrary user\n" info = "{} ({}) sends eth to arbitrary user\n"
info = info.format(func.contract.name, info = info.format(func.canonical_name,
func.name,
func.source_mapping_str) func.source_mapping_str)
info += '\tDangerous calls:\n' info += '\tDangerous calls:\n'
for node in nodes: for node in nodes:

@ -90,7 +90,7 @@ class ComplexFunction(AbstractDetector):
for issue in issues: for issue in issues:
func, cause = issue.values() func, cause = issue.values()
txt = "{}.{} ({}) is a complex function:\n" txt = "{} ({}) is a complex function:\n"
if cause == self.CAUSE_EXTERNAL_CALL: if cause == self.CAUSE_EXTERNAL_CALL:
txt += "\t- Reason: High number of external calls" txt += "\t- Reason: High number of external calls"
@ -99,8 +99,7 @@ class ComplexFunction(AbstractDetector):
if cause == self.CAUSE_STATE_VARS: if cause == self.CAUSE_STATE_VARS:
txt += "\t- Reason: High number of modified state variables" txt += "\t- Reason: High number of modified state variables"
info = txt.format(func.contract.name, info = txt.format(func.canonical_name,
func.name,
func.source_mapping_str) func.source_mapping_str)
info = info + "\n" info = info + "\n"
self.log(info) self.log(info)

@ -71,7 +71,7 @@ class ExternalFunction(AbstractDetector):
for contract in function.contract.inheritance + [function.contract]: for contract in function.contract.inheritance + [function.contract]:
# Loop through the functions not inherited (explicitly defined in this contract). # Loop through the functions not inherited (explicitly defined in this contract).
for f in contract.functions_not_inherited: for f in contract.functions_declared:
# If it matches names, this is the base most function. # If it matches names, this is the base most function.
if f.full_name == function.full_name: if f.full_name == function.full_name:
@ -120,7 +120,7 @@ class ExternalFunction(AbstractDetector):
continue continue
# Next we'll want to loop through all functions defined directly in this contract. # Next we'll want to loop through all functions defined directly in this contract.
for function in contract.functions_not_inherited: for function in contract.functions_declared:
# If the function is a constructor, or is public, we skip it. # If the function is a constructor, or is public, we skip it.
if function.is_constructor or function.visibility != "public": if function.is_constructor or function.visibility != "public":
@ -165,9 +165,8 @@ class ExternalFunction(AbstractDetector):
# Loop for each function definition, and recommend it be declared external. # Loop for each function definition, and recommend it be declared external.
for function_definition in all_function_definitions: for function_definition in all_function_definitions:
txt = "{}.{} ({}) should be declared external\n" txt = "{} ({}) should be declared external\n"
info = txt.format(function_definition.contract.name, info = txt.format(function_definition.canonical_name,
function_definition.name,
function_definition.source_mapping_str) function_definition.source_mapping_str)
json = self.generate_json_result(info) json = self.generate_json_result(info)

@ -59,7 +59,7 @@ Bob calls `kill` and destructs the contract.'''
def detect_suicidal(self, contract): def detect_suicidal(self, contract):
ret = [] ret = []
for f in [f for f in contract.functions if f.contract == contract]: for f in [f for f in contract.functions if f.contract_declarer == contract]:
if self.detect_suicidal_func(f): if self.detect_suicidal_func(f):
ret.append(f) ret.append(f)
return ret return ret
@ -72,9 +72,8 @@ Bob calls `kill` and destructs the contract.'''
functions = self.detect_suicidal(c) functions = self.detect_suicidal(c)
for func in functions: for func in functions:
txt = "{}.{} ({}) allows anyone to destruct the contract\n" txt = "{} ({}) allows anyone to destruct the contract\n"
info = txt.format(func.contract.name, info = txt.format(func.canonical_name,
func.name,
func.source_mapping_str) func.source_mapping_str)
json = self.generate_json_result(info) json = self.generate_json_result(info)

@ -67,13 +67,10 @@ Solidity defines a [naming convention](https://solidity.readthedocs.io/en/v0.4.2
}) })
results.append(json) results.append(json)
for struct in contract.structures: for struct in contract.structures_declared:
if struct.contract != contract:
continue
if not self.is_cap_words(struct.name): if not self.is_cap_words(struct.name):
info = "Struct '{}.{}' ({}) is not in CapWords\n" info = "Struct '{}' ({}) is not in CapWords\n"
info = info.format(struct.contract.name, struct.name, struct.source_mapping_str) info = info.format(struct.canonical_name, struct.source_mapping_str)
json = self.generate_json_result(info) json = self.generate_json_result(info)
self.add_struct_to_json(struct, json, { self.add_struct_to_json(struct, json, {
@ -82,13 +79,10 @@ Solidity defines a [naming convention](https://solidity.readthedocs.io/en/v0.4.2
}) })
results.append(json) results.append(json)
for event in contract.events: for event in contract.events_declared:
if event.contract != contract:
continue
if not self.is_cap_words(event.name): if not self.is_cap_words(event.name):
info = "Event '{}.{}' ({}) is not in CapWords\n" info = "Event '{}' ({}) is not in CapWords\n"
info = info.format(event.contract.name, event.name, event.source_mapping_str) info = info.format(event.canonical_name, event.source_mapping_str)
json = self.generate_json_result(info) json = self.generate_json_result(info)
self.add_event_to_json(event, json, { self.add_event_to_json(event, json, {
@ -97,13 +91,10 @@ Solidity defines a [naming convention](https://solidity.readthedocs.io/en/v0.4.2
}) })
results.append(json) results.append(json)
for func in contract.functions: for func in contract.functions_declared:
if func.contract != contract:
continue
if not self.is_mixed_case(func.name): if not self.is_mixed_case(func.name):
info = "Function '{}.{}' ({}) is not in mixedCase\n" info = "Function '{}' ({}) is not in mixedCase\n"
info = info.format(func.contract.name, func.name, func.source_mapping_str) info = info.format(func.canonical_name, func.source_mapping_str)
json = self.generate_json_result(info) json = self.generate_json_result(info)
self.add_function_to_json(func, json, { self.add_function_to_json(func, json, {
@ -118,10 +109,9 @@ Solidity defines a [naming convention](https://solidity.readthedocs.io/en/v0.4.2
else: else:
correct_naming = self.is_mixed_case_with_underscore(argument.name) correct_naming = self.is_mixed_case_with_underscore(argument.name)
if not correct_naming: if not correct_naming:
info = "Parameter '{}' of {}.{} ({}) is not in mixedCase\n" info = "Parameter '{}' of {} ({}) is not in mixedCase\n"
info = info.format(argument.name, info = info.format(argument.name,
argument.function.contract.name, argument.canonical_name,
argument.function,
argument.source_mapping_str) argument.source_mapping_str)
json = self.generate_json_result(info) json = self.generate_json_result(info)
@ -131,14 +121,11 @@ Solidity defines a [naming convention](https://solidity.readthedocs.io/en/v0.4.2
}) })
results.append(json) results.append(json)
for var in contract.state_variables: for var in contract.state_variables_declared:
if var.contract != contract:
continue
if self.should_avoid_name(var.name): if self.should_avoid_name(var.name):
if not self.is_upper_case_with_underscores(var.name): if not self.is_upper_case_with_underscores(var.name):
info = "Variable '{}.{}' ({}) used l, O, I, which should not be used\n" info = "Variable '{}' ({}) used l, O, I, which should not be used\n"
info = info.format(var.contract.name, var.name, var.source_mapping_str) info = info.format(var.canonical_name, var.source_mapping_str)
json = self.generate_json_result(info) json = self.generate_json_result(info)
self.add_variable_to_json(var, json, { self.add_variable_to_json(var, json, {
@ -153,8 +140,8 @@ Solidity defines a [naming convention](https://solidity.readthedocs.io/en/v0.4.2
continue continue
if not self.is_upper_case_with_underscores(var.name): if not self.is_upper_case_with_underscores(var.name):
info = "Constant '{}.{}' ({}) is not in UPPER_CASE_WITH_UNDERSCORES\n" info = "Constant '{}' ({}) is not in UPPER_CASE_WITH_UNDERSCORES\n"
info = info.format(var.contract.name, var.name, var.source_mapping_str) info = info.format(var.canonical_name, var.source_mapping_str)
json = self.generate_json_result(info) json = self.generate_json_result(info)
self.add_variable_to_json(var, json, { self.add_variable_to_json(var, json, {
@ -169,8 +156,8 @@ Solidity defines a [naming convention](https://solidity.readthedocs.io/en/v0.4.2
else: else:
correct_naming = self.is_mixed_case(var.name) correct_naming = self.is_mixed_case(var.name)
if not correct_naming: if not correct_naming:
info = "Variable '{}.{}' ({}) is not in mixedCase\n" info = "Variable '{}' ({}) is not in mixedCase\n"
info = info.format(var.contract.name, var.name, var.source_mapping_str) info = info.format(var.canonical_name, var.source_mapping_str)
json = self.generate_json_result(info) json = self.generate_json_result(info)
self.add_variable_to_json(var, json, { self.add_variable_to_json(var, json, {
@ -179,13 +166,10 @@ Solidity defines a [naming convention](https://solidity.readthedocs.io/en/v0.4.2
}) })
results.append(json) results.append(json)
for enum in contract.enums: for enum in contract.enums_declared:
if enum.contract != contract:
continue
if not self.is_cap_words(enum.name): if not self.is_cap_words(enum.name):
info = "Enum '{}.{}' ({}) is not in CapWords\n" info = "Enum '{}' ({}) is not in CapWords\n"
info = info.format(enum.contract.name, enum.name, enum.source_mapping_str) info = info.format(enum.canonical_name, enum.source_mapping_str)
json = self.generate_json_result(info) json = self.generate_json_result(info)
self.add_enum_to_json(enum, json, { self.add_enum_to_json(enum, json, {
@ -194,15 +178,10 @@ Solidity defines a [naming convention](https://solidity.readthedocs.io/en/v0.4.2
}) })
results.append(json) results.append(json)
for modifier in contract.modifiers_declared:
for modifier in contract.modifiers:
if modifier.contract != contract:
continue
if not self.is_mixed_case(modifier.name): if not self.is_mixed_case(modifier.name):
info = "Modifier '{}.{}' ({}) is not in mixedCase\n" info = "Modifier '{}' ({}) is not in mixedCase\n"
info = info.format(modifier.contract.name, info = info.format(modifier.canonical_name,
modifier.name,
modifier.source_mapping_str) modifier.source_mapping_str)
json = self.generate_json_result(info) json = self.generate_json_result(info)
@ -212,5 +191,4 @@ Solidity defines a [naming convention](https://solidity.readthedocs.io/en/v0.4.2
}) })
results.append(json) results.append(json)
return results return results

@ -54,7 +54,7 @@ class Timestamp(AbstractDetector):
list((Function), (list (Node))) list((Function), (list (Node)))
""" """
ret = [] ret = []
for f in [f for f in contract.functions if f.contract == contract]: for f in [f for f in contract.functions if f.contract_declarer == contract]:
nodes = self.timestamp(f) nodes = self.timestamp(f)
if nodes: if nodes:
ret.append((f, nodes)) ret.append((f, nodes))
@ -69,9 +69,8 @@ class Timestamp(AbstractDetector):
dangerous_timestamp = self.detect_dangerous_timestamp(c) dangerous_timestamp = self.detect_dangerous_timestamp(c)
for (func, nodes) in dangerous_timestamp: for (func, nodes) in dangerous_timestamp:
info = "{}.{} ({}) uses timestamp for comparisons\n" info = "{} ({}) uses timestamp for comparisons\n"
info = info.format(func.contract.name, info = info.format(func.canonical_name,
func.name,
func.source_mapping_str) func.source_mapping_str)
info += '\tDangerous comparisons:\n' info += '\tDangerous comparisons:\n'
for node in nodes: for node in nodes:

@ -33,7 +33,7 @@ class LowLevelCalls(AbstractDetector):
def detect_low_level_calls(self, contract): def detect_low_level_calls(self, contract):
ret = [] ret = []
for f in [f for f in contract.functions if contract == f.contract]: for f in [f for f in contract.functions if contract == f.contract_declarer]:
nodes = f.nodes nodes = f.nodes
assembly_nodes = [n for n in nodes if assembly_nodes = [n for n in nodes if
self._contains_low_level_calls(n)] self._contains_low_level_calls(n)]
@ -48,8 +48,8 @@ class LowLevelCalls(AbstractDetector):
for c in self.contracts: for c in self.contracts:
values = self.detect_low_level_calls(c) values = self.detect_low_level_calls(c)
for func, nodes in values: for func, nodes in values:
info = "Low level call in {}.{} ({}):\n" info = "Low level call in {} ({}):\n"
info = info.format(func.contract.name, func.name, func.source_mapping_str) info = info.format(func.canonical_name, func.source_mapping_str)
for node in nodes: for node in nodes:
info += "\t-{} {}\n".format(str(node.expression), node.source_mapping_str) info += "\t-{} {}\n".format(str(node.expression), node.source_mapping_str)

@ -15,7 +15,7 @@ class UncheckedLowLevel(UnusedReturnValues):
IMPACT = DetectorClassification.MEDIUM IMPACT = DetectorClassification.MEDIUM
CONFIDENCE = DetectorClassification.MEDIUM CONFIDENCE = DetectorClassification.MEDIUM
WIKI = 'https://github.com/crytic/slither/wiki/Detector-Documentation#unchecked-low-level' WIKI = 'https://github.com/crytic/slither/wiki/Detector-Documentation#unchecked-low-level-calls'
WIKI_TITLE = 'Unchecked low-level calls' WIKI_TITLE = 'Unchecked low-level calls'
WIKI_DESCRIPTION = 'The return value of a low-level call is not checked.' WIKI_DESCRIPTION = 'The return value of a low-level call is not checked.'

@ -67,14 +67,14 @@ contract MyConc{
results = [] results = []
for c in self.slither.contracts: for c in self.slither.contracts:
for f in c.functions + c.modifiers: for f in c.functions + c.modifiers:
if f.contract != c: if f.contract_declarer != c:
continue continue
unused_return = self.detect_unused_return_values(f) unused_return = self.detect_unused_return_values(f)
if unused_return: if unused_return:
for node in unused_return: for node in unused_return:
info = "{}.{} ({}) ignores return value by {} \"{}\" ({})\n" info = "{} ({}) ignores return value by {} \"{}\" ({})\n"
info = info.format(f.contract.name, info = info.format(f.canonical_name,
f.name,
f.source_mapping_str, f.source_mapping_str,
self._txt_description, self._txt_description,
node.expression, node.expression,

@ -181,7 +181,7 @@ class Reentrancy(AbstractDetector):
def detect_reentrancy(self, contract): def detect_reentrancy(self, contract):
""" """
""" """
for function in contract.functions_and_modifiers_not_inherited: for function in contract.functions_and_modifiers_declared:
if function.is_implemented: if function.is_implemented:
if self.KEY in function.context: if self.KEY in function.context:
continue continue

@ -45,7 +45,7 @@ Only report reentrancy that acts as a double call (see `reentrancy-eth`, `reentr
def find_reentrancies(self): def find_reentrancies(self):
result = {} result = {}
for contract in self.contracts: for contract in self.contracts:
for f in contract.functions_and_modifiers_not_inherited: for f in contract.functions_and_modifiers_declared:
for node in f.nodes: for node in f.nodes:
# dead code # dead code
if not self.KEY in node.context: if not self.KEY in node.context:
@ -84,8 +84,8 @@ Only report reentrancy that acts as a double call (see `reentrancy-eth`, `reentr
for (func, calls, send_eth), varsWritten in result_sorted: for (func, calls, send_eth), varsWritten in result_sorted:
calls = sorted(list(set(calls)), key=lambda x: x.node_id) calls = sorted(list(set(calls)), key=lambda x: x.node_id)
send_eth = sorted(list(set(send_eth)), key=lambda x: x.node_id) send_eth = sorted(list(set(send_eth)), key=lambda x: x.node_id)
info = 'Reentrancy in {}.{} ({}):\n' info = 'Reentrancy in {} ({}):\n'
info = info.format(func.contract.name, func.name, func.source_mapping_str) info = info.format(func.canonical_name, func.source_mapping_str)
info += '\tExternal calls:\n' info += '\tExternal calls:\n'
for call_info in calls: for call_info in calls:
info += '\t- {} ({})\n'.format(call_info.expression, call_info.source_mapping_str) info += '\t- {} ({})\n'.format(call_info.expression, call_info.source_mapping_str)

@ -47,7 +47,7 @@ Bob uses the re-entrancy bug to call `withdrawBalance` two times, and withdraw m
def find_reentrancies(self): def find_reentrancies(self):
result = {} result = {}
for contract in self.contracts: for contract in self.contracts:
for f in contract.functions_and_modifiers_not_inherited: for f in contract.functions_and_modifiers_declared:
for node in f.nodes: for node in f.nodes:
# dead code # dead code
if not self.KEY in node.context: if not self.KEY in node.context:
@ -87,8 +87,8 @@ Bob uses the re-entrancy bug to call `withdrawBalance` two times, and withdraw m
calls = sorted(list(set(calls)), key=lambda x: x.node_id) calls = sorted(list(set(calls)), key=lambda x: x.node_id)
send_eth = sorted(list(set(send_eth)), key=lambda x: x.node_id) send_eth = sorted(list(set(send_eth)), key=lambda x: x.node_id)
info = 'Reentrancy in {}.{} ({}):\n' info = 'Reentrancy in {} ({}):\n'
info = info.format(func.contract.name, func.name, func.source_mapping_str) info = info.format(func.canonical_name, func.source_mapping_str)
info += '\tExternal calls:\n' info += '\tExternal calls:\n'
for call_info in calls: for call_info in calls:
info += '\t- {} ({})\n'.format(call_info.expression, call_info.source_mapping_str) info += '\t- {} ({})\n'.format(call_info.expression, call_info.source_mapping_str)

@ -46,7 +46,7 @@ Do not report reentrancies that involve ethers (see `reentrancy-eth`)'''
def find_reentrancies(self): def find_reentrancies(self):
result = {} result = {}
for contract in self.contracts: for contract in self.contracts:
for f in contract.functions_and_modifiers_not_inherited: for f in contract.functions_and_modifiers_declared:
for node in f.nodes: for node in f.nodes:
# dead code # dead code
if not self.KEY in node.context: if not self.KEY in node.context:
@ -82,8 +82,8 @@ Do not report reentrancies that involve ethers (see `reentrancy-eth`)'''
result_sorted = sorted(list(reentrancies.items()), key=lambda x:x[0][0].name) result_sorted = sorted(list(reentrancies.items()), key=lambda x:x[0][0].name)
for (func, calls), varsWritten in result_sorted: for (func, calls), varsWritten in result_sorted:
calls = sorted(list(set(calls)), key=lambda x: x.node_id) calls = sorted(list(set(calls)), key=lambda x: x.node_id)
info = 'Reentrancy in {}.{} ({}):\n' info = 'Reentrancy in {} ({}):\n'
info = info.format(func.contract.name, func.name, func.source_mapping_str) info = info.format(func.canonical_name, func.source_mapping_str)
info += '\tExternal calls:\n' info += '\tExternal calls:\n'
for call_info in calls: for call_info in calls:
info += '\t- {} ({})\n'.format(call_info.expression, call_info.source_mapping_str) info += '\t- {} ({})\n'.format(call_info.expression, call_info.source_mapping_str)

@ -41,9 +41,9 @@ contract DerivedContract is BaseContract{
variables_fathers = [] variables_fathers = []
for father in contract.inheritance: for father in contract.inheritance:
if all(not f.is_implemented for f in father.functions + father.modifiers): if all(not f.is_implemented for f in father.functions + father.modifiers):
variables_fathers += [v for v in father.variables if v.contract == father] variables_fathers += father.state_variables_declared
for var in [v for v in contract.variables if v.contract == contract]: for var in contract.state_variables_declared:
shadow = [v for v in variables_fathers if v.name == var.name] shadow = [v for v in variables_fathers if v.name == var.name]
if shadow: if shadow:
ret.append([var] + shadow) ret.append([var] + shadow)
@ -65,12 +65,10 @@ contract DerivedContract is BaseContract{
for all_variables in shadowing: for all_variables in shadowing:
shadow = all_variables[0] shadow = all_variables[0]
variables = all_variables[1:] variables = all_variables[1:]
info = '{}.{} ({}) shadows:\n'.format(shadow.contract.name, info = '{} ({}) shadows:\n'.format(shadow.canonical_name,
shadow.name,
shadow.source_mapping_str) shadow.source_mapping_str)
for var in variables: for var in variables:
info += "\t- {}.{} ({})\n".format(var.contract.name, info += "\t- {} ({})\n".format(var.canonical_name,
var.name,
var.source_mapping_str) var.source_mapping_str)
json = self.generate_json_result(info) json = self.generate_json_result(info)

@ -90,22 +90,18 @@ contract Bug {
result = [] result = []
# Loop through all functions, modifiers, variables (state and local) to detect any built-in symbol keywords. # Loop through all functions, modifiers, variables (state and local) to detect any built-in symbol keywords.
for function in contract.functions: for function in contract.functions_declared:
if function.contract == contract:
if self.is_builtin_symbol(function.name): if self.is_builtin_symbol(function.name):
result.append((self.SHADOWING_FUNCTION, function, None)) result.append((self.SHADOWING_FUNCTION, function, None))
result += self.detect_builtin_shadowing_locals(function) result += self.detect_builtin_shadowing_locals(function)
for modifier in contract.modifiers: for modifier in contract.modifiers_declared:
if modifier.contract == contract:
if self.is_builtin_symbol(modifier.name): if self.is_builtin_symbol(modifier.name):
result.append((self.SHADOWING_MODIFIER, modifier, None)) result.append((self.SHADOWING_MODIFIER, modifier, None))
result += self.detect_builtin_shadowing_locals(modifier) result += self.detect_builtin_shadowing_locals(modifier)
for variable in contract.variables: for variable in contract.state_variables_declared:
if variable.contract == contract:
if self.is_builtin_symbol(variable.name): if self.is_builtin_symbol(variable.name):
result.append((self.SHADOWING_STATE_VARIABLE, variable, None)) result.append((self.SHADOWING_STATE_VARIABLE, variable, None))
for event in contract.events: for event in contract.events_declared:
if event.contract == contract:
if self.is_builtin_symbol(event.name): if self.is_builtin_symbol(event.name):
result.append((self.SHADOWING_EVENT, event, None)) result.append((self.SHADOWING_EVENT, event, None))

@ -58,7 +58,7 @@ contract Bug {
# Loop through all functions + modifiers in this contract. # Loop through all functions + modifiers in this contract.
for function in contract.functions + contract.modifiers: for function in contract.functions + contract.modifiers:
# We should only look for functions declared directly in this contract (not in a base contract). # We should only look for functions declared directly in this contract (not in a base contract).
if function.contract != contract: if function.contract_declarer != contract:
continue continue
# This function was declared in this contract, we check what its local variables might shadow. # This function was declared in this contract, we check what its local variables might shadow.
@ -66,20 +66,20 @@ contract Bug {
overshadowed = [] overshadowed = []
for scope_contract in [contract] + contract.inheritance: for scope_contract in [contract] + contract.inheritance:
# Check functions # Check functions
for scope_function in scope_contract.functions: for scope_function in scope_contract.functions_declared:
if variable.name == scope_function.name and scope_function.contract == scope_contract: if variable.name == scope_function.name:
overshadowed.append((self.OVERSHADOWED_FUNCTION, scope_contract.name, scope_function)) overshadowed.append((self.OVERSHADOWED_FUNCTION, scope_contract.name, scope_function))
# Check modifiers # Check modifiers
for scope_modifier in scope_contract.modifiers: for scope_modifier in scope_contract.modifiers_declared:
if variable.name == scope_modifier.name and scope_modifier.contract == scope_contract: if variable.name == scope_modifier.name:
overshadowed.append((self.OVERSHADOWED_MODIFIER, scope_contract.name, scope_modifier)) overshadowed.append((self.OVERSHADOWED_MODIFIER, scope_contract.name, scope_modifier))
# Check events # Check events
for scope_event in scope_contract.events: for scope_event in scope_contract.events_declared:
if variable.name == scope_event.name and scope_event.contract == scope_contract: if variable.name == scope_event.name:
overshadowed.append((self.OVERSHADOWED_EVENT, scope_contract.name, scope_event)) overshadowed.append((self.OVERSHADOWED_EVENT, scope_contract.name, scope_event))
# Check state variables # Check state variables
for scope_state_variable in scope_contract.variables: for scope_state_variable in scope_contract.state_variables_declared:
if variable.name == scope_state_variable.name and scope_state_variable.contract == scope_contract: if variable.name == scope_state_variable.name:
overshadowed.append((self.OVERSHADOWED_STATE_VARIABLE, scope_contract.name, scope_state_variable)) overshadowed.append((self.OVERSHADOWED_STATE_VARIABLE, scope_contract.name, scope_state_variable))
# If we have found any overshadowed objects, we'll want to add it to our result list. # If we have found any overshadowed objects, we'll want to add it to our result list.

@ -53,9 +53,9 @@ contract DerivedContract is BaseContract{
variables_fathers = [] variables_fathers = []
for father in contract.inheritance: for father in contract.inheritance:
if any(f.is_implemented for f in father.functions + father.modifiers): if any(f.is_implemented for f in father.functions + father.modifiers):
variables_fathers += [v for v in father.variables if v.contract == father] variables_fathers += father.state_variables_declared
for var in [v for v in contract.variables if v.contract == contract]: for var in contract.state_variables_declared:
shadow = [v for v in variables_fathers if v.name == var.name] shadow = [v for v in variables_fathers if v.name == var.name]
if shadow: if shadow:
ret.append([var] + shadow) ret.append([var] + shadow)
@ -76,12 +76,10 @@ contract DerivedContract is BaseContract{
for all_variables in shadowing: for all_variables in shadowing:
shadow = all_variables[0] shadow = all_variables[0]
variables = all_variables[1:] variables = all_variables[1:]
info = '{}.{} ({}) shadows:\n'.format(shadow.contract.name, info = '{} ({}) shadows:\n'.format(shadow.canonical_name,
shadow.name,
shadow.source_mapping_str) shadow.source_mapping_str)
for var in variables: for var in variables:
info += "\t- {}.{} ({})\n".format(var.contract.name, info += "\t- {} ({})\n".format(var.canonical_name,
var.name,
var.source_mapping_str) var.source_mapping_str)
json = self.generate_json_result(info) json = self.generate_json_result(info)

@ -35,7 +35,7 @@ class Assembly(AbstractDetector):
def detect_assembly(self, contract): def detect_assembly(self, contract):
ret = [] ret = []
for f in contract.functions: for f in contract.functions:
if f.contract != contract: if f.contract_declarer != contract:
continue continue
nodes = f.nodes nodes = f.nodes
assembly_nodes = [n for n in nodes if assembly_nodes = [n for n in nodes if
@ -51,8 +51,8 @@ class Assembly(AbstractDetector):
for c in self.contracts: for c in self.contracts:
values = self.detect_assembly(c) values = self.detect_assembly(c)
for func, nodes in values: for func, nodes in values:
info = "{}.{} uses assembly ({})\n" info = "{} uses assembly ({})\n"
info = info.format(func.contract.name, func.name, func.source_mapping_str) info = info.format(func.canonical_name, func.source_mapping_str)
for node in nodes: for node in nodes:
info += "\t- {}\n".format(node.source_mapping_str) info += "\t- {}\n".format(node.source_mapping_str)

@ -72,7 +72,7 @@ If one of the destinations has a fallback function which reverts, `bad` will alw
def detect_call_in_loop(contract): def detect_call_in_loop(contract):
ret = [] ret = []
for f in contract.functions + contract.modifiers: for f in contract.functions + contract.modifiers:
if f.contract == contract and f.is_implemented: if f.contract_declarer == contract and f.is_implemented:
MultipleCallsInLoop.call_in_loop(f.entry_point, MultipleCallsInLoop.call_in_loop(f.entry_point,
False, [], ret) False, [], ret)
@ -86,8 +86,9 @@ If one of the destinations has a fallback function which reverts, `bad` will alw
values = self.detect_call_in_loop(c) values = self.detect_call_in_loop(c)
for node in values: for node in values:
func = node.function func = node.function
info = "{}.{} has external calls inside a loop: \"{}\" ({})\n"
info = info.format(func.contract.name, func.name, node.expression, node.source_mapping_str) info = "{} has external calls inside a loop: \"{}\" ({})\n"
info = info.format(func.canonical_name, node.expression, node.source_mapping_str)
json = self.generate_json_result(info) json = self.generate_json_result(info)
self.add_node_to_json(node, json) self.add_node_to_json(node, json)

@ -42,7 +42,7 @@ Bob calls `delegate` and delegates the execution to its malicious contract. As a
for contract in self.slither.contracts: for contract in self.slither.contracts:
for f in contract.functions: for f in contract.functions:
if f.contract != contract: if f.contract_declarer != contract:
continue continue
nodes = self.controlled_delegatecall(f) nodes = self.controlled_delegatecall(f)
if nodes: if nodes:

@ -111,20 +111,14 @@ contract ContractWithDeprecatedReferences {
list of tuple: (state_variable | node, (detecting_signature, original_text, recommended_text))""" list of tuple: (state_variable | node, (detecting_signature, original_text, recommended_text))"""
results = [] results = []
for state_variable in contract.variables: for state_variable in contract.state_variables_declared:
if state_variable.contract != contract:
continue
if state_variable.expression: if state_variable.expression:
deprecated_results = self.detect_deprecation_in_expression(state_variable.expression) deprecated_results = self.detect_deprecation_in_expression(state_variable.expression)
if deprecated_results: if deprecated_results:
results.append((state_variable, deprecated_results)) results.append((state_variable, deprecated_results))
# Loop through all functions + modifiers in this contract. # Loop through all functions + modifiers in this contract.
for function in contract.functions + contract.modifiers: for function in contract.functions_and_modifiers_declared:
# We should only look for functions declared directly in this contract (not in a base contract).
if function.contract != contract:
continue
# Loop through each node in this function. # Loop through each node in this function.
for node in function.nodes: for node in function.nodes:
# Detect deprecated references in the node. # Detect deprecated references in the node.

@ -111,8 +111,8 @@ contract Crowdsale{
# sort ret to get deterministic results # sort ret to get deterministic results
ret = sorted(list(ret.items()), key=lambda x:x[0].name) ret = sorted(list(ret.items()), key=lambda x:x[0].name)
for f, nodes in ret: for f, nodes in ret:
func_info = "{}.{} ({}) uses a dangerous strict equality:\n".format(f.contract.name,
f.name, func_info = "{} ({}) uses a dangerous strict equality:\n".format(f.canonical_name,
f.source_mapping_str) f.source_mapping_str)
# sort the nodes to get deterministic results # sort the nodes to get deterministic results

@ -65,9 +65,9 @@ Bob is the owner of `TxOrigin`. Bob calls Eve's contract. Eve's contract calls `
for c in self.contracts: for c in self.contracts:
values = self.detect_tx_origin(c) values = self.detect_tx_origin(c)
for func, nodes in values: for func, nodes in values:
for node in nodes: for node in nodes:
info = "{}.{} uses tx.origin for authorization: \"{}\" ({})\n".format(func.contract.name, info = "{} uses tx.origin for authorization: \"{}\" ({})\n".format(func.canonical_name,
func.name,
node.expression, node.expression,
node.source_mapping_str) node.source_mapping_str)

@ -86,11 +86,11 @@ class ConstCandidateStateVars(AbstractDetector):
# Create a result for each finding # Create a result for each finding
for v in constable_variables: for v in constable_variables:
info = "{}.{} should be constant ({})\n".format(v.contract.name, info = "{} should be constant ({})\n".format(v.canonical_name,
v.name,
v.source_mapping_str) v.source_mapping_str)
json = self.generate_json_result(info) json = self.generate_json_result(info)
self.add_variable_to_json(v, json) self.add_variable_to_json(v, json)
results.append(json) results.append(json)
return results return results

@ -90,7 +90,7 @@ Bob calls `transfer`. As a result, the ethers are sent to the address 0x0 and ar
for contract in self.slither.contracts: for contract in self.slither.contracts:
for function in contract.functions: for function in contract.functions:
if function.is_implemented and function.contract == contract: if function.is_implemented and function.contract_declarer == contract:
if function.contains_assembly: if function.contains_assembly:
continue continue
# dont consider storage variable, as they are detected by another detector # dont consider storage variable, as they are detected by another detector
@ -101,10 +101,9 @@ Bob calls `transfer`. As a result, the ethers are sent to the address 0x0 and ar
for(function, uninitialized_local_variable) in all_results: for(function, uninitialized_local_variable) in all_results:
var_name = uninitialized_local_variable.name var_name = uninitialized_local_variable.name
info = "{} in {}.{} ({}) is a local variable never initialiazed\n" info = "{} in {} ({}) is a local variable never initialiazed\n"
info = info.format(var_name, info = info.format(var_name,
function.contract.name, function.canonical_name,
function.name,
uninitialized_local_variable.source_mapping_str) uninitialized_local_variable.source_mapping_str)

@ -92,9 +92,8 @@ Initialize all the variables. If a variable is meant to be initialized to zero,
for c in self.slither.contracts_derived: for c in self.slither.contracts_derived:
ret = self.detect_uninitialized(c) ret = self.detect_uninitialized(c)
for variable, functions in ret: for variable, functions in ret:
info = "{}.{} ({}) is never initialized. It is used in:\n" info = "{} ({}) is never initialized. It is used in:\n"
info = info.format(variable.contract.name, info = info.format(variable.canonical_name,
variable.name,
variable.source_mapping_str) variable.source_mapping_str)
for f in functions: for f in functions:
info += "\t- {} ({})\n".format(f.name, f.source_mapping_str) info += "\t- {} ({})\n".format(f.name, f.source_mapping_str)

@ -105,8 +105,8 @@ Bob calls `func`. As a result, `owner` is override to 0.
for(function, uninitialized_storage_variable) in self.results: for(function, uninitialized_storage_variable) in self.results:
var_name = uninitialized_storage_variable.name var_name = uninitialized_storage_variable.name
info = "{} in {}.{} ({}) is a storage variable never initialiazed\n" info = "{} in {} ({}) is a storage variable never initialiazed\n"
info = info.format(var_name, function.contract.name, function.name, uninitialized_storage_variable.source_mapping_str) info = info.format(var_name, function.canonical_name, uninitialized_storage_variable.source_mapping_str)
json = self.generate_json_result(info) json = self.generate_json_result(info)

@ -56,11 +56,11 @@ class UnusedStateVars(AbstractDetector):
unusedVars = self.detect_unused(c) unusedVars = self.detect_unused(c)
if unusedVars: if unusedVars:
for var in unusedVars: for var in unusedVars:
info = "{}.{} ({}) is never used in {}\n".format(var.contract.name, info = "{} ({}) is never used in {}\n".format(var.canonical_name,
var.name,
var.source_mapping_str, var.source_mapping_str,
c.name) c.name)
json = self.generate_json_result(info) json = self.generate_json_result(info)
self.add_variable_to_json(var, json) self.add_variable_to_json(var, json)
self.add_contract_to_json(c, json) self.add_contract_to_json(c, json)

@ -91,8 +91,8 @@ class PrinterInheritanceGraph(AbstractPrinter):
indirect_shadows = detect_c3_function_shadowing(contract) indirect_shadows = detect_c3_function_shadowing(contract)
if indirect_shadows: if indirect_shadows:
for collision_set in sorted(indirect_shadows, key=lambda x: x[0][1].name): for collision_set in sorted(indirect_shadows, key=lambda x: x[0][1].name):
winner = collision_set[-1][1].contract.name winner = collision_set[-1][1].contract_declarer.name
collision_steps = [colliding_function.contract.name for _, colliding_function in collision_set] collision_steps = [colliding_function.contract_declarer.name for _, colliding_function in collision_set]
collision_steps = ', '.join(collision_steps) collision_steps = ', '.join(collision_steps)
result.append(f"'{collision_set[0][1].full_name}' collides in inherited contracts {collision_steps} where {winner} is chosen.") result.append(f"'{collision_set[0][1].full_name}' collides in inherited contracts {collision_steps} where {winner} is chosen.")
return '\n'.join(result) return '\n'.join(result)
@ -116,23 +116,23 @@ class PrinterInheritanceGraph(AbstractPrinter):
# Functions # Functions
visibilities = ['public', 'external'] visibilities = ['public', 'external']
public_functions = [self._get_pattern_func(f, contract) for f in contract.functions if public_functions = [self._get_pattern_func(f, contract) for f in contract.functions if
not f.is_constructor and f.contract == contract and f.visibility in visibilities] not f.is_constructor and f.contract_declarer == contract and f.visibility in visibilities]
public_functions = ''.join(public_functions) public_functions = ''.join(public_functions)
private_functions = [self._get_pattern_func(f, contract) for f in contract.functions if private_functions = [self._get_pattern_func(f, contract) for f in contract.functions if
not f.is_constructor and f.contract == contract and f.visibility not in visibilities] not f.is_constructor and f.contract_declarer == contract and f.visibility not in visibilities]
private_functions = ''.join(private_functions) private_functions = ''.join(private_functions)
# Modifiers # Modifiers
modifiers = [self._get_pattern_func(m, contract) for m in contract.modifiers if m.contract == contract] modifiers = [self._get_pattern_func(m, contract) for m in contract.modifiers if m.contract_declarer == contract]
modifiers = ''.join(modifiers) modifiers = ''.join(modifiers)
# Public variables # Public variables
public_variables = [self._get_pattern_var(v, contract) for v in contract.variables if public_variables = [self._get_pattern_var(v, contract) for v in contract.state_variables_declared
v.contract == contract and v.visibility in visibilities] if v.visibility in visibilities]
public_variables = ''.join(public_variables) public_variables = ''.join(public_variables)
private_variables = [self._get_pattern_var(v, contract) for v in contract.variables if private_variables = [self._get_pattern_var(v, contract) for v in contract.state_variables_declared
v.contract == contract and v.visibility not in visibilities] if v.visibility not in visibilities]
private_variables = ''.join(private_variables) private_variables = ''.join(private_variables)
# Obtain any indirect shadowing information for this node. # Obtain any indirect shadowing information for this node.

@ -35,7 +35,7 @@ class DataDependency(AbstractPrinter):
txt += str(table) txt += str(table)
txt += "\n" txt += "\n"
for f in c.functions_and_modifiers_not_inherited: for f in c.functions_and_modifiers_declared:
txt += "\nFunction %s\n"%f.full_name txt += "\nFunction %s\n"%f.full_name
table = PrettyTable(['Variable', 'Dependencies']) table = PrettyTable(['Variable', 'Dependencies'])
for v in f.variables: for v in f.variables:

@ -72,13 +72,23 @@ class PrinterHumanSummary(AbstractPrinter):
checks_high = self.slither.detectors_high checks_high = self.slither.detectors_high
issues_informational = [c.detect() for c in checks_informational] issues_informational = [c.detect() for c in checks_informational]
issues_informational = [c for c in issues_informational if c]
issues_informational = [item for sublist in issues_informational for item in sublist] issues_informational = [item for sublist in issues_informational for item in sublist]
issues_low = [c.detect() for c in checks_low] issues_low = [c.detect() for c in checks_low]
issues_low = [c for c in issues_low if c] issues_low = [c for c in issues_low if c]
issues_low = [item for sublist in issues_low for item in sublist]
issues_medium = (c.detect() for c in checks_medium) issues_medium = (c.detect() for c in checks_medium)
issues_medium = [c for c in issues_medium if c] issues_medium = [c for c in issues_medium if c]
issues_medium = [item for sublist in issues_medium for item in sublist]
issues_high = [c.detect() for c in checks_high] issues_high = [c.detect() for c in checks_high]
issues_high = [c for c in issues_high if c] issues_high = [c for c in issues_high if c]
issues_high = [item for sublist in issues_high for item in sublist]
return (len(issues_informational), return (len(issues_informational),
len(issues_low), len(issues_low),
len(issues_medium), len(issues_medium),

@ -23,8 +23,7 @@ class PrinterSlithIR(AbstractPrinter):
for contract in self.contracts: for contract in self.contracts:
print('Contract {}'.format(contract.name)) print('Contract {}'.format(contract.name))
for function in contract.functions: for function in contract.functions:
if function.contract == contract: print(f'\tFunction {function.canonical_name}')
print('\tFunction {}'.format(function.full_name))
for node in function.nodes: for node in function.nodes:
if node.expression: if node.expression:
print('\t\tExpression: {}'.format(node.expression)) print('\t\tExpression: {}'.format(node.expression))
@ -36,8 +35,7 @@ class PrinterSlithIR(AbstractPrinter):
for ir in node.irs: for ir in node.irs:
print('\t\t\t{}'.format(ir)) print('\t\t\t{}'.format(ir))
for modifier in contract.modifiers: for modifier in contract.modifiers:
if modifier.contract == contract: print('\tModifier {}'.format(modifier.canonical_name))
print('\tModifier {}'.format(modifier.full_name))
for node in modifier.nodes: for node in modifier.nodes:
print(node) print(node)
if node.expression: if node.expression:

@ -23,8 +23,7 @@ class PrinterSlithIRSSA(AbstractPrinter):
for contract in self.contracts: for contract in self.contracts:
print('Contract {}'.format(contract.name)) print('Contract {}'.format(contract.name))
for function in contract.functions: for function in contract.functions:
if function.contract == contract: print('\tFunction {}'.format(function.canonical_name))
print('\tFunction {}'.format(function.full_name))
for node in function.nodes: for node in function.nodes:
if node.expression: if node.expression:
print('\t\tExpression: {}'.format(node.expression)) print('\t\tExpression: {}'.format(node.expression))
@ -33,8 +32,7 @@ class PrinterSlithIRSSA(AbstractPrinter):
for ir in node.irs_ssa: for ir in node.irs_ssa:
print('\t\t\t{}'.format(ir)) print('\t\t\t{}'.format(ir))
for modifier in contract.modifiers: for modifier in contract.modifiers:
if modifier.contract == contract: print('\tModifier {}'.format(modifier.canonical_name))
print('\tModifier {}'.format(modifier.full_name))
for node in modifier.nodes: for node in modifier.nodes:
print(node) print(node)
if node.expression: if node.expression:

@ -6,7 +6,7 @@ from slither.core.declarations import (Contract, Enum, Event, Function,
from slither.core.expressions import Identifier, Literal from slither.core.expressions import Identifier, Literal
from slither.core.solidity_types import (ArrayType, ElementaryType, from slither.core.solidity_types import (ArrayType, ElementaryType,
FunctionType, MappingType, FunctionType, MappingType,
UserDefinedType) UserDefinedType, TypeInformation)
from slither.core.solidity_types.elementary_type import Int as ElementaryTypeInt from slither.core.solidity_types.elementary_type import Int as ElementaryTypeInt
from slither.core.variables.variable import Variable from slither.core.variables.variable import Variable
from slither.core.variables.state_variable import StateVariable from slither.core.variables.state_variable import StateVariable
@ -104,6 +104,21 @@ def get_sig(ir, name):
argss = convert_arguments(ir.arguments) argss = convert_arguments(ir.arguments)
return [sig.format(name, ','.join(args)) for args in argss] return [sig.format(name, ','.join(args)) for args in argss]
def get_canonical_names(ir, function_name, contract_name):
'''
Return a list of potential signature
It is a list, as Constant variables can be converted to int256
Args:
ir (slithIR.operation)
Returns:
list(str)
'''
sig = '{}({})'
# list of list of arguments
argss = convert_arguments(ir.arguments)
return [sig.format(f'{contract_name}.{function_name}', ','.join(args)) for args in argss]
def convert_arguments(arguments): def convert_arguments(arguments):
argss = [[]] argss = [[]]
for arg in arguments: for arg in arguments:
@ -297,6 +312,44 @@ def propagate_type_and_convert_call(result, node):
idx = idx +1 idx = idx +1
return result return result
def _convert_type_contract(ir, slither):
assert isinstance(ir.variable_left.type, TypeInformation)
contract = ir.variable_left.type.type
if ir.variable_right == 'creationCode':
if slither.crytic_compile:
bytecode = slither.crytic_compile.bytecode_init(contract.name)
else:
logger.info(
'The codebase uses type(x).creationCode, but crytic-compile was not used. As a result, the bytecode cannot be found')
bytecode = "MISSING_BYTECODE"
assignment = Assignment(ir.lvalue,
Constant(str(bytecode)),
ElementaryType('bytes'))
assignment.lvalue.set_type(ElementaryType('bytes'))
return assignment
if ir.variable_right == 'runtimeCode':
if slither.crytic_compile:
bytecode = slither.crytic_compile.bytecode_runtime(contract.name)
else:
logger.info(
'The codebase uses type(x).runtimeCode, but crytic-compile was not used. As a result, the bytecode cannot be found')
bytecode = "MISSING_BYTECODE"
assignment = Assignment(ir.lvalue,
Constant(str(bytecode)),
ElementaryType('bytes'))
assignment.lvalue.set_type(ElementaryType('bytes'))
return assignment
if ir.variable_right == 'name':
assignment = Assignment(ir.lvalue,
Constant(contract.name),
ElementaryType('string'))
assignment.lvalue.set_type(ElementaryType('string'))
return assignment
raise SlithIRError(f'type({contract.name}).{ir.variable_right} is unknown')
def propagate_types(ir, node): def propagate_types(ir, node):
# propagate the type # propagate the type
using_for = node.function.contract.using_for using_for = node.function.contract.using_for
@ -361,7 +414,7 @@ def propagate_types(ir, node):
elif isinstance(ir, InternalCall): elif isinstance(ir, InternalCall):
# if its not a tuple, return a singleton # if its not a tuple, return a singleton
if ir.function is None: if ir.function is None:
convert_type_of_high_and_internal_level_call(ir, ir.contract) convert_type_of_high_and_internal_level_call(ir, node.function.contract)
return_type = ir.function.return_type return_type = ir.function.return_type
if return_type: if return_type:
if len(return_type) == 1: if len(return_type) == 1:
@ -398,6 +451,8 @@ def propagate_types(ir, node):
ElementaryType('bytes4')) ElementaryType('bytes4'))
assignment.lvalue.set_type(ElementaryType('bytes4')) assignment.lvalue.set_type(ElementaryType('bytes4'))
return assignment return assignment
if isinstance(ir.variable_left, TemporaryVariable) and isinstance(ir.variable_left.type, TypeInformation):
return _convert_type_contract(ir, node.function.slither)
left = ir.variable_left left = ir.variable_left
t = None t = None
if isinstance(left, (Variable, SolidityVariable)): if isinstance(left, (Variable, SolidityVariable)):
@ -426,6 +481,12 @@ def propagate_types(ir, node):
f = next((f for f in type_t.functions if f.name == ir.variable_right), None) f = next((f for f in type_t.functions if f.name == ir.variable_right), None)
if f: if f:
ir.lvalue.set_type(f) ir.lvalue.set_type(f)
else:
# Allow propgation for variable access through contract's nale
# like Base_contract.my_variable
v = next((v for v in type_t.state_variables if v.name == ir.variable_right), None)
if v:
ir.lvalue.set_type(v.type)
elif isinstance(ir, NewArray): elif isinstance(ir, NewArray):
ir.lvalue.set_type(ir.array_type) ir.lvalue.set_type(ir.array_type)
elif isinstance(ir, NewContract): elif isinstance(ir, NewContract):
@ -441,6 +502,8 @@ def propagate_types(ir, node):
elif isinstance(ir, Send): elif isinstance(ir, Send):
ir.lvalue.set_type(ElementaryType('bool')) ir.lvalue.set_type(ElementaryType('bool'))
elif isinstance(ir, SolidityCall): elif isinstance(ir, SolidityCall):
if ir.function.name == 'type(address)':
ir.function.return_type = [TypeInformation(ir.arguments[0])]
return_type = ir.function.return_type return_type = ir.function.return_type
if len(return_type) == 1: if len(return_type) == 1:
ir.lvalue.set_type(return_type[0]) ir.lvalue.set_type(return_type[0])
@ -472,7 +535,7 @@ def extract_tmp_call(ins, contract):
# If there is a call on an inherited contract, it is an internal call or an event # If there is a call on an inherited contract, it is an internal call or an event
if ins.ori.variable_left in contract.inheritance + [contract]: if ins.ori.variable_left in contract.inheritance + [contract]:
if str(ins.ori.variable_right) in [f.name for f in contract.functions]: if str(ins.ori.variable_right) in [f.name for f in contract.functions]:
internalcall = InternalCall(ins.ori.variable_right, ins.ori.variable_left, ins.nbr_arguments, ins.lvalue, ins.type_call) internalcall = InternalCall((ins.ori.variable_right, ins.ori.variable_left.name), ins.nbr_arguments, ins.lvalue, ins.type_call)
internalcall.call_id = ins.call_id internalcall.call_id = ins.call_id
return internalcall return internalcall
if str(ins.ori.variable_right) in [f.name for f in contract.events]: if str(ins.ori.variable_right) in [f.name for f in contract.events]:
@ -647,7 +710,10 @@ def look_for_library(contract, ir, node, using_for, t):
return None return None
def convert_to_library(ir, node, using_for): def convert_to_library(ir, node, using_for):
contract = node.function.contract # We use contract_declarer, because Solidity resolve the library
# before resolving the inheritance.
# Though we could use .contract as libraries cannot be shadowed
contract = node.function.contract_declarer
t = ir.destination.type t = ir.destination.type
if t in using_for: if t in using_for:
@ -711,9 +777,20 @@ def convert_type_library_call(ir, lib_contract):
def convert_type_of_high_and_internal_level_call(ir, contract): def convert_type_of_high_and_internal_level_call(ir, contract):
func = None func = None
if isinstance(ir, InternalCall):
sigs = get_canonical_names(ir, ir.function_name, ir.contract_name)
for sig in sigs:
func = contract.get_function_from_canonical_name(sig)
if not func:
func = contract.get_state_variable_from_name(ir.function_name)
if func:
# stop to explore if func is found (prevent dupplicate issue)
break
else:
assert isinstance(ir, HighLevelCall)
sigs = get_sig(ir, ir.function_name) sigs = get_sig(ir, ir.function_name)
for sig in sigs: for sig in sigs:
func = contract.get_function_from_signature(sig) func = contract.get_function_from_canonical_name(sig)
if not func: if not func:
func = contract.get_state_variable_from_name(ir.function_name) func = contract.get_state_variable_from_name(ir.function_name)
if func: if func:
@ -874,6 +951,9 @@ def convert_constant_types(irs):
if isinstance(func, StateVariable): if isinstance(func, StateVariable):
types = export_nested_types_from_variable(func) types = export_nested_types_from_variable(func)
else: else:
if func is None:
# TODO: add POP instruction
break
types = [p.type for p in func.parameters] types = [p.type for p in func.parameters]
for idx, arg in enumerate(ir.arguments): for idx, arg in enumerate(ir.arguments):
t = types[idx] t = types[idx]

@ -1,21 +1,20 @@
from slither.core.declarations.function import Function from slither.core.declarations.function import Function
from slither.slithir.operations.call import Call from slither.slithir.operations.call import Call
from slither.slithir.operations.lvalue import OperationWithLValue from slither.slithir.operations.lvalue import OperationWithLValue
from slither.core.variables.variable import Variable
from slither.slithir.variables import Constant from slither.slithir.variables import Constant
class InternalCall(Call, OperationWithLValue): class InternalCall(Call, OperationWithLValue):
def __init__(self, function, contract, nbr_arguments, result, type_call): def __init__(self, function, nbr_arguments, result, type_call):
super(InternalCall, self).__init__() super(InternalCall, self).__init__()
if isinstance(function, Function): if isinstance(function, Function):
self._function = function self._function = function
self._function_name = function.name self._function_name = function.name
self._contract_name = function.contract_declarer.name
else: else:
isinstance(function, Constant)
self._function = None self._function = None
self._function_name = function self._function_name, self._contract_name = function
self._contract = contract #self._contract = contract
self._nbr_arguments = nbr_arguments self._nbr_arguments = nbr_arguments
self._type_call = type_call self._type_call = type_call
self._lvalue = result self._lvalue = result
@ -32,14 +31,14 @@ class InternalCall(Call, OperationWithLValue):
def function(self, f): def function(self, f):
self._function = f self._function = f
@property
def contract(self):
return self._contract
@property @property
def function_name(self): def function_name(self):
return self._function_name return self._function_name
@property
def contract_name(self):
return self._contract_name
@property @property
def nbr_arguments(self): def nbr_arguments(self):
return self._nbr_arguments return self._nbr_arguments
@ -56,9 +55,8 @@ class InternalCall(Call, OperationWithLValue):
lvalue = '{}({}) = '.format(self.lvalue, ','.join(str(x) for x in self.lvalue.type)) lvalue = '{}({}) = '.format(self.lvalue, ','.join(str(x) for x in self.lvalue.type))
else: else:
lvalue = '{}({}) = '.format(self.lvalue, self.lvalue.type) lvalue = '{}({}) = '.format(self.lvalue, self.lvalue.type)
txt = '{}INTERNAL_CALL, {}.{}({})' txt = '{}INTERNAL_CALL, {}({})'
return txt.format(lvalue, return txt.format(lvalue,
self.function.contract.name, self.function.canonical_name,
self.function.full_name,
','.join(args)) ','.join(args))

@ -566,7 +566,7 @@ def copy_ir(ir, *instances):
nbr_arguments = ir.nbr_arguments nbr_arguments = ir.nbr_arguments
lvalue = get_variable(ir, lambda x: x.lvalue, *instances) lvalue = get_variable(ir, lambda x: x.lvalue, *instances)
type_call = ir.type_call type_call = ir.type_call
new_ir = InternalCall(function, function.contract, nbr_arguments, lvalue, type_call) new_ir = InternalCall(function, nbr_arguments, lvalue, type_call)
new_ir.arguments = get_arguments(ir, *instances) new_ir.arguments = get_arguments(ir, *instances)
return new_ir return new_ir
elif isinstance(ir, InternalDynamicCall): elif isinstance(ir, InternalDynamicCall):

@ -62,4 +62,6 @@ class NodeSolc(Node):
pp = FindCalls(expression) pp = FindCalls(expression)
self._expression_calls = pp.result() self._expression_calls = pp.result()
self._external_calls_as_expressions = [c for c in self.calls_as_expression if not isinstance(c.called, Identifier)] self._external_calls_as_expressions = [c for c in self.calls_as_expression if not isinstance(c.called, Identifier)]
self._internal_calls_as_expressions = [c for c in self.calls_as_expression if isinstance(c.called, Identifier)]

@ -232,8 +232,9 @@ class ContractSolc04(Contract):
def _parse_modifier(self, modifier): def _parse_modifier(self, modifier):
modif = ModifierSolc(modifier, self) modif = ModifierSolc(modifier, self, self)
modif.set_contract(self) modif.set_contract(self)
modif.set_contract_declarer(self)
modif.set_offset(modifier['src'], self.slither) modif.set_offset(modifier['src'], self.slither)
self.slither.add_modifier(modif) self.slither.add_modifier(modif)
self._modifiers_no_params.append(modif) self._modifiers_no_params.append(modif)
@ -247,7 +248,7 @@ class ContractSolc04(Contract):
return return
def _parse_function(self, function): def _parse_function(self, function):
func = FunctionSolc(function, self) func = FunctionSolc(function, self, self)
func.set_offset(function['src'], self.slither) func.set_offset(function['src'], self.slither)
self.slither.add_function(func) self.slither.add_function(func)
self._functions_no_params.append(func) self._functions_no_params.append(func)
@ -281,26 +282,52 @@ class ContractSolc04(Contract):
return return
def analyze_params_modifiers(self): def analyze_params_modifiers(self):
for father in self.inheritance_reverse:
self._modifiers.update(father.modifiers_as_dict())
for modifier in self._modifiers_no_params: elements_no_params = self._modifiers_no_params
modifier.analyze_params() getter = lambda f: f.modifiers
self._modifiers[modifier.full_name] = modifier getter_available = lambda f: f.available_modifiers_as_dict().items()
Cls = ModifierSolc
self._modifiers = self._analyze_params_elements(elements_no_params, getter, getter_available, Cls)
self._modifiers_no_params = [] self._modifiers_no_params = []
return return
def analyze_params_functions(self): def analyze_params_functions(self):
# keep track of the contracts visited
# to prevent an ovveride due to multiple inheritance of the same contract elements_no_params = self._functions_no_params
# A is B, C, D is C, --> the second C was already seen getter = lambda f: f.functions
contracts_visited = [] getter_available = lambda f: f.available_functions_as_dict().items()
for father in self.inheritance_reverse: Cls = FunctionSolc
functions = {k:v for (k, v) in father.functions_as_dict().items() self._functions = self._analyze_params_elements(elements_no_params, getter, getter_available, Cls)
if not v.contract in contracts_visited}
contracts_visited.append(father) self._functions_no_params = []
self._functions.update(functions) return
def _analyze_params_elements(self, elements_no_params, getter, getter_available, Cls):
"""
Analyze the parameters of the given elements (Function or Modifier).
The function iterates over the inheritance to create an instance or inherited elements (Function or Modifier)
If the element is shadowed, set is_shadowed to True
:param elements_no_params: list of elements to analyzer
:param getter: fun x
:param getter_available: fun x
:param Cls: Class to create for collision
:return:
"""
all_elements = {}
accessible_elements = {}
for father in self.inheritance:
for element in getter(father):
elem = Cls(element._functionNotParsed, self, element.contract_declarer)
elem.set_offset(element._functionNotParsed['src'], self.slither)
elem.analyze_params()
self.slither.add_function(elem)
all_elements[elem.canonical_name] = elem
accessible_elements = self.available_elements_from_inheritances(all_elements, getter_available)
# If there is a constructor in the functions # If there is a constructor in the functions
# We remove the previous constructor # We remove the previous constructor
@ -308,20 +335,25 @@ class ContractSolc04(Contract):
# #
# Note: contract.all_functions_called returns the constructors of the base contracts # Note: contract.all_functions_called returns the constructors of the base contracts
has_constructor = False has_constructor = False
for function in self._functions_no_params: for element in elements_no_params:
function.analyze_params() element.analyze_params()
if function.is_constructor: if element.is_constructor:
has_constructor = True has_constructor = True
if has_constructor: if has_constructor:
_functions = {k:v for (k, v) in self._functions.items() if not v.is_constructor} _accessible_functions = {k: v for (k, v) in accessible_elements.items() if not v.is_constructor}
self._functions = _functions
for element in elements_no_params:
accessible_elements[element.full_name] = element
all_elements[element.canonical_name] = element
for element in all_elements.values():
if accessible_elements[element.full_name] != all_elements[element.canonical_name]:
element.is_shadowed = True
return all_elements
for function in self._functions_no_params:
self._functions[function.full_name] = function
self._functions_no_params = []
return
def analyze_constant_state_variables(self): def analyze_constant_state_variables(self):
from slither.solc_parsing.expressions.expression_parsing import VariableNotFound from slither.solc_parsing.expressions.expression_parsing import VariableNotFound
@ -434,14 +466,12 @@ class ContractSolc04(Contract):
def convert_expression_to_slithir(self): def convert_expression_to_slithir(self):
for func in self.functions + self.modifiers: for func in self.functions + self.modifiers:
if func.contract == self:
func.generate_slithir_and_analyze() func.generate_slithir_and_analyze()
all_ssa_state_variables_instances = dict() all_ssa_state_variables_instances = dict()
for contract in self.inheritance: for contract in self.inheritance:
for v in contract.variables: for v in contract.state_variables_declared:
if v.contract == contract:
new_var = StateIRVariable(v) new_var = StateIRVariable(v)
all_ssa_state_variables_instances[v.canonical_name] = new_var all_ssa_state_variables_instances[v.canonical_name] = new_var
self._initial_state_variables.append(new_var) self._initial_state_variables.append(new_var)
@ -453,7 +483,6 @@ class ContractSolc04(Contract):
self._initial_state_variables.append(new_var) self._initial_state_variables.append(new_var)
for func in self.functions + self.modifiers: for func in self.functions + self.modifiers:
if func.contract == self:
func.generate_slithir_ssa(all_ssa_state_variables_instances) func.generate_slithir_ssa(all_ssa_state_variables_instances)
def fix_phi(self): def fix_phi(self):

@ -27,6 +27,7 @@ from slither.utils.utils import unroll
from slither.visitors.expression.export_values import ExportValues from slither.visitors.expression.export_values import ExportValues
from slither.visitors.expression.has_conditional import HasConditional from slither.visitors.expression.has_conditional import HasConditional
from slither.solc_parsing.exceptions import ParsingError from slither.solc_parsing.exceptions import ParsingError
from slither.core.source_mapping.source_mapping import SourceMapping
logger = logging.getLogger("FunctionSolc") logger = logging.getLogger("FunctionSolc")
@ -35,9 +36,10 @@ class FunctionSolc(Function):
""" """
# elems = [(type, name)] # elems = [(type, name)]
def __init__(self, function, contract): def __init__(self, function, contract, contract_declarer):
super(FunctionSolc, self).__init__() super(FunctionSolc, self).__init__()
self._contract = contract self._contract = contract
self._contract_declarer = contract_declarer
# Only present if compact AST # Only present if compact AST
self._referenced_declaration = None self._referenced_declaration = None
@ -835,6 +837,9 @@ class FunctionSolc(Function):
def _parse_params(self, params): def _parse_params(self, params):
assert params[self.get_key()] == 'ParameterList' assert params[self.get_key()] == 'ParameterList'
self.parameters_src = SourceMapping()
self.parameters_src.set_offset(params['src'], self.contract.slither)
if self.is_compact_ast: if self.is_compact_ast:
params = params['parameters'] params = params['parameters']
else: else:
@ -860,6 +865,9 @@ class FunctionSolc(Function):
assert returns[self.get_key()] == 'ParameterList' assert returns[self.get_key()] == 'ParameterList'
self.returns_src = SourceMapping()
self.returns_src.set_offset(returns['src'], self.contract.slither)
if self.is_compact_ast: if self.is_compact_ast:
returns = returns['parameters'] returns = returns['parameters']
else: else:

@ -5,3 +5,5 @@ class ParsingError(SlitherException): pass
class ParsingNameReuse(SlitherException): pass class ParsingNameReuse(SlitherException): pass
class ParsingContractNotFound(SlitherException): pass class ParsingContractNotFound(SlitherException): pass
class VariableNotFound(SlitherException): pass

@ -35,19 +35,11 @@ from slither.core.solidity_types import (ArrayType, ElementaryType,
FunctionType, MappingType) FunctionType, MappingType)
from slither.solc_parsing.solidity_types.type_parsing import (UnknownType, from slither.solc_parsing.solidity_types.type_parsing import (UnknownType,
parse_type) parse_type)
from slither.solc_parsing.exceptions import ParsingError from slither.solc_parsing.exceptions import ParsingError, VariableNotFound
logger = logging.getLogger("ExpressionParsing") logger = logging.getLogger("ExpressionParsing")
###################################################################################
###################################################################################
# region Exception
###################################################################################
###################################################################################
class VariableNotFound(Exception): pass
# endregion
################################################################################### ###################################################################################
################################################################################### ###################################################################################
# region Helpers # region Helpers
@ -68,15 +60,31 @@ def get_pointer_name(variable):
return None return None
def find_variable(var_name, caller_context, referenced_declaration=None): def find_variable(var_name, caller_context, referenced_declaration=None, is_super=False):
# variable are looked from the contract declarer
# functions can be shadowed, but are looked from the contract instance, rather than the contract declarer
# the difference between function and variable come from the fact that an internal call, or an variable access
# in a function does not behave similariy, for example in:
# contract C{
# function f(){
# state_var = 1
# f2()
# }
# state_var will refer to C.state_var, no mater if C is inherited
# while f2() will refer to the function definition of the inherited contract (C.f2() in the context of C, or
# the contract inheriting from C)
# for events it's unclear what should be the behavior, as they can be shadowed, but there is not impact
# structure/enums cannot be shadowed
if isinstance(caller_context, Contract): if isinstance(caller_context, Contract):
function = None function = None
contract = caller_context contract = caller_context
contract_declarer = caller_context
elif isinstance(caller_context, Function): elif isinstance(caller_context, Function):
function = caller_context function = caller_context
contract = function.contract contract = function.contract
contract_declarer = function.contract_declarer
else: else:
raise ParsingError('Incorrect caller context') raise ParsingError('Incorrect caller context')
@ -98,24 +106,35 @@ def find_variable(var_name, caller_context, referenced_declaration=None):
if var_name and var_name in func_variables_ptr: if var_name and var_name in func_variables_ptr:
return func_variables_ptr[var_name] return func_variables_ptr[var_name]
contract_variables = contract.variables_as_dict() # variable are looked from the contract declarer
contract_variables = contract_declarer.variables_as_dict()
if var_name in contract_variables: if var_name in contract_variables:
return contract_variables[var_name] return contract_variables[var_name]
# A state variable can be a pointer # A state variable can be a pointer
conc_variables_ptr = {get_pointer_name(f) : f for f in contract.variables} conc_variables_ptr = {get_pointer_name(f) : f for f in contract_declarer.variables}
if var_name and var_name in conc_variables_ptr: if var_name and var_name in conc_variables_ptr:
return conc_variables_ptr[var_name] return conc_variables_ptr[var_name]
if is_super:
functions = contract.functions_as_dict() getter_available = lambda f: f.available_functions_as_dict().items()
d = {f.canonical_name:f for f in contract.functions}
functions = {f.full_name:f for f in contract.available_elements_from_inheritances(d, getter_available).values()}
else:
functions = contract.available_functions_as_dict()
if var_name in functions: if var_name in functions:
return functions[var_name] return functions[var_name]
modifiers = contract.modifiers_as_dict() if is_super:
getter_available = lambda m: m.available_modifiers_as_dict().items()
d = {m.canonical_name: m for m in contract.modifiers}
modifiers = {m.full_name: m for m in contract.available_elements_from_inheritances(d, getter_available).values()}
else:
modifiers = contract.available_modifiers_as_dict()
if var_name in modifiers: if var_name in modifiers:
return modifiers[var_name] return modifiers[var_name]
# structures are looked on the contract declarer
structures = contract.structures_as_dict() structures = contract.structures_as_dict()
if var_name in structures: if var_name in structures:
return structures[var_name] return structures[var_name]
@ -157,7 +176,7 @@ def find_variable(var_name, caller_context, referenced_declaration=None):
if function.referenced_declaration == referenced_declaration: if function.referenced_declaration == referenced_declaration:
return function return function
raise VariableNotFound('Variable not found: {}'.format(var_name)) raise VariableNotFound('Variable not found: {} (context {})'.format(var_name, caller_context))
# endregion # endregion
################################################################################### ###################################################################################
@ -294,7 +313,9 @@ def parse_call(expression, caller_context):
if isinstance(called, SuperCallExpression): if isinstance(called, SuperCallExpression):
return SuperCallExpression(called, arguments, type_return) return SuperCallExpression(called, arguments, type_return)
return CallExpression(called, arguments, type_return) call_expression = CallExpression(called, arguments, type_return)
call_expression.set_offset(expression['src'], caller_context.slither)
return call_expression
def parse_super_name(expression, is_compact_ast): def parse_super_name(expression, is_compact_ast):
if is_compact_ast: if is_compact_ast:
@ -536,9 +557,11 @@ def parse_expression(expression, caller_context):
referenced_declaration = expression['referencedDeclaration'] referenced_declaration = expression['referencedDeclaration']
else: else:
referenced_declaration = None referenced_declaration = None
var = find_variable(value, caller_context, referenced_declaration) var = find_variable(value, caller_context, referenced_declaration)
identifier = Identifier(var) identifier = Identifier(var)
identifier.set_offset(expression['src'], caller_context.slither)
return identifier return identifier
elif name == 'IndexAccess': elif name == 'IndexAccess':
@ -576,18 +599,7 @@ def parse_expression(expression, caller_context):
member_expression = parse_expression(children[0], caller_context) member_expression = parse_expression(children[0], caller_context)
if str(member_expression) == 'super': if str(member_expression) == 'super':
super_name = parse_super_name(expression, is_compact_ast) super_name = parse_super_name(expression, is_compact_ast)
if isinstance(caller_context, Contract): var = find_variable(super_name, caller_context, is_super=True)
inheritance = caller_context.inheritance
else:
assert isinstance(caller_context, Function)
inheritance = caller_context.contract.inheritance
var = None
for father in inheritance:
try:
var = find_variable(super_name, father)
break
except VariableNotFound:
continue
if var is None: if var is None:
raise VariableNotFound('Variable not found: {}'.format(super_name)) raise VariableNotFound('Variable not found: {}'.format(super_name))
return SuperIdentifier(var) return SuperIdentifier(var)
@ -667,6 +679,7 @@ def parse_expression(expression, caller_context):
arguments = [parse_expression(a, caller_context) for a in children[1::]] arguments = [parse_expression(a, caller_context) for a in children[1::]]
call = CallExpression(called, arguments, 'Modifier') call = CallExpression(called, arguments, 'Modifier')
call.set_offset(expression['src'], caller_context.slither)
return call return call
raise ParsingError('Expression not parsed %s'%name) raise ParsingError('Expression not parsed %s'%name)

@ -136,7 +136,7 @@ class SlitherSolc(Slither):
# match any char for filename # match any char for filename
# filename can contain space, /, -, .. # filename can contain space, /, -, ..
name = re.findall('=* (.+) =*', filename) name = re.findall('=+ (.+) =+', filename)
if name: if name:
assert len(name) == 1 assert len(name) == 1
name = name[0] name = name[0]
@ -148,6 +148,14 @@ class SlitherSolc(Slither):
sourceUnit = re.findall('[0-9]*:[0-9]*:([0-9]*)', data['src']) sourceUnit = re.findall('[0-9]*:[0-9]*:([0-9]*)', data['src'])
if len(sourceUnit) == 1: if len(sourceUnit) == 1:
sourceUnit = int(sourceUnit[0]) sourceUnit = int(sourceUnit[0])
if sourceUnit == -1:
# if source unit is not found
# We can still deduce it, by assigning to the last source_code added
# This works only for crytic compile.
# which used --combined-json ast, rather than --ast-json
# As a result -1 is not used as index
if self.crytic_compile is not None:
sourceUnit = len(self.source_code)
self._source_units[sourceUnit] = name self._source_units[sourceUnit] = name
if os.path.isfile(name) and not name in self.source_code: if os.path.isfile(name) and not name in self.source_code:

@ -59,7 +59,7 @@ def _find_from_type_name(name, contract, contracts, structures, enums):
all_enums = [item for sublist in all_enums for item in sublist] all_enums = [item for sublist in all_enums for item in sublist]
var_type = next((e for e in all_enums if e.name == enum_name), None) var_type = next((e for e in all_enums if e.name == enum_name), None)
if not var_type: if not var_type:
var_type = next((e for e in all_enums if e.contract.name+"."+e.name == enum_name), None) var_type = next((e for e in all_enums if e.canonical_name == enum_name), None)
if not var_type: if not var_type:
# any contract can refer to another contract's structure # any contract can refer to another contract's structure
name_struct = name name_struct = name
@ -70,14 +70,14 @@ def _find_from_type_name(name, contract, contracts, structures, enums):
all_structures = [item for sublist in all_structures for item in sublist] all_structures = [item for sublist in all_structures for item in sublist]
var_type = next((st for st in all_structures if st.name == name_struct), None) var_type = next((st for st in all_structures if st.name == name_struct), None)
if not var_type: if not var_type:
var_type = next((st for st in all_structures if st.contract.name+"."+st.name == name_struct), None) var_type = next((st for st in all_structures if st.canonical_name == name_struct), None)
# case where struct xxx.xx[] where not well formed in the AST # case where struct xxx.xx[] where not well formed in the AST
if not var_type: if not var_type:
depth = 0 depth = 0
while name_struct.endswith('[]'): while name_struct.endswith('[]'):
name_struct = name_struct[0:-2] name_struct = name_struct[0:-2]
depth+=1 depth+=1
var_type = next((st for st in all_structures if st.contract.name+"."+st.name == name_struct), None) var_type = next((st for st in all_structures if st.canonical_name == name_struct), None)
if var_type: if var_type:
return ArrayType(UserDefinedType(var_type), Literal(depth, 'uint256')) return ArrayType(UserDefinedType(var_type), Literal(depth, 'uint256'))

@ -19,7 +19,7 @@ def detect_c3_function_shadowing(contract):
for i in range(0, len(contract.immediate_inheritance) - 1): for i in range(0, len(contract.immediate_inheritance) - 1):
inherited_contract1 = contract.immediate_inheritance[i] inherited_contract1 = contract.immediate_inheritance[i]
for function1 in inherited_contract1.functions_and_modifiers: for function1 in inherited_contract1.functions_and_modifiers_declared:
# If this function has already be handled or is unimplemented, we skip it # If this function has already be handled or is unimplemented, we skip it
if function1.full_name in results or function1.is_constructor or not function1.is_implemented: if function1.full_name in results or function1.is_constructor or not function1.is_implemented:
continue continue
@ -61,7 +61,7 @@ def detect_direct_function_shadowing(contract):
function (could have provided it through inheritance, does not need to directly define it). function (could have provided it through inheritance, does not need to directly define it).
-overshadowed_function is the function definition which is overshadowed by the provided contract's definition. -overshadowed_function is the function definition which is overshadowed by the provided contract's definition.
""" """
functions_declared = {function.full_name: function for function in contract.functions_and_modifiers_not_inherited} functions_declared = {function.full_name: function for function in contract.functions_and_modifiers_declared}
results = {} results = {}
for base_contract in reversed(contract.immediate_inheritance): for base_contract in reversed(contract.immediate_inheritance):
for base_function in base_contract.functions_and_modifiers: for base_function in base_contract.functions_and_modifiers:
@ -109,7 +109,7 @@ def detect_function_shadowing(contracts, direct_shadowing=True, indirect_shadowi
for y in range(x + 1, len(colliding_functions)): for y in range(x + 1, len(colliding_functions)):
# The same function definition can appear more than once in the inheritance chain, # The same function definition can appear more than once in the inheritance chain,
# overshadowing items between, so it is important to remember to filter it out here. # overshadowing items between, so it is important to remember to filter it out here.
if colliding_functions[y][1].contract != colliding_functions[x][1].contract: if colliding_functions[y][1].contract_declarer != colliding_functions[x][1].contract_declarer:
results.add((contract, colliding_functions[y][0], colliding_functions[y][1], results.add((contract, colliding_functions[y][0], colliding_functions[y][1],
colliding_functions[x][0], colliding_functions[x][1])) colliding_functions[x][0], colliding_functions[x][1]))
@ -128,8 +128,7 @@ def detect_state_variable_shadowing(contracts):
""" """
results = set() results = set()
for contract in contracts: for contract in contracts:
variables_declared = {variable.name: variable for variable in contract.variables variables_declared = {variable.name: variable for variable in contract.state_variables_declared}
if variable.contract == contract}
for immediate_base_contract in contract.immediate_inheritance: for immediate_base_contract in contract.immediate_inheritance:
for variable in immediate_base_contract.variables: for variable in immediate_base_contract.variables:
if variable.name in variables_declared: if variable.name in variables_declared:

@ -129,7 +129,7 @@ class ExpressionToSlithIR(ExpressionVisitor):
val = TupleVariable(self._node) val = TupleVariable(self._node)
else: else:
val = TemporaryVariable(self._node) val = TemporaryVariable(self._node)
internal_call = InternalCall(called, called.contract, len(args), val, expression.type_call) internal_call = InternalCall(called, len(args), val, expression.type_call)
self._result.append(internal_call) self._result.append(internal_call)
set_val(expression, val) set_val(expression, val)
else: else:

@ -1,6 +1,6 @@
INFO:CheckInitialization:Run initialization checks... (see https://github.com/crytic/slither/wiki/Upgradeability-Checks#initialization-checks) INFO:CheckInitialization:Run initialization checks... (see https://github.com/crytic/slither/wiki/Upgradeability-Checks#initialization-checks)
INFO:CheckInitialization:Contract_lack_to_call_modifier.initialize does not call initializer INFO:CheckInitialization:Contract_lack_to_call_modifier.initialize() does not call initializer
INFO:CheckInitialization:Missing call to Contract_no_bug.initialize in Contract_not_called_super_init INFO:CheckInitialization:Missing call to Contract_no_bug.initialize() in Contract_not_called_super_init
INFO:CheckInitialization:Contract_no_bug.initialize() is called multiple time in Contract_double_call INFO:CheckInitialization:Contract_no_bug.initialize() is called multiple time in Contract_double_call
INFO:CheckInitialization:Check the deployement script to ensure that these functions are called: INFO:CheckInitialization:Check the deployement script to ensure that these functions are called:
Contract_no_bug needs to be initialized by initialize() Contract_no_bug needs to be initialized by initialize()

@ -1,12 +1,13 @@
{ {
"success": true, "success": true,
"error": null, "error": null,
"results": [ "results": {
"detectors": [
{ {
"check": "arbitrary-send", "check": "arbitrary-send",
"impact": "High", "impact": "High",
"confidence": "Medium", "confidence": "Medium",
"description": "Test.direct (tests/arbitrary_send-0.5.1.sol#11-13) sends eth to arbitrary user\n\tDangerous calls:\n\t- msg.sender.send(address(this).balance) (tests/arbitrary_send-0.5.1.sol#12)\n", "description": "Test.direct() (tests/arbitrary_send-0.5.1.sol#11-13) sends eth to arbitrary user\n\tDangerous calls:\n\t- msg.sender.send(address(this).balance) (tests/arbitrary_send-0.5.1.sol#12)\n",
"elements": [ "elements": [
{ {
"type": "function", "type": "function",
@ -26,7 +27,8 @@
"starting_column": 5, "starting_column": 5,
"ending_column": 6 "ending_column": 6
}, },
"contract": { "type_specific_fields": {
"parent": {
"type": "contract", "type": "contract",
"name": "Test", "name": "Test",
"source_mapping": { "source_mapping": {
@ -82,6 +84,8 @@
"starting_column": 1, "starting_column": 1,
"ending_column": 2 "ending_column": 2
} }
},
"signature": "direct()"
} }
}, },
{ {
@ -100,7 +104,8 @@
"starting_column": 9, "starting_column": 9,
"ending_column": 47 "ending_column": 47
}, },
"function": { "type_specific_fields": {
"parent": {
"type": "function", "type": "function",
"name": "direct", "name": "direct",
"source_mapping": { "source_mapping": {
@ -118,7 +123,8 @@
"starting_column": 5, "starting_column": 5,
"ending_column": 6 "ending_column": 6
}, },
"contract": { "type_specific_fields": {
"parent": {
"type": "contract", "type": "contract",
"name": "Test", "name": "Test",
"source_mapping": { "source_mapping": {
@ -174,6 +180,9 @@
"starting_column": 1, "starting_column": 1,
"ending_column": 2 "ending_column": 2
} }
},
"signature": "direct()"
}
} }
} }
} }
@ -183,7 +192,7 @@
"check": "arbitrary-send", "check": "arbitrary-send",
"impact": "High", "impact": "High",
"confidence": "Medium", "confidence": "Medium",
"description": "Test.indirect (tests/arbitrary_send-0.5.1.sol#19-21) sends eth to arbitrary user\n\tDangerous calls:\n\t- destination.send(address(this).balance) (tests/arbitrary_send-0.5.1.sol#20)\n", "description": "Test.indirect() (tests/arbitrary_send-0.5.1.sol#19-21) sends eth to arbitrary user\n\tDangerous calls:\n\t- destination.send(address(this).balance) (tests/arbitrary_send-0.5.1.sol#20)\n",
"elements": [ "elements": [
{ {
"type": "function", "type": "function",
@ -203,7 +212,8 @@
"starting_column": 5, "starting_column": 5,
"ending_column": 6 "ending_column": 6
}, },
"contract": { "type_specific_fields": {
"parent": {
"type": "contract", "type": "contract",
"name": "Test", "name": "Test",
"source_mapping": { "source_mapping": {
@ -259,6 +269,8 @@
"starting_column": 1, "starting_column": 1,
"ending_column": 2 "ending_column": 2
} }
},
"signature": "indirect()"
} }
}, },
{ {
@ -277,7 +289,8 @@
"starting_column": 9, "starting_column": 9,
"ending_column": 48 "ending_column": 48
}, },
"function": { "type_specific_fields": {
"parent": {
"type": "function", "type": "function",
"name": "indirect", "name": "indirect",
"source_mapping": { "source_mapping": {
@ -295,7 +308,8 @@
"starting_column": 5, "starting_column": 5,
"ending_column": 6 "ending_column": 6
}, },
"contract": { "type_specific_fields": {
"parent": {
"type": "contract", "type": "contract",
"name": "Test", "name": "Test",
"source_mapping": { "source_mapping": {
@ -351,6 +365,9 @@
"starting_column": 1, "starting_column": 1,
"ending_column": 2 "ending_column": 2
} }
},
"signature": "indirect()"
}
} }
} }
} }
@ -358,3 +375,4 @@
} }
] ]
} }
}

@ -1,8 +1,8 @@
INFO:Detectors: INFO:Detectors:
Test.direct (tests/arbitrary_send-0.5.1.sol#11-13) sends eth to arbitrary user Test.direct() (tests/arbitrary_send-0.5.1.sol#11-13) sends eth to arbitrary user
Dangerous calls: Dangerous calls:
- msg.sender.send(address(this).balance) (tests/arbitrary_send-0.5.1.sol#12) - msg.sender.send(address(this).balance) (tests/arbitrary_send-0.5.1.sol#12)
Test.indirect (tests/arbitrary_send-0.5.1.sol#19-21) sends eth to arbitrary user Test.indirect() (tests/arbitrary_send-0.5.1.sol#19-21) sends eth to arbitrary user
Dangerous calls: Dangerous calls:
- destination.send(address(this).balance) (tests/arbitrary_send-0.5.1.sol#20) - destination.send(address(this).balance) (tests/arbitrary_send-0.5.1.sol#20)
Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#functions-that-send-ether-to-arbitrary-destinations Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#functions-that-send-ether-to-arbitrary-destinations

@ -1,12 +1,13 @@
{ {
"success": true, "success": true,
"error": null, "error": null,
"results": [ "results": {
"detectors": [
{ {
"check": "arbitrary-send", "check": "arbitrary-send",
"impact": "High", "impact": "High",
"confidence": "Medium", "confidence": "Medium",
"description": "Test.direct (tests/arbitrary_send.sol#11-13) sends eth to arbitrary user\n\tDangerous calls:\n\t- msg.sender.send(address(this).balance) (tests/arbitrary_send.sol#12)\n", "description": "Test.direct() (tests/arbitrary_send.sol#11-13) sends eth to arbitrary user\n\tDangerous calls:\n\t- msg.sender.send(address(this).balance) (tests/arbitrary_send.sol#12)\n",
"elements": [ "elements": [
{ {
"type": "function", "type": "function",
@ -26,7 +27,8 @@
"starting_column": 5, "starting_column": 5,
"ending_column": 6 "ending_column": 6
}, },
"contract": { "type_specific_fields": {
"parent": {
"type": "contract", "type": "contract",
"name": "Test", "name": "Test",
"source_mapping": { "source_mapping": {
@ -82,6 +84,8 @@
"starting_column": 1, "starting_column": 1,
"ending_column": 2 "ending_column": 2
} }
},
"signature": "direct()"
} }
}, },
{ {
@ -100,7 +104,8 @@
"starting_column": 9, "starting_column": 9,
"ending_column": 47 "ending_column": 47
}, },
"function": { "type_specific_fields": {
"parent": {
"type": "function", "type": "function",
"name": "direct", "name": "direct",
"source_mapping": { "source_mapping": {
@ -118,7 +123,8 @@
"starting_column": 5, "starting_column": 5,
"ending_column": 6 "ending_column": 6
}, },
"contract": { "type_specific_fields": {
"parent": {
"type": "contract", "type": "contract",
"name": "Test", "name": "Test",
"source_mapping": { "source_mapping": {
@ -174,6 +180,9 @@
"starting_column": 1, "starting_column": 1,
"ending_column": 2 "ending_column": 2
} }
},
"signature": "direct()"
}
} }
} }
} }
@ -183,7 +192,7 @@
"check": "arbitrary-send", "check": "arbitrary-send",
"impact": "High", "impact": "High",
"confidence": "Medium", "confidence": "Medium",
"description": "Test.indirect (tests/arbitrary_send.sol#19-21) sends eth to arbitrary user\n\tDangerous calls:\n\t- destination.send(address(this).balance) (tests/arbitrary_send.sol#20)\n", "description": "Test.indirect() (tests/arbitrary_send.sol#19-21) sends eth to arbitrary user\n\tDangerous calls:\n\t- destination.send(address(this).balance) (tests/arbitrary_send.sol#20)\n",
"elements": [ "elements": [
{ {
"type": "function", "type": "function",
@ -203,7 +212,8 @@
"starting_column": 5, "starting_column": 5,
"ending_column": 6 "ending_column": 6
}, },
"contract": { "type_specific_fields": {
"parent": {
"type": "contract", "type": "contract",
"name": "Test", "name": "Test",
"source_mapping": { "source_mapping": {
@ -259,6 +269,8 @@
"starting_column": 1, "starting_column": 1,
"ending_column": 2 "ending_column": 2
} }
},
"signature": "indirect()"
} }
}, },
{ {
@ -277,7 +289,8 @@
"starting_column": 9, "starting_column": 9,
"ending_column": 48 "ending_column": 48
}, },
"function": { "type_specific_fields": {
"parent": {
"type": "function", "type": "function",
"name": "indirect", "name": "indirect",
"source_mapping": { "source_mapping": {
@ -295,7 +308,8 @@
"starting_column": 5, "starting_column": 5,
"ending_column": 6 "ending_column": 6
}, },
"contract": { "type_specific_fields": {
"parent": {
"type": "contract", "type": "contract",
"name": "Test", "name": "Test",
"source_mapping": { "source_mapping": {
@ -351,6 +365,9 @@
"starting_column": 1, "starting_column": 1,
"ending_column": 2 "ending_column": 2
} }
},
"signature": "indirect()"
}
} }
} }
} }
@ -358,3 +375,4 @@
} }
] ]
} }
}

@ -1,8 +1,8 @@
INFO:Detectors: INFO:Detectors:
Test.direct (tests/arbitrary_send.sol#11-13) sends eth to arbitrary user Test.direct() (tests/arbitrary_send.sol#11-13) sends eth to arbitrary user
Dangerous calls: Dangerous calls:
- msg.sender.send(address(this).balance) (tests/arbitrary_send.sol#12) - msg.sender.send(address(this).balance) (tests/arbitrary_send.sol#12)
Test.indirect (tests/arbitrary_send.sol#19-21) sends eth to arbitrary user Test.indirect() (tests/arbitrary_send.sol#19-21) sends eth to arbitrary user
Dangerous calls: Dangerous calls:
- destination.send(address(this).balance) (tests/arbitrary_send.sol#20) - destination.send(address(this).balance) (tests/arbitrary_send.sol#20)
Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#functions-that-send-ether-to-arbitrary-destinations Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#functions-that-send-ether-to-arbitrary-destinations

@ -1,7 +1,8 @@
{ {
"success": true, "success": true,
"error": null, "error": null,
"results": [ "results": {
"detectors": [
{ {
"check": "backdoor", "check": "backdoor",
"impact": "High", "impact": "High",
@ -26,7 +27,8 @@
"starting_column": 5, "starting_column": 5,
"ending_column": 6 "ending_column": 6
}, },
"contract": { "type_specific_fields": {
"parent": {
"type": "contract", "type": "contract",
"name": "C", "name": "C",
"source_mapping": { "source_mapping": {
@ -48,9 +50,12 @@
"starting_column": 1, "starting_column": 1,
"ending_column": 2 "ending_column": 2
} }
},
"signature": "i_am_a_backdoor()"
} }
} }
] ]
} }
] ]
} }
}

@ -1,12 +1,13 @@
{ {
"success": true, "success": true,
"error": null, "error": null,
"results": [ "results": {
"detectors": [
{ {
"check": "suicidal", "check": "suicidal",
"impact": "High", "impact": "High",
"confidence": "High", "confidence": "High",
"description": "C.i_am_a_backdoor (tests/backdoor.sol#4-6) allows anyone to destruct the contract\n", "description": "C.i_am_a_backdoor() (tests/backdoor.sol#4-6) allows anyone to destruct the contract\n",
"elements": [ "elements": [
{ {
"type": "function", "type": "function",
@ -26,7 +27,8 @@
"starting_column": 5, "starting_column": 5,
"ending_column": 6 "ending_column": 6
}, },
"contract": { "type_specific_fields": {
"parent": {
"type": "contract", "type": "contract",
"name": "C", "name": "C",
"source_mapping": { "source_mapping": {
@ -48,9 +50,12 @@
"starting_column": 1, "starting_column": 1,
"ending_column": 2 "ending_column": 2
} }
},
"signature": "i_am_a_backdoor()"
} }
} }
] ]
} }
] ]
} }
}

@ -1,5 +1,5 @@
INFO:Detectors: INFO:Detectors:
C.i_am_a_backdoor (tests/backdoor.sol#4-6) allows anyone to destruct the contract C.i_am_a_backdoor() (tests/backdoor.sol#4-6) allows anyone to destruct the contract
Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#suicidal Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#suicidal
INFO:Slither:/home/travis/build/crytic/slither/scripts/../tests/expected_json/backdoor.suicidal.json exists already, the overwrite is prevented INFO:Slither:/home/travis/build/crytic/slither/scripts/../tests/expected_json/backdoor.suicidal.json exists already, the overwrite is prevented
INFO:Slither:tests/backdoor.sol analyzed (1 contracts), 1 result(s) found INFO:Slither:tests/backdoor.sol analyzed (1 contracts), 1 result(s) found

@ -1,7 +1,8 @@
{ {
"success": true, "success": true,
"error": null, "error": null,
"results": [ "results": {
"detectors": [
{ {
"check": "constable-states", "check": "constable-states",
"impact": "Informational", "impact": "Informational",
@ -24,7 +25,8 @@
"starting_column": 5, "starting_column": 5,
"ending_column": 81 "ending_column": 81
}, },
"contract": { "type_specific_fields": {
"parent": {
"type": "contract", "type": "contract",
"name": "A", "name": "A",
"source_mapping": { "source_mapping": {
@ -59,6 +61,7 @@
} }
} }
} }
}
] ]
}, },
{ {
@ -83,7 +86,8 @@
"starting_column": 5, "starting_column": 5,
"ending_column": 25 "ending_column": 25
}, },
"contract": { "type_specific_fields": {
"parent": {
"type": "contract", "type": "contract",
"name": "A", "name": "A",
"source_mapping": { "source_mapping": {
@ -118,6 +122,7 @@
} }
} }
} }
}
] ]
}, },
{ {
@ -142,7 +147,8 @@
"starting_column": 5, "starting_column": 5,
"ending_column": 25 "ending_column": 25
}, },
"contract": { "type_specific_fields": {
"parent": {
"type": "contract", "type": "contract",
"name": "A", "name": "A",
"source_mapping": { "source_mapping": {
@ -177,6 +183,7 @@
} }
} }
} }
}
] ]
}, },
{ {
@ -201,7 +208,8 @@
"starting_column": 5, "starting_column": 5,
"ending_column": 81 "ending_column": 81
}, },
"contract": { "type_specific_fields": {
"parent": {
"type": "contract", "type": "contract",
"name": "B", "name": "B",
"source_mapping": { "source_mapping": {
@ -232,6 +240,7 @@
} }
} }
} }
}
] ]
}, },
{ {
@ -256,7 +265,8 @@
"starting_column": 5, "starting_column": 5,
"ending_column": 47 "ending_column": 47
}, },
"contract": { "type_specific_fields": {
"parent": {
"type": "contract", "type": "contract",
"name": "MyConc", "name": "MyConc",
"source_mapping": { "source_mapping": {
@ -287,6 +297,7 @@
} }
} }
} }
}
] ]
}, },
{ {
@ -311,7 +322,8 @@
"starting_column": 5, "starting_column": 5,
"ending_column": 38 "ending_column": 38
}, },
"contract": { "type_specific_fields": {
"parent": {
"type": "contract", "type": "contract",
"name": "MyConc", "name": "MyConc",
"source_mapping": { "source_mapping": {
@ -342,7 +354,9 @@
} }
} }
} }
}
] ]
} }
] ]
} }
}

@ -1,12 +1,13 @@
{ {
"success": true, "success": true,
"error": null, "error": null,
"results": [ "results": {
"detectors": [
{ {
"check": "constant-function", "check": "constant-function",
"impact": "Medium", "impact": "Medium",
"confidence": "Medium", "confidence": "Medium",
"description": "Constant.test_assembly_bug (tests/constant-0.5.1.sol#15-17) is declared view but contains assembly code\n", "description": "Constant.test_assembly_bug() (tests/constant-0.5.1.sol#15-17) is declared view but contains assembly code\n",
"elements": [ "elements": [
{ {
"type": "function", "type": "function",
@ -26,7 +27,8 @@
"starting_column": 5, "starting_column": 5,
"ending_column": 6 "ending_column": 6
}, },
"contract": { "type_specific_fields": {
"parent": {
"type": "contract", "type": "contract",
"name": "Constant", "name": "Constant",
"source_mapping": { "source_mapping": {
@ -59,6 +61,8 @@
"starting_column": 1, "starting_column": 1,
"ending_column": 2 "ending_column": 2
} }
},
"signature": "test_assembly_bug()"
} }
} }
], ],
@ -68,3 +72,4 @@
} }
] ]
} }
}

@ -1,4 +1,4 @@
INFO:Detectors: INFO:Detectors:
Constant.test_assembly_bug (tests/constant-0.5.1.sol#15-17) is declared view but contains assembly code Constant.test_assembly_bug() (tests/constant-0.5.1.sol#15-17) is declared view but contains assembly code
Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#constant-functions-changing-the-state Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#constant-functions-changing-the-state
INFO:Slither:tests/constant-0.5.1.sol analyzed (1 contracts), 1 result(s) found INFO:Slither:tests/constant-0.5.1.sol analyzed (1 contracts), 1 result(s) found

@ -1,12 +1,13 @@
{ {
"success": true, "success": true,
"error": null, "error": null,
"results": [ "results": {
"detectors": [
{ {
"check": "constant-function", "check": "constant-function",
"impact": "Medium", "impact": "Medium",
"confidence": "Medium", "confidence": "Medium",
"description": "Constant.test_view_bug (tests/constant.sol#5-7) is declared view but changes state variables:\n\t- Constant.a\n", "description": "Constant.test_view_bug() (tests/constant.sol#5-7) is declared view but changes state variables:\n\t- Constant.a\n",
"elements": [ "elements": [
{ {
"type": "function", "type": "function",
@ -26,7 +27,8 @@
"starting_column": 5, "starting_column": 5,
"ending_column": 6 "ending_column": 6
}, },
"contract": { "type_specific_fields": {
"parent": {
"type": "contract", "type": "contract",
"name": "Constant", "name": "Constant",
"source_mapping": { "source_mapping": {
@ -66,6 +68,8 @@
"starting_column": 1, "starting_column": 1,
"ending_column": 2 "ending_column": 2
} }
},
"signature": "test_view_bug()"
} }
}, },
{ {
@ -84,7 +88,8 @@
"starting_column": 5, "starting_column": 5,
"ending_column": 11 "ending_column": 11
}, },
"contract": { "type_specific_fields": {
"parent": {
"type": "contract", "type": "contract",
"name": "Constant", "name": "Constant",
"source_mapping": { "source_mapping": {
@ -126,6 +131,7 @@
} }
} }
} }
}
], ],
"additional_fields": { "additional_fields": {
"contains_assembly": false "contains_assembly": false
@ -135,7 +141,7 @@
"check": "constant-function", "check": "constant-function",
"impact": "Medium", "impact": "Medium",
"confidence": "Medium", "confidence": "Medium",
"description": "Constant.test_constant_bug (tests/constant.sol#9-11) is declared view but changes state variables:\n\t- Constant.a\n", "description": "Constant.test_constant_bug() (tests/constant.sol#9-11) is declared view but changes state variables:\n\t- Constant.a\n",
"elements": [ "elements": [
{ {
"type": "function", "type": "function",
@ -155,7 +161,8 @@
"starting_column": 5, "starting_column": 5,
"ending_column": 6 "ending_column": 6
}, },
"contract": { "type_specific_fields": {
"parent": {
"type": "contract", "type": "contract",
"name": "Constant", "name": "Constant",
"source_mapping": { "source_mapping": {
@ -195,6 +202,8 @@
"starting_column": 1, "starting_column": 1,
"ending_column": 2 "ending_column": 2
} }
},
"signature": "test_constant_bug()"
} }
}, },
{ {
@ -213,7 +222,8 @@
"starting_column": 5, "starting_column": 5,
"ending_column": 11 "ending_column": 11
}, },
"contract": { "type_specific_fields": {
"parent": {
"type": "contract", "type": "contract",
"name": "Constant", "name": "Constant",
"source_mapping": { "source_mapping": {
@ -255,6 +265,7 @@
} }
} }
} }
}
], ],
"additional_fields": { "additional_fields": {
"contains_assembly": false "contains_assembly": false
@ -264,7 +275,7 @@
"check": "constant-function", "check": "constant-function",
"impact": "Medium", "impact": "Medium",
"confidence": "Medium", "confidence": "Medium",
"description": "Constant.test_assembly_bug (tests/constant.sol#22-24) is declared view but contains assembly code\n", "description": "Constant.test_assembly_bug() (tests/constant.sol#22-24) is declared view but contains assembly code\n",
"elements": [ "elements": [
{ {
"type": "function", "type": "function",
@ -284,7 +295,8 @@
"starting_column": 5, "starting_column": 5,
"ending_column": 6 "ending_column": 6
}, },
"contract": { "type_specific_fields": {
"parent": {
"type": "contract", "type": "contract",
"name": "Constant", "name": "Constant",
"source_mapping": { "source_mapping": {
@ -324,6 +336,8 @@
"starting_column": 1, "starting_column": 1,
"ending_column": 2 "ending_column": 2
} }
},
"signature": "test_assembly_bug()"
} }
} }
], ],
@ -333,3 +347,4 @@
} }
] ]
} }
}

@ -1,8 +1,8 @@
INFO:Detectors: INFO:Detectors:
Constant.test_view_bug (tests/constant.sol#5-7) is declared view but changes state variables: Constant.test_view_bug() (tests/constant.sol#5-7) is declared view but changes state variables:
- Constant.a - Constant.a
Constant.test_constant_bug (tests/constant.sol#9-11) is declared view but changes state variables: Constant.test_constant_bug() (tests/constant.sol#9-11) is declared view but changes state variables:
- Constant.a - Constant.a
Constant.test_assembly_bug (tests/constant.sol#22-24) is declared view but contains assembly code Constant.test_assembly_bug() (tests/constant.sol#22-24) is declared view but contains assembly code
Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#constant-functions-changing-the-state Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#constant-functions-changing-the-state
INFO:Slither:tests/constant.sol analyzed (1 contracts), 3 result(s) found INFO:Slither:tests/constant.sol analyzed (1 contracts), 3 result(s) found

@ -1,7 +1,8 @@
{ {
"success": true, "success": true,
"error": null, "error": null,
"results": [ "results": {
"detectors": [
{ {
"check": "controlled-delegatecall", "check": "controlled-delegatecall",
"impact": "High", "impact": "High",
@ -24,7 +25,8 @@
"starting_column": 9, "starting_column": 9,
"ending_column": 36 "ending_column": 36
}, },
"function": { "type_specific_fields": {
"parent": {
"type": "function", "type": "function",
"name": "bad_delegate_call", "name": "bad_delegate_call",
"source_mapping": { "source_mapping": {
@ -43,7 +45,8 @@
"starting_column": 5, "starting_column": 5,
"ending_column": 6 "ending_column": 6
}, },
"contract": { "type_specific_fields": {
"parent": {
"type": "contract", "type": "contract",
"name": "C", "name": "C",
"source_mapping": { "source_mapping": {
@ -83,6 +86,9 @@
"starting_column": 1, "starting_column": 1,
"ending_column": 2 "ending_column": 2
} }
},
"signature": "bad_delegate_call(bytes)"
}
} }
} }
}, },
@ -105,7 +111,8 @@
"starting_column": 5, "starting_column": 5,
"ending_column": 6 "ending_column": 6
}, },
"contract": { "type_specific_fields": {
"parent": {
"type": "contract", "type": "contract",
"name": "C", "name": "C",
"source_mapping": { "source_mapping": {
@ -145,6 +152,8 @@
"starting_column": 1, "starting_column": 1,
"ending_column": 2 "ending_column": 2
} }
},
"signature": "bad_delegate_call(bytes)"
} }
} }
] ]
@ -171,7 +180,8 @@
"starting_column": 9, "starting_column": 9,
"ending_column": 57 "ending_column": 57
}, },
"function": { "type_specific_fields": {
"parent": {
"type": "function", "type": "function",
"name": "bad_delegate_call2", "name": "bad_delegate_call2",
"source_mapping": { "source_mapping": {
@ -189,7 +199,8 @@
"starting_column": 5, "starting_column": 5,
"ending_column": 6 "ending_column": 6
}, },
"contract": { "type_specific_fields": {
"parent": {
"type": "contract", "type": "contract",
"name": "C", "name": "C",
"source_mapping": { "source_mapping": {
@ -229,6 +240,9 @@
"starting_column": 1, "starting_column": 1,
"ending_column": 2 "ending_column": 2
} }
},
"signature": "bad_delegate_call2(bytes)"
}
} }
} }
}, },
@ -250,7 +264,8 @@
"starting_column": 5, "starting_column": 5,
"ending_column": 6 "ending_column": 6
}, },
"contract": { "type_specific_fields": {
"parent": {
"type": "contract", "type": "contract",
"name": "C", "name": "C",
"source_mapping": { "source_mapping": {
@ -290,9 +305,12 @@
"starting_column": 1, "starting_column": 1,
"ending_column": 2 "ending_column": 2
} }
},
"signature": "bad_delegate_call2(bytes)"
} }
} }
] ]
} }
] ]
} }
}

@ -1,7 +1,8 @@
{ {
"success": true, "success": true,
"error": null, "error": null,
"results": [ "results": {
"detectors": [
{ {
"check": "deprecated-standards", "check": "deprecated-standards",
"impact": "Informational", "impact": "Informational",
@ -24,7 +25,8 @@
"starting_column": 5, "starting_column": 5,
"ending_column": 49 "ending_column": 49
}, },
"contract": { "type_specific_fields": {
"parent": {
"type": "contract", "type": "contract",
"name": "ContractWithDeprecatedReferences", "name": "ContractWithDeprecatedReferences",
"source_mapping": { "source_mapping": {
@ -68,6 +70,7 @@
} }
} }
} }
}
] ]
}, },
{ {
@ -95,7 +98,8 @@
"starting_column": 9, "starting_column": 9,
"ending_column": 10 "ending_column": 10
}, },
"function": { "type_specific_fields": {
"parent": {
"type": "function", "type": "function",
"name": "functionWithDeprecatedThrow", "name": "functionWithDeprecatedThrow",
"source_mapping": { "source_mapping": {
@ -117,7 +121,8 @@
"starting_column": 5, "starting_column": 5,
"ending_column": 6 "ending_column": 6
}, },
"contract": { "type_specific_fields": {
"parent": {
"type": "contract", "type": "contract",
"name": "ContractWithDeprecatedReferences", "name": "ContractWithDeprecatedReferences",
"source_mapping": { "source_mapping": {
@ -159,6 +164,9 @@
"starting_column": 1, "starting_column": 1,
"ending_column": null "ending_column": null
} }
},
"signature": "functionWithDeprecatedThrow()"
}
} }
} }
} }
@ -186,7 +194,8 @@
"starting_column": 13, "starting_column": 13,
"ending_column": 18 "ending_column": 18
}, },
"function": { "type_specific_fields": {
"parent": {
"type": "function", "type": "function",
"name": "functionWithDeprecatedThrow", "name": "functionWithDeprecatedThrow",
"source_mapping": { "source_mapping": {
@ -208,7 +217,8 @@
"starting_column": 5, "starting_column": 5,
"ending_column": 6 "ending_column": 6
}, },
"contract": { "type_specific_fields": {
"parent": {
"type": "contract", "type": "contract",
"name": "ContractWithDeprecatedReferences", "name": "ContractWithDeprecatedReferences",
"source_mapping": { "source_mapping": {
@ -250,6 +260,9 @@
"starting_column": 1, "starting_column": 1,
"ending_column": null "ending_column": null
} }
},
"signature": "functionWithDeprecatedThrow()"
}
} }
} }
} }
@ -277,7 +290,8 @@
"starting_column": 9, "starting_column": 9,
"ending_column": 64 "ending_column": 64
}, },
"function": { "type_specific_fields": {
"parent": {
"type": "function", "type": "function",
"name": "functionWithDeprecatedReferences", "name": "functionWithDeprecatedReferences",
"source_mapping": { "source_mapping": {
@ -305,7 +319,8 @@
"starting_column": 5, "starting_column": 5,
"ending_column": 6 "ending_column": 6
}, },
"contract": { "type_specific_fields": {
"parent": {
"type": "contract", "type": "contract",
"name": "ContractWithDeprecatedReferences", "name": "ContractWithDeprecatedReferences",
"source_mapping": { "source_mapping": {
@ -347,6 +362,9 @@
"starting_column": 1, "starting_column": 1,
"ending_column": null "ending_column": null
} }
},
"signature": "functionWithDeprecatedReferences()"
}
} }
} }
} }
@ -374,7 +392,8 @@
"starting_column": 9, "starting_column": 9,
"ending_column": 53 "ending_column": 53
}, },
"function": { "type_specific_fields": {
"parent": {
"type": "function", "type": "function",
"name": "functionWithDeprecatedReferences", "name": "functionWithDeprecatedReferences",
"source_mapping": { "source_mapping": {
@ -402,7 +421,8 @@
"starting_column": 5, "starting_column": 5,
"ending_column": 6 "ending_column": 6
}, },
"contract": { "type_specific_fields": {
"parent": {
"type": "contract", "type": "contract",
"name": "ContractWithDeprecatedReferences", "name": "ContractWithDeprecatedReferences",
"source_mapping": { "source_mapping": {
@ -444,6 +464,9 @@
"starting_column": 1, "starting_column": 1,
"ending_column": null "ending_column": null
} }
},
"signature": "functionWithDeprecatedReferences()"
}
} }
} }
} }
@ -471,7 +494,8 @@
"starting_column": 9, "starting_column": 9,
"ending_column": 33 "ending_column": 33
}, },
"function": { "type_specific_fields": {
"parent": {
"type": "function", "type": "function",
"name": "functionWithDeprecatedReferences", "name": "functionWithDeprecatedReferences",
"source_mapping": { "source_mapping": {
@ -499,7 +523,8 @@
"starting_column": 5, "starting_column": 5,
"ending_column": 6 "ending_column": 6
}, },
"contract": { "type_specific_fields": {
"parent": {
"type": "contract", "type": "contract",
"name": "ContractWithDeprecatedReferences", "name": "ContractWithDeprecatedReferences",
"source_mapping": { "source_mapping": {
@ -541,6 +566,9 @@
"starting_column": 1, "starting_column": 1,
"ending_column": null "ending_column": null
} }
},
"signature": "functionWithDeprecatedReferences()"
}
} }
} }
} }
@ -568,7 +596,8 @@
"starting_column": 9, "starting_column": 9,
"ending_column": 28 "ending_column": 28
}, },
"function": { "type_specific_fields": {
"parent": {
"type": "function", "type": "function",
"name": "functionWithDeprecatedReferences", "name": "functionWithDeprecatedReferences",
"source_mapping": { "source_mapping": {
@ -596,7 +625,8 @@
"starting_column": 5, "starting_column": 5,
"ending_column": 6 "ending_column": 6
}, },
"contract": { "type_specific_fields": {
"parent": {
"type": "contract", "type": "contract",
"name": "ContractWithDeprecatedReferences", "name": "ContractWithDeprecatedReferences",
"source_mapping": { "source_mapping": {
@ -638,6 +668,9 @@
"starting_column": 1, "starting_column": 1,
"ending_column": null "ending_column": null
} }
},
"signature": "functionWithDeprecatedReferences()"
}
} }
} }
} }
@ -645,3 +678,4 @@
} }
] ]
} }
}

@ -1,7 +1,8 @@
{ {
"success": true, "success": true,
"error": null, "error": null,
"results": [ "results": {
"detectors": [
{ {
"check": "erc20-indexed", "check": "erc20-indexed",
"impact": "Informational", "impact": "Informational",
@ -24,10 +25,8 @@
"starting_column": 5, "starting_column": 5,
"ending_column": 58 "ending_column": 58
}, },
"additional_fields": { "type_specific_fields": {
"parameter_name": "from" "parent": {
},
"contract": {
"type": "contract", "type": "contract",
"name": "IERC20Bad", "name": "IERC20Bad",
"source_mapping": { "source_mapping": {
@ -52,6 +51,11 @@
"starting_column": 1, "starting_column": 1,
"ending_column": 2 "ending_column": 2
} }
},
"signature": "Transfer(address,address,uint256)"
},
"additional_fields": {
"parameter_name": "from"
} }
} }
] ]
@ -78,10 +82,8 @@
"starting_column": 5, "starting_column": 5,
"ending_column": 58 "ending_column": 58
}, },
"additional_fields": { "type_specific_fields": {
"parameter_name": "to" "parent": {
},
"contract": {
"type": "contract", "type": "contract",
"name": "IERC20Bad", "name": "IERC20Bad",
"source_mapping": { "source_mapping": {
@ -106,6 +108,11 @@
"starting_column": 1, "starting_column": 1,
"ending_column": 2 "ending_column": 2
} }
},
"signature": "Transfer(address,address,uint256)"
},
"additional_fields": {
"parameter_name": "to"
} }
} }
] ]
@ -132,10 +139,8 @@
"starting_column": 5, "starting_column": 5,
"ending_column": 64 "ending_column": 64
}, },
"additional_fields": { "type_specific_fields": {
"parameter_name": "owner" "parent": {
},
"contract": {
"type": "contract", "type": "contract",
"name": "IERC20Bad", "name": "IERC20Bad",
"source_mapping": { "source_mapping": {
@ -160,6 +165,11 @@
"starting_column": 1, "starting_column": 1,
"ending_column": 2 "ending_column": 2
} }
},
"signature": "Approval(address,address,uint256)"
},
"additional_fields": {
"parameter_name": "owner"
} }
} }
] ]
@ -186,10 +196,8 @@
"starting_column": 5, "starting_column": 5,
"ending_column": 64 "ending_column": 64
}, },
"additional_fields": { "type_specific_fields": {
"parameter_name": "spender" "parent": {
},
"contract": {
"type": "contract", "type": "contract",
"name": "IERC20Bad", "name": "IERC20Bad",
"source_mapping": { "source_mapping": {
@ -214,9 +222,15 @@
"starting_column": 1, "starting_column": 1,
"ending_column": 2 "ending_column": 2
} }
},
"signature": "Approval(address,address,uint256)"
},
"additional_fields": {
"parameter_name": "spender"
} }
} }
] ]
} }
] ]
} }
}

@ -1,12 +1,13 @@
{ {
"success": true, "success": true,
"error": null, "error": null,
"results": [ "results": {
"detectors": [
{ {
"check": "external-function", "check": "external-function",
"impact": "Informational", "impact": "Informational",
"confidence": "High", "confidence": "High",
"description": "ContractWithFunctionNotCalled.funcNotCalled3 (tests/external_function.sol#13-15) should be declared external\n", "description": "ContractWithFunctionNotCalled.funcNotCalled3() (tests/external_function.sol#13-15) should be declared external\n",
"elements": [ "elements": [
{ {
"type": "function", "type": "function",
@ -26,7 +27,8 @@
"starting_column": 5, "starting_column": 5,
"ending_column": 6 "ending_column": 6
}, },
"contract": { "type_specific_fields": {
"parent": {
"type": "contract", "type": "contract",
"name": "ContractWithFunctionNotCalled", "name": "ContractWithFunctionNotCalled",
"source_mapping": { "source_mapping": {
@ -60,6 +62,8 @@
"starting_column": 1, "starting_column": 1,
"ending_column": 2 "ending_column": 2
} }
},
"signature": "funcNotCalled3()"
} }
} }
] ]
@ -68,7 +72,7 @@
"check": "external-function", "check": "external-function",
"impact": "Informational", "impact": "Informational",
"confidence": "High", "confidence": "High",
"description": "ContractWithFunctionNotCalled.funcNotCalled2 (tests/external_function.sol#17-19) should be declared external\n", "description": "ContractWithFunctionNotCalled.funcNotCalled2() (tests/external_function.sol#17-19) should be declared external\n",
"elements": [ "elements": [
{ {
"type": "function", "type": "function",
@ -88,7 +92,8 @@
"starting_column": 5, "starting_column": 5,
"ending_column": 6 "ending_column": 6
}, },
"contract": { "type_specific_fields": {
"parent": {
"type": "contract", "type": "contract",
"name": "ContractWithFunctionNotCalled", "name": "ContractWithFunctionNotCalled",
"source_mapping": { "source_mapping": {
@ -122,6 +127,8 @@
"starting_column": 1, "starting_column": 1,
"ending_column": 2 "ending_column": 2
} }
},
"signature": "funcNotCalled2()"
} }
} }
] ]
@ -130,7 +137,7 @@
"check": "external-function", "check": "external-function",
"impact": "Informational", "impact": "Informational",
"confidence": "High", "confidence": "High",
"description": "ContractWithFunctionNotCalled.funcNotCalled (tests/external_function.sol#21-23) should be declared external\n", "description": "ContractWithFunctionNotCalled.funcNotCalled() (tests/external_function.sol#21-23) should be declared external\n",
"elements": [ "elements": [
{ {
"type": "function", "type": "function",
@ -150,7 +157,8 @@
"starting_column": 5, "starting_column": 5,
"ending_column": 6 "ending_column": 6
}, },
"contract": { "type_specific_fields": {
"parent": {
"type": "contract", "type": "contract",
"name": "ContractWithFunctionNotCalled", "name": "ContractWithFunctionNotCalled",
"source_mapping": { "source_mapping": {
@ -184,6 +192,8 @@
"starting_column": 1, "starting_column": 1,
"ending_column": 2 "ending_column": 2
} }
},
"signature": "funcNotCalled()"
} }
} }
] ]
@ -192,7 +202,7 @@
"check": "external-function", "check": "external-function",
"impact": "Informational", "impact": "Informational",
"confidence": "High", "confidence": "High",
"description": "ContractWithFunctionNotCalled2.funcNotCalled (tests/external_function.sol#32-39) should be declared external\n", "description": "ContractWithFunctionNotCalled2.funcNotCalled() (tests/external_function.sol#32-39) should be declared external\n",
"elements": [ "elements": [
{ {
"type": "function", "type": "function",
@ -217,7 +227,8 @@
"starting_column": 5, "starting_column": 5,
"ending_column": 6 "ending_column": 6
}, },
"contract": { "type_specific_fields": {
"parent": {
"type": "contract", "type": "contract",
"name": "ContractWithFunctionNotCalled2", "name": "ContractWithFunctionNotCalled2",
"source_mapping": { "source_mapping": {
@ -242,9 +253,12 @@
"starting_column": 1, "starting_column": 1,
"ending_column": 2 "ending_column": 2
} }
},
"signature": "funcNotCalled()"
} }
} }
] ]
} }
] ]
} }
}

@ -1,7 +1,7 @@
INFO:Detectors: INFO:Detectors:
ContractWithFunctionNotCalled.funcNotCalled3 (tests/external_function.sol#13-15) should be declared external ContractWithFunctionNotCalled.funcNotCalled3() (tests/external_function.sol#13-15) should be declared external
ContractWithFunctionNotCalled.funcNotCalled2 (tests/external_function.sol#17-19) should be declared external ContractWithFunctionNotCalled.funcNotCalled2() (tests/external_function.sol#17-19) should be declared external
ContractWithFunctionNotCalled.funcNotCalled (tests/external_function.sol#21-23) should be declared external ContractWithFunctionNotCalled.funcNotCalled() (tests/external_function.sol#21-23) should be declared external
ContractWithFunctionNotCalled2.funcNotCalled (tests/external_function.sol#32-39) should be declared external ContractWithFunctionNotCalled2.funcNotCalled() (tests/external_function.sol#32-39) should be declared external
Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#public-function-that-could-be-declared-as-external Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#public-function-that-could-be-declared-as-external
INFO:Slither:tests/external_function.sol analyzed (5 contracts), 4 result(s) found INFO:Slither:tests/external_function.sol analyzed (5 contracts), 4 result(s) found

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

Loading…
Cancel
Save