Merge branch 'dev' into dev-upgradeability-util-cross-contract-taint

pull/1816/head
William E Bodell III 2 years ago committed by GitHub
commit 6a10e0a1e1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 4
      .github/scripts/unit_test_runner.sh
  2. 2
      .github/workflows/black.yml
  3. 1
      .github/workflows/ci.yml
  4. 2
      .github/workflows/linter.yml
  5. 2
      .github/workflows/pip-audit.yml
  6. 2
      .github/workflows/pylint.yml
  7. 2
      .github/workflows/test.yml
  8. 1
      .gitignore
  9. 83
      CONTRIBUTING.md
  10. 88
      Makefile
  11. 10
      examples/flat/a.sol
  12. 13
      examples/flat/b.sol
  13. 10
      scripts/ci_test_flat.sh
  14. 95
      scripts/ci_test_interface.sh
  15. 13
      setup.py
  16. 3
      slither/__init__.py
  17. 1
      slither/analyses/data_dependency/data_dependency.py
  18. 15
      slither/core/cfg/node.py
  19. 8
      slither/core/compilation_unit.py
  20. 2
      slither/core/declarations/contract.py
  21. 7
      slither/core/declarations/custom_error.py
  22. 4
      slither/core/declarations/solidity_variables.py
  23. 2
      slither/detectors/all_detectors.py
  24. 4
      slither/detectors/assembly/shift_parameter_mixup.py
  25. 104
      slither/detectors/operations/encode_packed.py
  26. 42
      slither/detectors/operations/unused_return_values.py
  27. 31
      slither/detectors/reentrancy/reentrancy_events.py
  28. 23
      slither/detectors/statements/incorrect_strict_equality.py
  29. 223
      slither/detectors/statements/incorrect_using_for.py
  30. 11
      slither/detectors/variables/uninitialized_local_variables.py
  31. 2
      slither/detectors/variables/var_read_using_this.py
  32. 2
      slither/printers/functions/authorization.py
  33. 2
      slither/printers/functions/cfg.py
  34. 4
      slither/printers/inheritance/inheritance.py
  35. 2
      slither/printers/inheritance/inheritance_graph.py
  36. 3
      slither/printers/summary/contract.py
  37. 2
      slither/printers/summary/data_depenency.py
  38. 2
      slither/printers/summary/function.py
  39. 2
      slither/printers/summary/human_summary.py
  40. 2
      slither/printers/summary/slithir.py
  41. 2
      slither/printers/summary/slithir_ssa.py
  42. 6
      slither/printers/summary/when_not_paused.py
  43. 9
      slither/slithir/convert.py
  44. 2
      slither/slithir/operations/assignment.py
  45. 1
      slither/slithir/operations/binary.py
  46. 5
      slither/slithir/operations/new_array.py
  47. 3
      slither/slithir/operations/new_contract.py
  48. 3
      slither/slithir/operations/new_structure.py
  49. 2
      slither/slithir/operations/unary.py
  50. 3
      slither/solc_parsing/declarations/contract.py
  51. 15
      slither/solc_parsing/declarations/function.py
  52. 3
      slither/solc_parsing/declarations/modifier.py
  53. 5
      slither/solc_parsing/slither_compilation_unit_solc.py
  54. 2
      slither/tools/flattening/export/export.py
  55. 45
      slither/tools/flattening/flattening.py
  56. 0
      slither/tools/interface/__init__.py
  57. 105
      slither/tools/interface/__main__.py
  58. 2
      slither/tools/read_storage/read_storage.py
  59. 181
      slither/utils/code_generation.py
  60. 11
      slither/utils/expression_manipulations.py
  61. 6
      slither/utils/myprettytable.py
  62. 15
      slither/utils/type.py
  63. 7
      slither/visitors/slithir/expression_to_slithir.py
  64. 79
      tests/conftest.py
  65. 13
      tests/e2e/compilation/test_resolution.py
  66. 18
      tests/e2e/detectors/snapshots/detectors__detector_ABIEncoderV2Array_0_4_25_storage_ABIEncoderV2_array_sol__0.txt
  67. 0
      tests/e2e/detectors/snapshots/detectors__detector_ABIEncoderV2Array_0_5_10_storage_ABIEncoderV2_array_sol__0.txt
  68. 18
      tests/e2e/detectors/snapshots/detectors__detector_ABIEncoderV2Array_0_5_9_storage_ABIEncoderV2_array_sol__0.txt
  69. 6
      tests/e2e/detectors/snapshots/detectors__detector_ArbitrarySendErc20NoPermit_0_4_25_arbitrary_send_erc20_sol__0.txt
  70. 6
      tests/e2e/detectors/snapshots/detectors__detector_ArbitrarySendErc20NoPermit_0_5_16_arbitrary_send_erc20_sol__0.txt
  71. 6
      tests/e2e/detectors/snapshots/detectors__detector_ArbitrarySendErc20NoPermit_0_6_11_arbitrary_send_erc20_sol__0.txt
  72. 6
      tests/e2e/detectors/snapshots/detectors__detector_ArbitrarySendErc20NoPermit_0_7_6_arbitrary_send_erc20_sol__0.txt
  73. 2
      tests/e2e/detectors/snapshots/detectors__detector_ArbitrarySendErc20NoPermit_0_8_0_arbitrary_send_erc20_inheritance_sol__0.txt
  74. 6
      tests/e2e/detectors/snapshots/detectors__detector_ArbitrarySendErc20NoPermit_0_8_0_arbitrary_send_erc20_sol__0.txt
  75. 8
      tests/e2e/detectors/snapshots/detectors__detector_ArbitrarySendErc20Permit_0_4_25_arbitrary_send_erc20_permit_sol__0.txt
  76. 8
      tests/e2e/detectors/snapshots/detectors__detector_ArbitrarySendErc20Permit_0_5_16_arbitrary_send_erc20_permit_sol__0.txt
  77. 8
      tests/e2e/detectors/snapshots/detectors__detector_ArbitrarySendErc20Permit_0_6_11_arbitrary_send_erc20_permit_sol__0.txt
  78. 8
      tests/e2e/detectors/snapshots/detectors__detector_ArbitrarySendErc20Permit_0_7_6_arbitrary_send_erc20_permit_sol__0.txt
  79. 8
      tests/e2e/detectors/snapshots/detectors__detector_ArbitrarySendErc20Permit_0_8_0_arbitrary_send_erc20_permit_sol__0.txt
  80. 8
      tests/e2e/detectors/snapshots/detectors__detector_ArbitrarySendEth_0_4_25_arbitrary_send_eth_sol__0.txt
  81. 8
      tests/e2e/detectors/snapshots/detectors__detector_ArbitrarySendEth_0_5_16_arbitrary_send_eth_sol__0.txt
  82. 8
      tests/e2e/detectors/snapshots/detectors__detector_ArbitrarySendEth_0_6_11_arbitrary_send_eth_sol__0.txt
  83. 8
      tests/e2e/detectors/snapshots/detectors__detector_ArbitrarySendEth_0_7_6_arbitrary_send_eth_sol__0.txt
  84. 12
      tests/e2e/detectors/snapshots/detectors__detector_ArrayByReference_0_4_25_array_by_reference_sol__0.txt
  85. 12
      tests/e2e/detectors/snapshots/detectors__detector_ArrayByReference_0_5_16_array_by_reference_sol__0.txt
  86. 12
      tests/e2e/detectors/snapshots/detectors__detector_ArrayByReference_0_6_11_array_by_reference_sol__0.txt
  87. 12
      tests/e2e/detectors/snapshots/detectors__detector_ArrayByReference_0_7_6_array_by_reference_sol__0.txt
  88. 9
      tests/e2e/detectors/snapshots/detectors__detector_ArrayLengthAssignment_0_4_25_array_length_assignment_sol__0.txt
  89. 9
      tests/e2e/detectors/snapshots/detectors__detector_ArrayLengthAssignment_0_5_16_array_length_assignment_sol__0.txt
  90. 3
      tests/e2e/detectors/snapshots/detectors__detector_Assembly_0_4_25_inline_assembly_contract_sol__0.txt
  91. 6
      tests/e2e/detectors/snapshots/detectors__detector_Assembly_0_4_25_inline_assembly_library_sol__0.txt
  92. 3
      tests/e2e/detectors/snapshots/detectors__detector_Assembly_0_5_16_inline_assembly_contract_sol__0.txt
  93. 6
      tests/e2e/detectors/snapshots/detectors__detector_Assembly_0_5_16_inline_assembly_library_sol__0.txt
  94. 3
      tests/e2e/detectors/snapshots/detectors__detector_Assembly_0_6_11_inline_assembly_contract_sol__0.txt
  95. 6
      tests/e2e/detectors/snapshots/detectors__detector_Assembly_0_6_11_inline_assembly_library_sol__0.txt
  96. 3
      tests/e2e/detectors/snapshots/detectors__detector_Assembly_0_7_6_inline_assembly_contract_sol__0.txt
  97. 6
      tests/e2e/detectors/snapshots/detectors__detector_Assembly_0_7_6_inline_assembly_library_sol__0.txt
  98. 12
      tests/e2e/detectors/snapshots/detectors__detector_AssertStateChange_0_4_25_assert_state_change_sol__0.txt
  99. 12
      tests/e2e/detectors/snapshots/detectors__detector_AssertStateChange_0_5_16_assert_state_change_sol__0.txt
  100. 12
      tests/e2e/detectors/snapshots/detectors__detector_AssertStateChange_0_6_11_assert_state_change_sol__0.txt
  101. Some files were not shown because too many files have changed in this diff Show More

@ -2,11 +2,11 @@
# used to pass --cov=$path and --cov-append to pytest # used to pass --cov=$path and --cov-append to pytest
if [ "$1" != "" ]; then if [ "$1" != "" ]; then
pytest "$1" tests/unit/ pytest "$1" tests/unit/ -n auto
status_code=$? status_code=$?
python -m coverage report python -m coverage report
else else
pytest tests/unit/ pytest tests/unit/ -n auto
status_code=$? status_code=$?
fi fi

@ -42,7 +42,7 @@ jobs:
cp pyproject.toml .github/linters cp pyproject.toml .github/linters
- name: Black - name: Black
uses: github/super-linter/slim@v4.9.2 uses: super-linter/super-linter/slim@v4.9.2
if: always() if: always()
env: env:
# run linter on everything to catch preexisting problems # run linter on everything to catch preexisting problems

@ -36,6 +36,7 @@ jobs:
"etherscan", "etherscan",
"find_paths", "find_paths",
"flat", "flat",
"interface",
"kspec", "kspec",
"printers", "printers",
# "prop" # "prop"

@ -43,7 +43,7 @@ jobs:
cp pyproject.toml .github/linters cp pyproject.toml .github/linters
- name: Lint everything else - name: Lint everything else
uses: github/super-linter/slim@v4.9.2 uses: super-linter/super-linter/slim@v4.9.2
if: always() if: always()
env: env:
# run linter on everything to catch preexisting problems # run linter on everything to catch preexisting problems

@ -34,6 +34,6 @@ jobs:
python -m pip install . python -m pip install .
- name: Run pip-audit - name: Run pip-audit
uses: trailofbits/gh-action-pip-audit@v0.0.4 uses: pypa/gh-action-pip-audit@v1.0.7
with: with:
virtual-environment: /tmp/pip-audit-env virtual-environment: /tmp/pip-audit-env

@ -37,7 +37,7 @@ jobs:
cp pyproject.toml .github/linters cp pyproject.toml .github/linters
- name: Pylint - name: Pylint
uses: github/super-linter/slim@v4.9.2 uses: super-linter/super-linter/slim@v4.9.2
if: always() if: always()
env: env:
# Run linters only on new files for pylint to speed up the CI # Run linters only on new files for pylint to speed up the CI

@ -37,8 +37,6 @@ jobs:
- name: Install dependencies - name: Install dependencies
run: | run: |
pip install ".[test]" pip install ".[test]"
solc-select install 0.8.0
solc-select use 0.8.0
- name: Setup node - name: Setup node
uses: actions/setup-node@v3 uses: actions/setup-node@v3

1
.gitignore vendored

@ -42,6 +42,7 @@ htmlcov/
.coverage .coverage
.coverage.* .coverage.*
.cache .cache
.pytest_cache
nosetests.xml nosetests.xml
coverage.xml coverage.xml
*.cover *.cover

