Merge branch 'dev' into bugfix/593

pull/625/head
Josselin 4 years ago
commit 4524bd958d
  1. 6
      .github/workflows/ci.yml
  2. 44
      .github/workflows/detectors.yml
  3. 4
      .github/workflows/linter.yml
  4. 44
      .github/workflows/parser.yml
  5. 2
      CONTRIBUTING.md
  6. 4
      examples/scripts/convert_to_ir.py
  7. 18
      examples/scripts/data_dependency.py
  8. 2
      examples/scripts/export_dominator_tree_to_dot.py
  9. 2
      examples/scripts/functions_called.py
  10. 4
      plugin_example/setup.py
  11. 3
      pyproject.toml
  12. 47
      scripts/ci_generate_test_detectors_4.sh
  13. 28
      scripts/ci_generate_test_detectors_5.sh
  14. 11
      scripts/ci_generate_test_detectors_6.sh
  15. 11
      scripts/ci_generate_test_detectors_7.sh
  16. 8
      scripts/ci_test_cli.sh
  17. 42
      scripts/ci_test_detectors_4.sh
  18. 51
      scripts/ci_test_detectors_5.sh
  19. 7
      scripts/ci_test_detectors_6.sh
  20. 7
      scripts/ci_test_detectors_7.sh
  21. 6
      setup.py
  22. 17
      slither/__main__.py
  23. 19
      slither/analyses/data_dependency/data_dependency.py
  24. 5
      slither/analyses/write/are_variables_written.py
  25. 12
      slither/core/cfg/node.py
  26. 37
      slither/core/declarations/contract.py
  27. 23
      slither/core/declarations/function.py
  28. 4
      slither/core/expressions/assignment_operation.py
  29. 2
      slither/core/expressions/expression_typed.py
  30. 7
      slither/core/slither_core.py
  31. 3
      slither/core/solidity_types/elementary_type.py
  32. 4
      slither/core/solidity_types/function_type.py
  33. 3
      slither/core/solidity_types/type_information.py
  34. 4
      slither/core/source_mapping/source_mapping.py
  35. 8
      slither/detectors/abstract_detector.py
  36. 3
      slither/detectors/attributes/locked_ether.py
  37. 8
      slither/detectors/functions/arbitrary_send.py
  38. 20
      slither/detectors/naming_convention/naming_convention.py
  39. 4
      slither/detectors/operations/block_timestamp.py
  40. 3
      slither/detectors/reentrancy/reentrancy.py
  41. 12
      slither/detectors/reentrancy/reentrancy_benign.py
  42. 10
      slither/detectors/reentrancy/reentrancy_eth.py
  43. 12
      slither/detectors/reentrancy/reentrancy_events.py
  44. 18
      slither/detectors/reentrancy/reentrancy_no_gas.py
  45. 7
      slither/detectors/reentrancy/reentrancy_read_before_write.py
  46. 7
      slither/formatters/attributes/constant_pragma.py
  47. 7
      slither/formatters/naming_convention/naming_convention.py
  48. 58
      slither/printers/call/call_graph.py
  49. 6
      slither/printers/functions/authorization.py
  50. 12
      slither/printers/functions/cfg.py
  51. 15
      slither/printers/guidance/echidna.py
  52. 6
      slither/printers/summary/evm.py
  53. 19
      slither/printers/summary/function.py
  54. 9
      slither/printers/summary/human_summary.py
  55. 5
      slither/printers/summary/require_calls.py
  56. 7
      slither/slither.py
  57. 30
      slither/slithir/convert.py
  58. 11
      slither/slithir/operations/binary.py
  59. 6
      slither/slithir/operations/library_call.py
  60. 9
      slither/slithir/tmp_operations/tmp_call.py
  61. 18
      slither/slithir/utils/ssa.py
  62. 9
      slither/slithir/utils/utils.py
  63. 13
      slither/solc_parsing/declarations/contract.py
  64. 20
      slither/solc_parsing/declarations/function.py
  65. 45
      slither/solc_parsing/expressions/expression_parsing.py
  66. 32
      slither/solc_parsing/slitherSolc.py
  67. 19
      slither/solc_parsing/solidity_types/type_parsing.py
  68. 2
      slither/solc_parsing/variables/variable_declaration.py
  69. 43
      slither/solc_parsing/yul/parse_yul.py
  70. 3
      slither/tools/erc_conformance/__main__.py
  71. 13
      slither/tools/flattening/__main__.py
  72. 4
      slither/tools/flattening/export/export.py
  73. 15
      slither/tools/flattening/flattening.py
  74. 11
      slither/tools/kspec_coverage/__main__.py
  75. 3
      slither/tools/possible_paths/__main__.py
  76. 3
      slither/tools/possible_paths/possible_paths.py
  77. 4
      slither/tools/properties/__main__.py
  78. 19
      slither/tools/properties/properties/erc20.py
  79. 11
      slither/tools/properties/properties/ercs/erc20/unit_tests/truffle.py
  80. 2
      slither/tools/properties/properties/properties.py
  81. 5
      slither/tools/similarity/__main__.py
  82. 15
      slither/tools/slither_format/__main__.py
  83. 5
      slither/tools/slither_format/slither_format.py
  84. 5
      slither/tools/upgradeability/__main__.py
  85. 6
      slither/tools/upgradeability/checks/abstract_checks.py
  86. 1
      slither/utils/command_line.py
  87. 26
      slither/utils/comparable_enum.py
  88. 47
      slither/utils/erc.py
  89. 12
      slither/utils/expression_manipulations.py
  90. 4
      slither/utils/inheritance_analysis.py
  91. 44
      slither/utils/output.py
  92. 11
      tests/ast-parsing/assembly-all.sol
  93. 15
      tests/ast-parsing/assignment-0.4.0.sol
  94. 17
      tests/ast-parsing/assignment-0.4.7.sol
  95. 22
      tests/ast-parsing/binaryoperation-0.4.0.sol
  96. 24
      tests/ast-parsing/binaryoperation-0.4.7.sol
  97. 21
      tests/ast-parsing/break-all.sol
  98. 9
      tests/ast-parsing/conditional-all.sol
  99. 21
      tests/ast-parsing/continue-all.sol
  100. 35
      tests/ast-parsing/contract-0.4.0.sol
  101. Some files were not shown because too many files have changed in this diff Show More

