Merge branch 'dev' into printer-cheatcode

pull/2413/head
Josselin Feist 2 months ago committed by GitHub
commit cca47a1d90
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 10
      .github/ISSUE_TEMPLATE/bug_report.yml
  2. 3
      .github/actions/upload-coverage/action.yml
  3. 4
      .github/scripts/tool_test_runner.sh
  4. 4
      .github/workflows/ci.yml
  5. 2
      .github/workflows/docker.yml
  6. 2
      .github/workflows/pip-audit.yml
  7. 4
      .github/workflows/publish.yml
  8. 5
      .gitignore
  9. 9
      .pre-commit-hooks.yaml
  10. 1
      Dockerfile
  11. 5
      FUNDING.json
  12. 27
      README.md
  13. 84
      scripts/ci_test.sh
  14. 31
      scripts/ci_test_upgradability.sh
  15. 27
      scripts/json_diff.py
  16. 6
      setup.py
  17. 89
      slither/__main__.py
  18. 8
      slither/analyses/evm/convert.py
  19. 3
      slither/core/cfg/node.py
  20. 2
      slither/core/declarations/contract.py
  21. 1
      slither/core/declarations/custom_error.py
  22. 9
      slither/core/declarations/function.py
  23. 2
      slither/core/declarations/import_directive.py
  24. 1
      slither/core/declarations/pragma_directive.py
  25. 1
      slither/core/declarations/solidity_variables.py
  26. 3
      slither/core/expressions/identifier.py
  27. 1
      slither/core/slither_core.py
  28. 2
      slither/core/solidity_types/elementary_type.py
  29. 1
      slither/core/solidity_types/type_alias.py
  30. 63
      slither/core/source_mapping/source_mapping.py
  31. 4
      slither/detectors/all_detectors.py
  32. 14
      slither/detectors/attributes/constant_pragma.py
  33. 12
      slither/detectors/attributes/incorrect_solc.py
  34. 3
      slither/detectors/functions/arbitrary_send_eth.py
  35. 5
      slither/detectors/functions/dead_code.py
  36. 9
      slither/detectors/statements/unprotected_upgradeable.py
  37. 84
      slither/detectors/statements/unused_import.py
  38. 106
      slither/detectors/variables/similar_variables.py
  39. 20
      slither/printers/inheritance/inheritance_graph.py
  40. 86
      slither/printers/summary/evm.py
  41. 4
      slither/printers/summary/loc.py
  42. 1
      slither/printers/summary/require_calls.py
  43. 4
      slither/slither.py
  44. 13
      slither/slithir/operations/assignment.py
  45. 21
      slither/slithir/operations/member.py
  46. 4
      slither/solc_parsing/expressions/expression_parsing.py
  47. 2
      slither/solc_parsing/expressions/find_variable.py
  48. 2
      slither/tools/documentation/README.md
  49. 4
      slither/tools/mutator/__main__.py
  50. 39
      slither/tools/mutator/mutators/AOR.py
  51. 4
      slither/tools/mutator/mutators/LIR.py
  52. 8
      slither/tools/mutator/mutators/abstract_mutator.py
  53. 2
      slither/tools/mutator/utils/file_handling.py
  54. 7
      slither/tools/mutator/utils/testing_generated_mutant.py
  55. 1
      slither/tools/upgradeability/checks/all_checks.py
  56. 122
      slither/tools/upgradeability/checks/initialization.py
  57. 5
      slither/utils/command_line.py
  58. 26
      slither/utils/myprettytable.py
  59. 28
      slither/utils/source_mapping.py
  60. 85
      slither/visitors/expression/expression.py
  61. 4
      slither/visitors/expression/read_var.py
  62. 6
      slither/visitors/slithir/expression_to_slithir.py
  63. 2
      slither/vyper_parsing/declarations/contract.py
  64. 3
      tests/e2e/compilation/test_data/test_change/README.md
  65. 6
      tests/e2e/compilation/test_data/test_change/foundry.toml
  66. 16
      tests/e2e/compilation/test_data/test_change/src/Counter.sol
  67. 40
      tests/e2e/compilation/test_diagnostic.py
  68. 8
      tests/e2e/detectors/snapshots/detectors__detector_ArbitrarySendEth_0_6_11_arbitrary_send_eth_sol__0.txt
  69. 8
      tests/e2e/detectors/snapshots/detectors__detector_ArbitrarySendEth_0_7_6_arbitrary_send_eth_sol__0.txt
  70. 4
      tests/e2e/detectors/snapshots/detectors__detector_ConstantPragma_0_4_25_pragma_0_4_25_sol__0.txt
  71. 4
      tests/e2e/detectors/snapshots/detectors__detector_ConstantPragma_0_5_16_pragma_0_5_16_sol__0.txt
  72. 4
      tests/e2e/detectors/snapshots/detectors__detector_ConstantPragma_0_6_11_pragma_0_6_11_sol__0.txt
  73. 4
      tests/e2e/detectors/snapshots/detectors__detector_ConstantPragma_0_7_6_pragma_0_7_6_sol__0.txt
  74. 8
      tests/e2e/detectors/snapshots/detectors__detector_IncorrectSolc_0_4_25_static_sol__0.txt
  75. 4
      tests/e2e/detectors/snapshots/detectors__detector_IncorrectSolc_0_5_14_static_sol__0.txt
  76. 4
      tests/e2e/detectors/snapshots/detectors__detector_IncorrectSolc_0_5_16_dynamic_1_sol__0.txt
  77. 4
      tests/e2e/detectors/snapshots/detectors__detector_IncorrectSolc_0_5_16_dynamic_2_sol__0.txt
  78. 4
      tests/e2e/detectors/snapshots/detectors__detector_IncorrectSolc_0_5_16_static_sol__0.txt
  79. 4
      tests/e2e/detectors/snapshots/detectors__detector_IncorrectSolc_0_6_10_static_sol__0.txt
  80. 4
      tests/e2e/detectors/snapshots/detectors__detector_IncorrectSolc_0_6_11_dynamic_1_sol__0.txt
  81. 4
      tests/e2e/detectors/snapshots/detectors__detector_IncorrectSolc_0_6_11_dynamic_2_sol__0.txt
  82. 4
      tests/e2e/detectors/snapshots/detectors__detector_IncorrectSolc_0_6_11_static_sol__0.txt
  83. 8
      tests/e2e/detectors/snapshots/detectors__detector_IncorrectSolc_0_7_4_static_sol__0.txt
  84. 8
      tests/e2e/detectors/snapshots/detectors__detector_IncorrectSolc_0_7_6_dynamic_1_sol__0.txt
  85. 8
      tests/e2e/detectors/snapshots/detectors__detector_IncorrectSolc_0_7_6_dynamic_2_sol__0.txt
  86. 8
      tests/e2e/detectors/snapshots/detectors__detector_IncorrectSolc_0_7_6_static_sol__0.txt
  87. 2
      tests/e2e/detectors/snapshots/detectors__detector_SimilarVarsDetection_0_4_25_similar_variables_sol__0.txt
  88. 2
      tests/e2e/detectors/snapshots/detectors__detector_SimilarVarsDetection_0_5_16_similar_variables_sol__0.txt
  89. 2
      tests/e2e/detectors/snapshots/detectors__detector_SimilarVarsDetection_0_6_11_similar_variables_sol__0.txt
  90. 2
      tests/e2e/detectors/snapshots/detectors__detector_SimilarVarsDetection_0_7_6_similar_variables_sol__0.txt
  91. 1
      tests/e2e/detectors/snapshots/detectors__detector_UnprotectedUpgradeable_0_4_25_AnyInitializer_sol__0.txt
  92. 1
      tests/e2e/detectors/snapshots/detectors__detector_UnprotectedUpgradeable_0_4_25_Reinitializer_sol__0.txt
  93. 1
      tests/e2e/detectors/snapshots/detectors__detector_UnprotectedUpgradeable_0_5_16_AnyInitializer_sol__0.txt
  94. 1
      tests/e2e/detectors/snapshots/detectors__detector_UnprotectedUpgradeable_0_5_16_Reinitializer_sol__0.txt
  95. 1
      tests/e2e/detectors/snapshots/detectors__detector_UnprotectedUpgradeable_0_6_11_AnyInitializer_sol__0.txt
  96. 1
      tests/e2e/detectors/snapshots/detectors__detector_UnprotectedUpgradeable_0_6_11_Reinitializer_sol__0.txt
  97. 1
      tests/e2e/detectors/snapshots/detectors__detector_UnprotectedUpgradeable_0_7_6_AnyInitializer_sol__0.txt
  98. 1
      tests/e2e/detectors/snapshots/detectors__detector_UnprotectedUpgradeable_0_7_6_Reinitializer_sol__0.txt
  99. 1
      tests/e2e/detectors/snapshots/detectors__detector_UnprotectedUpgradeable_0_8_15_AnyInitializer_sol__0.txt
  100. 1
      tests/e2e/detectors/snapshots/detectors__detector_UnprotectedUpgradeable_0_8_15_Reinitializer_sol__0.txt
  101. Some files were not shown because too many files have changed in this diff Show More

@ -3,9 +3,13 @@ body:
-
attributes:
value: |
Please check the issues tab to avoid duplicates.
Please check the issues tab to avoid duplicates, and
confirm that the bug exists on the latest release (upgrade
by running `python3 -m pip install --upgrade slither-analyzer`).
If you are having difficulty installing slither,
please head over to the "Discussions" page.
Thanks for taking the time to fill out this bug report!
type: markdown
-
@ -17,7 +21,7 @@ body:
required: true
-
attributes:
description: "It can be a github repo, etherscan link, or code snippet."
description: "It can be a github repo (preferred), etherscan link, or code snippet."
label: "Code example to reproduce the issue:"
placeholder: "`contract A {}`\n"
id: reproduce
@ -27,7 +31,7 @@ body:
-
attributes:
description: |
What version of slither are you running?
What version of slither are you running?
Run `slither --version`
label: "Version:"
id: version

@ -27,4 +27,5 @@ runs:
path: |
.coverage.*
*.lcov
if-no-files-found: ignore
if-no-files-found: ignore
include-hidden-files: true

@ -2,11 +2,11 @@
# used to pass --cov=$path and --cov-append to pytest
if [ "$1" != "" ]; then
pytest "$1" tests/tools/read-storage/test_read_storage.py
pytest "$1" tests/tools
status_code=$?
python -m coverage report
else
pytest tests/tools/read-storage/test_read_storage.py
pytest tests/tools
status_code=$?
fi

@ -67,11 +67,11 @@ jobs:
- name: Set up nix
if: matrix.type == 'dapp'
uses: cachix/install-nix-action@v26
uses: cachix/install-nix-action@V27
- name: Set up cachix
if: matrix.type == 'dapp'
uses: cachix/cachix-action@v14
uses: cachix/cachix-action@v15
with:
name: dapp

@ -47,7 +47,7 @@ jobs:
password: ${{ secrets.GITHUB_TOKEN }}
- name: Docker Build and Push
uses: docker/build-push-action@v5
uses: docker/build-push-action@v6
with:
platforms: linux/amd64,linux/arm64/v8,linux/arm/v7
target: final

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