@ -1,15 +1,19 @@
# Contributing to Slither # Contributing to Slither
First, thanks for your interest in contributing to Slither! We welcome and appreciate all contributions, including bug reports, feature suggestions, tutorials/blog posts, and code improvements. First, thanks for your interest in contributing to Slither! We welcome and appreciate all contributions, including bug reports, feature suggestions, tutorials/blog posts, and code improvements.
If you're unsure where to start, we recommend our [`good first issue`](https://github.com/crytic/slither/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22) and [`help wanted`](https://github.com/crytic/slither/issues?q=is%3Aissue+is%3Aopen+label%3A%22help+wanted%22) issue labels. If you're unsure where to start, we recommend our [`good first issue`](https://github.com/crytic/slither/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22) and [`help wanted`](https://github.com/crytic/slither/issues?q=is%3Aissue+is%3Aopen+label%3A%22help+wanted%22) issue labels.
## Bug reports and feature suggestions ## Bug reports and feature suggestions
Bug reports and feature suggestions can be submitted to our issue tracker. For bug reports, attaching the contract that caused the bug will help us in debugging and resolving the issue quickly. If you find a security vulnerability, do not open an issue; email opensource@trailofbits.com instead. Bug reports and feature suggestions can be submitted to our issue tracker. For bug reports, attaching the contract that caused the bug will help us in debugging and resolving the issue quickly. If you find a security vulnerability, do not open an issue; email opensource@trailofbits.com instead.
## Questions ## Questions
Questions can be submitted to the issue tracker, but you may get a faster response if you ask in our [chat room](https://empireslacking.herokuapp.com/) (in the #ethereum channel).
Questions can be submitted to the "Discussions" page, and you may also join our [chat room](https://empireslacking.herokuapp.com/) (in the #ethereum channel).
## Code ## Code
Slither uses the pull request contribution model. Please make an account on Github, fork this repo, and submit code contributions via pull request. For more documentation, look [here](https://guides.github.com/activities/forking/). Slither uses the pull request contribution model. Please make an account on Github, fork this repo, and submit code contributions via pull request. For more documentation, look [here](https://guides.github.com/activities/forking/).
Some pull request guidelines: Some pull request guidelines:
@ -23,6 +27,7 @@ Some pull request guidelines:
## Directory Structure ## Directory Structure
Below is a rough outline of slither's design: Below is a rough outline of slither's design:
```text ```text
. .
├── analyses # Provides additional info such as data dependency ├── analyses # Provides additional info such as data dependency
@ -39,9 +44,10 @@ Below is a rough outline of slither's design:
A code walkthrough is available [here](https://www.youtube.com/watch?v=EUl3UlYSluU). A code walkthrough is available [here](https://www.youtube.com/watch?v=EUl3UlYSluU).
## 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 to clone this repository and run `pip install ".[dev]"`. To run the unit tests, you need to clone this repository and run `make test`. Run a specific test with `make test TESTS=$test_name`. The names of tests can be obtained with `pytest tests --collect-only`.
### Linters ### Linters
@ -49,40 +55,63 @@ Several linters and security checkers are run on the PRs.
To run them locally in the root dir of the repository: To run them locally in the root dir of the repository:
- `pylint slither tests --rcfile pyproject.toml` - `make lint`
- `black . --config pyproject.toml`
> Note, this only validates but does not modify the code.
To automatically reformat the code:
- `make reformat`
We use pylint `2.13.4`, black `22.3.0`. We use pylint `2.13.4`, black `22.3.0`.
### Detectors tests ### Testing
Slither's test suite is divided into three categories end-to-end (`tests/e2e`), unit (`tests/unit`), and tools (`tests/tools/`).
How do I know what kind of test(s) to write?
- End-to-end: functionality that requires invoking `Slither` and inspecting some output such as printers and detectors.
- Unit: additions and modifications to objects should be accompanied by a unit test that defines the expected behavior. Aim to write functions in as pure a way as possible such that they are easier to test.
- Tools: tools built on top of Slither (`slither/tools`) but not apart of its core functionality
#### Adding detector 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.
- Create a test in `tests` 1. Create a folder in `tests/e2e/detectors/test_data` with the detector's argument name.
- Update `ALL_TEST` in `tests/test_detectors.py` 2. Create a test contract in `tests/e2e/detectors/test_data/<detector_name>/`.
- Run `python ./tests/test_detectors.py --generate`. This will generate the json artifacts in `tests/expected_json`. Add the generated files to git. 3. Update `ALL_TEST` in `tests/e2e/detectors/test_detectors.py`
- If updating an existing detector, identify the respective json artifacts and then delete them, or run `python ./tests/test_detectors.py --overwrite` instead. 4. Run `python tests/e2e/detectors/test_detectors.py --compile` to create a zip file of the compilation artifacts.
- Run `pytest ./tests/test_detectors.py` and check that everything worked. 5. `pytest tests/e2e/detectors/test_detectors.py --insta update-new`. This will generate a snapshot of the detector output in `tests/e2e/detectors/snapshots/`. If updating an existing detector, run `pytest tests/e2e/detectors/test_detectors.py --insta review` and accept or reject the updates.
6. Run `pytest tests/e2e/detectors/test_detectors.py` to ensure everything worked. Then, add and commit the files to git.
To see the tests coverage, run `pytest tests/test_detectors.py --cov=slither/detectors --cov-branch --cov-report html`.
To run tests for a specific detector, run `pytest tests/test_detectors.py -k ReentrancyReadBeforeWritten` (the detector's class name is the argument). > ##### Helpful commands for detector tests
To run tests for a specific version, run `pytest tests/test_detectors.py -k 0.7.6`. >
The IDs of tests can be inspected using `pytest tests/test_detectors.py --collect-only`. > - To see the tests coverage, run `pytest tests/e2e/detectors/test_detectors.py --cov=slither/detectors --cov-branch --cov-report html`.
> - To run tests for a specific detector, run `pytest tests/e2e/detectors/test_detectors.py -k ReentrancyReadBeforeWritten`(the detector's class name is the argument).
### Parser tests > - To run tests for a specific version, run `pytest tests/e2e/detectors/test_detectors.py -k 0.7.6`.
- Create a test in `tests/ast-parsing` > - The IDs of tests can be inspected using `pytest tests/e2e/detectors/test_detectors.py --collect-only`.
- Run `python ./tests/test_ast_parsing.py --compile`. This will compile the artifact in `tests/ast-parsing/compile`. Add the compiled artifact to git.
- Run `python ./tests/test_ast_parsing.py --generate`. This will generate the json artifacts in `tests/ast-parsing/expected_json`. Add the generated files to git. #### Adding parsing tests
- Run `pytest ./tests/test_ast_parsing.py` and check that everything worked.
1. Create a test in `tests/e2e/solc_parsing/`
To see the tests coverage, run `pytest tests/test_ast_parsing.py --cov=slither/solc_parsing --cov-branch --cov-report html` 2. Run `python tests/e2e/solc_parsing/test_ast_parsing.py --compile`. This will compile the artifact in `tests/e2e/solc_parsing/compile`. Add the compiled artifact to git.
To run tests for a specific test case, run `pytest tests/test_ast_parsing.py -k user_defined_value_type` (the filename is the argument). 3. Run `python tests/e2e/solc_parsing/test_ast_parsing.py --generate`. This will generate the json artifacts in `tests/e2e/solc_parsing/expected_json`. Add the generated files to git.
To run tests for a specific version, run `pytest tests/test_ast_parsing.py -k 0.8.12`. 4. Run `pytest tests/e2e/solc_parsing/test_ast_parsing.py` and check that everything worked.
To run tests for a specific compiler json format, run `pytest tests/test_ast_parsing.py -k legacy` (can be legacy or compact).
The IDs of tests can be inspected using ``pytest tests/test_ast_parsing.py --collect-only`. > ##### Helpful commands for parsing tests
>
> - To see the tests coverage, run `pytest tests/e2e/solc_parsing/test_ast_parsing.py --cov=slither/solc_parsing --cov-branch --cov-report html`
> - To run tests for a specific test case, run `pytest tests/e2e/solc_parsing/test_ast_parsing.py -k user_defined_value_type` (the filename is the argument).
> - To run tests for a specific version, run `pytest tests/e2e/solc_parsing/test_ast_parsing.py -k 0.8.12`.
> - To run tests for a specific compiler json format, run `pytest tests/e2e/solc_parsing/test_ast_parsing.py -k legacy` (can be legacy or compact).
> - The IDs of tests can be inspected using ``pytest tests/e2e/solc_parsing/test_ast_parsing.py --collect-only`.
### Synchronization with crytic-compile ### Synchronization with crytic-compile
By default, `slither` follows either the latest version of crytic-compile in pip, or `crytic-compile@master` (look for dependencies in [`setup.py`](./setup.py). If crytic-compile development comes with breaking changes, the process to update `slither` is: By default, `slither` follows either the latest version of crytic-compile in pip, or `crytic-compile@master` (look for dependencies in [`setup.py`](./setup.py). If crytic-compile development comes with breaking changes, the process to update `slither` is:
- Update `slither/setup.py` to point to the related crytic-compile's branch - Update `slither/setup.py` to point to the related crytic-compile's branch
- Create a PR in `slither` and ensure it passes the CI - Create a PR in `slither` and ensure it passes the CI
- Once the development branch is merged in `crytic-compile@master`, ensure `slither` follows the `master` branch - Once the development branch is merged in `crytic-compile@master`, ensure `slither` follows the `master` branch

@ -0,0 +1,88 @@
SHELL := /bin/bash
PY_MODULE := slither
TEST_MODULE := tests
ALL_PY_SRCS := $(shell find $(PY_MODULE) -name '*.py') \
$(shell find test -name '*.py')
# Optionally overriden by the user, if they're using a virtual environment manager.
VENV ?= env
# On Windows, venv scripts/shims are under `Scripts` instead of `bin`.
VENV_BIN := $(VENV)/bin
ifeq ($(OS),Windows_NT)
VENV_BIN := $(VENV)/Scripts
endif
# Optionally overridden by the user in the `release` target.
BUMP_ARGS :=
# Optionally overridden by the user in the `test` target.
TESTS :=
# Optionally overridden by the user/CI, to limit the installation to a specific
# subset of development dependencies.
SLITHER_EXTRA := dev
# If the user selects a specific test pattern to run, set `pytest` to fail fast
# and only run tests that match the pattern.
# Otherwise, run all tests and enable coverage assertions, since we expect
# complete test coverage.
ifneq ($(TESTS),)
TEST_ARGS := -x -k $(TESTS)
COV_ARGS :=
else
TEST_ARGS := -n auto
COV_ARGS := # --fail-under 100
endif
.PHONY: all
all:
@echo "Run my targets individually!"
.PHONY: dev
dev: $(VENV)/pyvenv.cfg
.PHONY: run
run: $(VENV)/pyvenv.cfg
@. $(VENV_BIN)/activate && slither $(ARGS)
$(VENV)/pyvenv.cfg: pyproject.toml
# Create our Python 3 virtual environment
python3 -m venv env
$(VENV_BIN)/python -m pip install --upgrade pip
$(VENV_BIN)/python -m pip install -e .[$(SLITHER_EXTRA)]
.PHONY: lint
lint: $(VENV)/pyvenv.cfg
. $(VENV_BIN)/activate && \
black --check . && \
pylint $(PY_MODULE) $(TEST_MODULE)
# ruff $(ALL_PY_SRCS) && \
# mypy $(PY_MODULE) &&
.PHONY: reformat
reformat:
. $(VENV_BIN)/activate && \
black .
.PHONY: test tests
test tests: $(VENV)/pyvenv.cfg
. $(VENV_BIN)/activate && \
pytest --cov=$(PY_MODULE) $(T) $(TEST_ARGS) && \
python -m coverage report -m $(COV_ARGS)
.PHONY: doc
doc: $(VENV)/pyvenv.cfg
. $(VENV_BIN)/activate && \
PDOC_ALLOW_EXEC=1 pdoc -o html slither '!slither.tools'
.PHONY: package
package: $(VENV)/pyvenv.cfg
. $(VENV_BIN)/activate && \
python3 -m build
.PHONY: edit
edit:
$(EDITOR) $(ALL_PY_SRCS)

@ -1,3 +1,9 @@
contract A{ pragma solidity 0.8.19;
} error RevertIt();
contract Example {
function reverts() external pure {
revert RevertIt();
}
}

@ -1,5 +1,16 @@
import "./a.sol"; import "./a.sol";
contract B is A{ pragma solidity 0.8.19;
enum B {
a,
b
} }
contract T {
Example e = new Example();
function b() public returns(uint) {
B b = B.a;
return 4;
}
}

@ -1,6 +1,8 @@
#!/usr/bin/env bash #!/usr/bin/env bash
shopt -s extglob
### Test slither-prop ### Test slither-flat
solc-select use 0.8.19 --always-install
cd examples/flat || exit 1 cd examples/flat || exit 1
@ -8,5 +10,11 @@ if ! slither-flat b.sol; then
echo "slither-flat failed" echo "slither-flat failed"
exit 1 exit 1
fi fi
SUFFIX="@(sol)"
if ! solc "crytic-export/flattening/"*$SUFFIX; then
echo "solc failed on flattened files"
exit 1
fi
exit 0 exit 0

@ -0,0 +1,95 @@
#!/usr/bin/env bash
### Test slither-interface
DIR_TESTS="tests/tools/interface"
solc-select use 0.8.19 --always-install
#Test 1 - Etherscan target
slither-interface WETH9 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2
DIFF=$(diff crytic-export/interfaces/IWETH9.sol "$DIR_TESTS/test_1.sol" --strip-trailing-cr)
if [ "$DIFF" != "" ]
then
echo "slither-interface test 1 failed"
cat "crytic-export/interfaces/IWETH9.sol"
echo ""
cat "$DIR_TESTS/test_1.sol"
exit 255
fi
#Test 2 - Local file target
slither-interface Mock tests/tools/interface/ContractMock.sol
DIFF=$(diff crytic-export/interfaces/IMock.sol "$DIR_TESTS/test_2.sol" --strip-trailing-cr)
if [ "$DIFF" != "" ]
then
echo "slither-interface test 2 failed"
cat "crytic-export/interfaces/IMock.sol"
echo ""
cat "$DIR_TESTS/test_2.sol"
exit 255
fi
#Test 3 - unroll structs
slither-interface Mock tests/tools/interface/ContractMock.sol --unroll-structs
DIFF=$(diff crytic-export/interfaces/IMock.sol "$DIR_TESTS/test_3.sol" --strip-trailing-cr)
if [ "$DIFF" != "" ]
then
echo "slither-interface test 3 failed"
cat "crytic-export/interfaces/IMock.sol"
echo ""
cat "$DIR_TESTS/test_3.sol"
exit 255
fi
#Test 4 - exclude structs
slither-interface Mock tests/tools/interface/ContractMock.sol --exclude-structs
DIFF=$(diff crytic-export/interfaces/IMock.sol "$DIR_TESTS/test_4.sol" --strip-trailing-cr)
if [ "$DIFF" != "" ]
then
echo "slither-interface test 4 failed"
cat "crytic-export/interfaces/IMock.sol"
echo ""
cat "$DIR_TESTS/test_4.sol"
exit 255
fi
#Test 5 - exclude errors
slither-interface Mock tests/tools/interface/ContractMock.sol --exclude-errors
DIFF=$(diff crytic-export/interfaces/IMock.sol "$DIR_TESTS/test_5.sol" --strip-trailing-cr)
if [ "$DIFF" != "" ]
then
echo "slither-interface test 5 failed"
cat "crytic-export/interfaces/IMock.sol"
echo ""
cat "$DIR_TESTS/test_5.sol"
exit 255
fi
#Test 6 - exclude enums
slither-interface Mock tests/tools/interface/ContractMock.sol --exclude-enums
DIFF=$(diff crytic-export/interfaces/IMock.sol "$DIR_TESTS/test_6.sol" --strip-trailing-cr)
if [ "$DIFF" != "" ]
then
echo "slither-interface test 6 failed"
cat "crytic-export/interfaces/IMock.sol"
echo ""
cat "$DIR_TESTS/test_6.sol"
exit 255
fi
#Test 7 - exclude events
slither-interface Mock tests/tools/interface/ContractMock.sol --exclude-events
DIFF=$(diff crytic-export/interfaces/IMock.sol "$DIR_TESTS/test_7.sol" --strip-trailing-cr)
if [ "$DIFF" != "" ]
then
echo "slither-interface test 7 failed"
cat "crytic-export/interfaces/IMock.sol"
echo ""
cat "$DIR_TESTS/test_7.sol"
exit 255
fi
rm -r crytic-export

@ -13,11 +13,14 @@ setup(
python_requires=">=3.8", python_requires=">=3.8",
install_requires=[ install_requires=[
"packaging", "packaging",
"prettytable>=0.7.2", "prettytable>=3.3.0",
"pycryptodome>=3.4.6", "pycryptodome>=3.4.6",
"crytic-compile>=0.3.1,<0.4.0", # "crytic-compile>=0.3.1,<0.4.0",
# "crytic-compile@git+https://github.com/crytic/crytic-compile.git@master#egg=crytic-compile", "crytic-compile@git+https://github.com/crytic/crytic-compile.git@dev#egg=crytic-compile",
"web3>=6.0.0", "web3>=6.0.0",
"eth-abi>=4.0.0",
"eth-typing>=3.0.0",
"eth-utils>=2.1.0",
], ],
extras_require={ extras_require={
"lint": [ "lint": [
@ -31,6 +34,9 @@ setup(
"deepdiff", "deepdiff",
"numpy", "numpy",
"coverage[toml]", "coverage[toml]",
"filelock",
"pytest-insta",
"solc-select@git+https://github.com/crytic/solc-select.git@query-artifact-path#egg=solc-select",
], ],
"doc": [ "doc": [
"pdoc", "pdoc",
@ -58,6 +64,7 @@ setup(
"slither-read-storage = slither.tools.read_storage.__main__:main", "slither-read-storage = slither.tools.read_storage.__main__:main",
"slither-doctor = slither.tools.doctor.__main__:main", "slither-doctor = slither.tools.doctor.__main__:main",
"slither-documentation = slither.tools.documentation.__main__:main", "slither-documentation = slither.tools.documentation.__main__:main",
"slither-interface = slither.tools.interface.__main__:main",
] ]
}, },
) )

@ -1 +1,4 @@
"""
.. include:: ../README.md
"""
from .slither import Slither from .slither import Slither

@ -129,6 +129,7 @@ GENERIC_TAINT = {
SolidityVariableComposed("msg.value"), SolidityVariableComposed("msg.value"),
SolidityVariableComposed("msg.data"), SolidityVariableComposed("msg.data"),
SolidityVariableComposed("tx.origin"), SolidityVariableComposed("tx.origin"),
SolidityVariableComposed("tx.gasprice"),
} }

@ -620,6 +620,21 @@ class Node(SourceMapping): # pylint: disable=too-many-public-methods
""" """
self._sons.append(son) self._sons.append(son)
def replace_son(self, ori_son: "Node", new_son: "Node") -> None:
"""Replace a son node. Do nothing if the node to replace is not a son
Args:
ori_son: son to replace
new_son: son to replace with
"""
for i, s in enumerate(self._sons):
if s.node_id == ori_son.node_id:
idx = i
break
else:
return
self._sons[idx] = new_son
def set_sons(self, sons: List["Node"]) -> None: def set_sons(self, sons: List["Node"]) -> None:
"""Set the son nodes """Set the son nodes

@ -13,7 +13,7 @@ from slither.core.declarations import (
Function, Function,
Modifier, Modifier,
) )
from slither.core.declarations.custom_error import CustomError from slither.core.declarations.custom_error_top_level import CustomErrorTopLevel
from slither.core.declarations.enum_top_level import EnumTopLevel from slither.core.declarations.enum_top_level import EnumTopLevel
from slither.core.declarations.function_top_level import FunctionTopLevel from slither.core.declarations.function_top_level import FunctionTopLevel
from slither.core.declarations.structure_top_level import StructureTopLevel from slither.core.declarations.structure_top_level import StructureTopLevel
@ -46,7 +46,7 @@ class SlitherCompilationUnit(Context):
self._using_for_top_level: List[UsingForTopLevel] = [] self._using_for_top_level: List[UsingForTopLevel] = []
self._pragma_directives: List[Pragma] = [] self._pragma_directives: List[Pragma] = []
self._import_directives: List[Import] = [] self._import_directives: List[Import] = []
self._custom_errors: List[CustomError] = [] self._custom_errors: List[CustomErrorTopLevel] = []
self._user_defined_value_types: Dict[str, TypeAliasTopLevel] = {} self._user_defined_value_types: Dict[str, TypeAliasTopLevel] = {}
self._all_functions: Set[Function] = set() self._all_functions: Set[Function] = set()
@ -128,7 +128,7 @@ class SlitherCompilationUnit(Context):
"""list(Contract): List of contracts that are derived and not inherited.""" """list(Contract): List of contracts that are derived and not inherited."""
inheritances = [x.inheritance for x in self.contracts] inheritances = [x.inheritance for x in self.contracts]
inheritance = [item for sublist in inheritances for item in sublist] inheritance = [item for sublist in inheritances for item in sublist]
return [c for c in self.contracts if c not in inheritance and not c.is_top_level] return [c for c in self.contracts if c not in inheritance]
def get_contract_from_name(self, contract_name: Union[str, Constant]) -> List[Contract]: def get_contract_from_name(self, contract_name: Union[str, Constant]) -> List[Contract]:
""" """
@ -216,7 +216,7 @@ class SlitherCompilationUnit(Context):
return self._using_for_top_level return self._using_for_top_level
@property @property
def custom_errors(self) -> List[CustomError]: def custom_errors(self) -> List[CustomErrorTopLevel]:
return self._custom_errors return self._custom_errors
@property @property

@ -100,8 +100,6 @@ class Contract(SourceMapping): # pylint: disable=too-many-public-methods
self._is_upgradeable_proxy: Optional[bool] = None self._is_upgradeable_proxy: Optional[bool] = None
self._upgradeable_version: Optional[str] = None self._upgradeable_version: Optional[str] = None
self.is_top_level = False # heavily used, so no @property
self._initial_state_variables: List["StateVariable"] = [] # ssa self._initial_state_variables: List["StateVariable"] = [] # ssa
self._is_incorrectly_parsed: bool = False self._is_incorrectly_parsed: bool = False

@ -1,8 +1,8 @@
from typing import List, TYPE_CHECKING, Optional, Type from typing import List, TYPE_CHECKING, Optional, Type
from slither.core.solidity_types import UserDefinedType
from slither.core.source_mapping.source_mapping import SourceMapping from slither.core.source_mapping.source_mapping import SourceMapping
from slither.core.variables.local_variable import LocalVariable from slither.core.variables.local_variable import LocalVariable
from slither.utils.type import is_underlying_type_address
if TYPE_CHECKING: if TYPE_CHECKING:
from slither.core.compilation_unit import SlitherCompilationUnit from slither.core.compilation_unit import SlitherCompilationUnit
@ -43,10 +43,7 @@ class CustomError(SourceMapping):
@staticmethod @staticmethod
def _convert_type_for_solidity_signature(t: Optional[Type]) -> str: def _convert_type_for_solidity_signature(t: Optional[Type]) -> str:
# pylint: disable=import-outside-toplevel if is_underlying_type_address(t):
from slither.core.declarations import Contract
if isinstance(t, UserDefinedType) and isinstance(t.type, Contract):
return "address" return "address"
return str(t) return str(t)

@ -201,6 +201,10 @@ class SolidityCustomRevert(SolidityFunction):
self._custom_error = custom_error self._custom_error = custom_error
self._return_type: List[Union[TypeInformation, ElementaryType]] = [] self._return_type: List[Union[TypeInformation, ElementaryType]] = []
@property
def custom_error(self) -> CustomError:
return self._custom_error
def __eq__(self, other: Any) -> bool: def __eq__(self, other: Any) -> bool:
return ( return (
self.__class__ == other.__class__ self.__class__ == other.__class__

@ -89,3 +89,5 @@ from .functions.protected_variable import ProtectedVariables
from .functions.permit_domain_signature_collision import DomainSeparatorCollision from .functions.permit_domain_signature_collision import DomainSeparatorCollision
from .functions.codex import Codex from .functions.codex import Codex
from .functions.cyclomatic_complexity import CyclomaticComplexity from .functions.cyclomatic_complexity import CyclomaticComplexity
from .statements.incorrect_using_for import IncorrectUsingFor
from .operations.encode_packed import EncodePackedCollision

@ -52,7 +52,9 @@ The shift statement will right-shift the constant 8 by `a` bits"""
BinaryType.LEFT_SHIFT, BinaryType.LEFT_SHIFT,
BinaryType.RIGHT_SHIFT, BinaryType.RIGHT_SHIFT,
]: ]:
if isinstance(ir.variable_left, Constant): if isinstance(ir.variable_left, Constant) and not isinstance(
ir.variable_right, Constant
):
info: DETECTOR_INFO = [ info: DETECTOR_INFO = [
f, f,
" contains an incorrect shift operation: ", " contains an incorrect shift operation: ",

@ -0,0 +1,104 @@
"""
Module detecting usage of more than one dynamic type in abi.encodePacked() arguments which could lead to collision
"""
from slither.detectors.abstract_detector import AbstractDetector, DetectorClassification
from slither.core.declarations.solidity_variables import SolidityFunction
from slither.slithir.operations import SolidityCall
from slither.analyses.data_dependency.data_dependency import is_tainted
from slither.core.solidity_types import ElementaryType
from slither.core.solidity_types import ArrayType
def _is_dynamic_type(arg):
"""
Args:
arg (function argument)
Returns:
Bool
"""
if isinstance(arg.type, ElementaryType) and (arg.type.name in ["string", "bytes"]):
return True
if isinstance(arg.type, ArrayType) and arg.type.length is None:
return True
return False
def _detect_abi_encodePacked_collision(contract):
"""
Args:
contract (Contract)
Returns:
list((Function), (list (Node)))
"""
ret = []
# pylint: disable=too-many-nested-blocks
for f in contract.functions_and_modifiers_declared:
for n in f.nodes:
for ir in n.irs:
if isinstance(ir, SolidityCall) and ir.function == SolidityFunction(
"abi.encodePacked()"
):
dynamic_type_count = 0
for arg in ir.arguments:
if is_tainted(arg, contract) and _is_dynamic_type(arg):
dynamic_type_count += 1
elif dynamic_type_count > 1:
ret.append((f, n))
dynamic_type_count = 0
else:
dynamic_type_count = 0
if dynamic_type_count > 1:
ret.append((f, n))
return ret
class EncodePackedCollision(AbstractDetector):
"""
Detect usage of more than one dynamic type in abi.encodePacked() arguments which could to collision
"""
ARGUMENT = "encode-packed-collision"
HELP = "ABI encodePacked Collision"
IMPACT = DetectorClassification.HIGH
CONFIDENCE = DetectorClassification.HIGH
WIKI = (
"https://github.com/crytic/slither/wiki/Detector-Documentation#abi-encodePacked-collision"
)
WIKI_TITLE = "ABI encodePacked Collision"
WIKI_DESCRIPTION = """Detect collision due to dynamic type usages in `abi.encodePacked`"""
WIKI_EXPLOIT_SCENARIO = """
```solidity
contract Sign {
function get_hash_for_signature(string name, string doc) external returns(bytes32) {
return keccak256(abi.encodePacked(name, doc));
}
}
```
Bob calls `get_hash_for_signature` with (`bob`, `This is the content`). The hash returned is used as an ID.
Eve creates a collision with the ID using (`bo`, `bThis is the content`) and compromises the system.
"""
WIKI_RECOMMENDATION = """Do not use more than one dynamic type in `abi.encodePacked()`
(see the [Solidity documentation](https://solidity.readthedocs.io/en/v0.5.10/abi-spec.html?highlight=abi.encodePacked#non-standard-packed-modeDynamic)).
Use `abi.encode()`, preferably."""
def _detect(self):
"""Detect usage of more than one dynamic type in abi.encodePacked(..) arguments which could lead to collision"""
results = []
for c in self.compilation_unit.contracts:
values = _detect_abi_encodePacked_collision(c)
for func, node in values:
info = [
func,
" calls abi.encodePacked() with multiple dynamic arguments:\n\t- ",
node,
"\n",
]
json = self.generate_result(info)
results.append(json)
return results

@ -3,7 +3,7 @@ Module detecting unused return values from external calls
""" """
from typing import List from typing import List
from slither.core.cfg.node import Node from slither.core.cfg.node import Node, NodeType
from slither.core.declarations import Function from slither.core.declarations import Function
from slither.core.declarations.function_contract import FunctionContract from slither.core.declarations.function_contract import FunctionContract
from slither.core.variables.state_variable import StateVariable from slither.core.variables.state_variable import StateVariable
@ -12,8 +12,8 @@ from slither.detectors.abstract_detector import (
DetectorClassification, DetectorClassification,
DETECTOR_INFO, DETECTOR_INFO,
) )
from slither.slithir.operations import HighLevelCall from slither.slithir.operations import HighLevelCall, Assignment, Unpack, Operation
from slither.slithir.operations.operation import Operation from slither.slithir.variables import TupleVariable
from slither.utils.output import Output from slither.utils.output import Output
@ -50,13 +50,18 @@ contract MyConc{
WIKI_RECOMMENDATION = "Ensure that all the return values of the function calls are used." WIKI_RECOMMENDATION = "Ensure that all the return values of the function calls are used."
def _is_instance(self, ir: Operation) -> bool: # pylint: disable=no-self-use def _is_instance(self, ir: Operation) -> bool: # pylint: disable=no-self-use
return isinstance(ir, HighLevelCall) and ( return (
( isinstance(ir, HighLevelCall)
isinstance(ir.function, Function) and (
and ir.function.solidity_signature (
not in ["transfer(address,uint256)", "transferFrom(address,address,uint256)"] isinstance(ir.function, Function)
and ir.function.solidity_signature
not in ["transfer(address,uint256)", "transferFrom(address,address,uint256)"]
)
or not isinstance(ir.function, Function)
) )
or not isinstance(ir.function, Function) or ir.node.type == NodeType.TRY
and isinstance(ir, (Assignment, Unpack))
) )
def detect_unused_return_values( def detect_unused_return_values(
@ -71,18 +76,27 @@ contract MyConc{
""" """
values_returned = [] values_returned = []
nodes_origin = {} nodes_origin = {}
# pylint: disable=too-many-nested-blocks
for n in f.nodes: for n in f.nodes:
for ir in n.irs: for ir in n.irs:
if self._is_instance(ir): if self._is_instance(ir):
# if a return value is stored in a state variable, it's ok # if a return value is stored in a state variable, it's ok
if ir.lvalue and not isinstance(ir.lvalue, StateVariable): if ir.lvalue and not isinstance(ir.lvalue, StateVariable):
values_returned.append(ir.lvalue) values_returned.append((ir.lvalue, None))
nodes_origin[ir.lvalue] = ir nodes_origin[ir.lvalue] = ir
if isinstance(ir.lvalue, TupleVariable):
# we iterate the number of elements the tuple has
# and add a (variable, index) in values_returned for each of them
for index in range(len(ir.lvalue.type)):
values_returned.append((ir.lvalue, index))
for read in ir.read: for read in ir.read:
if read in values_returned: remove = (read, ir.index) if isinstance(ir, Unpack) else (read, None)
values_returned.remove(read) if remove in values_returned:
# this is needed to remove the tuple variable when the first time one of its element is used
return [nodes_origin[value].node for value in values_returned] if remove[1] is not None and (remove[0], None) in values_returned:
values_returned.remove((remove[0], None))
values_returned.remove(remove)
return [nodes_origin[value].node for (value, _) in values_returned]
def _detect(self) -> List[Output]: def _detect(self) -> List[Output]:
"""Detect high level calls which return a value that are never used""" """Detect high level calls which return a value that are never used"""

@ -29,24 +29,45 @@ class ReentrancyEvent(Reentrancy):
# region wiki_description # region wiki_description
WIKI_DESCRIPTION = """ WIKI_DESCRIPTION = """
Detection of the [reentrancy bug](https://github.com/trailofbits/not-so-smart-contracts/tree/master/reentrancy). Detects [reentrancies](https://github.com/trailofbits/not-so-smart-contracts/tree/master/reentrancy) that allow manipulation of the order or value of events."""
Only report reentrancies leading to out-of-order events."""
# endregion wiki_description # endregion wiki_description
# region wiki_exploit_scenario # region wiki_exploit_scenario
WIKI_EXPLOIT_SCENARIO = """ WIKI_EXPLOIT_SCENARIO = """
```solidity ```solidity
function bug(Called d){ contract ReentrantContract {
function f() external {
if (BugReentrancyEvents(msg.sender).counter() == 1) {
BugReentrancyEvents(msg.sender).count(this);
}
}
}
contract Counter {
uint public counter;
event Counter(uint);
}
contract BugReentrancyEvents is Counter {
function count(ReentrantContract d) external {
counter += 1; counter += 1;
d.f(); d.f();
emit Counter(counter); emit Counter(counter);
} }
}
contract NoReentrancyEvents is Counter {
function count(ReentrantContract d) external {
counter += 1;
emit Counter(counter);
d.f();
}
}
``` ```
If `d.()` re-enters, the `Counter` events will be shown in an incorrect order, which might lead to issues for third parties.""" If the external call `d.f()` re-enters `BugReentrancyEvents`, the `Counter` events will be incorrect (`Counter(2)`, `Counter(2)`) whereas `NoReentrancyEvents` will correctly emit
(`Counter(1)`, `Counter(2)`). This may cause issues for offchain components that rely on the values of events e.g. checking for the amount deposited to a bridge."""
# endregion wiki_exploit_scenario # endregion wiki_exploit_scenario
WIKI_RECOMMENDATION = "Apply the [`check-effects-interactions` pattern](http://solidity.readthedocs.io/en/v0.4.21/security-considerations.html#re-entrancy)." WIKI_RECOMMENDATION = "Apply the [`check-effects-interactions` pattern](https://docs.soliditylang.org/en/latest/security-considerations.html#re-entrancy)."
STANDARD_JSON = False STANDARD_JSON = False

@ -31,6 +31,7 @@ from slither.slithir.variables.constant import Constant
from slither.slithir.variables.local_variable import LocalIRVariable from slither.slithir.variables.local_variable import LocalIRVariable
from slither.slithir.variables.temporary_ssa import TemporaryVariableSSA from slither.slithir.variables.temporary_ssa import TemporaryVariableSSA
from slither.utils.output import Output from slither.utils.output import Output
from slither.utils.type import is_underlying_type_address
class IncorrectStrictEquality(AbstractDetector): class IncorrectStrictEquality(AbstractDetector):
@ -72,6 +73,19 @@ contract Crowdsale{
def is_direct_comparison(ir: Operation) -> bool: def is_direct_comparison(ir: Operation) -> bool:
return isinstance(ir, Binary) and ir.type == BinaryType.EQUAL return isinstance(ir, Binary) and ir.type == BinaryType.EQUAL
@staticmethod
def is_not_comparing_addresses(ir: Binary) -> bool:
"""
Comparing addresses strictly should not be flagged.
"""
if is_underlying_type_address(ir.variable_left.type) and is_underlying_type_address(
ir.variable_right.type
):
return False
return True
@staticmethod @staticmethod
def is_any_tainted( def is_any_tainted(
variables: List[ variables: List[
@ -108,7 +122,6 @@ contract Crowdsale{
): ):
taints.append(ir.lvalue) taints.append(ir.lvalue)
if isinstance(ir, HighLevelCall): if isinstance(ir, HighLevelCall):
# print(ir.function.full_name)
if ( if (
isinstance(ir.function, Function) isinstance(ir.function, Function)
and ir.function.full_name == "balanceOf(address)" and ir.function.full_name == "balanceOf(address)"
@ -125,7 +138,6 @@ contract Crowdsale{
if isinstance(ir, Assignment): if isinstance(ir, Assignment):
if ir.rvalue in self.sources_taint: if ir.rvalue in self.sources_taint:
taints.append(ir.lvalue) taints.append(ir.lvalue)
return taints return taints
# Retrieve all tainted (node, function) pairs # Retrieve all tainted (node, function) pairs
@ -145,7 +157,12 @@ contract Crowdsale{
for ir in node.irs_ssa: for ir in node.irs_ssa:
# Filter to only tainted equality (==) comparisons # Filter to only tainted equality (==) comparisons
if self.is_direct_comparison(ir) and self.is_any_tainted(ir.used, taints, func): if (
self.is_direct_comparison(ir)
# Filter out address comparisons which may occur due to lack of field sensitivity in data dependency
and self.is_not_comparing_addresses(ir)
and self.is_any_tainted(ir.used, taints, func)
):
if func not in results: if func not in results:
results[func] = [] results[func] = []
results[func].append(node) results[func].append(node)

@ -0,0 +1,223 @@
from typing import List
from slither.core.declarations import Contract, Structure, Enum
from slither.core.declarations.using_for_top_level import UsingForTopLevel
from slither.core.solidity_types import (
UserDefinedType,
Type,
ElementaryType,
TypeAlias,
MappingType,
ArrayType,
)
from slither.core.solidity_types.elementary_type import Uint, Int, Byte
from slither.detectors.abstract_detector import (
AbstractDetector,
DetectorClassification,
DETECTOR_INFO,
)
from slither.utils.output import Output
def _is_correctly_used(type_: Type, library: Contract) -> bool:
"""
Checks if a `using library for type_` statement is used correctly (that is, does library contain any function
with type_ as the first argument).
"""
for f in library.functions:
if len(f.parameters) == 0:
continue
if f.parameters[0].type and not _implicitly_convertible_to(type_, f.parameters[0].type):
continue
return True
return False
def _implicitly_convertible_to(type1: Type, type2: Type) -> bool:
"""
Returns True if type1 may be implicitly converted to type2.
"""
if isinstance(type1, TypeAlias) or isinstance(type2, TypeAlias):
if isinstance(type1, TypeAlias) and isinstance(type2, TypeAlias):
return type1.type == type2.type
return False
if isinstance(type1, UserDefinedType) and isinstance(type2, UserDefinedType):
if isinstance(type1.type, Contract) and isinstance(type2.type, Contract):
return _implicitly_convertible_to_for_contracts(type1.type, type2.type)
if isinstance(type1.type, Structure) and isinstance(type2.type, Structure):
return type1.type.canonical_name == type2.type.canonical_name
if isinstance(type1.type, Enum) and isinstance(type2.type, Enum):
return type1.type.canonical_name == type2.type.canonical_name
if isinstance(type1, ElementaryType) and isinstance(type2, ElementaryType):
return _implicitly_convertible_to_for_elementary_types(type1, type2)
if isinstance(type1, MappingType) and isinstance(type2, MappingType):
return _implicitly_convertible_to_for_mappings(type1, type2)
if isinstance(type1, ArrayType) and isinstance(type2, ArrayType):
return _implicitly_convertible_to_for_arrays(type1, type2)
return False
def _implicitly_convertible_to_for_arrays(type1: ArrayType, type2: ArrayType) -> bool:
"""
Returns True if type1 may be implicitly converted to type2.
"""
return _implicitly_convertible_to(type1.type, type2.type)
def _implicitly_convertible_to_for_mappings(type1: MappingType, type2: MappingType) -> bool:
"""
Returns True if type1 may be implicitly converted to type2.
"""
return type1.type_from == type2.type_from and type1.type_to == type2.type_to
def _implicitly_convertible_to_for_elementary_types(
type1: ElementaryType, type2: ElementaryType
) -> bool:
"""
Returns True if type1 may be implicitly converted to type2.
"""
if type1.type == "bool" and type2.type == "bool":
return True
if type1.type == "string" and type2.type == "string":
return True
if type1.type == "bytes" and type2.type == "bytes":
return True
if type1.type == "address" and type2.type == "address":
return _implicitly_convertible_to_for_addresses(type1, type2)
if type1.type in Uint and type2.type in Uint:
return _implicitly_convertible_to_for_uints(type1, type2)
if type1.type in Int and type2.type in Int:
return _implicitly_convertible_to_for_ints(type1, type2)
if (
type1.type != "bytes"
and type2.type != "bytes"
and type1.type in Byte
and type2.type in Byte
):
return _implicitly_convertible_to_for_bytes(type1, type2)
return False
def _implicitly_convertible_to_for_bytes(type1: ElementaryType, type2: ElementaryType) -> bool:
"""
Returns True if type1 may be implicitly converted to type2 assuming they are both bytes.
"""
assert type1.type in Byte and type2.type in Byte
assert type1.size is not None
assert type2.size is not None
return type1.size <= type2.size
def _implicitly_convertible_to_for_addresses(type1: ElementaryType, type2: ElementaryType) -> bool:
"""
Returns True if type1 may be implicitly converted to type2 assuming they are both addresses.
"""
assert type1.type == "address" and type2.type == "address"
# payable attribute to be implemented; for now, always return True
return True
def _implicitly_convertible_to_for_ints(type1: ElementaryType, type2: ElementaryType) -> bool:
"""
Returns True if type1 may be implicitly converted to type2 assuming they are both ints.
"""
assert type1.type in Int and type2.type in Int
assert type1.size is not None
assert type2.size is not None
return type1.size <= type2.size
def _implicitly_convertible_to_for_uints(type1: ElementaryType, type2: ElementaryType) -> bool:
"""
Returns True if type1 may be implicitly converted to type2 assuming they are both uints.
"""
assert type1.type in Uint and type2.type in Uint
assert type1.size is not None
assert type2.size is not None
return type1.size <= type2.size
def _implicitly_convertible_to_for_contracts(contract1: Contract, contract2: Contract) -> bool:
"""
Returns True if contract1 may be implicitly converted to contract2.
"""
return contract1 == contract2 or contract2 in contract1.inheritance
class IncorrectUsingFor(AbstractDetector):
"""
Detector for incorrect using-for statement usage.
"""
ARGUMENT = "incorrect-using-for"
HELP = "Detects using-for statement usage when no function from a given library matches a given type"
IMPACT = DetectorClassification.INFORMATIONAL
CONFIDENCE = DetectorClassification.HIGH
WIKI = "https://github.com/crytic/slither/wiki/Detector-Documentation#incorrect-using-for-usage"
WIKI_TITLE = "Incorrect usage of using-for statement"
WIKI_DESCRIPTION = (
"In Solidity, it is possible to use libraries for certain types, by the `using-for` statement "
"(`using <library> for <type>`). However, the Solidity compiler doesn't check whether a given "
"library has at least one function matching a given type. If it doesn't, such a statement has "
"no effect and may be confusing. "
)
# region wiki_exploit_scenario
WIKI_EXPLOIT_SCENARIO = """
```solidity
library L {
function f(bool) public pure {}
}
using L for uint;
```
Such a code will compile despite the fact that `L` has no function with `uint` as its first argument."""
# endregion wiki_exploit_scenario
WIKI_RECOMMENDATION = (
"Make sure that the libraries used in `using-for` statements have at least one function "
"matching a type used in these statements. "
)
def _append_result(
self, results: List[Output], uf: UsingForTopLevel, type_: Type, library: Contract
) -> None:
info: DETECTOR_INFO = [
f"using-for statement at {uf.source_mapping} is incorrect - no matching function for {type_} found in ",
library,
".\n",
]
res = self.generate_result(info)
results.append(res)
def _detect(self) -> List[Output]:
results: List[Output] = []
for uf in self.compilation_unit.using_for_top_level:
# UsingForTopLevel.using_for is a dict with a single entry, which is mapped to a list of functions/libraries
# the following code extracts the type from using-for and skips using-for statements with functions
type_ = list(uf.using_for.keys())[0]
for lib_or_fcn in uf.using_for[type_]:
# checking for using-for with functions is already performed by the compiler; we only consider libraries
if isinstance(lib_or_fcn, UserDefinedType):
lib_or_fcn_type = lib_or_fcn.type
if (
isinstance(type_, Type)
and isinstance(lib_or_fcn_type, Contract)
and not _is_correctly_used(type_, lib_or_fcn_type)
):
self._append_result(results, uf, type_, lib_or_fcn_type)
return results

@ -6,7 +6,7 @@
""" """
from typing import List from typing import List
from slither.core.cfg.node import Node from slither.core.cfg.node import Node, NodeType
from slither.core.declarations.function_contract import FunctionContract from slither.core.declarations.function_contract import FunctionContract
from slither.detectors.abstract_detector import AbstractDetector, DetectorClassification from slither.detectors.abstract_detector import AbstractDetector, DetectorClassification
from slither.utils.output import Output from slither.utils.output import Output
@ -64,6 +64,15 @@ Bob calls `transfer`. As a result, all Ether is sent to the address `0x0` and is
self.visited_all_paths[node] = list(set(self.visited_all_paths[node] + fathers_context)) self.visited_all_paths[node] = list(set(self.visited_all_paths[node] + fathers_context))
# Remove a local variable declared in a for loop header
if (
node.type == NodeType.VARIABLE
and len(node.sons) == 1 # Should always be true for a node that has a STARTLOOP son
and node.sons[0].type == NodeType.STARTLOOP
):
if node.variable_declaration in fathers_context:
fathers_context.remove(node.variable_declaration)
if self.key in node.context: if self.key in node.context:
fathers_context += node.context[self.key] fathers_context += node.context[self.key]

@ -17,7 +17,7 @@ class VarReadUsingThis(AbstractDetector):
IMPACT = DetectorClassification.OPTIMIZATION IMPACT = DetectorClassification.OPTIMIZATION
CONFIDENCE = DetectorClassification.HIGH CONFIDENCE = DetectorClassification.HIGH
WIKI = "https://github.com/crytic/slither/wiki/Vulnerabilities-Description#public-variable-read-in-external-context" WIKI = "https://github.com/crytic/slither/wiki/Detector-Documentation#public-variable-read-in-external-context"
WIKI_TITLE = "Public variable read in external context" WIKI_TITLE = "Public variable read in external context"
WIKI_DESCRIPTION = "The contract reads its own variable using `this`, adding overhead of an unnecessary STATICCALL." WIKI_DESCRIPTION = "The contract reads its own variable using `this`, adding overhead of an unnecessary STATICCALL."

@ -47,8 +47,6 @@ class PrinterWrittenVariablesAndAuthorization(AbstractPrinter):
txt = "" txt = ""
all_tables = [] all_tables = []
for contract in self.contracts: # type: ignore for contract in self.contracts: # type: ignore
if contract.is_top_level:
continue
txt += f"\nContract {contract.name}\n" txt += f"\nContract {contract.name}\n"
table = MyPrettyTable( table = MyPrettyTable(
["Function", "State variables written", "Conditions on msg.sender"] ["Function", "State variables written", "Conditions on msg.sender"]

@ -19,8 +19,6 @@ class CFG(AbstractPrinter):
info = "" info = ""
all_files = [] all_files = []
for contract in self.contracts: # type: ignore for contract in self.contracts: # type: ignore
if contract.is_top_level:
continue
for function in contract.functions + list(contract.modifiers): for function in contract.functions + list(contract.modifiers):
if filename: if filename:
new_filename = f"{filename}-{contract.name}-{function.full_name}.dot" new_filename = f"{filename}-{contract.name}-{function.full_name}.dot"

@ -36,8 +36,6 @@ class PrinterInheritance(AbstractPrinter):
result = {"child_to_base": {}} result = {"child_to_base": {}}
for child in self.contracts: for child in self.contracts:
if child.is_top_level:
continue
info += blue(f"\n+ {child.name}\n") info += blue(f"\n+ {child.name}\n")
result["child_to_base"][child.name] = {"immediate": [], "not_immediate": []} result["child_to_base"][child.name] = {"immediate": [], "not_immediate": []}
if child.inheritance: if child.inheritance:
@ -58,8 +56,6 @@ class PrinterInheritance(AbstractPrinter):
result["base_to_child"] = {} result["base_to_child"] = {}
for base in self.contracts: for base in self.contracts:
if base.is_top_level:
continue
info += green(f"\n+ {base.name}") + "\n" info += green(f"\n+ {base.name}") + "\n"
children = list(self._get_child_contracts(base)) children = list(self._get_child_contracts(base))

@ -194,8 +194,6 @@ class PrinterInheritanceGraph(AbstractPrinter):
content = 'digraph "" {\n' content = 'digraph "" {\n'
for c in self.contracts: for c in self.contracts:
if c.is_top_level:
continue
content += self._summary(c) + "\n" content += self._summary(c) + "\n"
content += "}" content += "}"

@ -28,9 +28,6 @@ class ContractSummary(AbstractPrinter):
all_contracts = [] all_contracts = []
for c in self.contracts: for c in self.contracts:
if c.is_top_level:
continue
is_upgradeable_proxy = c.is_upgradeable_proxy is_upgradeable_proxy = c.is_upgradeable_proxy
is_upgradeable = c.is_upgradeable is_upgradeable = c.is_upgradeable

@ -40,8 +40,6 @@ class DataDependency(AbstractPrinter):
txt = "" txt = ""
for c in self.contracts: for c in self.contracts:
if c.is_top_level:
continue
txt += f"\nContract {c.name}\n" txt += f"\nContract {c.name}\n"
table = MyPrettyTable(["Variable", "Dependencies"]) table = MyPrettyTable(["Variable", "Dependencies"])
for v in c.state_variables: for v in c.state_variables:

@ -33,8 +33,6 @@ class FunctionSummary(AbstractPrinter):
all_txt = "" all_txt = ""
for c in self.contracts: for c in self.contracts:
if c.is_top_level:
continue
(name, inheritance, var, func_summaries, modif_summaries) = c.get_summary() (name, inheritance, var, func_summaries, modif_summaries) = c.get_summary()
txt = f"\nContract {name}" txt = f"\nContract {name}"
txt += "\nContract vars: " + str(var) txt += "\nContract vars: " + str(var)

@ -205,7 +205,7 @@ class PrinterHumanSummary(AbstractPrinter):
def _number_contracts(self): def _number_contracts(self):
if self.slither.crytic_compile is None: if self.slither.crytic_compile is None:
return len(self.slither.contracts), 0, 0 return len(self.slither.contracts), 0, 0
contracts = [c for c in self.slither.contracts if not c.is_top_level] contracts = self.slither.contracts
deps = [c for c in contracts if c.is_from_dependency()] deps = [c for c in contracts if c.is_from_dependency()]
tests = [c for c in contracts if c.is_test] tests = [c for c in contracts if c.is_test]
return len(contracts) - len(deps) - len(tests), len(deps), len(tests) return len(contracts) - len(deps) - len(tests), len(deps), len(tests)

@ -36,8 +36,6 @@ class PrinterSlithIR(AbstractPrinter):
txt = "" txt = ""
for compilation_unit in self.slither.compilation_units: for compilation_unit in self.slither.compilation_units:
for contract in compilation_unit.contracts: for contract in compilation_unit.contracts:
if contract.is_top_level:
continue
txt += f"Contract {contract.name}\n" txt += f"Contract {contract.name}\n"
for function in contract.functions: for function in contract.functions:
txt += f'\tFunction {function.canonical_name} {"" if function.is_shadowed else "(*)"}\n' txt += f'\tFunction {function.canonical_name} {"" if function.is_shadowed else "(*)"}\n'

@ -21,8 +21,6 @@ class PrinterSlithIRSSA(AbstractPrinter):
txt = "" txt = ""
for contract in self.contracts: for contract in self.contracts:
if contract.is_top_level:
continue
txt += f"Contract {contract.name}" + "\n" txt += f"Contract {contract.name}" + "\n"
for function in contract.functions: for function in contract.functions:
txt += f"\tFunction {function.canonical_name}" + "\n" txt += f"\tFunction {function.canonical_name}" + "\n"

@ -10,8 +10,6 @@ from slither.utils.myprettytable import MyPrettyTable
def _use_modifier(function: Function, modifier_name: str = "whenNotPaused") -> bool: def _use_modifier(function: Function, modifier_name: str = "whenNotPaused") -> bool:
if function.is_constructor or function.view or function.pure:
return False
for internal_call in function.all_internal_calls(): for internal_call in function.all_internal_calls():
if isinstance(internal_call, SolidityFunction): if isinstance(internal_call, SolidityFunction):
@ -23,7 +21,7 @@ def _use_modifier(function: Function, modifier_name: str = "whenNotPaused") -> b
class PrinterWhenNotPaused(AbstractPrinter): class PrinterWhenNotPaused(AbstractPrinter):
ARGUMENT = "pausable" ARGUMENT = "not-pausable"
HELP = "Print functions that do not use whenNotPaused" HELP = "Print functions that do not use whenNotPaused"
WIKI = "https://github.com/trailofbits/slither/wiki/Printer-documentation#when-not-paused" WIKI = "https://github.com/trailofbits/slither/wiki/Printer-documentation#when-not-paused"
@ -46,6 +44,8 @@ class PrinterWhenNotPaused(AbstractPrinter):
table = MyPrettyTable(["Name", "Use whenNotPaused"]) table = MyPrettyTable(["Name", "Use whenNotPaused"])
for function in contract.functions_entry_points: for function in contract.functions_entry_points:
if function.is_constructor or function.view or function.pure:
continue
status = "X" if _use_modifier(function, modifier_name) else "" status = "X" if _use_modifier(function, modifier_name) else ""
table.add_row([function.solidity_signature, status]) table.add_row([function.solidity_signature, status])

@ -1363,7 +1363,14 @@ def convert_to_pop(ir: HighLevelCall, node: "Node") -> List[Operation]:
# TODO the following is equivalent to length.points_to = arr # TODO the following is equivalent to length.points_to = arr
# Should it be removed? # Should it be removed?
ir_length.lvalue.points_to = arr ir_length.lvalue.points_to = arr
element_to_delete.set_type(ElementaryType("uint256")) # Note bytes is an ElementaryType not ArrayType so in that case we use ir.destination.type
# while in other cases such as uint256[] (ArrayType) we use ir.destination.type.type
# in this way we will have the type always set to the corresponding ElementaryType
element_to_delete.set_type(
ir.destination.type
if isinstance(ir.destination.type, ElementaryType)
else ir.destination.type.type
)
ir_assign_element_to_delete.set_expression(ir.expression) ir_assign_element_to_delete.set_expression(ir.expression)
ir_assign_element_to_delete.set_node(ir.node) ir_assign_element_to_delete.set_node(ir.node)
ret.append(ir_assign_element_to_delete) ret.append(ir_assign_element_to_delete)

@ -50,5 +50,5 @@ class Assignment(OperationWithLValue):
points = lvalue.points_to points = lvalue.points_to
while isinstance(points, ReferenceVariable): while isinstance(points, ReferenceVariable):
points = points.points_to points = points.points_to
return f"{lvalue} (->{points}) := {self.rvalue}({self.rvalue.type})" return f"{lvalue}({lvalue.type}) (->{points}) := {self.rvalue}({self.rvalue.type})"
return f"{lvalue}({lvalue.type}) := {self.rvalue}({self.rvalue.type})" return f"{lvalue}({lvalue.type}) := {self.rvalue}({self.rvalue.type})"

@ -94,7 +94,6 @@ class BinaryType(Enum):
return self in [ return self in [
BinaryType.POWER, BinaryType.POWER,
BinaryType.MULTIPLICATION, BinaryType.MULTIPLICATION,
BinaryType.MODULO,
BinaryType.ADDITION, BinaryType.ADDITION,
BinaryType.SUBTRACTION, BinaryType.SUBTRACTION,
BinaryType.DIVISION, BinaryType.DIVISION,

@ -38,4 +38,7 @@ class NewArray(Call, OperationWithLValue):
def __str__(self): def __str__(self):
args = [str(a) for a in self.arguments] args = [str(a) for a in self.arguments]
return f"{self.lvalue} = new {self.array_type}{'[]' * self.depth}({','.join(args)})" lvalue = self.lvalue
return (
f"{lvalue}({lvalue.type}) = new {self.array_type}{'[]' * self.depth}({','.join(args)})"
)

@ -104,4 +104,5 @@ class NewContract(Call, OperationWithLValue): # pylint: disable=too-many-instan
if self.call_salt: if self.call_salt:
options += f"salt:{self.call_salt} " options += f"salt:{self.call_salt} "
args = [str(a) for a in self.arguments] args = [str(a) for a in self.arguments]
return f"{self.lvalue} = new {self.contract_name}({','.join(args)}) {options}" lvalue = self.lvalue
return f"{lvalue}({lvalue.type}) = new {self.contract_name}({','.join(args)}) {options}"

@ -39,4 +39,5 @@ class NewStructure(Call, OperationWithLValue):
def __str__(self): def __str__(self):
args = [str(a) for a in self.arguments] args = [str(a) for a in self.arguments]
return f"{self.lvalue} = new {self.structure_name}({','.join(args)})" lvalue = self.lvalue
return f"{lvalue}({lvalue.type}) = new {self.structure_name}({','.join(args)})"

@ -58,7 +58,7 @@ class Unary(OperationWithLValue):
@property @property
def type_str(self): def type_str(self):
return self._type.value return str(self._type)
def __str__(self): def __str__(self):
return f"{self.lvalue} = {self.type_str} {self.rvalue} " return f"{self.lvalue} = {self.type_str} {self.rvalue} "

@ -170,10 +170,9 @@ class ContractSolc(CallerContextExpression):
elif attributes["contractKind"] == "library": elif attributes["contractKind"] == "library":
self._contract.is_library = True self._contract.is_library = True
self._contract.contract_kind = attributes["contractKind"] self._contract.contract_kind = attributes["contractKind"]
self._contract.is_fully_implemented = attributes["fullyImplemented"]
self._contract.is_fully_implemented = attributes["fullyImplemented"]
self._linearized_base_contracts = attributes["linearizedBaseContracts"] self._linearized_base_contracts = attributes["linearizedBaseContracts"]
# self._contract.fullyImplemented = attributes["fullyImplemented"]
# Parse base contract information # Parse base contract information
self._parse_base_contract_info() self._parse_base_contract_info()

@ -1,5 +1,5 @@
import logging import logging
from typing import Dict, Optional, Union, List, TYPE_CHECKING, Tuple from typing import Dict, Optional, Union, List, TYPE_CHECKING, Tuple, Set
from slither.core.cfg.node import NodeType, link_nodes, insert_node, Node from slither.core.cfg.node import NodeType, link_nodes, insert_node, Node
from slither.core.cfg.scope import Scope from slither.core.cfg.scope import Scope
@ -774,6 +774,7 @@ class FunctionSolc(CallerContextExpression):
"nodeType": "Identifier", "nodeType": "Identifier",
"src": v["src"], "src": v["src"],
"name": v["name"], "name": v["name"],
"referencedDeclaration": v["id"],
"typeDescriptions": {"typeString": v["typeDescriptions"]["typeString"]}, "typeDescriptions": {"typeString": v["typeDescriptions"]["typeString"]},
} }
var_identifiers.append(identifier) var_identifiers.append(identifier)
@ -1150,15 +1151,16 @@ class FunctionSolc(CallerContextExpression):
if end_node: if end_node:
for son in node.sons: for son in node.sons:
if son.type == NodeType.CATCH: if son.type == NodeType.CATCH:
self._fix_catch(son, end_node) self._fix_catch(son, end_node, set())
def _fix_catch(self, node: Node, end_node: Node) -> None: def _fix_catch(self, node: Node, end_node: Node, visited: Set[Node]) -> None:
if not node.sons: if not node.sons:
link_nodes(node, end_node) link_nodes(node, end_node)
else: else:
for son in node.sons: for son in node.sons:
if son != end_node: if son != end_node and son not in visited:
self._fix_catch(son, end_node) visited.add(son)
self._fix_catch(son, end_node, visited)
def _add_param(self, param: Dict) -> LocalVariableSolc: def _add_param(self, param: Dict) -> LocalVariableSolc:
@ -1395,8 +1397,7 @@ class FunctionSolc(CallerContextExpression):
endif_node = self._new_node(NodeType.ENDIF, node.source_mapping, node.scope) endif_node = self._new_node(NodeType.ENDIF, node.source_mapping, node.scope)
for father in node.fathers: for father in node.fathers:
father.remove_son(node) father.replace_son(node, condition_node.underlying_node)
father.add_son(condition_node.underlying_node)
condition_node.underlying_node.add_father(father) condition_node.underlying_node.add_father(father)
for son in node.sons: for son in node.sons:

@ -87,6 +87,9 @@ class ModifierSolc(FunctionSolc):
for node in self._node_to_nodesolc.values(): for node in self._node_to_nodesolc.values():
node.analyze_expressions(self) node.analyze_expressions(self)
for yul_parser in self._node_to_yulobject.values():
yul_parser.analyze_expressions()
self._rewrite_ternary_as_if_else() self._rewrite_ternary_as_if_else()
self._remove_alone_endif() self._remove_alone_endif()

@ -402,10 +402,7 @@ class SlitherCompilationUnitSolc(CallerContextExpression):
# First we save all the contracts in a dict # First we save all the contracts in a dict
# the key is the contractid # the key is the contractid
for contract in self._underlying_contract_to_parser: for contract in self._underlying_contract_to_parser:
if ( if contract.name.startswith("SlitherInternalTopLevelContract"):
contract.name.startswith("SlitherInternalTopLevelContract")
and not contract.is_top_level
):
raise SlitherException( raise SlitherException(
# region multi-line-string # region multi-line-string
"""Your codebase has a contract named 'SlitherInternalTopLevelContract'. """Your codebase has a contract named 'SlitherInternalTopLevelContract'.

@ -15,7 +15,7 @@ ZIP_TYPES_ACCEPTED = {
Export = namedtuple("Export", ["filename", "content"]) Export = namedtuple("Export", ["filename", "content"])
logger = logging.getLogger("Slither") logger = logging.getLogger("Slither-flat")
def save_to_zip(files: List[Export], zip_filename: str, zip_type: str = "lzma"): def save_to_zip(files: List[Export], zip_filename: str, zip_type: str = "lzma"):

@ -11,6 +11,7 @@ from slither.core.declarations import SolidityFunction, EnumContract, StructureC
from slither.core.declarations.contract import Contract from slither.core.declarations.contract import Contract
from slither.core.declarations.function_top_level import FunctionTopLevel from slither.core.declarations.function_top_level import FunctionTopLevel
from slither.core.declarations.top_level import TopLevel from slither.core.declarations.top_level import TopLevel
from slither.core.declarations.solidity_variables import SolidityCustomRevert
from slither.core.solidity_types import MappingType, ArrayType from slither.core.solidity_types import MappingType, ArrayType
from slither.core.solidity_types.type import Type from slither.core.solidity_types.type import Type
from slither.core.solidity_types.user_defined_type import UserDefinedType from slither.core.solidity_types.user_defined_type import UserDefinedType
@ -23,7 +24,8 @@ from slither.tools.flattening.export.export import (
save_to_disk, save_to_disk,
) )
logger = logging.getLogger("Slither-flattening") logger = logging.getLogger("Slither-flat")
logger.setLevel(logging.INFO)
# index: where to start # index: where to start
# patch_type: # patch_type:
@ -75,6 +77,7 @@ class Flattening:
self._get_source_code_top_level(compilation_unit.structures_top_level) self._get_source_code_top_level(compilation_unit.structures_top_level)
self._get_source_code_top_level(compilation_unit.enums_top_level) self._get_source_code_top_level(compilation_unit.enums_top_level)
self._get_source_code_top_level(compilation_unit.custom_errors)
self._get_source_code_top_level(compilation_unit.variables_top_level) self._get_source_code_top_level(compilation_unit.variables_top_level)
self._get_source_code_top_level(compilation_unit.functions_top_level) self._get_source_code_top_level(compilation_unit.functions_top_level)
@ -249,12 +252,14 @@ class Flattening:
t: Type, t: Type,
contract: Contract, contract: Contract,
exported: Set[str], exported: Set[str],
list_contract: List[Contract], list_contract: Set[Contract],
list_top_level: List[TopLevel], list_top_level: Set[TopLevel],
): ):
if isinstance(t, UserDefinedType): if isinstance(t, UserDefinedType):
t_type = t.type t_type = t.type
if isinstance(t_type, (EnumContract, StructureContract)): if isinstance(t_type, TopLevel):
list_top_level.add(t_type)
elif isinstance(t_type, (EnumContract, StructureContract)):
if t_type.contract != contract and t_type.contract not in exported: if t_type.contract != contract and t_type.contract not in exported:
self._export_list_used_contracts( self._export_list_used_contracts(
t_type.contract, exported, list_contract, list_top_level t_type.contract, exported, list_contract, list_top_level
@ -275,8 +280,8 @@ class Flattening:
self, self,
contract: Contract, contract: Contract,
exported: Set[str], exported: Set[str],
list_contract: List[Contract], list_contract: Set[Contract],
list_top_level: List[TopLevel], list_top_level: Set[TopLevel],
): ):
# TODO: investigate why this happen # TODO: investigate why this happen
if not isinstance(contract, Contract): if not isinstance(contract, Contract):
@ -332,19 +337,21 @@ class Flattening:
for read in ir.read: for read in ir.read:
if isinstance(read, TopLevel): if isinstance(read, TopLevel):
if read not in list_top_level: list_top_level.add(read)
list_top_level.append(read) if isinstance(ir, InternalCall) and isinstance(ir.function, FunctionTopLevel):
if isinstance(ir, InternalCall): list_top_level.add(ir.function)
function_called = ir.function if (
if isinstance(function_called, FunctionTopLevel): isinstance(ir, SolidityCall)
list_top_level.append(function_called) and isinstance(ir.function, SolidityCustomRevert)
and isinstance(ir.function.custom_error, TopLevel)
if contract not in list_contract: ):
list_contract.append(contract) list_top_level.add(ir.function.custom_error)
list_contract.add(contract)
def _export_contract_with_inheritance(self, contract) -> Export: def _export_contract_with_inheritance(self, contract) -> Export:
list_contracts: List[Contract] = [] # will contain contract itself list_contracts: Set[Contract] = set() # will contain contract itself
list_top_level: List[TopLevel] = [] list_top_level: Set[TopLevel] = set()
self._export_list_used_contracts(contract, set(), list_contracts, list_top_level) self._export_list_used_contracts(contract, set(), list_contracts, list_top_level)
path = Path(self._export_path, f"{contract.name}_{uuid.uuid4()}.sol") path = Path(self._export_path, f"{contract.name}_{uuid.uuid4()}.sol")
@ -401,8 +408,8 @@ class Flattening:
def _export_with_import(self) -> List[Export]: def _export_with_import(self) -> List[Export]:
exports: List[Export] = [] exports: List[Export] = []
for contract in self._compilation_unit.contracts: for contract in self._compilation_unit.contracts:
list_contracts: List[Contract] = [] # will contain contract itself list_contracts: Set[Contract] = set() # will contain contract itself
list_top_level: List[TopLevel] = [] list_top_level: Set[TopLevel] = set()
self._export_list_used_contracts(contract, set(), list_contracts, list_top_level) self._export_list_used_contracts(contract, set(), list_contracts, list_top_level)
if list_top_level: if list_top_level:

@ -0,0 +1,105 @@
import argparse
import logging
from pathlib import Path
from crytic_compile import cryticparser
from slither import Slither
from slither.utils.code_generation import generate_interface
logging.basicConfig()
logger = logging.getLogger("Slither-Interface")
logger.setLevel(logging.INFO)
def parse_args() -> argparse.Namespace:
"""
Parse the underlying arguments for the program.
:return: Returns the arguments for the program.
"""
parser = argparse.ArgumentParser(
description="Generates code for a Solidity interface from contract",
usage=("slither-interface <ContractName> <source file or deployment address>"),
)
parser.add_argument(
"contract_source",
help="The name of the contract (case sensitive) followed by the deployed contract address if verified on etherscan or project directory/filename for local contracts.",
nargs="+",
)
parser.add_argument(
"--unroll-structs",
help="Whether to use structures' underlying types instead of the user-defined type",
default=False,
action="store_true",
)
parser.add_argument(
"--exclude-events",
help="Excludes event signatures in the interface",
default=False,
action="store_true",
)
parser.add_argument(
"--exclude-errors",
help="Excludes custom error signatures in the interface",
default=False,
action="store_true",
)
parser.add_argument(
"--exclude-enums",
help="Excludes enum definitions in the interface",
default=False,
action="store_true",
)
parser.add_argument(
"--exclude-structs",
help="Exclude struct definitions in the interface",
default=False,
action="store_true",
)
cryticparser.init(parser)
return parser.parse_args()
def main() -> None:
args = parse_args()
contract_name, target = args.contract_source
slither = Slither(target, **vars(args))
_contract = slither.get_contract_from_name(contract_name)[0]
interface = generate_interface(
contract=_contract,
unroll_structs=args.unroll_structs,
include_events=not args.exclude_events,
include_errors=not args.exclude_errors,
include_enums=not args.exclude_enums,
include_structs=not args.exclude_structs,
)
# add version pragma
interface = (
f"pragma solidity {_contract.compilation_unit.pragma_directives[0].version};\n\n"
+ interface
)
# write interface to file
export = Path("crytic-export", "interfaces")
export.mkdir(parents=True, exist_ok=True)
filename = f"I{contract_name}.sol"
path = Path(export, filename)
logger.info(f" Interface exported to {path}")
with open(path, "w", encoding="utf8") as f:
f.write(interface)
if __name__ == "__main__":
main()

@ -441,7 +441,7 @@ class SlitherReadStorage:
if "int" in key_type: # without this eth_utils encoding fails if "int" in key_type: # without this eth_utils encoding fails
key = int(key) key = int(key)
key = coerce_type(key_type, key) key = coerce_type(key_type, key)
slot = keccak(encode([key_type, "uint256"], [key, decode("uint256", slot)])) slot = keccak(encode([key_type, "uint256"], [key, decode(["uint256"], slot)[0]]))
if isinstance(target_variable_type.type_to, UserDefinedType) and isinstance( if isinstance(target_variable_type.type_to, UserDefinedType) and isinstance(
target_variable_type.type_to.type, Structure target_variable_type.type_to.type, Structure

@ -1,62 +1,149 @@
# Functions for generating Solidity code # Functions for generating Solidity code
from typing import TYPE_CHECKING, Optional from typing import TYPE_CHECKING, Optional
from slither.utils.type import convert_type_for_solidity_signature_to_string from slither.utils.type import (
convert_type_for_solidity_signature_to_string,
export_nested_types_from_variable,
export_return_type_from_variable,
)
from slither.core.solidity_types import (
Type,
UserDefinedType,
MappingType,
ArrayType,
ElementaryType,
)
from slither.core.declarations import Structure, Enum, Contract
if TYPE_CHECKING: if TYPE_CHECKING:
from slither.core.declarations import FunctionContract, Structure, Contract from slither.core.declarations import FunctionContract, CustomErrorContract
from slither.core.variables.state_variable import StateVariable
from slither.core.variables.local_variable import LocalVariable
def generate_interface(contract: "Contract") -> str: # pylint: disable=too-many-arguments
def generate_interface(
contract: "Contract",
unroll_structs: bool = True,
include_events: bool = True,
include_errors: bool = True,
include_enums: bool = True,
include_structs: bool = True,
) -> str:
""" """
Generates code for a Solidity interface to the contract. Generates code for a Solidity interface to the contract.
Args: Args:
contract: A Contract object contract: A Contract object.
unroll_structs: Whether to use structures' underlying types instead of the user-defined type (default: True).
include_events: Whether to include event signatures in the interface (default: True).
include_errors: Whether to include custom error signatures in the interface (default: True).
include_enums: Whether to include enum definitions in the interface (default: True).
include_structs: Whether to include struct definitions in the interface (default: True).
Returns: Returns:
A string with the code for an interface, with function stubs for all public or external functions and A string with the code for an interface, with function stubs for all public or external functions and
state variables, as well as any events, custom errors and/or structs declared in the contract. state variables, as well as any events, custom errors and/or structs declared in the contract.
""" """
interface = f"interface I{contract.name} {{\n" interface = f"interface I{contract.name} {{\n"
for event in contract.events: if include_events:
name, args = event.signature for event in contract.events:
interface += f" event {name}({', '.join(args)});\n" name, args = event.signature
for error in contract.custom_errors: interface += f" event {name}({', '.join(args)});\n"
args = [ if include_errors:
convert_type_for_solidity_signature_to_string(arg.type) for error in contract.custom_errors:
.replace("(", "") interface += f" error {generate_custom_error_interface(error, unroll_structs)};\n"
.replace(")", "") if include_enums:
for arg in error.parameters for enum in contract.enums:
] interface += f" enum {enum.name} {{ {', '.join(enum.values)} }}\n"
interface += f" error {error.name}({', '.join(args)});\n" if include_structs:
for enum in contract.enums: for struct in contract.structures:
interface += f" enum {enum.name} {{ {', '.join(enum.values)} }}\n" interface += generate_struct_interface_str(struct, indent=4)
for struct in contract.structures:
interface += generate_struct_interface_str(struct)
for var in contract.state_variables_entry_points: for var in contract.state_variables_entry_points:
interface += f" function {var.signature_str.replace('returns', 'external returns ')};\n" interface += f" function {generate_interface_variable_signature(var, unroll_structs)};\n"
for func in contract.functions_entry_points: for func in contract.functions_entry_points:
if func.is_constructor or func.is_fallback or func.is_receive: if func.is_constructor or func.is_fallback or func.is_receive:
continue continue
interface += f" function {generate_interface_function_signature(func)};\n" interface += (
f" function {generate_interface_function_signature(func, unroll_structs)};\n"
)
interface += "}\n\n" interface += "}\n\n"
return interface return interface
def generate_interface_function_signature(func: "FunctionContract") -> Optional[str]: def generate_interface_variable_signature(
var: "StateVariable", unroll_structs: bool = True
) -> Optional[str]:
if var.visibility in ["private", "internal"]:
return None
if unroll_structs:
params = [
convert_type_for_solidity_signature_to_string(x).replace("(", "").replace(")", "")
for x in export_nested_types_from_variable(var)
]
returns = [
convert_type_for_solidity_signature_to_string(x).replace("(", "").replace(")", "")
for x in export_return_type_from_variable(var)
]
else:
_, params, _ = var.signature
params = [p + " memory" if p in ["bytes", "string"] else p for p in params]
returns = []
_type = var.type
while isinstance(_type, MappingType):
_type = _type.type_to
while isinstance(_type, (ArrayType, UserDefinedType)):
_type = _type.type
ret = str(_type)
if isinstance(_type, Structure) or (isinstance(_type, Type) and _type.is_dynamic):
ret += " memory"
elif isinstance(_type, Contract):
ret = "address"
returns.append(ret)
return f"{var.name}({','.join(params)}) external returns ({', '.join(returns)})"
def generate_interface_function_signature(
func: "FunctionContract", unroll_structs: bool = True
) -> Optional[str]:
""" """
Generates a string of the form: Generates a string of the form:
func_name(type1,type2) external {payable/view/pure} returns (type3) func_name(type1,type2) external {payable/view/pure} returns (type3)
Args: Args:
func: A FunctionContract object func: A FunctionContract object
unroll_structs: Determines whether structs are unrolled into underlying types (default: True)
Returns: Returns:
The function interface as a str (contains the return values). The function interface as a str (contains the return values).
Returns None if the function is private or internal, or is a constructor/fallback/receive. Returns None if the function is private or internal, or is a constructor/fallback/receive.
""" """
name, parameters, return_vars = func.signature def format_var(var: "LocalVariable", unroll: bool) -> str:
if unroll:
return (
convert_type_for_solidity_signature_to_string(var.type)
.replace("(", "")
.replace(")", "")
)
if isinstance(var.type, ArrayType) and isinstance(
var.type.type, (UserDefinedType, ElementaryType)
):
return (
convert_type_for_solidity_signature_to_string(var.type)
.replace("(", "")
.replace(")", "")
+ f" {var.location}"
)
if isinstance(var.type, UserDefinedType):
if isinstance(var.type.type, (Structure, Enum)):
return f"{str(var.type.type)} memory"
if isinstance(var.type.type, Contract):
return "address"
if var.type.is_dynamic:
return f"{var.type} {var.location}"
return str(var.type)
name, _, _ = func.signature
if ( if (
func not in func.contract.functions_entry_points func not in func.contract.functions_entry_points
or func.is_constructor or func.is_constructor
@ -64,26 +151,20 @@ def generate_interface_function_signature(func: "FunctionContract") -> Optional[
or func.is_receive or func.is_receive
): ):
return None return None
view = " view" if func.view else "" view = " view" if func.view and not func.pure else ""
pure = " pure" if func.pure else "" pure = " pure" if func.pure else ""
payable = " payable" if func.payable else "" payable = " payable" if func.payable else ""
returns = [ returns = [format_var(ret, unroll_structs) for ret in func.returns]
convert_type_for_solidity_signature_to_string(ret.type).replace("(", "").replace(")", "") parameters = [format_var(param, unroll_structs) for param in func.parameters]
for ret in func.returns
]
parameters = [
convert_type_for_solidity_signature_to_string(param.type).replace("(", "").replace(")", "")
for param in func.parameters
]
_interface_signature_str = ( _interface_signature_str = (
name + "(" + ",".join(parameters) + ") external" + payable + pure + view name + "(" + ",".join(parameters) + ") external" + payable + pure + view
) )
if len(return_vars) > 0: if len(returns) > 0:
_interface_signature_str += " returns (" + ",".join(returns) + ")" _interface_signature_str += " returns (" + ",".join(returns) + ")"
return _interface_signature_str return _interface_signature_str
def generate_struct_interface_str(struct: "Structure") -> str: def generate_struct_interface_str(struct: "Structure", indent: int = 0) -> str:
""" """
Generates code for a structure declaration in an interface of the form: Generates code for a structure declaration in an interface of the form:
struct struct_name { struct struct_name {
@ -92,13 +173,37 @@ def generate_struct_interface_str(struct: "Structure") -> str:
... ... ... ...
} }
Args: Args:
struct: A Structure object struct: A Structure object.
indent: Number of spaces to indent the code block with.
Returns: Returns:
The structure declaration code as a string. The structure declaration code as a string.
""" """
definition = f" struct {struct.name} {{\n" spaces = ""
for _ in range(0, indent):
spaces += " "
definition = f"{spaces}struct {struct.name} {{\n"
for elem in struct.elems_ordered: for elem in struct.elems_ordered:
definition += f" {elem.type} {elem.name};\n" if isinstance(elem.type, UserDefinedType):
definition += " }\n" if isinstance(elem.type.type, (Structure, Enum)):
definition += f"{spaces} {elem.type.type} {elem.name};\n"
elif isinstance(elem.type.type, Contract):
definition += f"{spaces} address {elem.name};\n"
else:
definition += f"{spaces} {elem.type} {elem.name};\n"
definition += f"{spaces}}}\n"
return definition return definition
def generate_custom_error_interface(
error: "CustomErrorContract", unroll_structs: bool = True
) -> str:
args = [
convert_type_for_solidity_signature_to_string(arg.type).replace("(", "").replace(")", "")
if unroll_structs
else str(arg.type.type)
if isinstance(arg.type, UserDefinedType) and isinstance(arg.type.type, (Structure, Enum))
else str(arg.type)
for arg in error.parameters
]
return f"{error.name}({', '.join(args)})"

@ -21,7 +21,7 @@ from slither.core.expressions.new_array import NewArray
from slither.core.expressions.new_contract import NewContract from slither.core.expressions.new_contract import NewContract
from slither.core.expressions.tuple_expression import TupleExpression from slither.core.expressions.tuple_expression import TupleExpression
from slither.core.expressions.type_conversion import TypeConversion from slither.core.expressions.type_conversion import TypeConversion
from slither.core.expressions.new_elementary_type import NewElementaryType
# pylint: disable=protected-access # pylint: disable=protected-access
def f_expressions( def f_expressions(
@ -100,7 +100,14 @@ class SplitTernaryExpression:
if isinstance( if isinstance(
expression, expression,
(Literal, Identifier, NewArray, NewContract, ElementaryTypeNameExpression), (
Literal,
Identifier,
NewArray,
NewContract,
ElementaryTypeNameExpression,
NewElementaryType,
),
): ):
return return

@ -1,6 +1,6 @@
from typing import List, Dict, Union from typing import List, Dict, Union
from prettytable import PrettyTable from prettytable.colortable import ColorTable, Themes
class MyPrettyTable: class MyPrettyTable:
@ -11,8 +11,8 @@ class MyPrettyTable:
def add_row(self, row: List[Union[str, List[str]]]) -> None: def add_row(self, row: List[Union[str, List[str]]]) -> None:
self._rows.append(row) self._rows.append(row)
def to_pretty_table(self) -> PrettyTable: def to_pretty_table(self) -> ColorTable:
table = PrettyTable(self._field_names) table = ColorTable(self._field_names, theme=Themes.OCEAN)
for row in self._rows: for row in self._rows:
table.add_row(row) table.add_row(row)
return table return table

@ -197,3 +197,18 @@ def export_return_type_from_variable(
return ret return ret
return [variable_or_type.type] return [variable_or_type.type]
def is_underlying_type_address(t: "Type") -> bool:
"""
Return true if the underlying type is an address
i.e. if the type is an address or a contract
"""
# pylint: disable=import-outside-toplevel
from slither.core.declarations.contract import Contract
if t == ElementaryType("address"):
return True
if isinstance(t, UserDefinedType) and isinstance(t.type, Contract):
return True
return False

@ -10,6 +10,7 @@ from slither.core.declarations import (
Contract, Contract,
EnumContract, EnumContract,
EnumTopLevel, EnumTopLevel,
Enum,
) )
from slither.core.expressions import ( from slither.core.expressions import (
AssignmentOperation, AssignmentOperation,
@ -405,10 +406,13 @@ class ExpressionToSlithIR(ExpressionVisitor):
right = get(expression.expression_right) right = get(expression.expression_right)
operation: Operation operation: Operation
# Left can be a type for abi.decode(var, uint[2]) # Left can be a type for abi.decode(var, uint[2])
if isinstance(left, Type): if isinstance(left, (Type, Contract, Enum)):
# Nested type are not yet supported by abi.decode, so the assumption # Nested type are not yet supported by abi.decode, so the assumption
# Is that the right variable must be a constant # Is that the right variable must be a constant
assert isinstance(right, Constant) assert isinstance(right, Constant)
# Case for abi.decode(var, I[2]) where I is an interface/contract or an enum
if isinstance(left, (Contract, Enum)):
left = UserDefinedType(left)
t = ArrayType(left, int(right.value)) t = ArrayType(left, int(right.value))
set_val(expression, t) set_val(expression, t)
return return
@ -622,7 +626,6 @@ class ExpressionToSlithIR(ExpressionVisitor):
set_val(expression, value) set_val(expression, value)
elif expression.type in [UnaryOperationType.MINUS_PRE]: elif expression.type in [UnaryOperationType.MINUS_PRE]:
lvalue = TemporaryVariable(self._node) lvalue = TemporaryVariable(self._node)
assert isinstance(value.type, ElementaryType)
operation = Binary(lvalue, Constant("0", value.type), value, BinaryType.SUBTRACTION) operation = Binary(lvalue, Constant("0", value.type), value, BinaryType.SUBTRACTION)
operation.set_expression(expression) operation.set_expression(expression)
self._result.append(operation) self._result.append(operation)

@ -0,0 +1,79 @@
# pylint: disable=redefined-outer-name
import os
from pathlib import Path
import tempfile
import shutil
from contextlib import contextmanager
import pytest
from filelock import FileLock
from solc_select import solc_select
from slither import Slither
def pytest_configure(config):
"""Create a temporary directory for the tests to use."""
if is_master():
config.stash["shared_directory"] = tempfile.mkdtemp()
def pytest_unconfigure(config):
"""Remove the temporary directory after the tests are done."""
if is_master():
shutil.rmtree(config.stash["shared_directory"])
def pytest_configure_node(node):
"""Configure each worker node with the shared directory."""
node.workerinput["shared_directory"] = node.config.stash["shared_directory"]
def is_master():
"""Returns True if the current process is the master process (which does not have a worker id)."""
return os.environ.get("PYTEST_XDIST_WORKER") is None
@pytest.fixture
def shared_directory(request):
"""Returns the shared directory for the current process."""
if is_master():
return request.config.stash["shared_directory"]
return request.config.workerinput["shared_directory"]
@pytest.fixture
def solc_binary_path(shared_directory):
"""
Returns the path to the solc binary for the given version.
If the binary is not installed, it will be installed.
"""
def inner(version):
lock = FileLock(f"{shared_directory}/{version}.lock", timeout=60)
with lock:
if not solc_select.artifact_path(version).exists():
print("Installing solc version", version)
solc_select.install_artifacts([version])
return solc_select.artifact_path(version).as_posix()
return inner
@pytest.fixture
def slither_from_source(solc_binary_path):
@contextmanager
def inner(source_code: str, solc_version: str = "0.8.19"):
"""Yields a Slither instance using source_code string and solc_version.
Creates a temporary file and compiles with solc_version.
"""
fname = ""
try:
with tempfile.NamedTemporaryFile(mode="w", suffix=".sol", delete=False) as f:
fname = f.name
f.write(source_code)
solc_path = solc_binary_path(solc_version)
yield Slither(fname, solc=solc_path)
finally:
Path(fname).unlink()
return inner

@ -3,7 +3,6 @@ import pytest
from crytic_compile import CryticCompile from crytic_compile import CryticCompile
from crytic_compile.platform.solc_standard_json import SolcStandardJson from crytic_compile.platform.solc_standard_json import SolcStandardJson
from solc_select import solc_select
from slither import Slither from slither import Slither
@ -24,8 +23,8 @@ def test_node_modules() -> None:
_run_all_detectors(slither) _run_all_detectors(slither)
def test_contract_name_collisions() -> None: def test_contract_name_collision(solc_binary_path) -> None:
solc_select.switch_global_version("0.8.0", always_install=True) solc_path = solc_binary_path("0.8.0")
standard_json = SolcStandardJson() standard_json = SolcStandardJson()
standard_json.add_source_file( standard_json.add_source_file(
Path(TEST_DATA_DIR, "test_contract_name_collisions", "a.sol").as_posix() Path(TEST_DATA_DIR, "test_contract_name_collisions", "a.sol").as_posix()
@ -34,13 +33,13 @@ def test_contract_name_collisions() -> None:
Path(TEST_DATA_DIR, "test_contract_name_collisions", "b.sol").as_posix() Path(TEST_DATA_DIR, "test_contract_name_collisions", "b.sol").as_posix()
) )
compilation = CryticCompile(standard_json) compilation = CryticCompile(standard_json, solc=solc_path)
slither = Slither(compilation) slither = Slither(compilation)
_run_all_detectors(slither) _run_all_detectors(slither)
def test_cycle() -> None: def test_cycle(solc_binary_path) -> None:
solc_select.switch_global_version("0.8.0", always_install=True) solc_path = solc_binary_path("0.8.0")
slither = Slither(Path(TEST_DATA_DIR, "test_cyclic_import", "a.sol").as_posix()) slither = Slither(Path(TEST_DATA_DIR, "test_cyclic_import", "a.sol").as_posix(), solc=solc_path)
_run_all_detectors(slither) _run_all_detectors(slither)

@ -0,0 +1,18 @@
Function A.bad3() (tests/e2e/detectors/test_data/abiencoderv2-array/0.4.25/storage_ABIEncoderV2_array.sol#39-41) trigger an abi encoding bug:
- b = abi.encode(s) (tests/e2e/detectors/test_data/abiencoderv2-array/0.4.25/storage_ABIEncoderV2_array.sol#40)
Function A.bad0() (tests/e2e/detectors/test_data/abiencoderv2-array/0.4.25/storage_ABIEncoderV2_array.sol#21-23) trigger an abi encoding bug:
- this.bad0_external(bad_arr) (tests/e2e/detectors/test_data/abiencoderv2-array/0.4.25/storage_ABIEncoderV2_array.sol#22)
Function A.bad4() (tests/e2e/detectors/test_data/abiencoderv2-array/0.4.25/storage_ABIEncoderV2_array.sol#44-46) trigger an abi encoding bug:
- event1_bad(bad_arr) (tests/e2e/detectors/test_data/abiencoderv2-array/0.4.25/storage_ABIEncoderV2_array.sol#45)
Function A.bad2() (tests/e2e/detectors/test_data/abiencoderv2-array/0.4.25/storage_ABIEncoderV2_array.sol#34-36) trigger an abi encoding bug:
- b = abi.encode(bad_arr) (tests/e2e/detectors/test_data/abiencoderv2-array/0.4.25/storage_ABIEncoderV2_array.sol#35)
Function A.bad1(A.S[3]) (tests/e2e/detectors/test_data/abiencoderv2-array/0.4.25/storage_ABIEncoderV2_array.sol#29-31) trigger an abi encoding bug:
- this.bad1_external(s) (tests/e2e/detectors/test_data/abiencoderv2-array/0.4.25/storage_ABIEncoderV2_array.sol#30)
Function A.bad5() (tests/e2e/detectors/test_data/abiencoderv2-array/0.4.25/storage_ABIEncoderV2_array.sol#49-51) trigger an abi encoding bug:
- event2_bad(s) (tests/e2e/detectors/test_data/abiencoderv2-array/0.4.25/storage_ABIEncoderV2_array.sol#50)

@ -0,0 +1,18 @@
Function A.bad5() (tests/e2e/detectors/test_data/abiencoderv2-array/0.5.9/storage_ABIEncoderV2_array.sol#49-51) trigger an abi encoding bug:
- event2_bad(s) (tests/e2e/detectors/test_data/abiencoderv2-array/0.5.9/storage_ABIEncoderV2_array.sol#50)
Function A.bad0() (tests/e2e/detectors/test_data/abiencoderv2-array/0.5.9/storage_ABIEncoderV2_array.sol#21-23) trigger an abi encoding bug:
- this.bad0_external(bad_arr) (tests/e2e/detectors/test_data/abiencoderv2-array/0.5.9/storage_ABIEncoderV2_array.sol#22)
Function A.bad4() (tests/e2e/detectors/test_data/abiencoderv2-array/0.5.9/storage_ABIEncoderV2_array.sol#44-46) trigger an abi encoding bug:
- event1_bad(bad_arr) (tests/e2e/detectors/test_data/abiencoderv2-array/0.5.9/storage_ABIEncoderV2_array.sol#45)
Function A.bad2() (tests/e2e/detectors/test_data/abiencoderv2-array/0.5.9/storage_ABIEncoderV2_array.sol#34-36) trigger an abi encoding bug:
- b = abi.encode(bad_arr) (tests/e2e/detectors/test_data/abiencoderv2-array/0.5.9/storage_ABIEncoderV2_array.sol#35)
Function A.bad1(A.S[3]) (tests/e2e/detectors/test_data/abiencoderv2-array/0.5.9/storage_ABIEncoderV2_array.sol#29-31) trigger an abi encoding bug:
- this.bad1_external(s) (tests/e2e/detectors/test_data/abiencoderv2-array/0.5.9/storage_ABIEncoderV2_array.sol#30)
Function A.bad3() (tests/e2e/detectors/test_data/abiencoderv2-array/0.5.9/storage_ABIEncoderV2_array.sol#39-41) trigger an abi encoding bug:
- b = abi.encode(s) (tests/e2e/detectors/test_data/abiencoderv2-array/0.5.9/storage_ABIEncoderV2_array.sol#40)

@ -0,0 +1,6 @@
C.bad4(address,address,uint256) (tests/e2e/detectors/test_data/arbitrary-send-erc20/0.4.25/arbitrary_send_erc20.sol#65-67) uses arbitrary from in transferFrom: SafeERC20.safeTransferFrom(erc20,from,to,amount) (tests/e2e/detectors/test_data/arbitrary-send-erc20/0.4.25/arbitrary_send_erc20.sol#66)
C.bad1(address,uint256) (tests/e2e/detectors/test_data/arbitrary-send-erc20/0.4.25/arbitrary_send_erc20.sol#35-37) uses arbitrary from in transferFrom: erc20.transferFrom(notsend,to,am) (tests/e2e/detectors/test_data/arbitrary-send-erc20/0.4.25/arbitrary_send_erc20.sol#36)
C.bad3(address,address,uint256) (tests/e2e/detectors/test_data/arbitrary-send-erc20/0.4.25/arbitrary_send_erc20.sol#57-59) uses arbitrary from in transferFrom: erc20.safeTransferFrom(from,to,amount) (tests/e2e/detectors/test_data/arbitrary-send-erc20/0.4.25/arbitrary_send_erc20.sol#58)

@ -0,0 +1,6 @@
C.bad4(address,address,uint256) (tests/e2e/detectors/test_data/arbitrary-send-erc20/0.5.16/arbitrary_send_erc20.sol#65-67) uses arbitrary from in transferFrom: SafeERC20.safeTransferFrom(erc20,from,to,amount) (tests/e2e/detectors/test_data/arbitrary-send-erc20/0.5.16/arbitrary_send_erc20.sol#66)
C.bad3(address,address,uint256) (tests/e2e/detectors/test_data/arbitrary-send-erc20/0.5.16/arbitrary_send_erc20.sol#57-59) uses arbitrary from in transferFrom: erc20.safeTransferFrom(from,to,amount) (tests/e2e/detectors/test_data/arbitrary-send-erc20/0.5.16/arbitrary_send_erc20.sol#58)
C.bad1(address,uint256) (tests/e2e/detectors/test_data/arbitrary-send-erc20/0.5.16/arbitrary_send_erc20.sol#35-37) uses arbitrary from in transferFrom: erc20.transferFrom(notsend,to,am) (tests/e2e/detectors/test_data/arbitrary-send-erc20/0.5.16/arbitrary_send_erc20.sol#36)

@ -0,0 +1,6 @@
C.bad1(address,uint256) (tests/e2e/detectors/test_data/arbitrary-send-erc20/0.6.11/arbitrary_send_erc20.sol#35-37) uses arbitrary from in transferFrom: erc20.transferFrom(notsend,to,am) (tests/e2e/detectors/test_data/arbitrary-send-erc20/0.6.11/arbitrary_send_erc20.sol#36)
C.bad3(address,address,uint256) (tests/e2e/detectors/test_data/arbitrary-send-erc20/0.6.11/arbitrary_send_erc20.sol#57-59) uses arbitrary from in transferFrom: erc20.safeTransferFrom(from,to,amount) (tests/e2e/detectors/test_data/arbitrary-send-erc20/0.6.11/arbitrary_send_erc20.sol#58)
C.bad4(address,address,uint256) (tests/e2e/detectors/test_data/arbitrary-send-erc20/0.6.11/arbitrary_send_erc20.sol#65-67) uses arbitrary from in transferFrom: SafeERC20.safeTransferFrom(erc20,from,to,amount) (tests/e2e/detectors/test_data/arbitrary-send-erc20/0.6.11/arbitrary_send_erc20.sol#66)

@ -0,0 +1,6 @@
C.bad4(address,address,uint256) (tests/e2e/detectors/test_data/arbitrary-send-erc20/0.7.6/arbitrary_send_erc20.sol#65-67) uses arbitrary from in transferFrom: SafeERC20.safeTransferFrom(erc20,from,to,amount) (tests/e2e/detectors/test_data/arbitrary-send-erc20/0.7.6/arbitrary_send_erc20.sol#66)
C.bad3(address,address,uint256) (tests/e2e/detectors/test_data/arbitrary-send-erc20/0.7.6/arbitrary_send_erc20.sol#57-59) uses arbitrary from in transferFrom: erc20.safeTransferFrom(from,to,amount) (tests/e2e/detectors/test_data/arbitrary-send-erc20/0.7.6/arbitrary_send_erc20.sol#58)
C.bad1(address,uint256) (tests/e2e/detectors/test_data/arbitrary-send-erc20/0.7.6/arbitrary_send_erc20.sol#35-37) uses arbitrary from in transferFrom: erc20.transferFrom(notsend,to,am) (tests/e2e/detectors/test_data/arbitrary-send-erc20/0.7.6/arbitrary_send_erc20.sol#36)

@ -0,0 +1,2 @@
T.bad(address) (tests/e2e/detectors/test_data/arbitrary-send-erc20/0.8.0/arbitrary_send_erc20_inheritance.sol#11-13) uses arbitrary from in transferFrom: erc20.safeTransferFrom(from,address(0x1),90) (tests/e2e/detectors/test_data/arbitrary-send-erc20/0.8.0/arbitrary_send_erc20_inheritance.sol#12)

@ -0,0 +1,6 @@
C.bad1(address,uint256) (tests/e2e/detectors/test_data/arbitrary-send-erc20/0.8.0/arbitrary_send_erc20.sol#35-37) uses arbitrary from in transferFrom: erc20.transferFrom(notsend,to,am) (tests/e2e/detectors/test_data/arbitrary-send-erc20/0.8.0/arbitrary_send_erc20.sol#36)
C.bad3(address,address,uint256) (tests/e2e/detectors/test_data/arbitrary-send-erc20/0.8.0/arbitrary_send_erc20.sol#57-59) uses arbitrary from in transferFrom: erc20.safeTransferFrom(from,to,amount) (tests/e2e/detectors/test_data/arbitrary-send-erc20/0.8.0/arbitrary_send_erc20.sol#58)
C.bad4(address,address,uint256) (tests/e2e/detectors/test_data/arbitrary-send-erc20/0.8.0/arbitrary_send_erc20.sol#65-67) uses arbitrary from in transferFrom: SafeERC20.safeTransferFrom(erc20,from,to,amount) (tests/e2e/detectors/test_data/arbitrary-send-erc20/0.8.0/arbitrary_send_erc20.sol#66)

@ -0,0 +1,8 @@
C.int_transferFrom(address,uint256,uint256,uint8,bytes32,bytes32,address) (tests/e2e/detectors/test_data/arbitrary-send-erc20-permit/0.4.25/arbitrary_send_erc20_permit.sol#42-45) uses arbitrary from in transferFrom in combination with permit: erc20.transferFrom(from,to,value) (tests/e2e/detectors/test_data/arbitrary-send-erc20-permit/0.4.25/arbitrary_send_erc20_permit.sol#44)
C.bad3(address,uint256,uint256,uint8,bytes32,bytes32,address) (tests/e2e/detectors/test_data/arbitrary-send-erc20-permit/0.4.25/arbitrary_send_erc20_permit.sol#47-50) uses arbitrary from in transferFrom in combination with permit: erc20.safeTransferFrom(from,to,value) (tests/e2e/detectors/test_data/arbitrary-send-erc20-permit/0.4.25/arbitrary_send_erc20_permit.sol#49)
C.bad4(address,uint256,uint256,uint8,bytes32,bytes32,address) (tests/e2e/detectors/test_data/arbitrary-send-erc20-permit/0.4.25/arbitrary_send_erc20_permit.sol#52-55) uses arbitrary from in transferFrom in combination with permit: SafeERC20.safeTransferFrom(erc20,from,to,value) (tests/e2e/detectors/test_data/arbitrary-send-erc20-permit/0.4.25/arbitrary_send_erc20_permit.sol#54)
C.bad1(address,uint256,uint256,uint8,bytes32,bytes32,address) (tests/e2e/detectors/test_data/arbitrary-send-erc20-permit/0.4.25/arbitrary_send_erc20_permit.sol#32-35) uses arbitrary from in transferFrom in combination with permit: erc20.transferFrom(from,to,value) (tests/e2e/detectors/test_data/arbitrary-send-erc20-permit/0.4.25/arbitrary_send_erc20_permit.sol#34)

@ -0,0 +1,8 @@
C.int_transferFrom(address,uint256,uint256,uint8,bytes32,bytes32,address) (tests/e2e/detectors/test_data/arbitrary-send-erc20-permit/0.5.16/arbitrary_send_erc20_permit.sol#42-45) uses arbitrary from in transferFrom in combination with permit: erc20.transferFrom(from,to,value) (tests/e2e/detectors/test_data/arbitrary-send-erc20-permit/0.5.16/arbitrary_send_erc20_permit.sol#44)
C.bad4(address,uint256,uint256,uint8,bytes32,bytes32,address) (tests/e2e/detectors/test_data/arbitrary-send-erc20-permit/0.5.16/arbitrary_send_erc20_permit.sol#52-55) uses arbitrary from in transferFrom in combination with permit: SafeERC20.safeTransferFrom(erc20,from,to,value) (tests/e2e/detectors/test_data/arbitrary-send-erc20-permit/0.5.16/arbitrary_send_erc20_permit.sol#54)
C.bad3(address,uint256,uint256,uint8,bytes32,bytes32,address) (tests/e2e/detectors/test_data/arbitrary-send-erc20-permit/0.5.16/arbitrary_send_erc20_permit.sol#47-50) uses arbitrary from in transferFrom in combination with permit: erc20.safeTransferFrom(from,to,value) (tests/e2e/detectors/test_data/arbitrary-send-erc20-permit/0.5.16/arbitrary_send_erc20_permit.sol#49)
C.bad1(address,uint256,uint256,uint8,bytes32,bytes32,address) (tests/e2e/detectors/test_data/arbitrary-send-erc20-permit/0.5.16/arbitrary_send_erc20_permit.sol#32-35) uses arbitrary from in transferFrom in combination with permit: erc20.transferFrom(from,to,value) (tests/e2e/detectors/test_data/arbitrary-send-erc20-permit/0.5.16/arbitrary_send_erc20_permit.sol#34)

@ -0,0 +1,8 @@
C.bad3(address,uint256,uint256,uint8,bytes32,bytes32,address) (tests/e2e/detectors/test_data/arbitrary-send-erc20-permit/0.6.11/arbitrary_send_erc20_permit.sol#47-50) uses arbitrary from in transferFrom in combination with permit: erc20.safeTransferFrom(from,to,value) (tests/e2e/detectors/test_data/arbitrary-send-erc20-permit/0.6.11/arbitrary_send_erc20_permit.sol#49)
C.bad4(address,uint256,uint256,uint8,bytes32,bytes32,address) (tests/e2e/detectors/test_data/arbitrary-send-erc20-permit/0.6.11/arbitrary_send_erc20_permit.sol#52-55) uses arbitrary from in transferFrom in combination with permit: SafeERC20.safeTransferFrom(erc20,from,to,value) (tests/e2e/detectors/test_data/arbitrary-send-erc20-permit/0.6.11/arbitrary_send_erc20_permit.sol#54)
C.int_transferFrom(address,uint256,uint256,uint8,bytes32,bytes32,address) (tests/e2e/detectors/test_data/arbitrary-send-erc20-permit/0.6.11/arbitrary_send_erc20_permit.sol#42-45) uses arbitrary from in transferFrom in combination with permit: erc20.transferFrom(from,to,value) (tests/e2e/detectors/test_data/arbitrary-send-erc20-permit/0.6.11/arbitrary_send_erc20_permit.sol#44)
C.bad1(address,uint256,uint256,uint8,bytes32,bytes32,address) (tests/e2e/detectors/test_data/arbitrary-send-erc20-permit/0.6.11/arbitrary_send_erc20_permit.sol#32-35) uses arbitrary from in transferFrom in combination with permit: erc20.transferFrom(from,to,value) (tests/e2e/detectors/test_data/arbitrary-send-erc20-permit/0.6.11/arbitrary_send_erc20_permit.sol#34)

@ -0,0 +1,8 @@
C.int_transferFrom(address,uint256,uint256,uint8,bytes32,bytes32,address) (tests/e2e/detectors/test_data/arbitrary-send-erc20-permit/0.7.6/arbitrary_send_erc20_permit.sol#42-45) uses arbitrary from in transferFrom in combination with permit: erc20.transferFrom(from,to,value) (tests/e2e/detectors/test_data/arbitrary-send-erc20-permit/0.7.6/arbitrary_send_erc20_permit.sol#44)
C.bad1(address,uint256,uint256,uint8,bytes32,bytes32,address) (tests/e2e/detectors/test_data/arbitrary-send-erc20-permit/0.7.6/arbitrary_send_erc20_permit.sol#32-35) uses arbitrary from in transferFrom in combination with permit: erc20.transferFrom(from,to,value) (tests/e2e/detectors/test_data/arbitrary-send-erc20-permit/0.7.6/arbitrary_send_erc20_permit.sol#34)
C.bad4(address,uint256,uint256,uint8,bytes32,bytes32,address) (tests/e2e/detectors/test_data/arbitrary-send-erc20-permit/0.7.6/arbitrary_send_erc20_permit.sol#52-55) uses arbitrary from in transferFrom in combination with permit: SafeERC20.safeTransferFrom(erc20,from,to,value) (tests/e2e/detectors/test_data/arbitrary-send-erc20-permit/0.7.6/arbitrary_send_erc20_permit.sol#54)
C.bad3(address,uint256,uint256,uint8,bytes32,bytes32,address) (tests/e2e/detectors/test_data/arbitrary-send-erc20-permit/0.7.6/arbitrary_send_erc20_permit.sol#47-50) uses arbitrary from in transferFrom in combination with permit: erc20.safeTransferFrom(from,to,value) (tests/e2e/detectors/test_data/arbitrary-send-erc20-permit/0.7.6/arbitrary_send_erc20_permit.sol#49)

@ -0,0 +1,8 @@
C.bad3(address,uint256,uint256,uint8,bytes32,bytes32,address) (tests/e2e/detectors/test_data/arbitrary-send-erc20-permit/0.8.0/arbitrary_send_erc20_permit.sol#47-50) uses arbitrary from in transferFrom in combination with permit: erc20.safeTransferFrom(from,to,value) (tests/e2e/detectors/test_data/arbitrary-send-erc20-permit/0.8.0/arbitrary_send_erc20_permit.sol#49)
C.bad4(address,uint256,uint256,uint8,bytes32,bytes32,address) (tests/e2e/detectors/test_data/arbitrary-send-erc20-permit/0.8.0/arbitrary_send_erc20_permit.sol#52-55) uses arbitrary from in transferFrom in combination with permit: SafeERC20.safeTransferFrom(erc20,from,to,value) (tests/e2e/detectors/test_data/arbitrary-send-erc20-permit/0.8.0/arbitrary_send_erc20_permit.sol#54)
C.int_transferFrom(address,uint256,uint256,uint8,bytes32,bytes32,address) (tests/e2e/detectors/test_data/arbitrary-send-erc20-permit/0.8.0/arbitrary_send_erc20_permit.sol#42-45) uses arbitrary from in transferFrom in combination with permit: erc20.transferFrom(from,to,value) (tests/e2e/detectors/test_data/arbitrary-send-erc20-permit/0.8.0/arbitrary_send_erc20_permit.sol#44)
C.bad1(address,uint256,uint256,uint8,bytes32,bytes32,address) (tests/e2e/detectors/test_data/arbitrary-send-erc20-permit/0.8.0/arbitrary_send_erc20_permit.sol#32-35) uses arbitrary from in transferFrom in combination with permit: erc20.transferFrom(from,to,value) (tests/e2e/detectors/test_data/arbitrary-send-erc20-permit/0.8.0/arbitrary_send_erc20_permit.sol#34)

@ -0,0 +1,8 @@
Test.indirect() (tests/e2e/detectors/test_data/arbitrary-send-eth/0.4.25/arbitrary_send_eth.sol#19-21) sends eth to arbitrary user
Dangerous calls:
- destination.send(address(this).balance) (tests/e2e/detectors/test_data/arbitrary-send-eth/0.4.25/arbitrary_send_eth.sol#20)
Test.direct() (tests/e2e/detectors/test_data/arbitrary-send-eth/0.4.25/arbitrary_send_eth.sol#11-13) sends eth to arbitrary user
Dangerous calls:
- msg.sender.send(address(this).balance) (tests/e2e/detectors/test_data/arbitrary-send-eth/0.4.25/arbitrary_send_eth.sol#12)

@ -0,0 +1,8 @@
Test.direct() (tests/e2e/detectors/test_data/arbitrary-send-eth/0.5.16/arbitrary_send_eth.sol#11-13) sends eth to arbitrary user
Dangerous calls:
- msg.sender.send(address(this).balance) (tests/e2e/detectors/test_data/arbitrary-send-eth/0.5.16/arbitrary_send_eth.sol#12)
Test.indirect() (tests/e2e/detectors/test_data/arbitrary-send-eth/0.5.16/arbitrary_send_eth.sol#19-21) sends eth to arbitrary user
Dangerous calls:
- destination.send(address(this).balance) (tests/e2e/detectors/test_data/arbitrary-send-eth/0.5.16/arbitrary_send_eth.sol#20)

@ -0,0 +1,8 @@
Test.indirect() (tests/e2e/detectors/test_data/arbitrary-send-eth/0.6.11/arbitrary_send_eth.sol#19-21) sends eth to arbitrary user
Dangerous calls:
- destination.send(address(this).balance) (tests/e2e/detectors/test_data/arbitrary-send-eth/0.6.11/arbitrary_send_eth.sol#20)
Test.direct() (tests/e2e/detectors/test_data/arbitrary-send-eth/0.6.11/arbitrary_send_eth.sol#11-13) sends eth to arbitrary user
Dangerous calls:
- msg.sender.send(address(this).balance) (tests/e2e/detectors/test_data/arbitrary-send-eth/0.6.11/arbitrary_send_eth.sol#12)

@ -0,0 +1,8 @@
Test.direct() (tests/e2e/detectors/test_data/arbitrary-send-eth/0.7.6/arbitrary_send_eth.sol#11-13) sends eth to arbitrary user
Dangerous calls:
- msg.sender.send(address(this).balance) (tests/e2e/detectors/test_data/arbitrary-send-eth/0.7.6/arbitrary_send_eth.sol#12)
Test.indirect() (tests/e2e/detectors/test_data/arbitrary-send-eth/0.7.6/arbitrary_send_eth.sol#19-21) sends eth to arbitrary user
Dangerous calls:
- destination.send(address(this).balance) (tests/e2e/detectors/test_data/arbitrary-send-eth/0.7.6/arbitrary_send_eth.sol#20)

@ -0,0 +1,12 @@
D.f() (tests/e2e/detectors/test_data/array-by-reference/0.4.25/array_by_reference.sol#42-48) passes array D.x (tests/e2e/detectors/test_data/array-by-reference/0.4.25/array_by_reference.sol#39)by reference to C.setByValueAndReturn(uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.4.25/array_by_reference.sol#25-28)which only takes arrays by value
D.f() (tests/e2e/detectors/test_data/array-by-reference/0.4.25/array_by_reference.sol#42-48) passes array D.x (tests/e2e/detectors/test_data/array-by-reference/0.4.25/array_by_reference.sol#39)by reference to C.setByValue(uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.4.25/array_by_reference.sol#21-23)which only takes arrays by value
C.f() (tests/e2e/detectors/test_data/array-by-reference/0.4.25/array_by_reference.sol#4-8) passes array C.x (tests/e2e/detectors/test_data/array-by-reference/0.4.25/array_by_reference.sol#2)by reference to C.setByValue(uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.4.25/array_by_reference.sol#21-23)which only takes arrays by value
C.f() (tests/e2e/detectors/test_data/array-by-reference/0.4.25/array_by_reference.sol#4-8) passes array C.x (tests/e2e/detectors/test_data/array-by-reference/0.4.25/array_by_reference.sol#2)by reference to C.setByValueAndReturn(uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.4.25/array_by_reference.sol#25-28)which only takes arrays by value
C.g() (tests/e2e/detectors/test_data/array-by-reference/0.4.25/array_by_reference.sol#10-15) passes array C.g().y (tests/e2e/detectors/test_data/array-by-reference/0.4.25/array_by_reference.sol#11)by reference to C.setByValueAndReturn(uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.4.25/array_by_reference.sol#25-28)which only takes arrays by value
C.g() (tests/e2e/detectors/test_data/array-by-reference/0.4.25/array_by_reference.sol#10-15) passes array C.g().y (tests/e2e/detectors/test_data/array-by-reference/0.4.25/array_by_reference.sol#11)by reference to C.setByValue(uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.4.25/array_by_reference.sol#21-23)which only takes arrays by value

@ -0,0 +1,12 @@
D.f() (tests/e2e/detectors/test_data/array-by-reference/0.5.16/array_by_reference.sol#42-48) passes array D.x (tests/e2e/detectors/test_data/array-by-reference/0.5.16/array_by_reference.sol#39)by reference to C.setByValueAndReturn(uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.5.16/array_by_reference.sol#25-28)which only takes arrays by value
D.f() (tests/e2e/detectors/test_data/array-by-reference/0.5.16/array_by_reference.sol#42-48) passes array D.x (tests/e2e/detectors/test_data/array-by-reference/0.5.16/array_by_reference.sol#39)by reference to C.setByValue(uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.5.16/array_by_reference.sol#21-23)which only takes arrays by value
C.f() (tests/e2e/detectors/test_data/array-by-reference/0.5.16/array_by_reference.sol#4-8) passes array C.x (tests/e2e/detectors/test_data/array-by-reference/0.5.16/array_by_reference.sol#2)by reference to C.setByValue(uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.5.16/array_by_reference.sol#21-23)which only takes arrays by value
C.f() (tests/e2e/detectors/test_data/array-by-reference/0.5.16/array_by_reference.sol#4-8) passes array C.x (tests/e2e/detectors/test_data/array-by-reference/0.5.16/array_by_reference.sol#2)by reference to C.setByValueAndReturn(uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.5.16/array_by_reference.sol#25-28)which only takes arrays by value
C.g() (tests/e2e/detectors/test_data/array-by-reference/0.5.16/array_by_reference.sol#10-15) passes array C.g().y (tests/e2e/detectors/test_data/array-by-reference/0.5.16/array_by_reference.sol#11)by reference to C.setByValueAndReturn(uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.5.16/array_by_reference.sol#25-28)which only takes arrays by value
C.g() (tests/e2e/detectors/test_data/array-by-reference/0.5.16/array_by_reference.sol#10-15) passes array C.g().y (tests/e2e/detectors/test_data/array-by-reference/0.5.16/array_by_reference.sol#11)by reference to C.setByValue(uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.5.16/array_by_reference.sol#21-23)which only takes arrays by value

@ -0,0 +1,12 @@
D.f() (tests/e2e/detectors/test_data/array-by-reference/0.6.11/array_by_reference.sol#42-48) passes array D.x (tests/e2e/detectors/test_data/array-by-reference/0.6.11/array_by_reference.sol#39)by reference to C.setByValueAndReturn(uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.6.11/array_by_reference.sol#25-28)which only takes arrays by value
D.f() (tests/e2e/detectors/test_data/array-by-reference/0.6.11/array_by_reference.sol#42-48) passes array D.x (tests/e2e/detectors/test_data/array-by-reference/0.6.11/array_by_reference.sol#39)by reference to C.setByValue(uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.6.11/array_by_reference.sol#21-23)which only takes arrays by value
C.f() (tests/e2e/detectors/test_data/array-by-reference/0.6.11/array_by_reference.sol#4-8) passes array C.x (tests/e2e/detectors/test_data/array-by-reference/0.6.11/array_by_reference.sol#2)by reference to C.setByValue(uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.6.11/array_by_reference.sol#21-23)which only takes arrays by value
C.f() (tests/e2e/detectors/test_data/array-by-reference/0.6.11/array_by_reference.sol#4-8) passes array C.x (tests/e2e/detectors/test_data/array-by-reference/0.6.11/array_by_reference.sol#2)by reference to C.setByValueAndReturn(uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.6.11/array_by_reference.sol#25-28)which only takes arrays by value
C.g() (tests/e2e/detectors/test_data/array-by-reference/0.6.11/array_by_reference.sol#10-15) passes array C.g().y (tests/e2e/detectors/test_data/array-by-reference/0.6.11/array_by_reference.sol#11)by reference to C.setByValueAndReturn(uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.6.11/array_by_reference.sol#25-28)which only takes arrays by value
C.g() (tests/e2e/detectors/test_data/array-by-reference/0.6.11/array_by_reference.sol#10-15) passes array C.g().y (tests/e2e/detectors/test_data/array-by-reference/0.6.11/array_by_reference.sol#11)by reference to C.setByValue(uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.6.11/array_by_reference.sol#21-23)which only takes arrays by value

@ -0,0 +1,12 @@
D.f() (tests/e2e/detectors/test_data/array-by-reference/0.7.6/array_by_reference.sol#42-48) passes array D.x (tests/e2e/detectors/test_data/array-by-reference/0.7.6/array_by_reference.sol#39)by reference to C.setByValueAndReturn(uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.7.6/array_by_reference.sol#25-28)which only takes arrays by value
D.f() (tests/e2e/detectors/test_data/array-by-reference/0.7.6/array_by_reference.sol#42-48) passes array D.x (tests/e2e/detectors/test_data/array-by-reference/0.7.6/array_by_reference.sol#39)by reference to C.setByValue(uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.7.6/array_by_reference.sol#21-23)which only takes arrays by value
C.f() (tests/e2e/detectors/test_data/array-by-reference/0.7.6/array_by_reference.sol#4-8) passes array C.x (tests/e2e/detectors/test_data/array-by-reference/0.7.6/array_by_reference.sol#2)by reference to C.setByValue(uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.7.6/array_by_reference.sol#21-23)which only takes arrays by value
C.f() (tests/e2e/detectors/test_data/array-by-reference/0.7.6/array_by_reference.sol#4-8) passes array C.x (tests/e2e/detectors/test_data/array-by-reference/0.7.6/array_by_reference.sol#2)by reference to C.setByValueAndReturn(uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.7.6/array_by_reference.sol#25-28)which only takes arrays by value
C.g() (tests/e2e/detectors/test_data/array-by-reference/0.7.6/array_by_reference.sol#10-15) passes array C.g().y (tests/e2e/detectors/test_data/array-by-reference/0.7.6/array_by_reference.sol#11)by reference to C.setByValueAndReturn(uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.7.6/array_by_reference.sol#25-28)which only takes arrays by value
C.g() (tests/e2e/detectors/test_data/array-by-reference/0.7.6/array_by_reference.sol#10-15) passes array C.g().y (tests/e2e/detectors/test_data/array-by-reference/0.7.6/array_by_reference.sol#11)by reference to C.setByValue(uint256[1]) (tests/e2e/detectors/test_data/array-by-reference/0.7.6/array_by_reference.sol#21-23)which only takes arrays by value

@ -0,0 +1,9 @@
ArrayLengthAssignment (tests/e2e/detectors/test_data/controlled-array-length/0.4.25/array_length_assignment.sol#1-46) contract sets array length with a user-controlled value:
- b.subStruct.x.length = param + 1 (tests/e2e/detectors/test_data/controlled-array-length/0.4.25/array_length_assignment.sol#41)
ArrayLengthAssignment (tests/e2e/detectors/test_data/controlled-array-length/0.4.25/array_length_assignment.sol#1-46) contract sets array length with a user-controlled value:
- a.x.length = param (tests/e2e/detectors/test_data/controlled-array-length/0.4.25/array_length_assignment.sol#36)
ArrayLengthAssignment (tests/e2e/detectors/test_data/controlled-array-length/0.4.25/array_length_assignment.sol#1-46) contract sets array length with a user-controlled value:
- arr.length = param (tests/e2e/detectors/test_data/controlled-array-length/0.4.25/array_length_assignment.sol#26)

@ -0,0 +1,9 @@
ArrayLengthAssignment (tests/e2e/detectors/test_data/controlled-array-length/0.5.16/array_length_assignment.sol#1-46) contract sets array length with a user-controlled value:
- b.subStruct.x.length = param + 1 (tests/e2e/detectors/test_data/controlled-array-length/0.5.16/array_length_assignment.sol#41)
ArrayLengthAssignment (tests/e2e/detectors/test_data/controlled-array-length/0.5.16/array_length_assignment.sol#1-46) contract sets array length with a user-controlled value:
- a.x.length = param (tests/e2e/detectors/test_data/controlled-array-length/0.5.16/array_length_assignment.sol#36)
ArrayLengthAssignment (tests/e2e/detectors/test_data/controlled-array-length/0.5.16/array_length_assignment.sol#1-46) contract sets array length with a user-controlled value:
- arr.length = param (tests/e2e/detectors/test_data/controlled-array-length/0.5.16/array_length_assignment.sol#26)

@ -0,0 +1,3 @@
GetCode.at(address) (tests/e2e/detectors/test_data/assembly/0.4.25/inline_assembly_contract.sol#6-20) uses assembly
- INLINE ASM (tests/e2e/detectors/test_data/assembly/0.4.25/inline_assembly_contract.sol#7-20)

@ -0,0 +1,6 @@
VectorSum.sumPureAsm(uint256[]) (tests/e2e/detectors/test_data/assembly/0.4.25/inline_assembly_library.sol#25-47) uses assembly
- INLINE ASM (tests/e2e/detectors/test_data/assembly/0.4.25/inline_assembly_library.sol#26-47)
VectorSum.sumAsm(uint256[]) (tests/e2e/detectors/test_data/assembly/0.4.25/inline_assembly_library.sol#16-22) uses assembly
- INLINE ASM (tests/e2e/detectors/test_data/assembly/0.4.25/inline_assembly_library.sol#18-21)

@ -0,0 +1,3 @@
GetCode.at(address) (tests/e2e/detectors/test_data/assembly/0.5.16/inline_assembly_contract.sol#6-20) uses assembly
- INLINE ASM (tests/e2e/detectors/test_data/assembly/0.5.16/inline_assembly_contract.sol#7-19)

@ -0,0 +1,6 @@
VectorSum.sumAsm(uint256[]) (tests/e2e/detectors/test_data/assembly/0.5.16/inline_assembly_library.sol#16-22) uses assembly
- INLINE ASM (tests/e2e/detectors/test_data/assembly/0.5.16/inline_assembly_library.sol#18-20)
VectorSum.sumPureAsm(uint256[]) (tests/e2e/detectors/test_data/assembly/0.5.16/inline_assembly_library.sol#25-47) uses assembly
- INLINE ASM (tests/e2e/detectors/test_data/assembly/0.5.16/inline_assembly_library.sol#26-46)

@ -0,0 +1,3 @@
GetCode.at(address) (tests/e2e/detectors/test_data/assembly/0.6.11/inline_assembly_contract.sol#4-18) uses assembly
- INLINE ASM (tests/e2e/detectors/test_data/assembly/0.6.11/inline_assembly_contract.sol#5-17)

@ -0,0 +1,6 @@
VectorSum.sumAsm(uint256[]) (tests/e2e/detectors/test_data/assembly/0.6.11/inline_assembly_library.sol#14-20) uses assembly
- INLINE ASM (tests/e2e/detectors/test_data/assembly/0.6.11/inline_assembly_library.sol#16-18)
VectorSum.sumPureAsm(uint256[]) (tests/e2e/detectors/test_data/assembly/0.6.11/inline_assembly_library.sol#23-45) uses assembly
- INLINE ASM (tests/e2e/detectors/test_data/assembly/0.6.11/inline_assembly_library.sol#24-44)

@ -0,0 +1,3 @@
GetCode.at(address) (tests/e2e/detectors/test_data/assembly/0.7.6/inline_assembly_contract.sol#4-18) uses assembly
- INLINE ASM (tests/e2e/detectors/test_data/assembly/0.7.6/inline_assembly_contract.sol#5-17)

@ -0,0 +1,6 @@
VectorSum.sumAsm(uint256[]) (tests/e2e/detectors/test_data/assembly/0.7.6/inline_assembly_library.sol#14-20) uses assembly
- INLINE ASM (tests/e2e/detectors/test_data/assembly/0.7.6/inline_assembly_library.sol#16-18)
VectorSum.sumPureAsm(uint256[]) (tests/e2e/detectors/test_data/assembly/0.7.6/inline_assembly_library.sol#23-45) uses assembly
- INLINE ASM (tests/e2e/detectors/test_data/assembly/0.7.6/inline_assembly_library.sol#24-44)

@ -0,0 +1,12 @@
A.bad2() (tests/e2e/detectors/test_data/assert-state-change/0.4.25/assert_state_change.sol#19-21) has an assert() call which possibly changes state.
-assert(bool)(bad2_callee()) (tests/e2e/detectors/test_data/assert-state-change/0.4.25/assert_state_change.sol#20)
Consider using require() or change the invariant to not modify the state.
A.bad0() (tests/e2e/detectors/test_data/assert-state-change/0.4.25/assert_state_change.sol#6-8) has an assert() call which possibly changes state.
-assert(bool)((s_a += 1) > 10) (tests/e2e/detectors/test_data/assert-state-change/0.4.25/assert_state_change.sol#7)
Consider using require() or change the invariant to not modify the state.
A.bad1(uint256) (tests/e2e/detectors/test_data/assert-state-change/0.4.25/assert_state_change.sol#11-13) has an assert() call which possibly changes state.
-assert(bool)((s_a += a) > 10) (tests/e2e/detectors/test_data/assert-state-change/0.4.25/assert_state_change.sol#12)
Consider using require() or change the invariant to not modify the state.

@ -0,0 +1,12 @@
A.bad1(uint256) (tests/e2e/detectors/test_data/assert-state-change/0.5.16/assert_state_change.sol#11-13) has an assert() call which possibly changes state.
-assert(bool)((s_a += a) > 10) (tests/e2e/detectors/test_data/assert-state-change/0.5.16/assert_state_change.sol#12)
Consider using require() or change the invariant to not modify the state.
A.bad2() (tests/e2e/detectors/test_data/assert-state-change/0.5.16/assert_state_change.sol#19-21) has an assert() call which possibly changes state.
-assert(bool)(bad2_callee()) (tests/e2e/detectors/test_data/assert-state-change/0.5.16/assert_state_change.sol#20)
Consider using require() or change the invariant to not modify the state.
A.bad0() (tests/e2e/detectors/test_data/assert-state-change/0.5.16/assert_state_change.sol#6-8) has an assert() call which possibly changes state.
-assert(bool)((s_a += 1) > 10) (tests/e2e/detectors/test_data/assert-state-change/0.5.16/assert_state_change.sol#7)
Consider using require() or change the invariant to not modify the state.

@ -0,0 +1,12 @@
A.bad0() (tests/e2e/detectors/test_data/assert-state-change/0.6.11/assert_state_change.sol#6-8) has an assert() call which possibly changes state.
-assert(bool)((s_a += 1) > 10) (tests/e2e/detectors/test_data/assert-state-change/0.6.11/assert_state_change.sol#7)
Consider using require() or change the invariant to not modify the state.
A.bad1(uint256) (tests/e2e/detectors/test_data/assert-state-change/0.6.11/assert_state_change.sol#11-13) has an assert() call which possibly changes state.
-assert(bool)((s_a += a) > 10) (tests/e2e/detectors/test_data/assert-state-change/0.6.11/assert_state_change.sol#12)
Consider using require() or change the invariant to not modify the state.
A.bad2() (tests/e2e/detectors/test_data/assert-state-change/0.6.11/assert_state_change.sol#19-21) has an assert() call which possibly changes state.
-assert(bool)(bad2_callee()) (tests/e2e/detectors/test_data/assert-state-change/0.6.11/assert_state_change.sol#20)
Consider using require() or change the invariant to not modify the state.

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

Loading…
Cancel
Save