@ -20,11 +20,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
strategy: strategy:
matrix: matrix:
type: ["detectors_4", type: ["cli",
"detectors_5",
"detectors_6",
"detectors_7",
"cli",
"data_dependency", "data_dependency",
"embark", "embark",
"erc", "erc",

@ -0,0 +1,44 @@
---
name: Detectors tests
defaults:
run:
# To load bashrc
shell: bash -ieo pipefail {0}
on:
pull_request:
branches: [master, dev]
schedule:
# run CI every day even if no PRs/merges occur
- cron: '0 12 * * *'
jobs:
build:
name: Detectors tests
runs-on: ubuntu-latest
steps:
- name: Checkout Code
uses: actions/checkout@v2
- name: Set up Python 3.6
uses: actions/setup-python@v2
with:
python-version: 3.6
- name: Install dependencies
run: |
python setup.py install
pip install deepdiff
pip install pytest
git clone https://github.com/crytic/solc-select.git
./solc-select/scripts/install.sh
export PATH=/home/runner/.solc-select:$PATH
echo "export PATH=/home/runner/.solc-select:$PATH" >> ~/.bashrc
- name: Test with pytest
run: |
pytest tests/test_detectors.py

@ -9,6 +9,9 @@ defaults:
on: on:
pull_request: pull_request:
branches: [master, dev] branches: [master, dev]
schedule:
# run CI every day even if no PRs/merges occur
- cron: '0 12 * * *'
jobs: jobs:
build: build:
@ -69,6 +72,7 @@ jobs:
VALIDATE_PYTHON: false VALIDATE_PYTHON: false
VALIDATE_PYTHON_PYLINT: false VALIDATE_PYTHON_PYLINT: false
VALIDATE_PYTHON_BLACK: false VALIDATE_PYTHON_BLACK: false
VALIDATE_PYTHON_ISORT: false
# Always false # Always false
VALIDATE_JSON: false VALIDATE_JSON: false
VALIDATE_JAVASCRIPT_STANDARD: false VALIDATE_JAVASCRIPT_STANDARD: false

@ -0,0 +1,44 @@
---
name: Parser tests
defaults:
run:
# To load bashrc
shell: bash -ieo pipefail {0}
on:
pull_request:
branches: [master, dev]
schedule:
# run CI every day even if no PRs/merges occur
- cron: '0 12 * * *'
jobs:
build:
name: Parser tests
runs-on: ubuntu-latest
steps:
- name: Checkout Code
uses: actions/checkout@v2
- name: Set up Python 3.6
uses: actions/setup-python@v2
with:
python-version: 3.6
- name: Install dependencies
run: |
python setup.py install
pip install deepdiff
pip install pytest
git clone https://github.com/crytic/solc-select.git
./solc-select/scripts/install.sh
export PATH=/home/runner/.solc-select:$PATH
echo "export PATH=/home/runner/.solc-select:$PATH" >> ~/.bashrc
- name: Test with pytest
run: |
pytest tests/test_ast_parsing.py

@ -23,6 +23,7 @@ Some pull request guidelines:
## Development Environment ## Development Environment
Instructions for installing a development version of Slither can be found in our [wiki](https://github.com/crytic/slither/wiki/Developer-installation). Instructions for installing a development version of Slither can be found in our [wiki](https://github.com/crytic/slither/wiki/Developer-installation).
To run the unit tests, you need `deepdiff` installed (`pip install deepdiff`).
## Linters ## Linters
Several linters and security checkers are run on the PRs. Several linters and security checkers are run on the PRs.
@ -32,6 +33,7 @@ To run them locally:
- `pylint slither --rconfig pyproject.toml` - `pylint slither --rconfig pyproject.toml`
- `black slither --config pyproject.toml` - `black slither --config pyproject.toml`
We use black `19.10b0`.
## Detectors regression tests ## Detectors regression tests
For each new detector, at least one regression tests must be present. For each new detector, at least one regression tests must be present.

@ -12,10 +12,10 @@ slither = Slither(sys.argv[1])
# Get the contract # Get the contract
contract = slither.get_contract_from_name("Test") contract = slither.get_contract_from_name("Test")
assert contract
# Get the variable # Get the variable
test = contract.get_function_from_signature("one()") test = contract.get_function_from_signature("one()")
assert test
nodes = test.nodes nodes = test.nodes
for node in nodes: for node in nodes:

@ -14,7 +14,7 @@ if len(sys.argv) != 2:
slither = Slither(sys.argv[1]) slither = Slither(sys.argv[1])
contract = slither.get_contract_from_name("Simple") contract = slither.get_contract_from_name("Simple")
assert contract
destination = contract.get_state_variable_from_name("destination") destination = contract.get_state_variable_from_name("destination")
source = contract.get_state_variable_from_name("source") source = contract.get_state_variable_from_name("source")
@ -36,9 +36,11 @@ print("{} is tainted {}".format(destination, is_tainted(destination, contract)))
assert is_tainted(destination, contract) assert is_tainted(destination, contract)
contract = slither.get_contract_from_name("Reference") contract = slither.get_contract_from_name("Reference")
assert contract
destination = contract.get_state_variable_from_name("destination") destination = contract.get_state_variable_from_name("destination")
assert destination
source = contract.get_state_variable_from_name("source") source = contract.get_state_variable_from_name("source")
assert source
print("Reference contract") print("Reference contract")
print( print(
@ -72,9 +74,11 @@ assert is_tainted(destination_indirect_2, contract)
print("SolidityVar contract") print("SolidityVar contract")
contract = slither.get_contract_from_name("SolidityVar") contract = slither.get_contract_from_name("SolidityVar")
assert contract
addr_1 = contract.get_state_variable_from_name("addr_1") addr_1 = contract.get_state_variable_from_name("addr_1")
assert addr_1
addr_2 = contract.get_state_variable_from_name("addr_2") addr_2 = contract.get_state_variable_from_name("addr_2")
assert addr_2
msgsender = SolidityVariableComposed("msg.sender") msgsender = SolidityVariableComposed("msg.sender")
print( print(
"{} is dependent of {}: {}".format(addr_1, msgsender, is_dependent(addr_1, msgsender, contract)) "{} is dependent of {}: {}".format(addr_1, msgsender, is_dependent(addr_1, msgsender, contract))
@ -88,8 +92,11 @@ assert not is_dependent(addr_2, msgsender, contract)
print("Intermediate contract") print("Intermediate contract")
contract = slither.get_contract_from_name("Intermediate") contract = slither.get_contract_from_name("Intermediate")
assert contract
destination = contract.get_state_variable_from_name("destination") destination = contract.get_state_variable_from_name("destination")
assert destination
source = contract.get_state_variable_from_name("source") source = contract.get_state_variable_from_name("source")
assert source
print( print(
"{} is dependent of {}: {}".format( "{} is dependent of {}: {}".format(
@ -119,11 +126,16 @@ assert is_dependent(destination, source, contract_derived)
print("PropagateThroughArguments contract") print("PropagateThroughArguments contract")
contract = slither.get_contract_from_name("PropagateThroughArguments") contract = slither.get_contract_from_name("PropagateThroughArguments")
assert contract
var_tainted = contract.get_state_variable_from_name("var_tainted") var_tainted = contract.get_state_variable_from_name("var_tainted")
assert var_tainted
var_not_tainted = contract.get_state_variable_from_name("var_not_tainted") var_not_tainted = contract.get_state_variable_from_name("var_not_tainted")
assert var_not_tainted
var_dependant = contract.get_state_variable_from_name("var_dependant") var_dependant = contract.get_state_variable_from_name("var_dependant")
assert var_dependant
f = contract.get_function_from_signature("f(uint256)") f = contract.get_function_from_signature("f(uint256)")
assert f
user_input = f.parameters[0] user_input = f.parameters[0]
f2 = contract.get_function_from_signature("f2(uint256,uint256)") f2 = contract.get_function_from_signature("f2(uint256,uint256)")

@ -10,7 +10,7 @@ if len(sys.argv) != 2:
slither = Slither(sys.argv[1]) slither = Slither(sys.argv[1])
for contract in slither.contracts: for contract in slither.contracts:
for function in contract.functions + contract.modifiers: for function in list(contract.functions) + list(contract.modifiers):
filename = "{}-{}-{}_dom.dot".format(sys.argv[1], contract.name, function.full_name) filename = "{}-{}-{}_dom.dot".format(sys.argv[1], contract.name, function.full_name)
print("Export {}".format(filename)) print("Export {}".format(filename))
function.dominator_tree_to_dot(filename) function.dominator_tree_to_dot(filename)

@ -10,9 +10,11 @@ slither = Slither(sys.argv[1])
# Get the contract # Get the contract
contract = slither.get_contract_from_name("Contract") contract = slither.get_contract_from_name("Contract")
assert contract
# Get the variable # Get the variable
entry_point = contract.get_function_from_signature("entry_point()") entry_point = contract.get_function_from_signature("entry_point()")
assert entry_point
all_calls = entry_point.all_internal_calls() all_calls = entry_point.all_internal_calls()

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

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

@ -1,47 +0,0 @@
#!/usr/bin/env bash
source "$(dirname "$0")""/ci_test.sh"
solc use "0.4.25"
# Be sure that only one of the following line is uncommented before running the script
#generate_expected_json tests/deprecated_calls.sol "deprecated-standards"
#generate_expected_json tests/erc20_indexed.sol "erc20-indexed"
#generate_expected_json tests/incorrect_erc20_interface.sol "erc20-interface"
#generate_expected_json tests/incorrect_erc721_interface.sol "erc721-interface"
#generate_expected_json tests/uninitialized.sol "uninitialized-state"
#generate_expected_json tests/backdoor.sol "backdoor"
#generate_expected_json tests/backdoor.sol "suicidal"
#generate_expected_json tests/pragma.0.4.24.sol "pragma"
#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-unlimited-gas"
#generate_expected_json tests/uninitialized_storage_pointer.sol "uninitialized-storage"
#generate_expected_json tests/tx_origin.sol "tx-origin"
#generate_expected_json tests/unused_state.sol "unused-state"
#generate_expected_json tests/locked_ether.sol "locked-ether"
#generate_expected_json tests/arbitrary_send.sol "arbitrary-send"
#generate_expected_json tests/inline_assembly_contract.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/const_state_variables.sol "constable-states"
#generate_expected_json tests/external_function.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/uninitialized_local_variable.sol "uninitialized-local"
#generate_expected_json tests/controlled_delegatecall.sol "controlled-delegatecall"
#generate_expected_json tests/constant.sol "constant-function-asm"
#generate_expected_json tests/constant.sol "constant-function-state"
#generate_expected_json tests/unused_return.sol "unused-return"
#generate_expected_json tests/shadowing_state_variable.sol "shadowing-state"
#generate_expected_json tests/shadowing_abstract.sol "shadowing-abstract"
#generate_expected_json tests/timestamp.sol "timestamp"
#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_local_variable.sol "shadowing-local"
#generate_expected_json tests/solc_version_incorrect.sol "solc-version"
#generate_expected_json tests/right_to_left_override.sol "rtlo"
#generate_expected_json tests/unchecked_lowlevel.sol "unchecked-lowlevel"

@ -1,28 +0,0 @@
#!/usr/bin/env bash
source "$(dirname "$0")""/ci_test.sh"
solc use "0.5.1"
# Be sure that only one of the following line is uncommented before running the script
# generate_expected_json tests/void-cst.sol "void-cst"
# generate_expected_json tests/solc_version_incorrect_05.ast.json "solc-version"
# generate_expected_json tests/uninitialized-0.5.1.sol "uninitialized-state"
# generate_expected_json tests/backdoor.sol "backdoor"
# generate_expected_json tests/backdoor.sol "suicidal"
# generate_expected_json tests/old_solc.sol.json "solc-version"
# generate_expected_json tests/reentrancy-0.5.1.sol "reentrancy-eth"
# generate_expected_json tests/reentrancy-0.5.1-events.sol "reentrancy-events"
# generate_expected_json tests/tx_origin-0.5.1.sol "tx-origin"
# generate_expected_json tests/locked_ether-0.5.1.sol "locked-ether"
# generate_expected_json tests/arbitrary_send-0.5.1.sol "arbitrary-send"
# generate_expected_json tests/inline_assembly_contract-0.5.1.sol "assembly"
# generate_expected_json tests/inline_assembly_library-0.5.1.sol "assembly"
# generate_expected_json tests/constant-0.5.1.sol "constant-function-asm"
# generate_expected_json tests/constant-0.5.1.sol "constant-function-state"
# generate_expected_json tests/incorrect_equality.sol "incorrect-equality"
# generate_expected_json tests/too_many_digits.sol "too-many-digits"
# generate_expected_json tests/unchecked_lowlevel-0.5.1.sol "unchecked-lowlevel"
# generate_expected_json tests/unchecked_send-0.5.1.sol "unchecked-send"

@ -1,11 +0,0 @@
#!/usr/bin/env bash
source "$(dirname "$0")""/ci_test.sh"
solc use "0.6.11"
# Be sure that only one of the following line is uncommented before running the script
#generate_expected_json tests/filename.sol "detector_name"

@ -1,11 +0,0 @@
#!/usr/bin/env bash
source "$(dirname "$0")""/ci_test.sh"
solc use "0.7.0"
# Be sure that only one of the following line is uncommented before running the script
#generate_expected_json tests/filename.sol "detector_name"

@ -2,17 +2,19 @@
### Test ### Test
if ! slither "tests/*.json" --solc-ast --ignore-return-value; then solc use 0.7.0
if ! slither "tests/config/test.sol" --solc-ast --ignore-return-value; then
echo "--solc-ast failed" echo "--solc-ast failed"
exit 1 exit 1
fi fi
if ! slither "tests/*0.5*.sol" --solc-disable-warnings --ignore-return-value; then if ! slither "tests/config/test.sol" --solc-disable-warnings --ignore-return-value; then
echo "--solc-disable-warnings failed" echo "--solc-disable-warnings failed"
exit 1 exit 1
fi fi
if ! slither "tests/*0.5*.sol" --disable-color --ignore-return-value; then if ! slither "tests/config/test.sol" --disable-color --ignore-return-value; then
echo "--disable-color failed" echo "--disable-color failed"
exit 1 exit 1
fi fi

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

@ -1,51 +0,0 @@
#!/usr/bin/env bash
source "$(dirname "$0")""/ci_test.sh"
solc use "0.5.1"
test_slither tests/void-cst.sol "void-cst"
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_send-0.5.1.sol "unchecked-send"
test_slither tests/uninitialized-0.5.1.sol "uninitialized-state"
test_slither tests/backdoor.sol "backdoor"
test_slither tests/backdoor.sol "suicidal"
test_slither tests/old_solc.sol.json "solc-version"
test_slither tests/reentrancy-0.5.1.sol "reentrancy-eth"
test_slither tests/reentrancy-0.5.1-events.sol "reentrancy-events"
test_slither tests/tx_origin-0.5.1.sol "tx-origin"
test_slither tests/unused_state.sol "unused-state"
test_slither tests/locked_ether-0.5.1.sol "locked-ether"
test_slither tests/arbitrary_send-0.5.1.sol "arbitrary-send"
test_slither tests/inline_assembly_contract-0.5.1.sol "assembly"
test_slither tests/inline_assembly_library-0.5.1.sol "assembly"
test_slither tests/low_level_calls.sol "low-level-calls"
test_slither tests/const_state_variables.sol "constable-states"
test_slither tests/external_function.sol "external-function"
test_slither tests/external_function_2.sol "external-function"
test_slither tests/naming_convention.sol "naming-convention"
#test_slither tests/complex_func.sol "complex-function"
test_slither tests/controlled_delegatecall.sol "controlled-delegatecall"
test_slither tests/constant-0.5.1.sol "constant-function-asm"
test_slither tests/constant-0.5.1.sol "constant-function-state"
test_slither tests/unused_return.sol "unused-return"
test_slither tests/timestamp.sol "timestamp"
test_slither tests/incorrect_equality.sol "incorrect-equality"
test_slither tests/too_many_digits.sol "too-many-digits"
### Test scripts
if ! python examples/scripts/functions_called.py examples/scripts/functions_called.sol; then
exit 1
fi
if ! python examples/scripts/functions_writing.py examples/scripts/functions_writing.sol; then
exit 1
fi
if ! python examples/scripts/variable_in_condition.py examples/scripts/variable_in_condition.sol; then
exit 1
fi
exit 0

@ -1,7 +0,0 @@
#!/usr/bin/env bash
source "$(dirname "$0")""/ci_test.sh"
solc use "0.6.11"
# test_slither tests/filename.sol "detector_name"

@ -1,7 +0,0 @@
#!/usr/bin/env bash
source "$(dirname "$0")""/ci_test.sh"
solc use "0.7.0"
# test_slither tests/filename.sol "detector_name"

@ -11,10 +11,10 @@ setup(
install_requires=[ install_requires=[
"prettytable>=0.7.2", "prettytable>=0.7.2",
"pysha3>=1.0.2", "pysha3>=1.0.2",
"crytic-compile>=0.1.9", # "crytic-compile>=0.1.9",
# "crytic-compile", "crytic-compile",
], ],
# dependency_links=["git+https://github.com/crytic/crytic-compile.git@master#egg=crytic-compile"], dependency_links=["git+https://github.com/crytic/crytic-compile.git@master#egg=crytic-compile"],
license="AGPL-3.0", license="AGPL-3.0",
long_description=open("README.md").read(), long_description=open("README.md").read(),
entry_points={ entry_points={

@ -402,10 +402,7 @@ def parse_args(detector_classes, printer_classes):
) )
group_misc.add_argument( group_misc.add_argument(
"--markdown-root", "--markdown-root", help="URL for markdown generation", action="store", default="",
help="URL for markdown generation",
action="store",
default="",
) )
group_misc.add_argument( group_misc.add_argument(
@ -440,10 +437,7 @@ def parse_args(detector_classes, printer_classes):
) )
group_misc.add_argument( group_misc.add_argument(
"--solc-ast", "--solc-ast", help="Provide the contract as a json AST", action="store_true", default=False,
help="Provide the contract as a json AST",
action="store_true",
default=False,
) )
group_misc.add_argument( group_misc.add_argument(
@ -481,6 +475,13 @@ def parse_args(detector_classes, printer_classes):
default=defaults_flag_in_config["legacy_ast"], default=defaults_flag_in_config["legacy_ast"],
) )
parser.add_argument(
"--skip-assembly",
help=argparse.SUPPRESS,
action="store_true",
default=defaults_flag_in_config["skip_assembly"],
)
parser.add_argument( parser.add_argument(
"--ignore-return-value", "--ignore-return-value",
help=argparse.SUPPRESS, help=argparse.SUPPRESS,

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

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

@ -101,7 +101,7 @@ class NodeType(Enum):
# Node not related to the CFG # Node not related to the CFG
# Use for state variable declaration # Use for state variable declaration
OTHER_ENTRYPOINT = 0x50 OTHER_ENTRYPOINT = 0x60
# @staticmethod # @staticmethod
def __str__(self): def __str__(self):
@ -137,6 +137,8 @@ class NodeType(Enum):
return "BEGIN_LOOP" return "BEGIN_LOOP"
if self == NodeType.ENDLOOP: if self == NodeType.ENDLOOP:
return "END_LOOP" return "END_LOOP"
if self == NodeType.OTHER_ENTRYPOINT:
return "OTHER_ENTRYPOINT"
return "Unknown type {}".format(hex(self.value)) return "Unknown type {}".format(hex(self.value))
@ -798,15 +800,11 @@ class Node(SourceMapping, ChildFunction): # pylint: disable=too-many-public-met
################################################################################### ###################################################################################
@property @property
def phi_origins_local_variables( def phi_origins_local_variables(self,) -> Dict[str, Tuple[LocalVariable, Set["Node"]]]:
self,
) -> Dict[str, Tuple[LocalVariable, Set["Node"]]]:
return self._phi_origins_local_variables return self._phi_origins_local_variables
@property @property
def phi_origins_state_variables( def phi_origins_state_variables(self,) -> Dict[str, Tuple[StateVariable, Set["Node"]]]:
self,
) -> Dict[str, Tuple[StateVariable, Set["Node"]]]:
return self._phi_origins_state_variables return self._phi_origins_state_variables
# @property # @property

@ -24,7 +24,7 @@ from slither.utils.tests_pattern import is_test_contract
# pylint: disable=too-many-lines,too-many-instance-attributes,import-outside-toplevel,too-many-nested-blocks # pylint: disable=too-many-lines,too-many-instance-attributes,import-outside-toplevel,too-many-nested-blocks
if TYPE_CHECKING: if TYPE_CHECKING:
from slither.utils.type_helpers import LibraryCallType, HighLevelCallType from slither.utils.type_helpers import LibraryCallType, HighLevelCallType, InternalCallType
from slither.core.declarations import Enum, Event, Modifier from slither.core.declarations import Enum, Event, Modifier
from slither.core.declarations import Structure from slither.core.declarations import Structure
from slither.slithir.variables.variable import SlithIRVariable from slither.slithir.variables.variable import SlithIRVariable
@ -282,8 +282,8 @@ class Contract(ChildSlither, SourceMapping): # pylint: disable=too-many-public-
""" """
List all of the slithir variables (non SSA) List all of the slithir variables (non SSA)
""" """
slithir_variables = [f.slithir_variables for f in self.functions + self.modifiers] # type: ignore slithir_variabless = [f.slithir_variables for f in self.functions + self.modifiers] # type: ignore
slithir_variables = [item for sublist in slithir_variables for item in sublist] slithir_variables = [item for sublist in slithir_variabless for item in sublist]
return list(set(slithir_variables)) return list(set(slithir_variables))
# endregion # endregion
@ -726,20 +726,20 @@ class Contract(ChildSlither, SourceMapping): # pylint: disable=too-many-public-
################################################################################### ###################################################################################
@property @property
def all_functions_called(self) -> List["Function"]: def all_functions_called(self) -> List["InternalCallType"]:
""" """
list(Function): List of functions reachable from the contract list(Function): List of functions reachable from the contract
Includes super, and private/internal functions not shadowed Includes super, and private/internal functions not shadowed
""" """
all_calls = [f for f in self.functions + self.modifiers if not f.is_shadowed] # type: ignore all_functions = [f for f in self.functions + self.modifiers if not f.is_shadowed] # type: ignore
all_callss = [f.all_internal_calls() for f in all_calls] + [all_calls] all_callss = [f.all_internal_calls() for f in all_functions] + [list(all_functions)]
all_calls = [item for sublist in all_callss for item in sublist] all_calls = [item for sublist in all_callss 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 if c.constructor] all_constructors = [c.constructor for c in self.inheritance if c.constructor]
all_constructors = list(set(all_constructors)) all_constructors = list(set(all_constructors))
set_all_calls = set(all_calls + all_constructors) set_all_calls = set(all_calls + list(all_constructors))
return [c for c in set_all_calls if isinstance(c, Function)] return [c for c in set_all_calls if isinstance(c, Function)]
@ -748,11 +748,11 @@ class Contract(ChildSlither, SourceMapping): # pylint: disable=too-many-public-
""" """
list(StateVariable): List all of the state variables written list(StateVariable): List all of the state variables written
""" """
all_state_variables_written = [ all_state_variables_writtens = [
f.all_state_variables_written() for f in self.functions + self.modifiers # type: ignore f.all_state_variables_written() for f in self.functions + self.modifiers # type: ignore
] ]
all_state_variables_written = [ all_state_variables_written = [
item for sublist in all_state_variables_written for item in sublist item for sublist in all_state_variables_writtens for item in sublist
] ]
return list(set(all_state_variables_written)) return list(set(all_state_variables_written))
@ -761,11 +761,11 @@ class Contract(ChildSlither, SourceMapping): # pylint: disable=too-many-public-
""" """
list(StateVariable): List all of the state variables read list(StateVariable): List all of the state variables read
""" """
all_state_variables_read = [ all_state_variables_reads = [
f.all_state_variables_read() for f in self.functions + self.modifiers # type: ignore f.all_state_variables_read() for f in self.functions + self.modifiers # type: ignore
] ]
all_state_variables_read = [ all_state_variables_read = [
item for sublist in all_state_variables_read for item in sublist item for sublist in all_state_variables_reads for item in sublist
] ]
return list(set(all_state_variables_read)) return list(set(all_state_variables_read))
@ -774,8 +774,8 @@ class Contract(ChildSlither, SourceMapping): # pylint: disable=too-many-public-
""" """
list((Contract, Function): List all of the libraries func called list((Contract, Function): List all of the libraries func called
""" """
all_high_level_calls = [f.all_library_calls() for f in self.functions + self.modifiers] # type: ignore all_high_level_callss = [f.all_library_calls() for f in self.functions + self.modifiers] # type: ignore
all_high_level_calls = [item for sublist in all_high_level_calls for item in sublist] all_high_level_calls = [item for sublist in all_high_level_callss for item in sublist]
return list(set(all_high_level_calls)) return list(set(all_high_level_calls))
@property @property
@ -783,8 +783,8 @@ class Contract(ChildSlither, SourceMapping): # pylint: disable=too-many-public-
""" """
list((Contract, Function|Variable)): List all of the external high level calls list((Contract, Function|Variable)): List all of the external high level calls
""" """
all_high_level_calls = [f.all_high_level_calls() for f in self.functions + self.modifiers] # type: ignore all_high_level_callss = [f.all_high_level_calls() for f in self.functions + self.modifiers] # type: ignore
all_high_level_calls = [item for sublist in all_high_level_calls for item in sublist] all_high_level_calls = [item for sublist in all_high_level_callss for item in sublist]
return list(set(all_high_level_calls)) return list(set(all_high_level_calls))
# endregion # endregion
@ -794,9 +794,7 @@ class Contract(ChildSlither, SourceMapping): # pylint: disable=too-many-public-
################################################################################### ###################################################################################
################################################################################### ###################################################################################
def get_summary( def get_summary(self, include_shadowed=True) -> Tuple[str, List[str], List[str], List, List]:
self, include_shadowed=True
) -> Tuple[str, List[str], List[str], List[str], List[str]]:
"""Return the function summary """Return the function summary
:param include_shadowed: boolean to indicate if shadowed functions should be included (default True) :param include_shadowed: boolean to indicate if shadowed functions should be included (default True)
@ -1097,6 +1095,7 @@ class Contract(ChildSlither, SourceMapping): # pylint: disable=too-many-public-
v.node_initialization = next_node v.node_initialization = next_node
prev_node.add_son(next_node) prev_node.add_son(next_node)
next_node.add_father(prev_node) next_node.add_father(prev_node)
prev_node = next_node
counter += 1 counter += 1
break break
@ -1124,6 +1123,7 @@ class Contract(ChildSlither, SourceMapping): # pylint: disable=too-many-public-
v.node_initialization = next_node v.node_initialization = next_node
prev_node.add_son(next_node) prev_node.add_son(next_node)
next_node.add_father(prev_node) next_node.add_father(prev_node)
prev_node = next_node
counter += 1 counter += 1
break break
@ -1141,6 +1141,7 @@ class Contract(ChildSlither, SourceMapping): # pylint: disable=too-many-public-
node.set_offset(variable.source_mapping, self.slither) node.set_offset(variable.source_mapping, self.slither)
node.set_function(func) node.set_function(func)
func.add_node(node) func.add_node(node)
assert variable.expression
expression = AssignmentOperation( expression = AssignmentOperation(
Identifier(variable), Identifier(variable),
variable.expression, variable.expression,

@ -53,10 +53,7 @@ ReacheableNode = namedtuple("ReacheableNode", ["node", "ir"])
class ModifierStatements: class ModifierStatements:
def __init__( def __init__(
self, self, modifier: Union["Contract", "Function"], entry_point: "Node", nodes: List["Node"],
modifier: Union["Contract", "Function"],
entry_point: "Node",
nodes: List["Node"],
): ):
self._modifier = modifier self._modifier = modifier
self._entry_point = entry_point self._entry_point = entry_point
@ -1144,9 +1141,7 @@ class Function(
@staticmethod @staticmethod
def _explore_func_conditional( def _explore_func_conditional(
func: "Function", func: "Function", f: Callable[["Node"], List[SolidityVariable]], include_loop: bool,
f: Callable[["Node"], List[SolidityVariable]],
include_loop: bool,
): ):
ret = [f(n) for n in func.nodes if n.is_conditional(include_loop)] ret = [f(n) for n in func.nodes if n.is_conditional(include_loop)]
return [item for sublist in ret for item in sublist] return [item for sublist in ret for item in sublist]
@ -1298,7 +1293,7 @@ class Function(
with open(filename, "w", encoding="utf8") as f: with open(filename, "w", encoding="utf8") as f:
f.write(content) f.write(content)
def slithir_cfg_to_dot_str(self) -> str: def slithir_cfg_to_dot_str(self, skip_expressions=False) -> str:
""" """
Export the CFG to a DOT format. The nodes includes the Solidity expressions and the IRs Export the CFG to a DOT format. The nodes includes the Solidity expressions and the IRs
:return: the DOT content :return: the DOT content
@ -1310,9 +1305,9 @@ class Function(
content += "digraph{\n" content += "digraph{\n"
for node in self.nodes: for node in self.nodes:
label = "Node Type: {} {}\n".format(str(node.type), node.node_id) label = "Node Type: {} {}\n".format(str(node.type), node.node_id)
if node.expression: if node.expression and not skip_expressions:
label += "\nEXPRESSION:\n{}\n".format(node.expression) label += "\nEXPRESSION:\n{}\n".format(node.expression)
if node.irs: if node.irs and not skip_expressions:
label += "\nIRs:\n" + "\n".join([str(ir) for ir in node.irs]) label += "\nIRs:\n" + "\n".join([str(ir) for ir in node.irs])
content += '{}[label="{}"];\n'.format(node.node_id, label) content += '{}[label="{}"];\n'.format(node.node_id, label)
if node.type in [NodeType.IF, NodeType.IFLOOP]: if node.type in [NodeType.IF, NodeType.IFLOOP]:
@ -1602,14 +1597,10 @@ class Function(
return ret return ret
def get_last_ssa_state_variables_instances( def get_last_ssa_state_variables_instances(self,) -> Dict[str, Set["SlithIRVariable"]]:
self,
) -> Dict[str, Set["SlithIRVariable"]]:
return self._get_last_ssa_variable_instances(target_state=True, target_local=False) return self._get_last_ssa_variable_instances(target_state=True, target_local=False)
def get_last_ssa_local_variables_instances( def get_last_ssa_local_variables_instances(self,) -> Dict[str, Set["SlithIRVariable"]]:
self,
) -> Dict[str, Set["SlithIRVariable"]]:
return self._get_last_ssa_variable_instances(target_state=False, target_local=True) return self._get_last_ssa_variable_instances(target_state=False, target_local=True)
@staticmethod @staticmethod

@ -91,7 +91,7 @@ class AssignmentOperation(ExpressionTyped):
super().__init__() super().__init__()
left_expression.set_lvalue() left_expression.set_lvalue()
self._expressions = [left_expression, right_expression] self._expressions = [left_expression, right_expression]
self._type = expression_type self._type: Optional["Type"] = expression_type
self._expression_return_type: Optional["Type"] = expression_return_type self._expression_return_type: Optional["Type"] = expression_return_type
@property @property
@ -111,7 +111,7 @@ class AssignmentOperation(ExpressionTyped):
return self._expressions[1] return self._expressions[1]
@property @property
def type(self) -> AssignmentOperationType: def type(self) -> Optional["Type"]:
return self._type return self._type
def __str__(self): def __str__(self):

@ -12,7 +12,7 @@ class ExpressionTyped(Expression): # pylint: disable=too-few-public-methods
self._type: Optional["Type"] = None self._type: Optional["Type"] = None
@property @property
def type(self): def type(self) -> Optional["Type"]:
return self._type return self._type
@type.setter @type.setter

@ -76,6 +76,7 @@ class SlitherCore(Context): # pylint: disable=too-many-instance-attributes,too-
# If set to true, slither will not catch errors during parsing # If set to true, slither will not catch errors during parsing
self._disallow_partial: bool = False self._disallow_partial: bool = False
self._skip_assembly: bool = False
################################################################################### ###################################################################################
################################################################################### ###################################################################################
@ -273,7 +274,7 @@ class SlitherCore(Context): # pylint: disable=too-many-instance-attributes,too-
- The --exclude-dependencies flag is set and results are only related to dependencies - The --exclude-dependencies flag is set and results are only related to dependencies
""" """
source_mapping_elements = [ source_mapping_elements = [
elem["source_mapping"]["filename_absolute"] elem["source_mapping"].get("filename_absolute", "unknown")
for elem in r["elements"] for elem in r["elements"]
if "source_mapping" in elem if "source_mapping" in elem
] ]
@ -390,6 +391,10 @@ class SlitherCore(Context): # pylint: disable=too-many-instance-attributes,too-
""" """
return self._disallow_partial return self._disallow_partial
@property
def skip_assembly(self) -> bool:
return self._skip_assembly
# endregion # endregion
################################################################################### ###################################################################################
################################################################################### ###################################################################################

@ -176,7 +176,8 @@ class ElementaryType(Type):
def storage_size(self) -> Tuple[int, bool]: def storage_size(self) -> Tuple[int, bool]:
if self._type == "string" or self._type == "bytes": if self._type == "string" or self._type == "bytes":
return 32, True return 32, True
if self.size is None:
return 32, True
return int(self.size / 8), False return int(self.size / 8), False
def __str__(self): def __str__(self):

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

@ -1,5 +1,6 @@
from typing import TYPE_CHECKING, Tuple from typing import TYPE_CHECKING, Tuple
from slither.core.solidity_types import ElementaryType
from slither.core.solidity_types.type import Type from slither.core.solidity_types.type import Type
if TYPE_CHECKING: if TYPE_CHECKING:
@ -13,7 +14,7 @@ class TypeInformation(Type):
# pylint: disable=import-outside-toplevel # pylint: disable=import-outside-toplevel
from slither.core.declarations.contract import Contract from slither.core.declarations.contract import Contract
assert isinstance(c, Contract) assert isinstance(c, (Contract, ElementaryType))
super().__init__() super().__init__()
self._type = c self._type = c

@ -150,9 +150,9 @@ class SourceMapping(Context):
def source_mapping_to_markdown(self, markdown_root: str) -> str: def source_mapping_to_markdown(self, markdown_root: str) -> str:
lines = self._get_lines_str(line_descr="L") lines = self._get_lines_str(line_descr="L")
return f'{markdown_root}{self.source_mapping["filename_relative"]}{lines}' return f'{markdown_root}{self.source_mapping.get("filename_relative", "")}{lines}'
@property @property
def source_mapping_str(self) -> str: def source_mapping_str(self) -> str:
lines = self._get_lines_str() lines = self._get_lines_str()
return f'{self.source_mapping["filename_short"]}{lines}' return f'{self.source_mapping.get("filename_short", "")}{lines}'

@ -1,9 +1,11 @@
import abc import abc
import re import re
from typing import Optional
from slither.utils.colors import green, yellow, red from slither.utils.colors import green, yellow, red
from slither.formatters.exceptions import FormatImpossible from slither.formatters.exceptions import FormatImpossible
from slither.formatters.utils.patches import apply_patch, create_diff from slither.formatters.utils.patches import apply_patch, create_diff
from slither.utils.comparable_enum import ComparableEnum
from slither.utils.output import Output from slither.utils.output import Output
@ -11,7 +13,7 @@ class IncorrectDetectorInitialization(Exception):
pass pass
class DetectorClassification: # pylint: disable=too-few-public-methods class DetectorClassification(ComparableEnum):
HIGH = 0 HIGH = 0
MEDIUM = 1 MEDIUM = 1
LOW = 2 LOW = 2
@ -39,8 +41,8 @@ classification_txt = {
class AbstractDetector(metaclass=abc.ABCMeta): class AbstractDetector(metaclass=abc.ABCMeta):
ARGUMENT = "" # run the detector with slither.py --ARGUMENT ARGUMENT = "" # run the detector with slither.py --ARGUMENT
HELP = "" # help information HELP = "" # help information
IMPACT = None IMPACT: Optional[DetectorClassification] = None
CONFIDENCE = None CONFIDENCE: Optional[DetectorClassification] = None
WIKI = "" WIKI = ""

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

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

@ -19,7 +19,7 @@ class NamingConvention(AbstractDetector):
IMPACT = DetectorClassification.INFORMATIONAL IMPACT = DetectorClassification.INFORMATIONAL
CONFIDENCE = DetectorClassification.HIGH CONFIDENCE = DetectorClassification.HIGH
WIKI = "https://github.com/crytic/slither/wiki/Detector-Documentation#conformity-to-solidity-naming-conventions" WIKI = "https://github.com/crytic/slither/wiki/Detector-Documentation#conformance-to-solidity-naming-conventions"
WIKI_TITLE = "Conformance to Solidity naming conventions" WIKI_TITLE = "Conformance to Solidity naming conventions"
WIKI_DESCRIPTION = """ WIKI_DESCRIPTION = """
@ -86,14 +86,10 @@ Solidity defines a [naming convention](https://solidity.readthedocs.io/en/v0.4.2
if func.is_constructor: if func.is_constructor:
continue continue
if not self.is_mixed_case(func.name): if not self.is_mixed_case(func.name):
if ( if func.visibility in [
func.visibility "internal",
in [ "private",
"internal", ] and self.is_mixed_case_with_underscore(func.name):
"private",
]
and self.is_mixed_case_with_underscore(func.name)
):
continue continue
if func.name.startswith("echidna_") or func.name.startswith("crytic_"): if func.name.startswith("echidna_") or func.name.startswith("crytic_"):
continue continue
@ -129,11 +125,7 @@ Solidity defines a [naming convention](https://solidity.readthedocs.io/en/v0.4.2
res = self.generate_result(info) res = self.generate_result(info)
res.add( res.add(
var, var, {"target": "variable", "convention": "l_O_I_should_not_be_used",},
{
"target": "variable",
"convention": "l_O_I_should_not_be_used",
},
) )
results.append(res) results.append(res)

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

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

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

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

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

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

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

@ -70,10 +70,5 @@ def _patch(
in_file_str = slither.source_code[in_file].encode("utf8") in_file_str = slither.source_code[in_file].encode("utf8")
old_str_of_interest = in_file_str[modify_loc_start:modify_loc_end] old_str_of_interest = in_file_str[modify_loc_start:modify_loc_end]
create_patch( create_patch(
result, result, in_file, int(modify_loc_start), int(modify_loc_end), old_str_of_interest, pragma,
in_file,
int(modify_loc_start),
int(modify_loc_end),
old_str_of_interest,
pragma,
) )

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

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

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

@ -22,14 +22,16 @@ class CFG(AbstractPrinter):
continue continue
for function in contract.functions + contract.modifiers: for function in contract.functions + contract.modifiers:
if filename: if filename:
filename = "{}-{}-{}.dot".format(filename, contract.name, function.full_name) new_filename = "{}-{}-{}.dot".format(
filename, contract.name, function.full_name
)
else: else:
filename = "{}-{}.dot".format(contract.name, function.full_name) new_filename = "{}-{}.dot".format(contract.name, function.full_name)
info += "Export {}\n".format(filename) info += "Export {}\n".format(new_filename)
content = function.slithir_cfg_to_dot_str() content = function.slithir_cfg_to_dot_str()
with open(filename, "w", encoding="utf8") as f: with open(new_filename, "w", encoding="utf8") as f:
f.write(content) f.write(content)
all_files.append((filename, content)) all_files.append((new_filename, content))
self.info(info) self.info(info)

@ -76,8 +76,9 @@ def _is_constant(f: Function) -> bool: # pylint: disable=too-many-branches
:return: :return:
""" """
if f.view or f.pure: if f.view or f.pure:
if not f.contract.slither.crytic_compile.compiler_version.version.startswith("0.4"): if f.contract.slither.crytic_compile and f.contract.slither.crytic_compile.compiler_version:
return True if not f.contract.slither.crytic_compile.compiler_version.version.startswith("0.4"):
return True
if f.payable: if f.payable:
return False return False
if not f.is_implemented: if not f.is_implemented:
@ -99,8 +100,12 @@ def _is_constant(f: Function) -> bool: # pylint: disable=too-many-branches
if isinstance(ir, HighLevelCall): if isinstance(ir, HighLevelCall):
if isinstance(ir.function, Variable) or ir.function.view or ir.function.pure: if isinstance(ir.function, Variable) or ir.function.view or ir.function.pure:
# External call to constant functions are ensured to be constant only for solidity >= 0.5 # External call to constant functions are ensured to be constant only for solidity >= 0.5
if f.contract.slither.crytic_compile.compiler_version.version.startswith("0.4"): if (
return False f.contract.slither.crytic_compile
and f.contract.slither.crytic_compile.compiler_version
):
if f.contract.slither.crytic_compile.compiler_version.version.startswith("0.4"):
return False
else: else:
return False return False
if isinstance(ir, InternalCall): if isinstance(ir, InternalCall):
@ -153,7 +158,7 @@ def json_serializable(cls):
@json_serializable @json_serializable
class ConstantValue(NamedTuple): class ConstantValue(NamedTuple): # pylint: disable=inherit-non-class,too-few-public-methods
# Here value should be Union[str, int, bool] # Here value should be Union[str, int, bool]
# But the json lib in Echidna does not handle large integer in json # But the json lib in Echidna does not handle large integer in json
# So we convert everything to string # So we convert everything to string

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

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

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

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

@ -59,6 +59,7 @@ class Slither(SlitherCore): # pylint: disable=too-many-instance-attributes
self._parser: SlitherSolc # This could be another parser, like SlitherVyper, interface needs to be determined self._parser: SlitherSolc # This could be another parser, like SlitherVyper, interface needs to be determined
self._disallow_partial: bool = kwargs.get("disallow_partial", False) self._disallow_partial: bool = kwargs.get("disallow_partial", False)
self._skip_assembly: bool = kwargs.get("skip_assembly", False)
# list of files provided (see --splitted option) # list of files provided (see --splitted option)
if isinstance(target, list): if isinstance(target, list):
@ -97,7 +98,11 @@ class Slither(SlitherCore): # pylint: disable=too-many-instance-attributes
triage_mode = kwargs.get("triage_mode", False) triage_mode = kwargs.get("triage_mode", False)
self._triage_mode = triage_mode self._triage_mode = triage_mode
self._parser.analyze_contracts() self._parser.parse_contracts()
# skip_analyze is only used for testing
if not kwargs.get("skip_analyze", False):
self._parser.analyze_contracts()
def _init_from_raw_json(self, filename): def _init_from_raw_json(self, filename):
if not os.path.isfile(filename): if not os.path.isfile(filename):

@ -206,8 +206,7 @@ def convert_arguments(arguments):
def is_temporary(ins): def is_temporary(ins):
return isinstance( return isinstance(
ins, ins, (Argument, TmpNewElementaryType, TmpNewContract, TmpNewArray, TmpNewStructure),
(Argument, TmpNewElementaryType, TmpNewContract, TmpNewArray, TmpNewStructure),
) )
@ -334,11 +333,11 @@ def propagate_type_and_convert_call(result, node):
result[idx] = ins result[idx] = ins
if isinstance(ins, Argument): if isinstance(ins, Argument):
# In case of dupplicate arguments we overwrite the value
# This can happen because of addr.call.value(1).value(2)
if ins.get_type() in [ArgumentType.GAS]: if ins.get_type() in [ArgumentType.GAS]:
assert not ins.call_id in calls_gas
calls_gas[ins.call_id] = ins.argument calls_gas[ins.call_id] = ins.argument
elif ins.get_type() in [ArgumentType.VALUE]: elif ins.get_type() in [ArgumentType.VALUE]:
assert not ins.call_id in calls_value
calls_value[ins.call_id] = ins.argument calls_value[ins.call_id] = ins.argument
else: else:
assert ins.get_type() == ArgumentType.CALL assert ins.get_type() == ArgumentType.CALL
@ -619,8 +618,7 @@ def propagate_types(ir, node): # pylint: disable=too-many-locals
# We dont need to check for function collision, as solc prevents the use of selector # We dont need to check for function collision, as solc prevents the use of selector
# if there are multiple functions with the same name # if there are multiple functions with the same name
f = next( f = next(
(f for f in type_t.functions if f.name == ir.variable_right), (f for f in type_t.functions if f.name == ir.variable_right), None,
None,
) )
if f: if f:
ir.lvalue.set_type(f) ir.lvalue.set_type(f)
@ -827,10 +825,7 @@ def extract_tmp_call(ins, contract): # pylint: disable=too-many-locals
ins.called = SolidityFunction("blockhash(uint256)") ins.called = SolidityFunction("blockhash(uint256)")
elif str(ins.called) == "this.balance": elif str(ins.called) == "this.balance":
s = SolidityCall( s = SolidityCall(
SolidityFunction("this.balance()"), SolidityFunction("this.balance()"), ins.nbr_arguments, ins.lvalue, ins.type_call,
ins.nbr_arguments,
ins.lvalue,
ins.type_call,
) )
s.set_expression(ins.expression) s.set_expression(ins.expression)
return s return s
@ -1148,11 +1143,7 @@ def look_for_library(contract, ir, using_for, t):
lib_contract = contract.slither.get_contract_from_name(str(destination)) lib_contract = contract.slither.get_contract_from_name(str(destination))
if lib_contract: if lib_contract:
lib_call = LibraryCall( lib_call = LibraryCall(
lib_contract, lib_contract, ir.function_name, ir.nbr_arguments, ir.lvalue, ir.type_call,
ir.function_name,
ir.nbr_arguments,
ir.lvalue,
ir.type_call,
) )
lib_call.set_expression(ir.expression) lib_call.set_expression(ir.expression)
lib_call.set_node(ir.node) lib_call.set_node(ir.node)
@ -1385,14 +1376,7 @@ def remove_temporary(result):
ins ins
for ins in result for ins in result
if not isinstance( if not isinstance(
ins, ins, (Argument, TmpNewElementaryType, TmpNewContract, TmpNewArray, TmpNewStructure,),
(
Argument,
TmpNewElementaryType,
TmpNewContract,
TmpNewArray,
TmpNewStructure,
),
) )
] ]

@ -1,6 +1,7 @@
import logging import logging
from enum import Enum from enum import Enum
from slither.core.declarations import Function
from slither.core.solidity_types import ElementaryType from slither.core.solidity_types import ElementaryType
from slither.slithir.exceptions import SlithIRError from slither.slithir.exceptions import SlithIRError
from slither.slithir.operations.lvalue import OperationWithLValue from slither.slithir.operations.lvalue import OperationWithLValue
@ -131,8 +132,8 @@ class BinaryType(Enum):
class Binary(OperationWithLValue): class Binary(OperationWithLValue):
def __init__(self, result, left_variable, right_variable, operation_type): def __init__(self, result, left_variable, right_variable, operation_type):
assert is_valid_rvalue(left_variable) assert is_valid_rvalue(left_variable) or isinstance(left_variable, Function)
assert is_valid_rvalue(right_variable) assert is_valid_rvalue(right_variable) or isinstance(right_variable, Function)
assert is_valid_lvalue(result) assert is_valid_lvalue(result)
assert isinstance(operation_type, BinaryType) assert isinstance(operation_type, BinaryType)
super().__init__() super().__init__()
@ -174,11 +175,7 @@ class Binary(OperationWithLValue):
while isinstance(points, ReferenceVariable): while isinstance(points, ReferenceVariable):
points = points.points_to points = points.points_to
return "{}(-> {}) = {} {} {}".format( return "{}(-> {}) = {} {} {}".format(
str(self.lvalue), str(self.lvalue), points, self.variable_left, self.type_str, self.variable_right,
points,
self.variable_left,
self.type_str,
self.variable_right,
) )
return "{}({}) = {} {} {}".format( return "{}({}) = {} {} {}".format(
str(self.lvalue), str(self.lvalue),

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

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

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

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

@ -51,8 +51,8 @@ class ContractSolc:
# use to remap inheritance id # use to remap inheritance id
self._remapping: Dict[str, str] = {} self._remapping: Dict[str, str] = {}
self.baseContracts = [] self.baseContracts: List[str] = []
self.baseConstructorContractsCalled = [] self.baseConstructorContractsCalled: List[str] = []
self._linearized_base_contracts: List[int] self._linearized_base_contracts: List[int]
self._variables_parser: List[StateVariableSolc] = [] self._variables_parser: List[StateVariableSolc] = []
@ -441,15 +441,10 @@ class ContractSolc:
elem.set_contract(self._contract) elem.set_contract(self._contract)
elem.set_contract_declarer(element_parser.underlying_function.contract_declarer) elem.set_contract_declarer(element_parser.underlying_function.contract_declarer)
elem.set_offset( elem.set_offset(
element_parser.function_not_parsed["src"], element_parser.function_not_parsed["src"], self._contract.slither,
self._contract.slither,
) )
elem_parser = Cls_parser( elem_parser = Cls_parser(elem, element_parser.function_not_parsed, self,)
elem,
element_parser.function_not_parsed,
self,
)
elem_parser.analyze_params() elem_parser.analyze_params()
if isinstance(elem, Modifier): if isinstance(elem, Modifier):
self._contract.slither.add_modifier(elem) self._contract.slither.add_modifier(elem)

@ -48,10 +48,7 @@ class FunctionSolc:
# elems = [(type, name)] # elems = [(type, name)]
def __init__( def __init__(
self, self, function: Function, function_data: Dict, contract_parser: "ContractSolc",
function: Function,
function_data: Dict,
contract_parser: "ContractSolc",
): ):
self._slither_parser: "SlitherSolc" = contract_parser.slither_parser self._slither_parser: "SlitherSolc" = contract_parser.slither_parser
self._contract_parser = contract_parser self._contract_parser = contract_parser
@ -276,7 +273,7 @@ class FunctionSolc:
self._content_was_analyzed = True self._content_was_analyzed = True
if self.is_compact_ast: if self.is_compact_ast:
body = self._functionNotParsed["body"] body = self._functionNotParsed.get("body", None)
if body and body[self.get_key()] == "Block": if body and body[self.get_key()] == "Block":
self._function.is_implemented = True self._function.is_implemented = True
@ -405,9 +402,9 @@ class FunctionSolc:
def _parse_for_compact_ast(self, statement: Dict, node: NodeSolc) -> NodeSolc: def _parse_for_compact_ast(self, statement: Dict, node: NodeSolc) -> NodeSolc:
body = statement["body"] body = statement["body"]
init_expression = statement["initializationExpression"] init_expression = statement.get("initializationExpression", None)
condition = statement["condition"] condition = statement.get("condition", None)
loop_expression = statement["loopExpression"] loop_expression = statement.get("loopExpression", None)
node_startLoop = self._new_node(NodeType.STARTLOOP, statement["src"]) node_startLoop = self._new_node(NodeType.STARTLOOP, statement["src"])
node_endLoop = self._new_node(NodeType.ENDLOOP, statement["src"]) node_endLoop = self._new_node(NodeType.ENDLOOP, statement["src"])
@ -575,8 +572,7 @@ class FunctionSolc:
link_underlying_nodes(node_startDoWhile, node_condition) link_underlying_nodes(node_startDoWhile, node_condition)
else: else:
link_nodes( link_nodes(
node_startDoWhile.underlying_node, node_startDoWhile.underlying_node, node_condition.underlying_node.sons[0],
node_condition.underlying_node.sons[0],
) )
link_underlying_nodes(statement, node_condition) link_underlying_nodes(statement, node_condition)
link_underlying_nodes(node_condition, node_endDoWhile) link_underlying_nodes(node_condition, node_endDoWhile)
@ -606,7 +602,7 @@ class FunctionSolc:
link_underlying_nodes(node, try_node) link_underlying_nodes(node, try_node)
if self.is_compact_ast: if self.is_compact_ast:
params = statement["parameters"] params = statement.get("parameters", None)
else: else:
params = statement[self.get_children("children")] params = statement[self.get_children("children")]
@ -835,7 +831,7 @@ class FunctionSolc:
node = self._parse_block(statement, node) node = self._parse_block(statement, node)
elif name == "InlineAssembly": elif name == "InlineAssembly":
# Added with solc 0.6 - the yul code is an AST # Added with solc 0.6 - the yul code is an AST
if "AST" in statement: if "AST" in statement and not self.slither.skip_assembly:
self._function.contains_assembly = True self._function.contains_assembly = True
yul_object = self._new_yul_block(statement["src"]) yul_object = self._new_yul_block(statement["src"])
entrypoint = yul_object.entrypoint entrypoint = yul_object.entrypoint

@ -84,14 +84,7 @@ def find_variable( # pylint: disable=too-many-locals,too-many-statements
referenced_declaration: Optional[int] = None, referenced_declaration: Optional[int] = None,
is_super=False, is_super=False,
) -> Union[ ) -> Union[
Variable, Variable, Function, Contract, SolidityVariable, SolidityFunction, Event, Enum, Structure,
Function,
Contract,
SolidityVariable,
SolidityFunction,
Event,
Enum,
Structure,
]: ]:
from slither.solc_parsing.declarations.contract import ContractSolc from slither.solc_parsing.declarations.contract import ContractSolc
from slither.solc_parsing.declarations.function import FunctionSolc from slither.solc_parsing.declarations.function import FunctionSolc
@ -124,9 +117,9 @@ def find_variable( # pylint: disable=too-many-locals,too-many-statements
if function: if function:
# We look for variable declared with the referencedDeclaration attr # We look for variable declared with the referencedDeclaration attr
func_variables = function.variables_renamed func_variables_renamed = function.variables_renamed
if referenced_declaration and referenced_declaration in func_variables: if referenced_declaration and referenced_declaration in func_variables_renamed:
return func_variables[referenced_declaration].underlying_variable return func_variables_renamed[referenced_declaration].underlying_variable
# If not found, check for name # If not found, check for name
func_variables = function.underlying_function.variables_as_dict func_variables = function.underlying_function.variables_as_dict
if var_name in func_variables: if var_name in func_variables:
@ -209,8 +202,8 @@ def find_variable( # pylint: disable=too-many-locals,too-many-statements
return enums[var_name] return enums[var_name]
# Could refer to any enum # Could refer to any enum
all_enums = [c.enums_as_dict for c in contract.slither.contracts] all_enumss = [c.enums_as_dict for c in contract.slither.contracts]
all_enums = {k: v for d in all_enums for k, v in d.items()} all_enums = {k: v for d in all_enumss for k, v in d.items()}
if var_name in all_enums: if var_name in all_enums:
return all_enums[var_name] return all_enums[var_name]
@ -399,8 +392,10 @@ def _parse_elementary_type_name_expression(
if is_compact_ast: if is_compact_ast:
value = expression["typeName"] value = expression["typeName"]
else: else:
assert "children" not in expression if "children" in expression:
value = expression["attributes"]["value"] value = expression["children"][0]["attributes"]["name"]
else:
value = expression["attributes"]["value"]
if isinstance(value, dict): if isinstance(value, dict):
t = parse_type(value, caller_context) t = parse_type(value, caller_context)
else: else:
@ -484,8 +479,11 @@ def parse_expression(expression: Dict, caller_context: CallerContext) -> "Expres
if name == "FunctionCallOptions": if name == "FunctionCallOptions":
# call/gas info are handled in parse_call # call/gas info are handled in parse_call
called = parse_expression(expression["expression"], caller_context) if is_compact_ast:
assert isinstance(called, (MemberAccess, NewContract)) called = parse_expression(expression["expression"], caller_context)
else:
called = parse_expression(expression["children"][0], caller_context)
assert isinstance(called, (MemberAccess, NewContract, Identifier, TupleExpression))
return called return called
if name == "TupleExpression": if name == "TupleExpression":
@ -650,13 +648,12 @@ def parse_expression(expression: Dict, caller_context: CallerContext) -> "Expres
if is_compact_ast: if is_compact_ast:
index_type = expression["typeDescriptions"]["typeString"] index_type = expression["typeDescriptions"]["typeString"]
left = expression["baseExpression"] left = expression["baseExpression"]
right = expression["indexExpression"] right = expression.get("indexExpression", None)
else: else:
index_type = expression["attributes"]["type"] index_type = expression["attributes"]["type"]
children = expression["children"] children = expression["children"]
assert len(children) == 2
left = children[0] left = children[0]
right = children[1] right = children[1] if len(children) > 1 else None
# IndexAccess is used to describe ElementaryTypeNameExpression # IndexAccess is used to describe ElementaryTypeNameExpression
# if abi.decode is used # if abi.decode is used
# For example, abi.decode(data, ...(uint[]) ) # For example, abi.decode(data, ...(uint[]) )
@ -696,9 +693,9 @@ def parse_expression(expression: Dict, caller_context: CallerContext) -> "Expres
member_access = MemberAccess(member_name, member_type, member_expression) member_access = MemberAccess(member_name, member_type, member_expression)
member_access.set_offset(src, caller_context.slither) member_access.set_offset(src, caller_context.slither)
if str(member_access) in SOLIDITY_VARIABLES_COMPOSED: if str(member_access) in SOLIDITY_VARIABLES_COMPOSED:
idx = Identifier(SolidityVariableComposed(str(member_access))) id_idx = Identifier(SolidityVariableComposed(str(member_access)))
idx.set_offset(src, caller_context.slither) id_idx.set_offset(src, caller_context.slither)
return idx return id_idx
return member_access return member_access
if name == "ElementaryTypeNameExpression": if name == "ElementaryTypeNameExpression":
@ -768,7 +765,7 @@ def parse_expression(expression: Dict, caller_context: CallerContext) -> "Expres
if is_compact_ast: if is_compact_ast:
called = parse_expression(expression["modifierName"], caller_context) called = parse_expression(expression["modifierName"], caller_context)
arguments = [] arguments = []
if expression["arguments"]: if expression.get("arguments", None):
arguments = [parse_expression(a, caller_context) for a in expression["arguments"]] arguments = [parse_expression(a, caller_context) for a in expression["arguments"]]
else: else:
children = expression["children"] children = expression["children"]

@ -20,11 +20,12 @@ logger.setLevel(logging.INFO)
class SlitherSolc: class SlitherSolc:
# pylint: disable=no-self-use # pylint: disable=no-self-use,too-many-instance-attributes
def __init__(self, filename: str, core: SlitherCore): def __init__(self, filename: str, core: SlitherCore):
super().__init__() super().__init__()
core.filename = filename core.filename = filename
self._contracts_by_id: Dict[int, ContractSolc] = {} self._contracts_by_id: Dict[int, ContractSolc] = {}
self._parsed = False
self._analyzed = False self._analyzed = False
self._underlying_contract_to_parser: Dict[Contract, ContractSolc] = dict() self._underlying_contract_to_parser: Dict[Contract, ContractSolc] = dict()
@ -128,7 +129,6 @@ class SlitherSolc:
return return
for contract_data in data_loaded[self.get_children()]: for contract_data in data_loaded[self.get_children()]:
assert contract_data[self.get_key()] in [ assert contract_data[self.get_key()] in [
"ContractDefinition", "ContractDefinition",
"PragmaDirective", "PragmaDirective",
@ -155,7 +155,7 @@ class SlitherSolc:
if self.is_compact_ast: if self.is_compact_ast:
import_directive = Import(contract_data["absolutePath"]) import_directive = Import(contract_data["absolutePath"])
else: else:
import_directive = Import(contract_data["attributes"]["absolutePath"]) import_directive = Import(contract_data["attributes"].get("absolutePath", ""))
import_directive.set_offset(contract_data["src"], self._core) import_directive.set_offset(contract_data["src"], self._core)
self._core.import_directives.append(import_directive) self._core.import_directives.append(import_directive)
@ -233,16 +233,20 @@ class SlitherSolc:
################################################################################### ###################################################################################
################################################################################### ###################################################################################
@property
def parsed(self) -> bool:
return self._parsed
@property @property
def analyzed(self) -> bool: def analyzed(self) -> bool:
return self._analyzed return self._analyzed
def analyze_contracts(self): # pylint: disable=too-many-statements,too-many-branches def parse_contracts(self): # pylint: disable=too-many-statements,too-many-branches
if not self._underlying_contract_to_parser: if not self._underlying_contract_to_parser:
logger.info( logger.info(
f"No contract were found in {self._core.filename}, check the correct compilation" f"No contract were found in {self._core.filename}, check the correct compilation"
) )
if self._analyzed: if self._parsed:
raise Exception("Contract analysis can be run only once!") raise Exception("Contract analysis can be run only once!")
# First we save all the contracts in a dict # First we save all the contracts in a dict
@ -348,12 +352,16 @@ Please rename it, this name is reserved for Slither's internals"""
# Then we analyse state variables, functions and modifiers # Then we analyse state variables, functions and modifiers
self._analyze_third_part(contracts_to_be_analyzed, libraries) self._analyze_third_part(contracts_to_be_analyzed, libraries)
self._analyzed = True self._parsed = True
def analyze_contracts(self): # pylint: disable=too-many-statements,too-many-branches
if not self._parsed:
raise SlitherException("Parse the contract before running analyses")
self._convert_to_slithir() self._convert_to_slithir()
compute_dependency(self._core) compute_dependency(self._core)
self._core.compute_storage_layout() self._core.compute_storage_layout()
self._analyzed = True
def _analyze_all_enums(self, contracts_to_be_analyzed: List[ContractSolc]): def _analyze_all_enums(self, contracts_to_be_analyzed: List[ContractSolc]):
while contracts_to_be_analyzed: while contracts_to_be_analyzed:
@ -371,9 +379,7 @@ Please rename it, this name is reserved for Slither's internals"""
contracts_to_be_analyzed += [contract] contracts_to_be_analyzed += [contract]
def _analyze_first_part( def _analyze_first_part(
self, self, contracts_to_be_analyzed: List[ContractSolc], libraries: List[ContractSolc],
contracts_to_be_analyzed: List[ContractSolc],
libraries: List[ContractSolc],
): ):
for lib in libraries: for lib in libraries:
self._parse_struct_var_modifiers_functions(lib) self._parse_struct_var_modifiers_functions(lib)
@ -398,9 +404,7 @@ Please rename it, this name is reserved for Slither's internals"""
contracts_to_be_analyzed += [contract] contracts_to_be_analyzed += [contract]
def _analyze_second_part( def _analyze_second_part(
self, self, contracts_to_be_analyzed: List[ContractSolc], libraries: List[ContractSolc],
contracts_to_be_analyzed: List[ContractSolc],
libraries: List[ContractSolc],
): ):
for lib in libraries: for lib in libraries:
self._analyze_struct_events(lib) self._analyze_struct_events(lib)
@ -425,9 +429,7 @@ Please rename it, this name is reserved for Slither's internals"""
contracts_to_be_analyzed += [contract] contracts_to_be_analyzed += [contract]
def _analyze_third_part( def _analyze_third_part(
self, self, contracts_to_be_analyzed: List[ContractSolc], libraries: List[ContractSolc],
contracts_to_be_analyzed: List[ContractSolc],
libraries: List[ContractSolc],
): ):
for lib in libraries: for lib in libraries:
self._analyze_variables_modifiers_functions(lib) self._analyze_variables_modifiers_functions(lib)

@ -102,11 +102,13 @@ def _find_from_type_name( # pylint: disable=too-many-locals,too-many-branches,t
if not var_type: if not var_type:
if name.startswith("function "): if name.startswith("function "):
found = re.findall( found = re.findall(
"function \(([ ()a-zA-Z0-9\.,]*)\) returns \(([a-zA-Z0-9\.,]*)\)", name "function \(([ ()a-zA-Z0-9\.,]*?)\)(?: returns \(([a-zA-Z0-9() \.,]*)\))?", name
) )
assert len(found) == 1 assert len(found) == 1
params = found[0][0].split(",") params = [v for v in found[0][0].split(",") if v != ""]
return_values = found[0][1].split(",") return_values = (
[v for v in found[0][1].split(",") if v != ""] if len(found[0]) > 1 else []
)
params = [ params = [
_find_from_type_name(p, contract, contracts, structures, enums) for p in params _find_from_type_name(p, contract, contracts, structures, enums) for p in params
] ]
@ -132,8 +134,7 @@ def _find_from_type_name( # pylint: disable=too-many-locals,too-many-branches,t
found = re.findall("mapping\(([a-zA-Z0-9\.]*) => ([a-zA-Z0-9\.\[\]]*)\)", name) found = re.findall("mapping\(([a-zA-Z0-9\.]*) => ([a-zA-Z0-9\.\[\]]*)\)", name)
else: else:
found = re.findall( found = re.findall(
"mapping\(([a-zA-Z0-9\.]*) => (mapping\([=> a-zA-Z0-9\.\[\]]*\))\)", "mapping\(([a-zA-Z0-9\.]*) => (mapping\([=> a-zA-Z0-9\.\[\]]*\))\)", name,
name,
) )
assert len(found) == 1 assert len(found) == 1
from_ = found[0][0] from_ = found[0][0]
@ -189,11 +190,7 @@ def parse_type(t: Union[Dict, UnknownType], caller_context):
if t[key] == "UserDefinedTypeName": if t[key] == "UserDefinedTypeName":
if is_compact_ast: if is_compact_ast:
return _find_from_type_name( return _find_from_type_name(
t["typeDescriptions"]["typeString"], t["typeDescriptions"]["typeString"], contract, contracts, structures, enums,
contract,
contracts,
structures,
enums,
) )
# Determine if we have a type node (otherwise we use the name node, as some older solc did not have 'type'). # Determine if we have a type node (otherwise we use the name node, as some older solc did not have 'type').
@ -205,7 +202,7 @@ def parse_type(t: Union[Dict, UnknownType], caller_context):
if t[key] == "ArrayTypeName": if t[key] == "ArrayTypeName":
length = None length = None
if is_compact_ast: if is_compact_ast:
if t["length"]: if t.get("length", None):
length = parse_expression(t["length"], caller_context) length = parse_expression(t["length"], caller_context)
array_type = parse_type(t["baseType"], contract_parser) array_type = parse_type(t["baseType"], contract_parser)
else: else:

@ -62,7 +62,7 @@ class VariableDeclarationSolc:
init = variable_data["initialValue"] init = variable_data["initialValue"]
self._init_from_declaration(variable_data["declarations"][0], init) self._init_from_declaration(variable_data["declarations"][0], init)
elif nodeType == "VariableDeclaration": elif nodeType == "VariableDeclaration":
self._init_from_declaration(variable_data, variable_data["value"]) self._init_from_declaration(variable_data, variable_data.get("value", None))
else: else:
raise ParsingError("Incorrect variable declaration type {}".format(nodeType)) raise ParsingError("Incorrect variable declaration type {}".format(nodeType))

@ -6,7 +6,6 @@ from slither.core.cfg.node import NodeType, Node, link_nodes
from slither.core.declarations import ( from slither.core.declarations import (
Function, Function,
SolidityFunction, SolidityFunction,
SolidityVariable,
Contract, Contract,
) )
from slither.core.expressions import ( from slither.core.expressions import (
@ -90,6 +89,21 @@ def link_underlying_nodes(node1: YulNode, node2: YulNode):
link_nodes(node1.underlying_node, node2.underlying_node) link_nodes(node1.underlying_node, node2.underlying_node)
def _name_to_yul_name(variable_name: str, yul_id: List[str]) -> str:
"""
Translate the variable name to a unique yul name
Within the same function, yul blocks can declare
different variables with the same name
We need to create unique name per variable
to prevent collision during the SSA generation
:param var:
:param yul_id:
:return:
"""
return variable_name + f"_{'_'.join(yul_id)}"
class YulScope(metaclass=abc.ABCMeta): class YulScope(metaclass=abc.ABCMeta):
__slots__ = [ __slots__ = [
"_contract", "_contract",
@ -136,7 +150,11 @@ class YulScope(metaclass=abc.ABCMeta):
def get_yul_local_variable_from_name(self, variable_name): def get_yul_local_variable_from_name(self, variable_name):
return next( return next(
(v for v in self._yul_local_variables if v.underlying.name == variable_name), (
v
for v in self._yul_local_variables
if v.underlying.name == _name_to_yul_name(variable_name, self.id)
),
None, None,
) )
@ -144,10 +162,7 @@ class YulScope(metaclass=abc.ABCMeta):
self._yul_local_functions.append(func) self._yul_local_functions.append(func)
def get_yul_local_function_from_name(self, func_name): def get_yul_local_function_from_name(self, func_name):
return next( return next((v for v in self._yul_local_functions if v.underlying.name == func_name), None,)
(v for v in self._yul_local_functions if v.underlying.name == func_name),
None,
)
class YulLocalVariable: # pylint: disable=too-few-public-methods class YulLocalVariable: # pylint: disable=too-few-public-methods
@ -163,7 +178,7 @@ class YulLocalVariable: # pylint: disable=too-few-public-methods
var.set_function(root.function) var.set_function(root.function)
var.set_offset(ast["src"], root.slither) var.set_offset(ast["src"], root.slither)
var.name = ast["name"] var.name = _name_to_yul_name(ast["name"], root.id)
var.set_type(ElementaryType("uint256")) var.set_type(ElementaryType("uint256"))
var.set_location("memory") var.set_location("memory")
@ -438,11 +453,7 @@ def convert_yul_switch(root: YulScope, parent: YulNode, ast: Dict) -> YulNode:
"name": "eq", "name": "eq",
}, },
"arguments": [ "arguments": [
{ {"nodeType": "YulIdentifier", "src": case_ast["src"], "name": switch_expr_var,},
"nodeType": "YulIdentifier",
"src": case_ast["src"],
"name": switch_expr_var,
},
value_ast, value_ast,
], ],
}, },
@ -517,7 +528,6 @@ def convert_yul_typed_name(root: YulScope, parent: YulNode, ast: Dict) -> YulNod
local_var = LocalVariable() local_var = LocalVariable()
var = YulLocalVariable(local_var, root, ast) var = YulLocalVariable(local_var, root, ast)
root.add_yul_local_variable(var) root.add_yul_local_variable(var)
node = root.new_node(NodeType.VARIABLE, ast["src"]) node = root.new_node(NodeType.VARIABLE, ast["src"])
@ -668,12 +678,15 @@ def parse_yul_identifier(root: YulScope, _node: YulNode, ast: Dict) -> Optional[
potential_name = name[:-5] potential_name = name[:-5]
var = root.function.contract.get_state_variable_from_name(potential_name) var = root.function.contract.get_state_variable_from_name(potential_name)
if var: if var:
return Identifier(SolidityVariable(name)) return Identifier(var)
var = root.function.get_local_variable_from_name(potential_name)
if var and var.is_storage:
return Identifier(var)
if name.endswith("_offset"): if name.endswith("_offset"):
potential_name = name[:-7] potential_name = name[:-7]
var = root.function.contract.get_state_variable_from_name(potential_name) var = root.function.contract.get_state_variable_from_name(potential_name)
if var: if var:
return Identifier(SolidityVariable(name)) return Identifier(var)
raise SlitherException(f"unresolved reference to identifier {name}") raise SlitherException(f"unresolved reference to identifier {name}")

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

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

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

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

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

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

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

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

@ -50,7 +50,9 @@ ERC20_PROPERTIES = {
} }
def generate_erc20(contract: Contract, type_property: str, addresses: Addresses): def generate_erc20(
contract: Contract, type_property: str, addresses: Addresses
): # pylint: disable=too-many-locals
""" """
Generate the ERC20 tests Generate the ERC20 tests
Files generated: Files generated:
@ -67,6 +69,9 @@ def generate_erc20(contract: Contract, type_property: str, addresses: Addresses)
:param type_property: One of ERC20_PROPERTIES.keys() :param type_property: One of ERC20_PROPERTIES.keys()
:return: :return:
""" """
if contract.slither.crytic_compile is None:
logging.error("Please compile with crytic-compile")
return
if contract.slither.crytic_compile.type not in [ if contract.slither.crytic_compile.type not in [
PlatformType.TRUFFLE, PlatformType.TRUFFLE,
PlatformType.SOLC, PlatformType.SOLC,
@ -80,11 +85,11 @@ def generate_erc20(contract: Contract, type_property: str, addresses: Addresses)
logger.error(red(errors)) logger.error(red(errors))
return return
properties = ERC20_PROPERTIES.get(type_property, None) erc_properties = ERC20_PROPERTIES.get(type_property, None)
if properties is None: if erc_properties is None:
logger.error(f"{type_property} unknown. Types available {ERC20_PROPERTIES.keys()}") logger.error(f"{type_property} unknown. Types available {ERC20_PROPERTIES.keys()}")
return return
properties = properties.properties properties = erc_properties.properties
# Generate the output directory # Generate the output directory
output_dir = _platform_to_output_dir(contract.slither.crytic_compile.platform) output_dir = _platform_to_output_dir(contract.slither.crytic_compile.platform)
@ -102,11 +107,7 @@ def generate_erc20(contract: Contract, type_property: str, addresses: Addresses)
# Generate the Test contract # Generate the Test contract
initialization_recommendation = _initialization_recommendation(type_property) initialization_recommendation = _initialization_recommendation(type_property)
contract_filename, contract_name = generate_test_contract( contract_filename, contract_name = generate_test_contract(
contract, contract, type_property, output_dir, property_file, initialization_recommendation,
type_property,
output_dir,
property_file,
initialization_recommendation,
) )
# Generate Echidna config file # Generate Echidna config file

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

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

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

@ -8,7 +8,7 @@ from slither.tools.slither_format.slither_format import slither_format
logging.basicConfig() logging.basicConfig()
logger = logging.getLogger("Slither").setLevel(logging.INFO) logging.getLogger("Slither").setLevel(logging.INFO)
# Slither detectors for which slither-format currently works # Slither detectors for which slither-format currently works
available_detectors = [ available_detectors = [
@ -22,8 +22,6 @@ available_detectors = [
"constatnt-function-state", "constatnt-function-state",
] ]
detectors_to_run = []
def parse_args(): def parse_args():
""" """
@ -43,17 +41,10 @@ def parse_args():
default=False, default=False,
) )
parser.add_argument( parser.add_argument(
"--verbose-json", "--verbose-json", "-j", help="verbose json output", action="store_true", default=False,
"-j",
help="verbose json output",
action="store_true",
default=False,
) )
parser.add_argument( parser.add_argument(
"--version", "--version", help="displays the current version", version="0.1.0", action="version",
help="displays the current version",
version="0.1.0",
action="version",
) )
parser.add_argument( parser.add_argument(

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

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

@ -1,6 +1,8 @@
import abc import abc
from typing import Optional
from slither.utils.colors import green, yellow, red from slither.utils.colors import green, yellow, red
from slither.utils.comparable_enum import ComparableEnum
from slither.utils.output import Output from slither.utils.output import Output
@ -8,7 +10,7 @@ class IncorrectCheckInitialization(Exception):
pass pass
class CheckClassification: # pylint: disable=too-few-public-methods class CheckClassification(ComparableEnum):
HIGH = 0 HIGH = 0
MEDIUM = 1 MEDIUM = 1
LOW = 2 LOW = 2
@ -33,7 +35,7 @@ classification_txt = {
class AbstractCheck(metaclass=abc.ABCMeta): class AbstractCheck(metaclass=abc.ABCMeta):
ARGUMENT = "" ARGUMENT = ""
HELP = "" HELP = ""
IMPACT = None IMPACT: Optional[CheckClassification] = None
WIKI = "" WIKI = ""

@ -39,6 +39,7 @@ defaults_flag_in_config = {
"filter_paths": None, "filter_paths": None,
"generate_patches": False, "generate_patches": False,
# debug command # debug command
"skip_assembly": False,
"legacy_ast": False, "legacy_ast": False,
"ignore_return_value": False, "ignore_return_value": False,
"zip": None, "zip": None,

@ -0,0 +1,26 @@
from enum import Enum
# pylint: disable=comparison-with-callable
class ComparableEnum(Enum):
def __eq__(self, other):
if isinstance(other, ComparableEnum):
return self.value == other.value
return False
def __ne__(self, other):
if isinstance(other, ComparableEnum):
return self.value != other.value
return False
def __lt__(self, other):
if isinstance(other, ComparableEnum):
return self.value < other.value
return False
def __repr__(self):
return "%s" % (str(self.value))
def __hash__(self):
return hash(self.value)

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

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

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

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

@ -0,0 +1,11 @@
contract C {
function f() public {
assembly {
let x := 0
}
assembly "evmasm" {
let x := 0
}
}
}

@ -0,0 +1,15 @@
contract C {
function f() public {
uint assign;
assign = 10;
assign |= 10;
assign ^= 10;
assign &= 10;
assign += 10;
assign -= 10;
assign *= 10;
assign /= 10;
assign %= 10;
}
}

@ -0,0 +1,17 @@
contract C {
function f() public {
uint assign;
assign = 10;
assign |= 10;
assign ^= 10;
assign &= 10;
assign <<= 10;
assign >>= 10;
assign += 10;
assign -= 10;
assign *= 10;
assign /= 10;
assign %= 10;
}
}

@ -0,0 +1,22 @@
contract C {
function f() public {
1**2;
1*2;
1/2;
1&2;
1+2;
1-2;
1 & 2;
1 ^ 2;
1 | 2;
1 < 2;
1 > 2;
1 <= 2;
1 >= 2;
1 == 2;
1 != 2;
true && false;
true || false;
}
}

@ -0,0 +1,24 @@
contract C {
function f() public {
1**2;
1*2;
1/2;
1&2;
1+2;
1-2;
1 << 2;
1 >> 2;
1 & 2;
1 ^ 2;
1 | 2;
1 < 2;
1 > 2;
1 <= 2;
1 >= 2;
1 == 2;
1 != 2;
true && false;
true || false;
}
}

@ -0,0 +1,21 @@
contract C {
function f() public {
uint c;
for (uint i = 0; i < 10; i++) {
if (i % 2 == 0) {
break;
}
c++;
}
for (uint j = 0; j < 10; j++) {
for (uint k = 0; k < 10; k++) {
if (j % 2 == 0 && k % 3 == 0) {
break;
}
c++;
}
}
}
}

@ -0,0 +1,9 @@
contract C {
function f() public {
false ? 1 : 2;
5 == 6 ? 1 : 2;
1 + 2 == 3 ? 4 + 5 == 6 ? int8(0) : -1 : -2;
true ? "a" : "b";
false ? (1, 2) : (3, 4);
}
}

@ -0,0 +1,21 @@
contract C {
function f() public {
uint c;
for (uint i = 0; i < 10; i++) {
if (i % 2 == 0) {
continue;
}
c++;
}
for (uint j = 0; j < 10; j++) {
for (uint k = 0; k < 10; k++) {
if (j % 2 == 0 && k % 3 == 0) {
continue;
}
c++;
}
}
}
}

@ -0,0 +1,35 @@
// a simple contract
contract A {
}
// inheritance, no constructor
contract B is A {
function B(uint a) {
}
}
// inheritance, init in inheritance
contract C is B(4) {
}
// inheritance, init in constructor
contract D is B {
function D() B(2) {
}
}
// abstract contract
contract E is B {
}
// diamond inheritance
contract F is A {}
contract G is A {}
contract H is F, G {
}

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

Loading…
Cancel
Save