@ -44,10 +44,10 @@ jobs:
path: dist/
- name: publish
uses: pypa/gh-action-pypi-publish@v1.8.14
uses: pypa/gh-action-pypi-publish@v1.9.0
- name: sign
uses: sigstore/gh-action-sigstore-python@v2.1.1
uses: sigstore/gh-action-sigstore-python@v3.0.0
with:
inputs: ./dist/*.tar.gz ./dist/*.whl
release-signing-artifacts: true

5
.gitignore vendored

@ -114,4 +114,7 @@ test_artifacts/
crytic-export/
# Auto-generated Github pages docs
docs/
docs/
# slither.db.json
slither.db.json

@ -0,0 +1,9 @@
- id: slither
name: Slither
description: Run Slither on your project
entry: slither
args:
- .
pass_filenames: false
language: python
files: \.sol$

@ -3,6 +3,7 @@ FROM ubuntu:jammy AS python-wheels
RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
gcc \
git \
make \
python3-dev \
python3-pip \
&& rm -rf /var/lib/apt/lists/*

@ -3,5 +3,10 @@
"op-mainnet": {
"ownedBy": "0xc44F30Be3eBBEfdDBB5a85168710b4f0e18f4Ff0"
}
},
"drips": {
"ethereum": {
"ownedBy": "0x5e2BA02F62bD4efa939e3B80955bBC21d015DbA0"
}
}
}

@ -102,6 +102,13 @@ docker run -it -v /home/share:/share trailofbits/eth-security-toolbox
### Integration
* For GitHub action integration, use [slither-action](https://github.com/marketplace/actions/slither-action).
* For pre-commit integration, use (replace `$GIT_TAG` with real tag)
```YAML
- repo: https://github.com/crytic/slither
rev: $GIT_TAG
hooks:
- id: slither
```
* To generate a Markdown report, use `slither [target] --checklist`.
* To generate a Markdown with GitHub source code highlighting, use `slither [target] --checklist --markdown-root https://github.com/ORG/REPO/blob/COMMIT/` (replace `ORG`, `REPO`, `COMMIT`)
@ -196,13 +203,12 @@ Num | Detector | What it Detects | Impact | Confidence
85 | `costly-loop` | [Costly operations in a loop](https://github.com/crytic/slither/wiki/Detector-Documentation#costly-operations-inside-a-loop) | Informational | Medium
86 | `dead-code` | [Functions that are not used](https://github.com/crytic/slither/wiki/Detector-Documentation#dead-code) | Informational | Medium
87 | `reentrancy-unlimited-gas` | [Reentrancy vulnerabilities through send and transfer](https://github.com/crytic/slither/wiki/Detector-Documentation#reentrancy-vulnerabilities-4) | Informational | Medium
88 | `similar-names` | [Variable names are too similar](https://github.com/crytic/slither/wiki/Detector-Documentation#variable-names-too-similar) | Informational | Medium
89 | `too-many-digits` | [Conformance to numeric notation best practices](https://github.com/crytic/slither/wiki/Detector-Documentation#too-many-digits) | Informational | Medium
90 | `cache-array-length` | [Detects `for` loops that use `length` member of some storage array in their loop condition and don't modify it.](https://github.com/crytic/slither/wiki/Detector-Documentation#cache-array-length) | Optimization | High
91 | `constable-states` | [State variables that could be declared constant](https://github.com/crytic/slither/wiki/Detector-Documentation#state-variables-that-could-be-declared-constant) | Optimization | High
92 | `external-function` | [Public function that could be declared external](https://github.com/crytic/slither/wiki/Detector-Documentation#public-function-that-could-be-declared-external) | Optimization | High
93 | `immutable-states` | [State variables that could be declared immutable](https://github.com/crytic/slither/wiki/Detector-Documentation#state-variables-that-could-be-declared-immutable) | Optimization | High
94 | `var-read-using-this` | [Contract reads its own variable using `this`](https://github.com/crytic/slither/wiki/Detector-Documentation#public-variable-read-in-external-context) | Optimization | High
88 | `too-many-digits` | [Conformance to numeric notation best practices](https://github.com/crytic/slither/wiki/Detector-Documentation#too-many-digits) | Informational | Medium
89 | `cache-array-length` | [Detects `for` loops that use `length` member of some storage array in their loop condition and don't modify it.](https://github.com/crytic/slither/wiki/Detector-Documentation#cache-array-length) | Optimization | High
90 | `constable-states` | [State variables that could be declared constant](https://github.com/crytic/slither/wiki/Detector-Documentation#state-variables-that-could-be-declared-constant) | Optimization | High
91 | `external-function` | [Public function that could be declared external](https://github.com/crytic/slither/wiki/Detector-Documentation#public-function-that-could-be-declared-external) | Optimization | High
92 | `immutable-states` | [State variables that could be declared immutable](https://github.com/crytic/slither/wiki/Detector-Documentation#state-variables-that-could-be-declared-immutable) | Optimization | High
93 | `var-read-using-this` | [Contract reads its own variable using `this`](https://github.com/crytic/slither/wiki/Detector-Documentation#public-variable-read-in-external-context) | Optimization | High
For more information, see
@ -302,5 +308,12 @@ Title | Usage | Authors | Venue | Code
[Do Not Rug on Me: Leveraging Machine Learning Techniques for Automated Scam Detection](https://www.mdpi.com/2227-7390/10/6/949) | Use Slither to extract tokens' features (mintable, pausable, ..) | Mazorra, Bruno, Victor Adan, and Vanesa Daza | Mathematics 10.6 (2022) | -
[MANDO: Multi-Level Heterogeneous Graph Embeddings for Fine-Grained Detection of Smart Contract Vulnerabilities](https://arxiv.org/abs/2208.13252) | Use Slither to extract the CFG and call graph | Hoang Nguyen, Nhat-Minh Nguyen, Chunyao Xie, Zahra Ahmadi, Daniel Kudendo, Thanh-Nam Doan and Lingxiao Jiang| IEEE 9th International Conference on Data Science and Advanced Analytics (DSAA, 2022) | [ge-sc](https://github.com/MANDO-Project/ge-sc)
[Automated Auditing of Price Gouging TOD Vulnerabilities in Smart Contracts](https://www.cs.toronto.edu/~fanl/papers/price-icbc22.pdf) | Use Slither to extract the CFG and data dependencies| Sidi Mohamed Beillahi, Eric Keilty, Keerthi Nelaturu, Andreas Veneris, and Fan Long | 2022 IEEE International Conference on Blockchain and Cryptocurrency (ICBC) | [Smart-Contract-Repair](https://github.com/Veneris-Group/TOD-Location-Rectification)
[Modeling and Enforcing Access Control Policies for Smart Contracts](https://publikationen.bibliothek.kit.edu/1000152805/151859658) | Extend Slither's data dependencies | Jan-Philipp Toberg, Jonas Schiffl, Frederik Reiche, Bernhard Beckert, Robert Heinrich, Ralf Reussner | IEEE International Conference on Decentralized Applications and Infrastructures (DAPPS), 2022 | [SolidityAccessControlEnforcement](https://github.com/KASTEL-CSSDA/SolidityAccessControlEnforcement)
[Smart Contract Vulnerability Detection Based on Deep Learning and Multimodal Decision Fusion](https://www.mdpi.com/1424-8220/23/16/7246) | Use Slither to extract the CFG | Weichu Deng, Huanchun Wei, Teng Huang, Cong Cao, Yun Peng, and Xuan Hu | Sensors 2023, 23, 7246 | -
[Semantic-enriched Code Knowledge Graph to Reveal Unknowns in Smart Contract Code Reuse](https://www.researchgate.net/profile/Qing-Huang-26/publication/370638129_Semantic-enriched_Code_Knowledge_Graph_to_Reveal_Unknowns_in_Smart_Contract_Code_Reuse/links/645b7b8639c408339b3a54da/Semantic-Enriched-Code-Knowledge-Graph-to-Reveal-Unknowns-in-Smart-Contract-Code-Reuse.pdf) | Use Slither to extract the code features (CFG, function, parameters types, ..) | Qing Huang, Dianshu Liao, Zhenchang Xing, Zhengkang Zuo, Changjing Wang, Xin Xia | ACM Transactions on Software Engineering and Methodology, 2023 | -
[Smart Contract Parallel Execution with Fine-Grained State Accesses](https://personal.ntu.edu.sg/yi_li/files/Qi2023SCP.pdf) | Use Slither to build state access graphs | Xiaodong Qi, Jiao Jiao, Yi Li | International Conference on Distributed Computing Systems (ICDCS), 2023 | -
[Bad Apples: Understanding the Centralized Security Risks in Decentralized Ecosystems](https://diaowenrui.github.io/paper/www23-yan.pdf) | Implement an internal analysis on top of Slither | Kailun Yan , Jilian Zhang , Xiangyu Liu , Wenrui Diao , Shanqing Guo | ACM Web Conference April 2023 | -
[Identifying Vulnerabilities in Smart Contracts using Interval Analysis](https://arxiv.org/pdf/2309.13805.pdf) | Create 4 detectors on top of Slither | Ştefan-Claudiu Susan, Andrei Arusoaie | FROM 2023 | -
Storage State Analysis and Extraction of Ethereum Blockchain Smart Contracts (no PDF in open access) | Rely on Slither's CFG and AST | Maha Ayub , Tania Saleem , Muhammad Janjua , Talha Ahmad | TOSEM 2023 | [SmartMuv](https://github.com/WaizKhan7/SmartMuv)
If you are using Slither on an academic work, consider applying to the [Crytic $10k Research Prize](https://blog.trailofbits.com/2019/11/13/announcing-the-crytic-10k-research-prize/).

@ -1,84 +0,0 @@
#!/usr/bin/env bash
### Test Detectors
DIR="$(cd "$(dirname "$0")" && pwd)"
CURRENT_PATH=$(pwd)
TRAVIS_PATH='/home/travis/build/crytic/slither'
# test_slither file.sol detectors
test_slither(){
expected="$DIR/../tests/expected_json/$(basename "$1" .sol).$2.json"
# run slither detector on input file and save output as json
if ! slither "$1" --solc-disable-warnings --detect "$2" --json "$DIR/tmp-test.json";
then
echo "Slither crashed"
exit 255
fi
if [ ! -f "$DIR/tmp-test.json" ]; then
echo ""
echo "Missing generated file"
echo ""
exit 1
fi
sed "s|$CURRENT_PATH|$TRAVIS_PATH|g" "$DIR/tmp-test.json" -i
result=$(python "$DIR/json_diff.py" "$expected" "$DIR/tmp-test.json")
rm "$DIR/tmp-test.json"
if [ "$result" != "{}" ]; then
echo ""
echo "failed test of file: $1, detector: $2"
echo ""
echo "$result"
echo ""
exit 1
fi
# run slither detector on input file and save output as json
if ! slither "$1" --solc-disable-warnings --detect "$2" --legacy-ast --json "$DIR/tmp-test.json";
then
echo "Slither crashed"
exit 255
fi
if [ ! -f "$DIR/tmp-test.json" ]; then
echo ""
echo "Missing generated file"
echo ""
exit 1
fi
sed "s|$CURRENT_PATH|$TRAVIS_PATH|g" "$DIR/tmp-test.json" -i
result=$(python "$DIR/json_diff.py" "$expected" "$DIR/tmp-test.json")
rm "$DIR/tmp-test.json"
if [ "$result" != "{}" ]; then
echo ""
echo "failed test of file: $1, detector: $2"
echo ""
echo "$result"
echo ""
exit 1
fi
}
# generate_expected_json file.sol detectors
generate_expected_json(){
# generate output filename
# e.g. file: uninitialized.sol detector: uninitialized-state
# ---> uninitialized.uninitialized-state.json
output_filename="$DIR/../tests/expected_json/$(basename "$1" .sol).$2.json"
output_filename_txt="$DIR/../tests/expected_json/$(basename "$1" .sol).$2.txt"
# run slither detector on input file and save output as json
slither "$1" --solc-disable-warnings --detect "$2" --json "$output_filename" > "$output_filename_txt" 2>&1
sed "s|$CURRENT_PATH|$TRAVIS_PATH|g" "$output_filename" -i
sed "s|$CURRENT_PATH|$TRAVIS_PATH|g" "$output_filename_txt" -i
}

@ -2,7 +2,8 @@
### Test slither-check-upgradeability
DIR_TESTS="tests/check-upgradeability"
DIR_TESTS="tests/tools/check_upgradeability"
solc-select install "0.5.0"
solc-select use "0.5.0"
slither-check-upgradeability "$DIR_TESTS/contractV1.sol" ContractV1 --proxy-filename "$DIR_TESTS/proxy.sol" --proxy-name Proxy > test_1.txt 2>&1
@ -181,6 +182,32 @@ then
exit 255
fi
slither-check-upgradeability "$DIR_TESTS/contract_initialization.sol" Contract_no_bug_reinitializer --proxy-filename "$DIR_TESTS/proxy.sol" --proxy-name Proxy > test_14.txt 2>&1
DIFF=$(diff test_14.txt "$DIR_TESTS/test_14.txt")
if [ "$DIFF" != "" ]
then
echo "slither-check-upgradeability 14 failed"
cat test_14.txt
echo ""
cat "$DIR_TESTS/test_14.txt"
echo ""
echo "$DIFF"
exit 255
fi
slither-check-upgradeability "$DIR_TESTS/contract_initialization.sol" Contract_reinitializer_V2 --new-contract-name Counter_reinitializer_V3_V4 > test_15.txt 2>&1
DIFF=$(diff test_15.txt "$DIR_TESTS/test_15.txt")
if [ "$DIFF" != "" ]
then
echo "slither-check-upgradeability 14 failed"
cat test_15.txt
echo ""
cat "$DIR_TESTS/test_15.txt"
echo ""
echo "$DIFF"
exit 255
fi
rm test_1.txt
rm test_2.txt
rm test_3.txt
@ -194,3 +221,5 @@ rm test_10.txt
rm test_11.txt
rm test_12.txt
rm test_13.txt
rm test_14.txt
rm test_15.txt

@ -1,27 +0,0 @@
import sys
import json
from pprint import pprint
from deepdiff import DeepDiff # pip install deepdiff
if len(sys.argv) != 3:
print("Usage: python json_diff.py 1.json 2.json")
sys.exit(-1)
with open(sys.argv[1], encoding="utf8") as f:
d1 = json.load(f)
with open(sys.argv[2], encoding="utf8") as f:
d2 = json.load(f)
# Remove description field to allow non deterministic print
for elem in d1:
if "description" in elem:
del elem["description"]
for elem in d2:
if "description" in elem:
del elem["description"]
pprint(DeepDiff(d1, d2, ignore_order=True, verbose_level=2))

@ -8,16 +8,16 @@ setup(
description="Slither is a Solidity and Vyper static analysis framework written in Python 3.",
url="https://github.com/crytic/slither",
author="Trail of Bits",
version="0.10.2",
version="0.10.4",
packages=find_packages(),
python_requires=">=3.8",
install_requires=[
"packaging",
"prettytable>=3.3.0",
"prettytable>=3.10.2",
"pycryptodome>=3.4.6",
"crytic-compile>=0.3.7,<0.4.0",
# "crytic-compile@git+https://github.com/crytic/crytic-compile.git@master#egg=crytic-compile",
"web3>=6.0.0",
"web3>=6.20.2, <7",
"eth-abi>=4.0.0",
"eth-typing>=3.0.0",
"eth-utils>=2.1.0",

@ -11,10 +11,10 @@ import pstats
import sys
import traceback
from importlib import metadata
from typing import Tuple, Optional, List, Dict, Type, Union, Any, Sequence
from typing import Any, Dict, List, Optional, Sequence, Set, Tuple, Type, Union
from crytic_compile import cryticparser, CryticCompile, InvalidCompilation
from crytic_compile import cryticparser, CryticCompile
from crytic_compile.platform.standard import generate_standard_export
from crytic_compile.platform.etherscan import SUPPORTED_NETWORK
from crytic_compile import compile_all, is_supported
@ -93,13 +93,7 @@ def process_all(
detector_classes: List[Type[AbstractDetector]],
printer_classes: List[Type[AbstractPrinter]],
) -> Tuple[List[Slither], List[Dict], List[Output], int]:
try:
compilations = compile_all(target, **vars(args))
except InvalidCompilation:
logger.error("Unable to compile all targets.")
sys.exit(2)
compilations = compile_all(target, **vars(args))
slither_instances = []
results_detectors = []
results_printers = []
@ -211,43 +205,50 @@ def choose_detectors(
if args.detectors_to_run == "all":
detectors_to_run = all_detector_classes
if args.detectors_to_exclude:
detectors_excluded = args.detectors_to_exclude.split(",")
for detector in detectors:
if detector in detectors_excluded:
detectors_to_run.remove(detectors[detector])
else:
for detector in args.detectors_to_run.split(","):
if detector in detectors:
detectors_to_run.append(detectors[detector])
else:
raise ValueError(f"Error: {detector} is not a detector")
detectors_to_run = sorted(detectors_to_run, key=lambda x: x.IMPACT)
detectors_to_run = __include_detectors(
set(detectors_to_run), args.detectors_to_run, detectors
)
return detectors_to_run
if args.exclude_optimization:
detectors_to_run = [
d for d in detectors_to_run if d.IMPACT != DetectorClassification.OPTIMIZATION
]
classification_map = {
DetectorClassification.HIGH: args.exclude_high,
DetectorClassification.MEDIUM: args.exclude_medium,
DetectorClassification.LOW: args.exclude_low,
DetectorClassification.INFORMATIONAL: args.exclude_informational,
DetectorClassification.OPTIMIZATION: args.exclude_optimization,
}
excluded_classification = [
classification for classification, included in classification_map.items() if included
]
detectors_to_run = [d for d in detectors_to_run if d.IMPACT not in excluded_classification]
if args.exclude_informational:
detectors_to_run = [
d for d in detectors_to_run if d.IMPACT != DetectorClassification.INFORMATIONAL
]
if args.exclude_low:
detectors_to_run = [d for d in detectors_to_run if d.IMPACT != DetectorClassification.LOW]
if args.exclude_medium:
detectors_to_run = [
d for d in detectors_to_run if d.IMPACT != DetectorClassification.MEDIUM
]
if args.exclude_high:
detectors_to_run = [d for d in detectors_to_run if d.IMPACT != DetectorClassification.HIGH]
if args.detectors_to_exclude:
detectors_to_run = [
d for d in detectors_to_run if d.ARGUMENT not in args.detectors_to_exclude
]
if args.detectors_to_include:
detectors_to_run = __include_detectors(
set(detectors_to_run), args.detectors_to_include, detectors
)
detectors_to_run = sorted(detectors_to_run, key=lambda x: x.IMPACT)
return detectors_to_run
def __include_detectors(
detectors_to_run: Set[Type[AbstractDetector]],
detectors_to_include: str,
detectors: Dict[str, Type[AbstractDetector]],
) -> List[Type[AbstractDetector]]:
include_detectors = detectors_to_include.split(",")
for detector in include_detectors:
if detector in detectors:
detectors_to_run.add(detectors[detector])
else:
raise ValueError(f"Error: {detector} is not a detector")
return detectors_to_run
@ -341,6 +342,14 @@ def parse_args(
default=defaults_flag_in_config["printers_to_run"],
)
group_printer.add_argument(
"--include-interfaces",
help="Include interfaces from inheritance-graph printer",
action="store_true",
dest="include_interfaces",
default=False,
)
group_detector.add_argument(
"--list-detectors",
help="List available detectors",
@ -407,6 +416,14 @@ def parse_args(
default=defaults_flag_in_config["exclude_high"],
)
group_detector.add_argument(
"--include-detectors",
help="Comma-separated list of detectors that should be included",
action="store",
dest="detectors_to_include",
default=defaults_flag_in_config["detectors_to_include"],
)
fail_on_group = group_detector.add_mutually_exclusive_group()
fail_on_group.add_argument(
"--fail-pedantic",

@ -178,15 +178,14 @@ def generate_source_to_evm_ins_mapping(evm_instructions, srcmap_runtime, slither
# In order to compress these source mappings especially for bytecode, the following rules are used:
# If a field is empty, the value of the preceding element is used.
# If a : is missing, all following fields are considered empty.
mapping_item = mapping.split(":")
mapping_item += prev_mapping[len(mapping_item) :]
for i, _ in enumerate(mapping_item):
if mapping_item[i] == "":
mapping_item[i] = int(prev_mapping[i])
mapping_item[i] = prev_mapping[i]
offset, _length, file_id, *_ = mapping_item
offset, _, file_id, *_ = mapping_item
prev_mapping = mapping_item
if file_id == "-1":
@ -194,8 +193,7 @@ def generate_source_to_evm_ins_mapping(evm_instructions, srcmap_runtime, slither
# See https://github.com/ethereum/solidity/issues/6119#issuecomment-467797635
continue
offset = int(offset)
line_number = file_source[0:offset].count("\n".encode("utf-8")) + 1
line_number = file_source[0 : int(offset)].count("\n".encode("utf-8")) + 1
# Append evm instructions to the corresponding source line number
# Note: Some evm instructions in mapping are not necessarily in program execution order

@ -529,7 +529,8 @@ class Node(SourceMapping): # pylint: disable=too-many-public-methods
bool: True if the node has a require or assert call
"""
return any(
c.name in ["require(bool)", "require(bool,string)", "assert(bool)"]
c.name
in ["require(bool)", "require(bool,string)", "require(bool,error)", "assert(bool)"]
for c in self.internal_calls
)

@ -1372,8 +1372,6 @@ class Contract(SourceMapping): # pylint: disable=too-many-public-methods
def is_upgradeable(self) -> bool:
if self._is_upgradeable is None:
self._is_upgradeable = False
if self.is_upgradeable_proxy:
return False
initializable = self.file_scope.get_contract_from_name("Initializable")
if initializable:
if initializable in self.inheritance:

@ -17,6 +17,7 @@ class CustomError(SourceMapping):
self._solidity_signature: Optional[str] = None
self._full_name: Optional[str] = None
self._pattern = "error"
@property
def name(self) -> str:

@ -94,14 +94,11 @@ class FunctionType(Enum):
def _filter_state_variables_written(expressions: List["Expression"]):
ret = []
for expression in expressions:
if isinstance(expression, Identifier):
ret.append(expression)
if isinstance(expression, UnaryOperation):
ret.append(expression.expression)
if isinstance(expression, MemberAccess):
if isinstance(expression, (Identifier, UnaryOperation, MemberAccess)):
ret.append(expression.expression)
if isinstance(expression, IndexAccess):
elif isinstance(expression, IndexAccess):
ret.append(expression.expression_left)
return ret

@ -16,6 +16,8 @@ class Import(SourceMapping):
# Map local name -> original name
self.renaming: Dict[str, str] = {}
self._pattern = "import"
@property
def filename(self) -> str:
"""

@ -11,6 +11,7 @@ class Pragma(SourceMapping):
super().__init__()
self._directive = directive
self.scope: "FileScope" = scope
self._pattern = "pragma"
@property
def directive(self) -> List[str]:

@ -50,6 +50,7 @@ SOLIDITY_FUNCTIONS: Dict[str, List[str]] = {
"assert(bool)": [],
"require(bool)": [],
"require(bool,string)": [],
"require(bool,error)": [], # Solidity 0.8.26 via-ir and Solidity >= 0.8.27
"revert()": [],
"revert(string)": [],
"revert ": [],

@ -78,3 +78,6 @@ class Identifier(Expression):
def __str__(self) -> str:
return str(self._value)
def expression(self):
return self

@ -524,6 +524,7 @@ class SlitherCore(Context):
def save_results_to_hide(self, results: List[Dict]) -> None:
self._results_to_hide += results
self.write_results_to_hide()
def add_path_to_filter(self, path: str):
"""

@ -225,4 +225,4 @@ class ElementaryType(Type):
return self.type == other.type
def __hash__(self) -> int:
return hash(str(self))
return hash(self._type)

@ -15,6 +15,7 @@ class TypeAlias(Type):
super().__init__()
self.name = name
self.underlying_type = underlying_type
self._pattern = "type"
@property
def type(self) -> ElementaryType:

@ -1,10 +1,10 @@
import re
from abc import ABCMeta
from typing import Dict, Union, List, Tuple, TYPE_CHECKING, Optional, Any
from Crypto.Hash import SHA1
from crytic_compile.utils.naming import Filename
from slither.core.context.context import Context
from slither.exceptions import SlitherException
if TYPE_CHECKING:
from slither.core.compilation_unit import SlitherCompilationUnit
@ -99,21 +99,29 @@ class Source:
return f"{filename_short}{lines}"
def __hash__(self) -> int:
return hash(str(self))
return hash(
(
self.start,
self.length,
self.filename.relative,
self.end,
)
)
def __eq__(self, other: Any) -> bool:
if not isinstance(other, type(self)):
try:
return (
self.start == other.start
and self.length == other.length
and self.filename == other.filename
and self.is_dependency == other.is_dependency
and self.lines == other.lines
and self.starting_column == other.starting_column
and self.ending_column == other.ending_column
and self.end == other.end
)
except AttributeError:
return NotImplemented
return (
self.start == other.start
and self.length == other.length
and self.filename == other.filename
and self.is_dependency == other.is_dependency
and self.lines == other.lines
and self.starting_column == other.starting_column
and self.ending_column == other.ending_column
and self.end == other.end
)
def _compute_line(
@ -129,9 +137,20 @@ def _compute_line(
start_line, starting_column = compilation_unit.core.crytic_compile.get_line_from_offset(
filename, start
)
end_line, ending_column = compilation_unit.core.crytic_compile.get_line_from_offset(
filename, start + length
)
try:
end_line, ending_column = compilation_unit.core.crytic_compile.get_line_from_offset(
filename, start + length
)
except KeyError:
# This error may occur when the build is not synchronised with the source code on disk.
# See the GitHub issue https://github.com/crytic/slither/issues/2296
msg = f"""The source code appears to be out of sync with the build artifacts on disk.
This discrepancy can occur after recent modifications to {filename.short}. To resolve this
issue, consider executing the clean command of the build system (e.g. forge clean).
"""
# We still re-raise the exception as a SlitherException here
raise SlitherException(msg) from None
return list(range(start_line, end_line + 1)), starting_column, ending_column
@ -183,12 +202,14 @@ def _convert_source_mapping(
return new_source
class SourceMapping(Context, metaclass=ABCMeta):
class SourceMapping(Context):
def __init__(self) -> None:
super().__init__()
self.source_mapping: Optional[Source] = None
self.references: List[Source] = []
self._pattern: Union[str, None] = None
def set_offset(
self, offset: Union["Source", str], compilation_unit: "SlitherCompilationUnit"
) -> None:
@ -204,3 +225,11 @@ class SourceMapping(Context, metaclass=ABCMeta):
) -> None:
s = _convert_source_mapping(offset, compilation_unit)
self.references.append(s)
@property
def pattern(self) -> str:
if self._pattern is None:
# Add " " to look after the first solidity keyword
return f" {self.name}" # pylint: disable=no-member
return self._pattern

@ -57,7 +57,6 @@ from .slither.name_reused import NameReused
from .functions.unimplemented import UnimplementedFunctionDetection
from .statements.mapping_deletion import MappingDeletionDetection
from .statements.array_length_assignment import ArrayLengthAssignment
from .variables.similar_variables import SimilarVarsDetection
from .variables.function_init_state_variables import FunctionInitializedState
from .statements.redundant_statements import RedundantStatements
from .operations.bad_prng import BadPRNG
@ -98,4 +97,5 @@ from .operations.incorrect_exp import IncorrectOperatorExponentiation
from .statements.tautological_compare import TautologicalCompare
from .statements.return_bomb import ReturnBomb
from .functions.out_of_order_retryable import OutOfOrderRetryable
from .statements.unused_import import UnusedImport
# from .statements.unused_import import UnusedImport

@ -36,21 +36,19 @@ class ConstantPragma(AbstractDetector):
for pragma in self.compilation_unit.pragma_directives:
if pragma.is_solidity_version:
if pragma.version not in pragma_directives_by_version:
pragma_directives_by_version[
pragma.version
] = f"\t\t- {str(pragma.source_mapping)}\n"
pragma_directives_by_version[pragma.version] = [pragma]
else:
pragma_directives_by_version[
pragma.version
] += f"\t\t- {str(pragma.source_mapping)}\n"
pragma_directives_by_version[pragma.version].append(pragma)
versions = list(pragma_directives_by_version.keys())
if len(versions) > 1:
info: DETECTOR_INFO = [f"{len(versions)} different versions of Solidity are used:\n"]
for version in versions:
pragma = pragma_directives_by_version[version]
info += [f"\t- Version constraint {version} is used by:\n {pragma}"]
pragmas = pragma_directives_by_version[version]
info += [f"\t- Version constraint {version} is used by:\n"]
for pragma in pragmas:
info += ["\t\t-", pragma, "\n"]
res = self.generate_result(info)

@ -71,7 +71,7 @@ Consider using the latest version of Solidity for testing."""
if op and op not in [">", ">=", "^"]:
return self.LESS_THAN_TXT
version_number = ".".join(version[2:])
if version_number in bugs_by_version:
if version_number in bugs_by_version and len(bugs_by_version[version_number]):
bugs = "\n".join([f"\t- {bug}" for bug in bugs_by_version[version_number]])
return self.BUGGY_VERSION_TXT + f"\n{bugs}"
return None
@ -115,16 +115,18 @@ Consider using the latest version of Solidity for testing."""
continue
if p.version in disallowed_pragmas and reason in disallowed_pragmas[p.version]:
disallowed_pragmas[p.version][reason] += f"\t- {str(p.source_mapping)}\n"
disallowed_pragmas[p.version][reason].append(p)
else:
disallowed_pragmas[p.version] = {reason: f"\t- {str(p.source_mapping)}\n"}
disallowed_pragmas[p.version] = {reason: [p]}
# If we found any disallowed pragmas, we output our findings.
if len(disallowed_pragmas.keys()):
for p, reasons in disallowed_pragmas.items():
info: DETECTOR_INFO = []
for r, v in reasons.items():
info += [f"Version constraint {p} {r}.\n It is used by:\n{v}"]
for r, vers in reasons.items():
info += [f"Version constraint {p} {r}.\nIt is used by:\n"]
for ver in vers:
info += ["\t- ", ver, "\n"]
json = self.generate_result(info)

@ -30,6 +30,7 @@ from slither.slithir.operations import (
SolidityCall,
Transfer,
)
from slither.core.variables.state_variable import StateVariable
# pylint: disable=too-many-nested-blocks,too-many-branches
from slither.utils.output import Output
@ -67,6 +68,8 @@ def arbitrary_send(func: Function) -> Union[bool, List[Node]]:
continue
if ir.call_value == SolidityVariableComposed("msg.value"):
continue
if isinstance(ir.destination, StateVariable) and ir.destination.is_immutable:
continue
if is_dependent(
ir.call_value,
SolidityVariableComposed("msg.value"),

@ -25,7 +25,7 @@ class DeadCode(AbstractDetector):
WIKI = "https://github.com/crytic/slither/wiki/Detector-Documentation#dead-code"
WIKI_TITLE = "Dead-code"
WIKI_DESCRIPTION = "Functions that are not sued."
WIKI_DESCRIPTION = "Functions that are not used."
# region wiki_exploit_scenario
WIKI_EXPLOIT_SCENARIO = """
@ -71,9 +71,10 @@ contract Contract{
continue
if isinstance(function, FunctionContract) and (
function.contract_declarer.is_from_dependency()
or function.contract_declarer.is_library
):
continue
# Continue if the functon is not implemented because it means the contract is abstract
# Continue if the function is not implemented because it means the contract is abstract
if not function.is_implemented:
continue
info: DETECTOR_INFO = [function, " is never used and should be removed\n"]

@ -52,7 +52,14 @@ def _whitelisted_modifiers(f: Function) -> bool:
def _initialize_functions(contract: Contract) -> List[Function]:
return list(
filter(_whitelisted_modifiers, [f for f in contract.functions if f.name == "initialize"])
filter(
_whitelisted_modifiers,
[
f
for f in contract.functions
if any((m.name in ["initializer", "reinitializer"]) for m in f.modifiers)
],
)
)

@ -1,5 +1,6 @@
from typing import List
from slither.detectors.abstract_detector import AbstractDetector, DetectorClassification, Output
from slither.core.scope.scope import FileScope
# pylint: disable=protected-access,too-many-nested-blocks
class UnusedImport(AbstractDetector):
@ -30,26 +31,71 @@ class UnusedImport(AbstractDetector):
"Remove the unused import. If the import is needed later, it can be added back."
)
def _detect(self) -> List[Output]:
@staticmethod
def _is_import_container(scope: FileScope) -> bool: # pylint: disable=too-many-branches
"""
Returns True if a given file (provided as a `FileScope` object) contains only `import` directives (and pragmas).
Such a file doesn't need the imports it contains, but its purpose is to aggregate certain correlated imports.
"""
for c in scope.contracts.values():
if c.file_scope == scope:
return False
for err in scope.custom_errors:
if err.file_scope == scope:
return False
for en in scope.enums.values():
if en.file_scope == scope:
return False
for f in scope.functions:
if f.file_scope == scope:
return False
for st in scope.structures.values():
if st.file_scope == scope:
return False
for ct in scope.type_aliases.values():
if ct.source_mapping and ct.source_mapping.filename == scope.filename:
return False
for uf in scope.using_for_directives:
if uf.file_scope == scope:
return False
for v in scope.variables.values():
if v.file_scope == scope:
return False
return True
def _detect(self) -> List[Output]: # pylint: disable=too-many-branches
results: List[Output] = []
# This is computed lazily and then memoized so we need to trigger the computation.
self.slither._compute_offsets_to_ref_impl_decl()
for unit in self.slither.compilation_units:
for filename, scope in unit.scopes.items():
unused = []
for i in scope.imports:
for filename, current_scope in unit.scopes.items():
# Skip files that are dependencies
if unit.crytic_compile.is_dependency(filename.absolute):
continue
unused_list = []
for i in current_scope.imports:
# `scope.imports` contains all transitive imports so we need to filter out imports not explicitly imported in the file.
# Otherwise, we would recommend removing an import that is used by a leaf contract and cause compilation errors.
if i.scope != scope:
if i.scope != current_scope:
continue
# If a scope doesn't define any contract, function, etc., it is an import container.
# The second case accounts for importing from an import container as a reference will only be in the definition's file.
if self._is_import_container(i.scope) or self._is_import_container(
unit.get_scope(i.filename)
):
continue
import_path = self.slither.crytic_compile.filename_lookup(i.filename)
imported_path = self.slither.crytic_compile.filename_lookup(i.filename)
use_found = False
# Search through all references to the imported file
for _, refs in self.slither._offset_to_references[import_path].items():
for ref in refs:
for _, refs_to_imported_path in self.slither._offset_to_references[
imported_path
].items():
for ref in refs_to_imported_path:
# If there is a reference in this file to the imported file, it is used.
if ref.filename == filename:
use_found = True
@ -59,17 +105,15 @@ class UnusedImport(AbstractDetector):
break
if not use_found:
unused.append(f"{i.source_mapping.content} ({i.source_mapping})")
if len(unused) > 0:
unused_list = "\n\t-" + "\n\t-".join(unused)
results.append(
self.generate_result(
[
f"The following unused import(s) in {filename.used} should be removed: {unused_list}\n",
]
)
)
unused_list.append(f"{i.source_mapping.content} ({i.source_mapping})")
if len(unused_list) > 0:
info = [
f"The following unused import(s) in {filename.used} should be removed:",
]
for unused in unused_list:
info += ["\n\t-", unused, "\n"]
results.append(self.generate_result(info))
return results

@ -1,106 +0,0 @@
"""
Check for state variables too similar
Do not check contract inheritance
"""
import difflib
from typing import List, Set, Tuple
from slither.core.declarations.contract import Contract
from slither.core.variables.local_variable import LocalVariable
from slither.detectors.abstract_detector import (
AbstractDetector,
DetectorClassification,
DETECTOR_INFO,
)
from slither.utils.output import Output
class SimilarVarsDetection(AbstractDetector):
"""
Variable similar detector
"""
ARGUMENT = "similar-names"
HELP = "Variable names are too similar"
IMPACT = DetectorClassification.INFORMATIONAL
CONFIDENCE = DetectorClassification.MEDIUM
WIKI = (
"https://github.com/crytic/slither/wiki/Detector-Documentation#variable-names-too-similar"
)
WIKI_TITLE = "Variable names too similar"
WIKI_DESCRIPTION = "Detect variables with names that are too similar."
WIKI_EXPLOIT_SCENARIO = "Bob uses several variables with similar names. As a result, his code is difficult to review."
WIKI_RECOMMENDATION = "Prevent variables from having similar names."
@staticmethod
def similar(seq1: str, seq2: str) -> bool:
"""Test the name similarity
Two name are similar if difflib.SequenceMatcher on the lowercase
version of the name is greater than 0.90
See: https://docs.python.org/2/library/difflib.html
Args:
seq1 (str): first name
seq2 (str): second name
Returns:
bool: true if names are similar
"""
val = difflib.SequenceMatcher(a=seq1, b=seq2).ratio()
ret = val > 0.90
return ret
@staticmethod
def detect_sim(contract: Contract) -> Set[Tuple[LocalVariable, LocalVariable]]:
"""Detect variables with similar name
Returns:
bool: true if variables have similar name
"""
all_var = [x.variables for x in contract.functions]
all_var = [x for l in all_var for x in l]
contract_var = contract.variables
all_var = list(set(all_var + contract_var))
ret = set()
# pylint: disable=consider-using-enumerate
for i in range(len(all_var)):
v1 = all_var[i]
_v1_name_lower = v1.name.lower()
for j in range(i, len(all_var)):
v2 = all_var[j]
if len(v1.name) != len(v2.name):
continue
_v2_name_lower = v2.name.lower()
if _v1_name_lower != _v2_name_lower:
if SimilarVarsDetection.similar(_v1_name_lower, _v2_name_lower):
ret.add((v1, v2))
return ret
def _detect(self) -> List[Output]:
"""Detect similar variables name
Returns:
list: {'vuln', 'filename,'contract','vars'}
"""
results = []
for c in self.contracts:
allVars = self.detect_sim(c)
if allVars:
for (v1, v2) in sorted(allVars, key=lambda x: (x[0].name, x[1].name)):
v_left = v1 if v1.name < v2.name else v2
v_right = v2 if v_left == v1 else v1
info: DETECTOR_INFO = [
"Variable ",
v_left,
" is too similar to ",
v_right,
"\n",
]
json = self.generate_result(info)
results.append(json)
return results

@ -98,12 +98,21 @@ class PrinterInheritanceGraph(AbstractPrinter):
"""
ret = ""
# Remove contracts that have "mock" in the name and if --include-interfaces in False (default)
# removes inherited interfaces
inheritance = [
i
for i in contract.immediate_inheritance
if "mock" not in i.name.lower()
and (not i.is_interface or self.slither.include_interfaces)
]
# Add arrows (number them if there is more than one path so we know order of declaration for inheritance).
if len(contract.immediate_inheritance) == 1:
if len(inheritance) == 1:
immediate_inheritance = contract.immediate_inheritance[0]
ret += f"c{contract.id}_{contract.name} -> c{immediate_inheritance.id}_{immediate_inheritance};\n"
else:
for i, immediate_inheritance in enumerate(contract.immediate_inheritance):
for i, immediate_inheritance in enumerate(inheritance):
ret += f'c{contract.id}_{contract.name} -> c{immediate_inheritance.id}_{immediate_inheritance} [ label="{i + 1}" ];\n'
# Functions
@ -113,6 +122,7 @@ class PrinterInheritanceGraph(AbstractPrinter):
for f in contract.functions
if not f.is_constructor
and not f.is_constructor_variables
and not f.is_virtual
and f.contract_declarer == contract
and f.visibility in visibilities
]
@ -195,6 +205,12 @@ class PrinterInheritanceGraph(AbstractPrinter):
content = 'digraph "" {\n'
for c in self.contracts:
if (
"mock" in c.name.lower()
or c.is_library
or (c.is_interface and not self.slither.include_interfaces)
):
continue
content += self._summary(c) + "\n"
content += "}"

@ -2,8 +2,11 @@
Module printing evm mapping of the contract
"""
import logging
from typing import Union, List, Dict
from slither.printers.abstract_printer import AbstractPrinter
from slither.core.declarations.function import Function
from slither.core.declarations.modifier import Modifier
from slither.analyses.evm import (
generate_source_to_evm_ins_mapping,
load_evm_cfg_builder,
@ -77,6 +80,33 @@ class PrinterEVM(AbstractPrinter):
WIKI = "https://github.com/trailofbits/slither/wiki/Printer-documentation#evm"
def build_element_node_str(
self,
element: Union["Modifier", "Function"],
contract_pcs: Dict[int, List[int]],
contract_cfg,
) -> str:
element_file = self.slither.source_code[
element.contract_declarer.source_mapping.filename.absolute
].splitlines()
return_string = ""
for node in element.nodes:
return_string += green(f"\t\tNode: {node}\n")
node_source_line = node.source_mapping.lines[0]
return_string += green(
f"\t\tSource line {node_source_line}: {element_file[node_source_line - 1].rstrip()}\n"
)
return_string += magenta("\t\tEVM Instructions:\n")
node_pcs = contract_pcs.get(node_source_line, [])
for pc in node_pcs:
return_string += magenta(
f"\t\t\t{hex(pc)}: {contract_cfg.get_instruction_at(pc)}\n"
)
return return_string
def output(self, _filename):
"""
_filename is not used
@ -99,53 +129,27 @@ class PrinterEVM(AbstractPrinter):
txt += "\tempty contract\n"
continue
contract_file = self.slither.source_code[
contract.source_mapping.filename.absolute
].encode("utf-8")
with open(contract.source_mapping.filename.absolute, "r", encoding="utf8") as f:
contract_file_lines = f.readlines()
contract_pcs = {}
contract_cfg = {}
for function in contract.functions:
txt += blue(f"\tFunction {function.canonical_name}\n")
# CFG and source mapping depend on function being constructor or not
if function.is_constructor:
contract_cfg = evm_info["cfg_init", contract.name]
contract_pcs = evm_info["mapping_init", contract.name]
else:
contract_cfg = evm_info["cfg", contract.name]
contract_pcs = evm_info["mapping", contract.name]
for node in function.nodes:
txt += green("\t\tNode: " + str(node) + "\n")
node_source_line = (
contract_file[0 : node.source_mapping.start].count("\n".encode("utf-8")) + 1
)
txt += green(
f"\t\tSource line {node_source_line}: {contract_file_lines[node_source_line - 1].rstrip()}\n"
)
txt += magenta("\t\tEVM Instructions:\n")
node_pcs = contract_pcs.get(node_source_line, [])
for pc in node_pcs:
txt += magenta(f"\t\t\t{hex(pc)}: {contract_cfg.get_instruction_at(pc)}\n")
txt += self.build_element_node_str(
function,
evm_info["mapping", contract.name]
if not function.is_constructor
else evm_info["mapping_init", contract.name],
evm_info["cfg", contract.name]
if not function.is_constructor
else evm_info["cfg_init", contract.name],
)
for modifier in contract.modifiers:
txt += blue(f"\tModifier {modifier.canonical_name}\n")
for node in modifier.nodes:
txt += green("\t\tNode: " + str(node) + "\n")
node_source_line = (
contract_file[0 : node.source_mapping.start].count("\n".encode("utf-8")) + 1
)
txt += green(
f"\t\tSource line {node_source_line}: {contract_file_lines[node_source_line - 1].rstrip()}\n"
)
txt += magenta("\t\tEVM Instructions:\n")
node_pcs = contract_pcs.get(node_source_line, [])
for pc in node_pcs:
txt += magenta(f"\t\t\t{hex(pc)}: {contract_cfg.get_instruction_at(pc)}\n")
txt += self.build_element_node_str(
modifier,
evm_info["mapping", contract.name],
evm_info["cfg", contract.name],
)
self.info(txt)
res = self.generate_output(txt)

@ -17,8 +17,8 @@ from slither.utils.output import Output
class LocPrinter(AbstractPrinter):
ARGUMENT = "loc"
HELP = """Count the total number lines of code (LOC), source lines of code (SLOC), \
and comment lines of code (CLOC) found in source files (SRC), dependencies (DEP), \
HELP = """Count the total number lines of code (LOC), source lines of code (SLOC),
and comment lines of code (CLOC) found in source files (SRC), dependencies (DEP),
and test files (TEST)."""
WIKI = "https://github.com/trailofbits/slither/wiki/Printer-documentation#loc"

@ -11,6 +11,7 @@ require_or_assert = [
SolidityFunction("assert(bool)"),
SolidityFunction("require(bool)"),
SolidityFunction("require(bool,string)"),
SolidityFunction("require(bool,error)"),
]

@ -196,10 +196,12 @@ class Slither(
if printers_to_run == "echidna":
self.skip_data_dependency = True
# Used in inheritance-graph printer
self.include_interfaces = kwargs.get("include_interfaces", False)
self._init_parsing_and_analyses(kwargs.get("skip_analyze", False))
def _init_parsing_and_analyses(self, skip_analyze: bool) -> None:
for parser in self._parsers:
try:
parser.parse_contracts()

@ -45,10 +45,19 @@ class Assignment(OperationWithLValue):
def __str__(self) -> str:
lvalue = self.lvalue
# When rvalues are functions, we want to properly display their return type
# Fix: https://github.com/crytic/slither/issues/2266
if isinstance(self.rvalue.type, list):
rvalue_type = ",".join(f"{rvalue_type}" for rvalue_type in self.rvalue.type)
else:
rvalue_type = f"{self.rvalue.type}"
assert lvalue
if lvalue and isinstance(lvalue, ReferenceVariable):
points = lvalue.points_to
while isinstance(points, ReferenceVariable):
points = points.points_to
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}) (->{points}) := {self.rvalue}({rvalue_type})"
return f"{lvalue}({lvalue.type}) := {self.rvalue}({rvalue_type})"

@ -1,5 +1,5 @@
from typing import List, Union
from slither.core.declarations import Contract, Function
from slither.core.declarations import Contract, Function, Event
from slither.core.declarations.custom_error import CustomError
from slither.core.declarations.enum import Enum
from slither.core.declarations.solidity_import_placeholder import SolidityImportPlaceHolder
@ -33,14 +33,29 @@ class Member(OperationWithLValue):
# Can be an ElementaryType because of bytes.concat, string.concat
assert is_valid_rvalue(variable_left) or isinstance(
variable_left,
(Contract, Enum, Function, CustomError, SolidityImportPlaceHolder, ElementaryType),
(
Contract,
Enum,
Function,
Event,
CustomError,
SolidityImportPlaceHolder,
ElementaryType,
),
)
assert isinstance(variable_right, Constant)
assert isinstance(result, ReferenceVariable)
super().__init__()
self._variable_left: Union[
RVALUE, Contract, Enum, Function, CustomError, SolidityImportPlaceHolder, ElementaryType
RVALUE,
Contract,
Enum,
Function,
Event,
CustomError,
SolidityImportPlaceHolder,
ElementaryType,
] = variable_left
self._variable_right = variable_right
self._lvalue = result

@ -501,7 +501,9 @@ def parse_expression(expression: Dict, caller_context: CallerContextExpression)
referenced_declaration = expression["attributes"]["referencedDeclaration"]
if t:
found = re.findall(r"[struct|enum|function|modifier] \(([\[\] ()a-zA-Z0-9\.,_]*)\)", t)
found = re.findall(
r"(?:struct|enum|function|modifier) \(([\[\] ()a-zA-Z0-9\.,_]*)\)", t
)
assert len(found) <= 1
if found:
value = value + "(" + found[0] + ")"

@ -304,7 +304,7 @@ def _find_variable_init(
scope = underlying_function.file_scope
else:
assert isinstance(underlying_function, FunctionContract)
scope = underlying_function.contract.file_scope
scope = underlying_function.contract_declarer.file_scope
elif isinstance(caller_context, StructureTopLevelSolc):
direct_contracts = []

@ -1,5 +1,5 @@
# slither-documentation
`slither-documentation` uses [codex](https://beta.openai.com) to generate natspec documenation.
`slither-documentation` uses [codex](https://platform.openai.com) to generate natspec documenation.
This tool is experimental. See [solmate documentation](https://github.com/montyly/solmate/pull/1) for an example of usage.

@ -6,7 +6,7 @@ import shutil
import sys
import time
from pathlib import Path
from typing import Type, List, Any, Optional
from typing import Type, List, Any, Optional, Union
from crytic_compile import cryticparser
from slither import Slither
from slither.tools.mutator.utils.testing_generated_mutant import run_test_cmd
@ -116,7 +116,7 @@ def parse_args() -> argparse.Namespace:
return parser.parse_args()
def _get_mutators(mutators_list: List[str] | None) -> List[Type[AbstractMutator]]:
def _get_mutators(mutators_list: Union[List[str], None]) -> List[Type[AbstractMutator]]:
detectors_ = [getattr(all_mutators, name) for name in dir(all_mutators)]
if mutators_list is not None:
detectors = [

@ -2,7 +2,12 @@ from typing import Dict
from slither.slithir.operations import Binary, BinaryType
from slither.tools.mutator.utils.patch import create_patch_with_line
from slither.tools.mutator.mutators.abstract_mutator import AbstractMutator
from slither.core.variables.variable import Variable
from slither.core.expressions.unary_operation import UnaryOperation
from slither.core.expressions.call_expression import CallExpression
from slither.core.expressions.member_access import MemberAccess
from slither.core.expressions.identifier import Identifier
from slither.core.solidity_types.array_type import ArrayType
arithmetic_operators = [
BinaryType.ADDITION,
@ -27,7 +32,39 @@ class AOR(AbstractMutator): # pylint: disable=too-few-public-methods
ir_expression = node.expression
except: # pylint: disable=bare-except
continue
for ir in node.irs:
# Special cases handling .push and .pop on dynamic arrays.
# The IR for these operations has a binary operation due to internal conversion
# (see convert_to_push and convert_to_pop in slithir/convert.py)
# however it's not present in the source code and should not be mutated.
# pylint: disable=too-many-boolean-expressions
if (
isinstance(ir_expression, CallExpression)
and isinstance(ir_expression.called, MemberAccess)
and ir_expression.called.member_name == "pop"
and isinstance(ir_expression.called.expression, Identifier)
and isinstance(ir_expression.called.expression.value, Variable)
and isinstance(ir_expression.called.expression.value.type, ArrayType)
):
continue
# For a .push instruction we skip the last 6 IR operations
# because they are fixed based on the internal conversion to the IR
# while we need to look at the preceding instructions because
# they might contain Binary IR to be mutated.
# For example for a.push(3+x) it's correct to mutate 3+x.
irs = (
node.irs[:-6]
if isinstance(ir_expression, CallExpression)
and isinstance(ir_expression.called, MemberAccess)
and ir_expression.called.member_name == "push"
and isinstance(ir_expression.called.expression, Identifier)
and isinstance(ir_expression.called.expression.value, Variable)
and isinstance(ir_expression.called.expression.value.type, ArrayType)
else node.irs
)
for ir in irs:
if isinstance(ir, Binary) and ir.type in arithmetic_operators:
if isinstance(ir_expression, UnaryOperation):
continue

@ -31,7 +31,7 @@ class LIR(AbstractMutator): # pylint: disable=too-few-public-methods
literal_replacements.append(variable.type.max) # append data type max value
if str(variable.type).startswith("uint"):
literal_replacements.append("1")
elif str(variable.type).startswith("uint"):
elif str(variable.type).startswith("int"):
literal_replacements.append("-1")
# Get the string
start = variable.source_mapping.start
@ -63,7 +63,7 @@ class LIR(AbstractMutator): # pylint: disable=too-few-public-methods
literal_replacements.append(variable.type.max)
if str(variable.type).startswith("uint"):
literal_replacements.append("1")
elif str(variable.type).startswith("uint"):
elif str(variable.type).startswith("int"):
literal_replacements.append("-1")
start = variable.source_mapping.start
stop = start + variable.source_mapping.length

@ -1,7 +1,7 @@
import abc
import logging
from pathlib import Path
from typing import Optional, Dict, Tuple, List
from typing import Optional, Dict, Tuple, List, Union
from slither.core.compilation_unit import SlitherCompilationUnit
from slither.formatters.utils.patches import apply_patch, create_diff
from slither.tools.mutator.utils.testing_generated_mutant import test_patch
@ -27,7 +27,7 @@ class AbstractMutator(
testing_command: str,
testing_directory: str,
contract_instance: Contract,
solc_remappings: str | None,
solc_remappings: Union[str, None],
verbose: bool,
very_verbose: bool,
output_folder: Path,
@ -81,7 +81,7 @@ class AbstractMutator(
(all_patches) = self._mutate()
if "patches" not in all_patches:
logger.debug("No patches found by %s", self.NAME)
return ([0, 0, 0], [0, 0, 0], self.dont_mutate_line)
return [0, 0, 0], [0, 0, 0], self.dont_mutate_line
for file in all_patches["patches"]: # Note: This should only loop over a single file
original_txt = self.slither.source_code[file].encode("utf8")
@ -146,4 +146,4 @@ class AbstractMutator(
f"Found {self.uncaught_mutant_counts[2]} uncaught tweak mutants so far (out of {self.total_mutant_counts[2]} that compile)"
)
return (self.total_mutant_counts, self.uncaught_mutant_counts, self.dont_mutate_line)
return self.total_mutant_counts, self.uncaught_mutant_counts, self.dont_mutate_line

@ -111,7 +111,7 @@ def get_sol_file_list(codebase: Path, ignore_paths: Union[List[str], None]) -> L
# if input is folder
if codebase.is_dir():
for file_name in codebase.rglob("*.sol"):
if not any(part in ignore_paths for part in file_name.parts):
if file_name.is_file() and not any(part in ignore_paths for part in file_name.parts):
sol_file_list.append(file_name.as_posix())
return sol_file_list

@ -22,7 +22,12 @@ def compile_generated_mutant(file_path: str, mappings: str) -> bool:
return False
def run_test_cmd(cmd: str, timeout: int | None, target_file: str | None, verbose: bool) -> bool:
def run_test_cmd(
cmd: str,
timeout: Union[int, None] = None,
target_file: Union[str, None] = None,
verbose: bool = False,
) -> bool:
"""
function to run codebase tests
returns: boolean whether the tests passed or not

@ -7,6 +7,7 @@ from slither.tools.upgradeability.checks.initialization import (
MissingCalls,
MultipleCalls,
InitializeTarget,
MultipleReinitializers,
)
from slither.tools.upgradeability.checks.functions_ids import IDCollision, FunctionShadowing

@ -7,6 +7,7 @@ from slither.tools.upgradeability.checks.abstract_checks import (
CheckClassification,
)
from slither.utils.colors import red
from slither.exceptions import SlitherError
logger = logging.getLogger("Slither-check-upgradeability")
@ -18,7 +19,7 @@ class MultipleInitTarget(Exception):
def _has_initialize_modifier(function: Function):
if not function.modifiers:
return False
return any((m.name == "initializer") for m in function.modifiers)
return any((m.name in ("initializer", "reinitializer")) for m in function.modifiers)
def _get_initialize_functions(contract):
@ -164,7 +165,7 @@ class MissingInitializerModifier(AbstractCheck):
# region wiki_description
WIKI_DESCRIPTION = """
Detect if `Initializable.initializer()` is called.
Detect if `Initializable.initializer()` or `Initializable.reinitializer(uint64)` is called.
"""
# endregion wiki_description
@ -184,7 +185,7 @@ contract Contract{
# region wiki_recommendation
WIKI_RECOMMENDATION = """
Use `Initializable.initializer()`.
Use `Initializable.initializer()` or `Initializable.reinitializer(uint64)`.
"""
# endregion wiki_recommendation
@ -199,15 +200,18 @@ Use `Initializable.initializer()`.
if initializable not in self.contract.inheritance:
return []
initializer = self.contract.get_modifier_from_canonical_name("Initializable.initializer()")
reinitializer = self.contract.get_modifier_from_canonical_name(
"Initializable.reinitializer(uint64)"
)
# InitializableInitializer
if initializer is None:
if initializer is None and reinitializer is None:
return []
results = []
all_init_functions = _get_initialize_functions(self.contract)
for f in all_init_functions:
if initializer not in f.modifiers:
info = [f, " does not call the initializer modifier.\n"]
if initializer not in f.modifiers and reinitializer not in f.modifiers:
info = [f, " does not call the initializer or reinitializer modifier.\n"]
json = self.generate_result(info)
results.append(json)
return results
@ -271,6 +275,9 @@ Ensure all the initialize functions are reached by the most derived initialize f
all_init_functions_called = _get_all_internal_calls(most_derived_init) + [most_derived_init]
missing_calls = [f for f in all_init_functions if not f in all_init_functions_called]
for f in missing_calls:
# we don't account reinitializers
if any((m.name == "reinitializer") for m in f.modifiers):
continue
info = ["Missing call to ", f, " in ", most_derived_init, ".\n"]
json = self.generate_result(info)
results.append(json)
@ -396,3 +403,106 @@ Ensure that the function is called at deployment.
]
json = self.generate_result(info)
return [json]
class MultipleReinitializers(AbstractCheck):
ARGUMENT = "multiple-new-reinitializers"
IMPACT = CheckClassification.LOW
HELP = "Multiple new reinitializers in the updated contract"
WIKI = (
"https://github.com/crytic/slither/wiki/Upgradeability-Checks#multiple-new-reinitializers"
)
WIKI_TITLE = "Multiple new reinitializers in the updated contract"
# region wiki_description
WIKI_DESCRIPTION = """
Detect multiple new reinitializers in the updated contract`.
"""
# endregion wiki_description
# region wiki_exploit_scenario
WIKI_EXPLOIT_SCENARIO = """
```solidity
contract Counter is Initializable {
uint256 public x;
function initialize(uint256 _x) public initializer {
x = _x;
}
function changeX() public {
x++;
}
}
contract CounterV2 is Initializable {
uint256 public x;
uint256 public y;
uint256 public z;
function initializeV2(uint256 _y) public reinitializer(2) {
y = _y;
}
function initializeV3(uint256 _z) public reinitializer(3) {
z = _z;
}
function changeX() public {
x = x + y + z;
}
}
```
`CounterV2` has two reinitializers with new versions `2` and `3`. If `initializeV3()` is called first, the `initializeV2()` can't be called to initialize `y`, which may brings security risks.
"""
# endregion wiki_exploit_scenario
# region wiki_recommendation
WIKI_RECOMMENDATION = """
Do not use multiple reinitializers with higher versions in the updated contract. Please consider combining new reinitializers into a single one.
"""
# endregion wiki_recommendation
REQUIRE_CONTRACT = True
REQUIRE_CONTRACT_V2 = True
def _check(self):
contract_v1 = self.contract
contract_v2 = self.contract_v2
if contract_v2 is None:
raise SlitherError("multiple-new-reinitializers requires a V2 contract")
initializerV1 = contract_v1.get_modifier_from_canonical_name("Initializable.initializer()")
reinitializerV1 = contract_v1.get_modifier_from_canonical_name(
"Initializable.reinitializer(uint64)"
)
reinitializerV2 = contract_v2.get_modifier_from_canonical_name(
"Initializable.reinitializer(uint64)"
)
# contractV1 has initializer or reinitializer
if initializerV1 is None and reinitializerV1 is None:
return []
# contractV2 has reinitializer
if reinitializerV2 is None:
return []
initializer_funcs_v1 = _get_initialize_functions(contract_v1)
initializer_funcs_v2 = _get_initialize_functions(contract_v2)
new_reinitializer_funcs = []
for fv2 in initializer_funcs_v2:
if not any((fv2.full_name == fv1.full_name) for fv1 in initializer_funcs_v1):
new_reinitializer_funcs.append(fv2)
results = []
if len(new_reinitializer_funcs) > 1:
for f in new_reinitializer_funcs:
info = [
f,
" multiple new reinitializers which should be combined into one per upgrade.\n",
]
json = self.generate_result(info)
results.append(json)
return results

@ -48,6 +48,7 @@ defaults_flag_in_config = {
"detectors_to_run": "all",
"printers_to_run": None,
"detectors_to_exclude": None,
"detectors_to_include": None,
"exclude_dependencies": False,
"exclude_informational": False,
"exclude_optimization": False,
@ -359,8 +360,10 @@ def output_printers(printer_classes: List[Type[AbstractPrinter]]) -> None:
printers_list = sorted(printers_list, key=lambda element: (element[0]))
idx = 1
for (argument, help_info) in printers_list:
table.add_row([str(idx), argument, help_info])
# Clean multi line HELP info
table.add_row([str(idx), argument, " ".join(x.strip() for x in help_info.splitlines())])
idx = idx + 1
print(table)

@ -1,3 +1,4 @@
from shutil import get_terminal_size
from typing import List, Dict, Union
from prettytable import PrettyTable
@ -7,7 +8,12 @@ from slither.utils.colors import Colors
class MyPrettyTable:
def __init__(self, field_names: List[str], pretty_align: bool = True): # TODO: True by default?
def __init__(
self,
field_names: List[str],
pretty_align: bool = True,
max_width: Union[int, None] = "max", # Default value is "max"
):
self._field_names = field_names
self._rows: List = []
self._options: Dict = {}
@ -19,6 +25,17 @@ class MyPrettyTable:
else:
self._options["set_alignment"] = []
self.max_width = None
if max_width == "max":
# We use (0,0) as a fallback to detect if we are not attached to a terminal
# In this case, we fall back to the default behavior (i.e. printing as much as possible)
terminal_column = get_terminal_size((0, 0)).columns
if terminal_column != 0:
# We reduce slightly the max-width to take into account inconsistencies in terminals
self.max_width = terminal_column - 3
else:
self.max_width = max_width
def add_row(self, row: List[Union[str, List[str]]]) -> None:
self._rows.append(row)
@ -28,6 +45,9 @@ class MyPrettyTable:
else:
table = PrettyTable(self._field_names)
if self.max_width is not None:
table.max_table_width = self.max_width
for row in self._rows:
table.add_row(row)
if len(self._options["set_alignment"]):
@ -63,7 +83,5 @@ def make_pretty_table(
table_row = [row] + [body[row][key] for key in headers[1:]]
table.add_row(table_row)
if totals:
table.add_row(
[total_header] + [sum([body[row][key] for row in body]) for key in headers[1:]]
)
table.add_row([total_header] + [sum(body[row][key] for row in body) for key in headers[1:]])
return table

@ -2,37 +2,17 @@ from typing import List, Set
from crytic_compile import CryticCompile
from slither.core.declarations import (
Contract,
Function,
Enum,
Event,
Import,
Pragma,
Structure,
CustomError,
FunctionContract,
)
from slither.core.solidity_types import Type, TypeAlias
from slither.core.source_mapping.source_mapping import Source, SourceMapping
from slither.core.variables.variable import Variable
from slither.exceptions import SlitherError
def get_definition(target: SourceMapping, crytic_compile: CryticCompile) -> Source:
if isinstance(target, (Contract, Function, Enum, Event, Structure, Variable)):
# Add " " to look after the first solidity keyword
pattern = " " + target.name
elif isinstance(target, Import):
pattern = "import"
elif isinstance(target, Pragma):
pattern = "pragma" # todo maybe return with the while pragma statement
elif isinstance(target, CustomError):
pattern = "error"
elif isinstance(target, TypeAlias):
pattern = "type"
elif isinstance(target, Type):
raise SlitherError("get_definition_generic not implemented for types")
else:
raise SlitherError(f"get_definition_generic not implemented for {type(target)}")
try:
pattern = target.pattern
except AttributeError as exc:
raise SlitherError(f"get_definition_generic not implemented for {type(target)}") from exc
file_content = crytic_compile.src_content_for_file(target.source_mapping.filename.absolute)
txt = file_content[

@ -1,4 +1,5 @@
import logging
from functools import lru_cache
from slither.core.expressions.assignment_operation import AssignmentOperation
from slither.core.expressions.binary_operation import BinaryOperation
@ -16,11 +17,39 @@ from slither.core.expressions.new_elementary_type import NewElementaryType
from slither.core.expressions.tuple_expression import TupleExpression
from slither.core.expressions.type_conversion import TypeConversion
from slither.core.expressions.unary_operation import UnaryOperation
from slither.core.expressions.super_call_expression import SuperCallExpression
from slither.core.expressions.super_identifier import SuperIdentifier
from slither.core.expressions.self_identifier import SelfIdentifier
from slither.exceptions import SlitherError
logger = logging.getLogger("ExpressionVisitor")
@lru_cache()
def get_visitor_mapping():
"""Returns a visitor mapping from expression type to visiting functions."""
return {
AssignmentOperation: "_visit_assignement_operation",
BinaryOperation: "_visit_binary_operation",
CallExpression: "_visit_call_expression",
ConditionalExpression: "_visit_conditional_expression",
ElementaryTypeNameExpression: "_visit_elementary_type_name_expression",
Identifier: "_visit_identifier",
IndexAccess: "_visit_index_access",
Literal: "_visit_literal",
MemberAccess: "_visit_member_access",
NewArray: "_visit_new_array",
NewContract: "_visit_new_contract",
NewElementaryType: "_visit_new_elementary_type",
TupleExpression: "_visit_tuple_expression",
TypeConversion: "_visit_type_conversion",
UnaryOperation: "_visit_unary_operation",
SelfIdentifier: "_visit_identifier",
SuperIdentifier: "_visit_identifier",
SuperCallExpression: "_visit_call_expression",
}
# pylint: disable=too-few-public-methods
class ExpressionVisitor:
def __init__(self, expression: Expression) -> None:
@ -35,60 +64,16 @@ class ExpressionVisitor:
# visit an expression
# call pre_visit, visit_expression_name, post_visit
# pylint: disable=too-many-branches
def _visit_expression(self, expression: Expression) -> None:
self._pre_visit(expression)
if isinstance(expression, AssignmentOperation):
self._visit_assignement_operation(expression)
elif isinstance(expression, BinaryOperation):
self._visit_binary_operation(expression)
elif isinstance(expression, CallExpression):
self._visit_call_expression(expression)
elif isinstance(expression, ConditionalExpression):
self._visit_conditional_expression(expression)
elif isinstance(expression, ElementaryTypeNameExpression):
self._visit_elementary_type_name_expression(expression)
elif isinstance(expression, Identifier):
self._visit_identifier(expression)
elif isinstance(expression, IndexAccess):
self._visit_index_access(expression)
elif isinstance(expression, Literal):
self._visit_literal(expression)
elif isinstance(expression, MemberAccess):
self._visit_member_access(expression)
elif isinstance(expression, NewArray):
self._visit_new_array(expression)
elif isinstance(expression, NewContract):
self._visit_new_contract(expression)
elif isinstance(expression, NewElementaryType):
self._visit_new_elementary_type(expression)
elif isinstance(expression, TupleExpression):
self._visit_tuple_expression(expression)
if expression is not None:
visitor_method = get_visitor_mapping().get(expression.__class__)
if not visitor_method:
raise SlitherError(f"Expression not handled: {expression}")
elif isinstance(expression, TypeConversion):
self._visit_type_conversion(expression)
elif isinstance(expression, UnaryOperation):
self._visit_unary_operation(expression)
elif expression is None:
pass
else:
raise SlitherError(f"Expression not handled: {expression}")
visitor = getattr(self, visitor_method)
visitor(expression)
self._post_visit(expression)

@ -50,8 +50,8 @@ class ReadVar(ExpressionVisitor):
self._result = list(set(get(self.expression)))
return self._result
# overide assignement
# dont explore if its direct assignement (we explore if its +=, -=, ...)
# override assignment
# dont explore if its direct assignment (we explore if its +=, -=, ...)
def _visit_assignement_operation(self, expression: AssignmentOperation) -> None:
if expression.type != AssignmentOperationType.ASSIGN:
self._visit_expression(expression.expression_left)

@ -188,7 +188,7 @@ class ExpressionToSlithIR(ExpressionVisitor):
right = get(expression.expression_right)
operation: Operation
if isinstance(left, list): # tuple expression:
if isinstance(right, list): # unbox assigment
if isinstance(right, list): # unbox assignment
assert len(left) == len(right)
for idx, _ in enumerate(left):
if (
@ -448,12 +448,12 @@ class ExpressionToSlithIR(ExpressionVisitor):
right = get(expression.expression_right)
operation: Operation
# Left can be a type for abi.decode(var, uint[2])
if isinstance(left, (Type, Contract, Enum)):
if isinstance(left, (Type, Contract, Enum, Structure)):
# Nested type are not yet supported by abi.decode, so the assumption
# Is that the right variable must be a 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)):
if isinstance(left, (Contract, Enum, Structure)):
left = UserDefinedType(left)
t = ArrayType(left, int(right.value))
set_val(expression, t)

@ -84,7 +84,7 @@ class ContractVyper: # pylint: disable=too-many-instance-attributes
self._structuresNotParsed.append(node)
elif isinstance(node, ImportFrom):
# TOOD aliases
# We create an `InterfaceDef` sense the compilatuion unit does not contain the actual interface
# We create an `InterfaceDef` sense the compilation unit does not contain the actual interface
# https://github.com/vyperlang/vyper/tree/master/vyper/builtins/interfaces
if node.module == "vyper.interfaces":
interfaces = {

@ -0,0 +1,3 @@
# Foundry
To init this test case, run `forge install --no-commit --no-git foundry-rs/forge-std`

@ -0,0 +1,6 @@
[profile.default]
src = "src"
out = "out"
libs = ["lib"]
# See more config options https://github.com/foundry-rs/foundry/blob/master/crates/config/README.md#all-options

@ -0,0 +1,16 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;
contract Counter {
uint256 public number;
function setNumber(uint256 newNumber) public {
number = newNumber;
}
//START
function increment() public {
number++;
}
//END
}

@ -0,0 +1,40 @@
from pathlib import Path
import shutil
import re
import pytest
from slither import Slither
from slither.exceptions import SlitherException
TEST_DATA_DIR = Path(__file__).resolve().parent / "test_data"
foundry_available = shutil.which("forge") is not None
project_ready = Path(TEST_DATA_DIR, "test_change/lib/forge-std").exists()
@pytest.mark.skipif(
not foundry_available or not project_ready, reason="requires Foundry and project setup"
)
def test_diagnostic():
test_file_directory = TEST_DATA_DIR / "test_change"
sl = Slither(test_file_directory.as_posix())
assert len(sl.compilation_units) == 1
counter_file = test_file_directory / "src" / "Counter.sol"
shutil.copy(counter_file, counter_file.with_suffix(".bak"))
with counter_file.open("r") as file:
content = file.read()
with counter_file.open("w") as file:
file.write(re.sub(r"//START.*?//END\n?", "", content, flags=re.DOTALL))
with pytest.raises(SlitherException):
Slither(test_file_directory.as_posix(), ignore_compile=True)
# Restore the original counter so the test is idempotent
Path(counter_file.with_suffix(".bak")).rename(counter_file)

@ -1,8 +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
Test.direct() (tests/e2e/detectors/test_data/arbitrary-send-eth/0.6.11/arbitrary_send_eth.sol#16-18) 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)
- msg.sender.send(address(this).balance) (tests/e2e/detectors/test_data/arbitrary-send-eth/0.6.11/arbitrary_send_eth.sol#17)
Test.direct() (tests/e2e/detectors/test_data/arbitrary-send-eth/0.6.11/arbitrary_send_eth.sol#11-13) sends eth to arbitrary user
Test.indirect() (tests/e2e/detectors/test_data/arbitrary-send-eth/0.6.11/arbitrary_send_eth.sol#24-26) 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)
- destination.send(address(this).balance) (tests/e2e/detectors/test_data/arbitrary-send-eth/0.6.11/arbitrary_send_eth.sol#25)

@ -1,8 +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
Test.direct() (tests/e2e/detectors/test_data/arbitrary-send-eth/0.7.6/arbitrary_send_eth.sol#16-18) 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)
- msg.sender.send(address(this).balance) (tests/e2e/detectors/test_data/arbitrary-send-eth/0.7.6/arbitrary_send_eth.sol#17)
Test.indirect() (tests/e2e/detectors/test_data/arbitrary-send-eth/0.7.6/arbitrary_send_eth.sol#19-21) sends eth to arbitrary user
Test.indirect() (tests/e2e/detectors/test_data/arbitrary-send-eth/0.7.6/arbitrary_send_eth.sol#24-26) 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)
- destination.send(address(this).balance) (tests/e2e/detectors/test_data/arbitrary-send-eth/0.7.6/arbitrary_send_eth.sol#25)

@ -1,6 +1,6 @@
2 different versions of Solidity are used:
- Version constraint ^0.4.25 is used by:
- tests/e2e/detectors/test_data/pragma/0.4.25/pragma.0.4.25.sol#1
-^0.4.25 (tests/e2e/detectors/test_data/pragma/0.4.25/pragma.0.4.25.sol#1)
- Version constraint ^0.4.24 is used by:
- tests/e2e/detectors/test_data/pragma/0.4.25/pragma.0.4.24.sol#1
-^0.4.24 (tests/e2e/detectors/test_data/pragma/0.4.25/pragma.0.4.24.sol#1)

@ -1,6 +1,6 @@
2 different versions of Solidity are used:
- Version constraint ^0.5.16 is used by:
- tests/e2e/detectors/test_data/pragma/0.5.16/pragma.0.5.16.sol#1
-^0.5.16 (tests/e2e/detectors/test_data/pragma/0.5.16/pragma.0.5.16.sol#1)
- Version constraint ^0.5.15 is used by:
- tests/e2e/detectors/test_data/pragma/0.5.16/pragma.0.5.15.sol#1
-^0.5.15 (tests/e2e/detectors/test_data/pragma/0.5.16/pragma.0.5.15.sol#1)

@ -1,6 +1,6 @@
2 different versions of Solidity are used:
- Version constraint ^0.6.11 is used by:
- tests/e2e/detectors/test_data/pragma/0.6.11/pragma.0.6.11.sol#1
-^0.6.11 (tests/e2e/detectors/test_data/pragma/0.6.11/pragma.0.6.11.sol#1)
- Version constraint ^0.6.10 is used by:
- tests/e2e/detectors/test_data/pragma/0.6.11/pragma.0.6.10.sol#1
-^0.6.10 (tests/e2e/detectors/test_data/pragma/0.6.11/pragma.0.6.10.sol#1)

@ -1,6 +1,6 @@
2 different versions of Solidity are used:
- Version constraint ^0.7.6 is used by:
- tests/e2e/detectors/test_data/pragma/0.7.6/pragma.0.7.6.sol#1
-^0.7.6 (tests/e2e/detectors/test_data/pragma/0.7.6/pragma.0.7.6.sol#1)
- Version constraint ^0.7.5 is used by:
- tests/e2e/detectors/test_data/pragma/0.7.6/pragma.0.7.5.sol#1
-^0.7.5 (tests/e2e/detectors/test_data/pragma/0.7.6/pragma.0.7.5.sol#1)

@ -1,5 +1,3 @@
solc-0.4.25 is an outdated solc version. Use a more recent version (at least 0.8.0), if possible.
Version constraint 0.4.25 contains known severe issues (https://solidity.readthedocs.io/en/latest/bugs.html)
- DirtyBytesArrayToStorage
- ABIDecodeTwoDimensionalArrayMemory
@ -16,6 +14,8 @@ Version constraint 0.4.25 contains known severe issues (https://solidity.readthe
- UninitializedFunctionPointerInConstructor_0.4.x
- IncorrectEventSignatureInLibraries_0.4.x
- ABIEncoderV2PackedStorage_0.4.x.
It is used by:
- tests/e2e/detectors/test_data/solc-version/0.4.25/static.sol#1
It is used by:
- 0.4.25 (tests/e2e/detectors/test_data/solc-version/0.4.25/static.sol#1)
solc-0.4.25 is an outdated solc version. Use a more recent version (at least 0.8.0), if possible.

@ -13,8 +13,8 @@ Version constraint 0.5.14 contains known severe issues (https://solidity.readthe
- privateCanBeOverridden
- YulOptimizerRedundantAssignmentBreakContinue0.5
- ABIEncoderV2LoopYulOptimizer.
It is used by:
- tests/e2e/detectors/test_data/solc-version/0.5.14/static.sol#1
It is used by:
- 0.5.14 (tests/e2e/detectors/test_data/solc-version/0.5.14/static.sol#1)
solc-0.5.14 is an outdated solc version. Use a more recent version (at least 0.8.0), if possible.

@ -12,8 +12,8 @@ Version constraint ^0.5.15 contains known severe issues (https://solidity.readth
- MemoryArrayCreationOverflow
- privateCanBeOverridden
- YulOptimizerRedundantAssignmentBreakContinue0.5.
It is used by:
- tests/e2e/detectors/test_data/solc-version/0.5.16/dynamic_1.sol#1
It is used by:
- ^0.5.15 (tests/e2e/detectors/test_data/solc-version/0.5.16/dynamic_1.sol#1)
solc-0.5.16 is an outdated solc version. Use a more recent version (at least 0.8.0), if possible.

@ -14,8 +14,8 @@ Version constraint >=0.5.0<0.6.0 contains known severe issues (https://solidity.
- UninitializedFunctionPointerInConstructor
- IncorrectEventSignatureInLibraries
- ABIEncoderV2PackedStorage.
It is used by:
- tests/e2e/detectors/test_data/solc-version/0.5.16/dynamic_2.sol#1
It is used by:
- >=0.5.0<0.6.0 (tests/e2e/detectors/test_data/solc-version/0.5.16/dynamic_2.sol#1)
solc-0.5.16 is an outdated solc version. Use a more recent version (at least 0.8.0), if possible.

@ -11,8 +11,8 @@ Version constraint 0.5.16 contains known severe issues (https://solidity.readthe
- TupleAssignmentMultiStackSlotComponents
- MemoryArrayCreationOverflow
- privateCanBeOverridden.
It is used by:
- tests/e2e/detectors/test_data/solc-version/0.5.16/static.sol#1
It is used by:
- 0.5.16 (tests/e2e/detectors/test_data/solc-version/0.5.16/static.sol#1)
solc-0.5.16 is an outdated solc version. Use a more recent version (at least 0.8.0), if possible.

@ -12,6 +12,6 @@ Version constraint 0.6.10 contains known severe issues (https://solidity.readthe
- KeccakCaching
- EmptyByteArrayCopy
- DynamicArrayCleanup.
It is used by:
- tests/e2e/detectors/test_data/solc-version/0.6.10/static.sol#1
It is used by:
- 0.6.10 (tests/e2e/detectors/test_data/solc-version/0.6.10/static.sol#1)

@ -10,8 +10,8 @@ Version constraint ^0.6.10 contains known severe issues (https://solidity.readth
- KeccakCaching
- EmptyByteArrayCopy
- DynamicArrayCleanup.
It is used by:
- tests/e2e/detectors/test_data/solc-version/0.6.11/dynamic_1.sol#1
It is used by:
- ^0.6.10 (tests/e2e/detectors/test_data/solc-version/0.6.11/dynamic_1.sol#1)
solc-0.6.11 is an outdated solc version. Use a more recent version (at least 0.8.0), if possible.

@ -12,8 +12,8 @@ Version constraint >=0.6.0<0.7.0 contains known severe issues (https://solidity.
- TupleAssignmentMultiStackSlotComponents
- MemoryArrayCreationOverflow
- YulOptimizerRedundantAssignmentBreakContinue.
It is used by:
- tests/e2e/detectors/test_data/solc-version/0.6.11/dynamic_2.sol#1
It is used by:
- >=0.6.0<0.7.0 (tests/e2e/detectors/test_data/solc-version/0.6.11/dynamic_2.sol#1)
solc-0.6.11 is an outdated solc version. Use a more recent version (at least 0.8.0), if possible.

@ -10,8 +10,8 @@ Version constraint 0.6.11 contains known severe issues (https://solidity.readthe
- KeccakCaching
- EmptyByteArrayCopy
- DynamicArrayCleanup.
It is used by:
- tests/e2e/detectors/test_data/solc-version/0.6.11/static.sol#1
It is used by:
- 0.6.11 (tests/e2e/detectors/test_data/solc-version/0.6.11/static.sol#1)
solc-0.6.11 is an outdated solc version. Use a more recent version (at least 0.8.0), if possible.

@ -1,3 +1,5 @@
solc-0.7.4 is an outdated solc version. Use a more recent version (at least 0.8.0), if possible.
Version constraint 0.7.4 contains known severe issues (https://solidity.readthedocs.io/en/latest/bugs.html)
- FullInlinerNonExpressionSplitArgumentEvaluationOrder
- MissingSideEffectsOnSelectorAccess
@ -8,8 +10,6 @@ Version constraint 0.7.4 contains known severe issues (https://solidity.readthed
- SignedImmutables
- ABIDecodeTwoDimensionalArrayMemory
- KeccakCaching.
It is used by:
- tests/e2e/detectors/test_data/solc-version/0.7.4/static.sol#1
solc-0.7.4 is an outdated solc version. Use a more recent version (at least 0.8.0), if possible.
It is used by:
- 0.7.4 (tests/e2e/detectors/test_data/solc-version/0.7.4/static.sol#1)

@ -1,5 +1,3 @@
solc-0.7.6 is an outdated solc version. Use a more recent version (at least 0.8.0), if possible.
Version constraint ^0.7.4 contains known severe issues (https://solidity.readthedocs.io/en/latest/bugs.html)
- FullInlinerNonExpressionSplitArgumentEvaluationOrder
- MissingSideEffectsOnSelectorAccess
@ -10,6 +8,8 @@ Version constraint ^0.7.4 contains known severe issues (https://solidity.readthe
- SignedImmutables
- ABIDecodeTwoDimensionalArrayMemory
- KeccakCaching.
It is used by:
- tests/e2e/detectors/test_data/solc-version/0.7.6/dynamic_1.sol#1
It is used by:
- ^0.7.4 (tests/e2e/detectors/test_data/solc-version/0.7.6/dynamic_1.sol#1)
solc-0.7.6 is an outdated solc version. Use a more recent version (at least 0.8.0), if possible.

@ -1,6 +1,6 @@
Version constraint >=0.7.0<=0.7.6 is too complex.
It is used by:
- tests/e2e/detectors/test_data/solc-version/0.7.6/dynamic_2.sol#1
solc-0.7.6 is an outdated solc version. Use a more recent version (at least 0.8.0), if possible.
Version constraint >=0.7.0<=0.7.6 is too complex.
It is used by:
- >=0.7.0<=0.7.6 (tests/e2e/detectors/test_data/solc-version/0.7.6/dynamic_2.sol#1)

@ -1,5 +1,3 @@
solc-0.7.6 is an outdated solc version. Use a more recent version (at least 0.8.0), if possible.
Version constraint 0.7.6 contains known severe issues (https://solidity.readthedocs.io/en/latest/bugs.html)
- FullInlinerNonExpressionSplitArgumentEvaluationOrder
- MissingSideEffectsOnSelectorAccess
@ -10,6 +8,8 @@ Version constraint 0.7.6 contains known severe issues (https://solidity.readthed
- SignedImmutables
- ABIDecodeTwoDimensionalArrayMemory
- KeccakCaching.
It is used by:
- tests/e2e/detectors/test_data/solc-version/0.7.6/static.sol#1
It is used by:
- 0.7.6 (tests/e2e/detectors/test_data/solc-version/0.7.6/static.sol#1)
solc-0.7.6 is an outdated solc version. Use a more recent version (at least 0.8.0), if possible.

@ -1,2 +0,0 @@
Variable Similar.f().testVariable (tests/e2e/detectors/test_data/similar-names/0.4.25/similar_variables.sol#3) is too similar to Similar.f().textVariable (tests/e2e/detectors/test_data/similar-names/0.4.25/similar_variables.sol#4)

@ -1,2 +0,0 @@
Variable Similar.f().testVariable (tests/e2e/detectors/test_data/similar-names/0.5.16/similar_variables.sol#3) is too similar to Similar.f().textVariable (tests/e2e/detectors/test_data/similar-names/0.5.16/similar_variables.sol#4)

@ -1,2 +0,0 @@
Variable Similar.f().testVariable (tests/e2e/detectors/test_data/similar-names/0.6.11/similar_variables.sol#3) is too similar to Similar.f().textVariable (tests/e2e/detectors/test_data/similar-names/0.6.11/similar_variables.sol#4)

@ -1,2 +0,0 @@
Variable Similar.f().testVariable (tests/e2e/detectors/test_data/similar-names/0.7.6/similar_variables.sol#3) is too similar to Similar.f().textVariable (tests/e2e/detectors/test_data/similar-names/0.7.6/similar_variables.sol#4)

@ -0,0 +1 @@
AnyInitializer (tests/e2e/detectors/test_data/unprotected-upgrade/0.4.25/AnyInitializer.sol#3-15) is an upgradeable contract that does not protect its initialize functions: AnyInitializer.anyName() (tests/e2e/detectors/test_data/unprotected-upgrade/0.4.25/AnyInitializer.sol#6-9). Anyone can delete the contract with: AnyInitializer.kill() (tests/e2e/detectors/test_data/unprotected-upgrade/0.4.25/AnyInitializer.sol#11-14)

@ -0,0 +1 @@
Reinitializer (tests/e2e/detectors/test_data/unprotected-upgrade/0.4.25/Reinitializer.sol#3-15) is an upgradeable contract that does not protect its initialize functions: Reinitializer.initialize() (tests/e2e/detectors/test_data/unprotected-upgrade/0.4.25/Reinitializer.sol#6-9). Anyone can delete the contract with: Reinitializer.kill() (tests/e2e/detectors/test_data/unprotected-upgrade/0.4.25/Reinitializer.sol#11-14)

@ -0,0 +1 @@
AnyInitializer (tests/e2e/detectors/test_data/unprotected-upgrade/0.5.16/AnyInitializer.sol#3-15) is an upgradeable contract that does not protect its initialize functions: AnyInitializer.anyName() (tests/e2e/detectors/test_data/unprotected-upgrade/0.5.16/AnyInitializer.sol#6-9). Anyone can delete the contract with: AnyInitializer.kill() (tests/e2e/detectors/test_data/unprotected-upgrade/0.5.16/AnyInitializer.sol#11-14)

@ -0,0 +1 @@
Reinitializer (tests/e2e/detectors/test_data/unprotected-upgrade/0.5.16/Reinitializer.sol#3-15) is an upgradeable contract that does not protect its initialize functions: Reinitializer.initialize() (tests/e2e/detectors/test_data/unprotected-upgrade/0.5.16/Reinitializer.sol#6-9). Anyone can delete the contract with: Reinitializer.kill() (tests/e2e/detectors/test_data/unprotected-upgrade/0.5.16/Reinitializer.sol#11-14)

@ -0,0 +1 @@
AnyInitializer (tests/e2e/detectors/test_data/unprotected-upgrade/0.6.11/AnyInitializer.sol#3-15) is an upgradeable contract that does not protect its initialize functions: AnyInitializer.anyName() (tests/e2e/detectors/test_data/unprotected-upgrade/0.6.11/AnyInitializer.sol#6-9). Anyone can delete the contract with: AnyInitializer.kill() (tests/e2e/detectors/test_data/unprotected-upgrade/0.6.11/AnyInitializer.sol#11-14)

@ -0,0 +1 @@
Reinitializer (tests/e2e/detectors/test_data/unprotected-upgrade/0.6.11/Reinitializer.sol#3-15) is an upgradeable contract that does not protect its initialize functions: Reinitializer.initialize() (tests/e2e/detectors/test_data/unprotected-upgrade/0.6.11/Reinitializer.sol#6-9). Anyone can delete the contract with: Reinitializer.kill() (tests/e2e/detectors/test_data/unprotected-upgrade/0.6.11/Reinitializer.sol#11-14)

@ -0,0 +1 @@
AnyInitializer (tests/e2e/detectors/test_data/unprotected-upgrade/0.7.6/AnyInitializer.sol#3-15) is an upgradeable contract that does not protect its initialize functions: AnyInitializer.anyName() (tests/e2e/detectors/test_data/unprotected-upgrade/0.7.6/AnyInitializer.sol#6-9). Anyone can delete the contract with: AnyInitializer.kill() (tests/e2e/detectors/test_data/unprotected-upgrade/0.7.6/AnyInitializer.sol#11-14)

@ -0,0 +1 @@
Reinitializer (tests/e2e/detectors/test_data/unprotected-upgrade/0.7.6/Reinitializer.sol#3-15) is an upgradeable contract that does not protect its initialize functions: Reinitializer.initialize() (tests/e2e/detectors/test_data/unprotected-upgrade/0.7.6/Reinitializer.sol#6-9). Anyone can delete the contract with: Reinitializer.kill() (tests/e2e/detectors/test_data/unprotected-upgrade/0.7.6/Reinitializer.sol#11-14)

@ -0,0 +1 @@
AnyInitializer (tests/e2e/detectors/test_data/unprotected-upgrade/0.8.15/AnyInitializer.sol#3-15) is an upgradeable contract that does not protect its initialize functions: AnyInitializer.anyName() (tests/e2e/detectors/test_data/unprotected-upgrade/0.8.15/AnyInitializer.sol#6-9). Anyone can delete the contract with: AnyInitializer.kill() (tests/e2e/detectors/test_data/unprotected-upgrade/0.8.15/AnyInitializer.sol#11-14)

@ -0,0 +1 @@
Reinitializer (tests/e2e/detectors/test_data/unprotected-upgrade/0.8.15/Reinitializer.sol#3-15) is an upgradeable contract that does not protect its initialize functions: Reinitializer.initialize() (tests/e2e/detectors/test_data/unprotected-upgrade/0.8.15/Reinitializer.sol#6-9). Anyone can delete the contract with: Reinitializer.kill() (tests/e2e/detectors/test_data/unprotected-upgrade/0.8.15/Reinitializer.sol#11-14)

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

Loading…
Cancel
Save