Merge pull request #2475 from crytic/dev

sync master <> dev
pull/2493/head
alpharush 6 months ago committed by GitHub
commit 353d4da401
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 25
      .coderabbit.yaml
  2. 4
      .github/workflows/ci.yml
  3. 5
      .gitignore
  4. 20
      README.md
  5. 4
      plugin_example/setup.py
  6. 0
      plugin_example/slither_my_plugin/detectors/__init__.py
  7. 10
      plugin_example/slither_my_plugin/detectors/example.py
  8. 84
      scripts/ci_test.sh
  9. 27
      scripts/json_diff.py
  10. 2
      setup.py
  11. 91
      slither/__main__.py
  12. 8
      slither/analyses/evm/convert.py
  13. 2
      slither/core/declarations/contract.py
  14. 1
      slither/core/declarations/custom_error.py
  15. 17
      slither/core/declarations/function.py
  16. 2
      slither/core/declarations/import_directive.py
  17. 1
      slither/core/declarations/pragma_directive.py
  18. 3
      slither/core/expressions/identifier.py
  19. 1
      slither/core/slither_core.py
  20. 2
      slither/core/solidity_types/elementary_type.py
  21. 1
      slither/core/solidity_types/type_alias.py
  22. 63
      slither/core/source_mapping/source_mapping.py
  23. 1
      slither/detectors/all_detectors.py
  24. 14
      slither/detectors/attributes/constant_pragma.py
  25. 10
      slither/detectors/attributes/incorrect_solc.py
  26. 2
      slither/detectors/compiler_bugs/reused_base_constructor.py
  27. 2
      slither/detectors/naming_convention/naming_convention.py
  28. 4
      slither/detectors/statements/divide_before_multiply.py
  29. 9
      slither/detectors/statements/unprotected_upgradeable.py
  30. 84
      slither/detectors/statements/unused_import.py
  31. 106
      slither/detectors/variables/similar_variables.py
  32. 14
      slither/detectors/variables/unused_state_variables.py
  33. 20
      slither/printers/inheritance/inheritance_graph.py
  34. 103
      slither/printers/summary/evm.py
  35. 4
      slither/slither.py
  36. 2
      slither/slithir/convert.py
  37. 2
      slither/solc_parsing/declarations/contract.py
  38. 4
      slither/solc_parsing/expressions/expression_parsing.py
  39. 2
      slither/solc_parsing/expressions/find_variable.py
  40. 2
      slither/tools/documentation/README.md
  41. 2
      slither/tools/kspec_coverage/analysis.py
  42. 36
      slither/utils/command_line.py
  43. 28
      slither/utils/source_mapping.py
  44. 85
      slither/visitors/expression/expression.py
  45. 4
      slither/visitors/expression/read_var.py
  46. 6
      slither/visitors/slithir/expression_to_slithir.py
  47. 2
      slither/vyper_parsing/declarations/contract.py
  48. 3
      tests/e2e/compilation/test_data/test_change/README.md
  49. 6
      tests/e2e/compilation/test_data/test_change/foundry.toml
  50. 16
      tests/e2e/compilation/test_data/test_change/src/Counter.sol
  51. 40
      tests/e2e/compilation/test_diagnostic.py
  52. 2
      tests/e2e/config/test_path_filtering/slither.config.json
  53. 4
      tests/e2e/detectors/snapshots/detectors__detector_ConstantPragma_0_4_25_pragma_0_4_25_sol__0.txt
  54. 4
      tests/e2e/detectors/snapshots/detectors__detector_ConstantPragma_0_5_16_pragma_0_5_16_sol__0.txt
  55. 4
      tests/e2e/detectors/snapshots/detectors__detector_ConstantPragma_0_6_11_pragma_0_6_11_sol__0.txt
  56. 4
      tests/e2e/detectors/snapshots/detectors__detector_ConstantPragma_0_7_6_pragma_0_7_6_sol__0.txt
  57. 8
      tests/e2e/detectors/snapshots/detectors__detector_IncorrectSolc_0_4_25_static_sol__0.txt
  58. 4
      tests/e2e/detectors/snapshots/detectors__detector_IncorrectSolc_0_5_14_static_sol__0.txt
  59. 4
      tests/e2e/detectors/snapshots/detectors__detector_IncorrectSolc_0_5_16_dynamic_1_sol__0.txt
  60. 4
      tests/e2e/detectors/snapshots/detectors__detector_IncorrectSolc_0_5_16_dynamic_2_sol__0.txt
  61. 4
      tests/e2e/detectors/snapshots/detectors__detector_IncorrectSolc_0_5_16_static_sol__0.txt
  62. 4
      tests/e2e/detectors/snapshots/detectors__detector_IncorrectSolc_0_6_10_static_sol__0.txt
  63. 4
      tests/e2e/detectors/snapshots/detectors__detector_IncorrectSolc_0_6_11_dynamic_1_sol__0.txt
  64. 4
      tests/e2e/detectors/snapshots/detectors__detector_IncorrectSolc_0_6_11_dynamic_2_sol__0.txt
  65. 4
      tests/e2e/detectors/snapshots/detectors__detector_IncorrectSolc_0_6_11_static_sol__0.txt
  66. 8
      tests/e2e/detectors/snapshots/detectors__detector_IncorrectSolc_0_7_4_static_sol__0.txt
  67. 8
      tests/e2e/detectors/snapshots/detectors__detector_IncorrectSolc_0_7_6_dynamic_1_sol__0.txt
  68. 8
      tests/e2e/detectors/snapshots/detectors__detector_IncorrectSolc_0_7_6_dynamic_2_sol__0.txt
  69. 8
      tests/e2e/detectors/snapshots/detectors__detector_IncorrectSolc_0_7_6_static_sol__0.txt
  70. 2
      tests/e2e/detectors/snapshots/detectors__detector_SimilarVarsDetection_0_4_25_similar_variables_sol__0.txt
  71. 2
      tests/e2e/detectors/snapshots/detectors__detector_SimilarVarsDetection_0_5_16_similar_variables_sol__0.txt
  72. 2
      tests/e2e/detectors/snapshots/detectors__detector_SimilarVarsDetection_0_6_11_similar_variables_sol__0.txt
  73. 2
      tests/e2e/detectors/snapshots/detectors__detector_SimilarVarsDetection_0_7_6_similar_variables_sol__0.txt
  74. 1
      tests/e2e/detectors/snapshots/detectors__detector_UnprotectedUpgradeable_0_4_25_AnyInitializer_sol__0.txt
  75. 1
      tests/e2e/detectors/snapshots/detectors__detector_UnprotectedUpgradeable_0_4_25_Reinitializer_sol__0.txt
  76. 1
      tests/e2e/detectors/snapshots/detectors__detector_UnprotectedUpgradeable_0_5_16_AnyInitializer_sol__0.txt
  77. 1
      tests/e2e/detectors/snapshots/detectors__detector_UnprotectedUpgradeable_0_5_16_Reinitializer_sol__0.txt
  78. 1
      tests/e2e/detectors/snapshots/detectors__detector_UnprotectedUpgradeable_0_6_11_AnyInitializer_sol__0.txt
  79. 1
      tests/e2e/detectors/snapshots/detectors__detector_UnprotectedUpgradeable_0_6_11_Reinitializer_sol__0.txt
  80. 1
      tests/e2e/detectors/snapshots/detectors__detector_UnprotectedUpgradeable_0_7_6_AnyInitializer_sol__0.txt
  81. 1
      tests/e2e/detectors/snapshots/detectors__detector_UnprotectedUpgradeable_0_7_6_Reinitializer_sol__0.txt
  82. 1
      tests/e2e/detectors/snapshots/detectors__detector_UnprotectedUpgradeable_0_8_15_AnyInitializer_sol__0.txt
  83. 1
      tests/e2e/detectors/snapshots/detectors__detector_UnprotectedUpgradeable_0_8_15_Reinitializer_sol__0.txt
  84. 8
      tests/e2e/detectors/snapshots/detectors__detector_UnusedImport_0_8_16_C_sol__0.txt
  85. 4
      tests/e2e/detectors/snapshots/detectors__detector_UnusedStateVars_0_7_6_unused_state_sol__0.txt
  86. 7
      tests/e2e/detectors/test_data/similar-names/0.4.25/similar_variables.sol
  87. BIN
      tests/e2e/detectors/test_data/similar-names/0.4.25/similar_variables.sol-0.4.25.zip
  88. 7
      tests/e2e/detectors/test_data/similar-names/0.5.16/similar_variables.sol
  89. BIN
      tests/e2e/detectors/test_data/similar-names/0.5.16/similar_variables.sol-0.5.16.zip
  90. 7
      tests/e2e/detectors/test_data/similar-names/0.6.11/similar_variables.sol
  91. BIN
      tests/e2e/detectors/test_data/similar-names/0.6.11/similar_variables.sol-0.6.11.zip
  92. 7
      tests/e2e/detectors/test_data/similar-names/0.7.6/similar_variables.sol
  93. BIN
      tests/e2e/detectors/test_data/similar-names/0.7.6/similar_variables.sol-0.7.6.zip
  94. 15
      tests/e2e/detectors/test_data/unprotected-upgrade/0.4.25/AnyInitializer.sol
  95. BIN
      tests/e2e/detectors/test_data/unprotected-upgrade/0.4.25/AnyInitializer.sol-0.4.25.zip
  96. 14
      tests/e2e/detectors/test_data/unprotected-upgrade/0.4.25/Initializable.sol
  97. 15
      tests/e2e/detectors/test_data/unprotected-upgrade/0.4.25/Reinitializer.sol
  98. BIN
      tests/e2e/detectors/test_data/unprotected-upgrade/0.4.25/Reinitializer.sol-0.4.25.zip
  99. 15
      tests/e2e/detectors/test_data/unprotected-upgrade/0.5.16/AnyInitializer.sol
  100. BIN
      tests/e2e/detectors/test_data/unprotected-upgrade/0.5.16/AnyInitializer.sol-0.5.16.zip
  101. Some files were not shown because too many files have changed in this diff Show More

@ -0,0 +1,25 @@
# yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json
language: "en"
early_access: false
knowledge_base:
learnings:
scope: auto
issues:
scope: global
reviews:
profile: "chill"
request_changes_workflow: false
high_level_summary: true
poem: false
review_status: true
collapse_walkthrough: true
auto_review:
enabled: true
ignore_title_keywords:
- "WIP"
- "DO NOT MERGE"
drafts: false
base_branches:
- dev
chat:
auto_reply: true

@ -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

5
.gitignore vendored

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

@ -196,13 +196,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 +301,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,14 +1,14 @@
from setuptools import setup, find_packages
setup(
name="slither-my-plugins",
name="slither_my_plugin",
description="This is an example of detectors and printers to Slither.",
url="https://github.com/trailofbits/slither-plugins",
author="Trail of Bits",
version="0.0",
packages=find_packages(),
python_requires=">=3.8",
install_requires=["slither-analyzer==0.1"],
install_requires=["slither-analyzer>=0.6.0"],
entry_points={
"slither_analyzer.plugin": "slither my-plugin=slither_my_plugin:make_plugin",
},

@ -11,12 +11,12 @@ class Example(AbstractDetector): # pylint: disable=too-few-public-methods
IMPACT = DetectorClassification.HIGH
CONFIDENCE = DetectorClassification.HIGH
WIKI = ""
WIKI = "https://www.example.com/#example-detector"
WIKI_TITLE = ""
WIKI_DESCRIPTION = ""
WIKI_EXPLOIT_SCENARIO = ""
WIKI_RECOMMENDATION = ""
WIKI_TITLE = "example detector"
WIKI_DESCRIPTION = "This is an example detector that always generates a finding"
WIKI_EXPLOIT_SCENARIO = "Scenario goes here"
WIKI_RECOMMENDATION = "Customize the detector"
def _detect(self):

@ -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
}

@ -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,7 +8,7 @@ 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.3",
packages=find_packages(),
python_requires=">=3.8",
install_requires=[

@ -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
from crytic_compile import cryticparser, CryticCompile, InvalidCompilation
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,7 +93,13 @@ def process_all(
detector_classes: List[Type[AbstractDetector]],
printer_classes: List[Type[AbstractPrinter]],
) -> Tuple[List[Slither], List[Dict], List[Output], int]:
compilations = compile_all(target, **vars(args))
try:
compilations = compile_all(target, **vars(args))
except InvalidCompilation:
logger.error("Unable to compile all targets.")
sys.exit(2)
slither_instances = []
results_detectors = []
results_printers = []
@ -205,47 +211,54 @@ 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
]
detectors_to_run = sorted(detectors_to_run, key=lambda x: x.IMPACT)
if args.detectors_to_include:
detectors_to_run = __include_detectors(
set(detectors_to_run), args.detectors_to_include, detectors
)
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")
detectors_to_run = sorted(detectors_to_run, key=lambda x: x.IMPACT)
return detectors_to_run
def choose_printers(
args: argparse.Namespace, all_printer_classes: List[Type[AbstractPrinter]]
) -> List[Type[AbstractPrinter]]:
@ -335,6 +348,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",
@ -401,6 +422,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

@ -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
@ -1593,7 +1590,7 @@ class Function(SourceMapping, metaclass=ABCMeta): # pylint: disable=too-many-pu
write_var = [x for x in write_var if x]
write_var = [item for sublist in write_var for item in sublist]
write_var = list(set(write_var))
# Remove dupplicate if they share the same string representation
# Remove duplicate if they share the same string representation
write_var = [
next(obj)
for i, obj in groupby(sorted(write_var, key=lambda x: str(x)), lambda x: str(x))
@ -1604,7 +1601,7 @@ class Function(SourceMapping, metaclass=ABCMeta): # pylint: disable=too-many-pu
write_var = [x for x in write_var if x]
write_var = [item for sublist in write_var for item in sublist]
write_var = list(set(write_var))
# Remove dupplicate if they share the same string representation
# Remove duplicate if they share the same string representation
write_var = [
next(obj)
for i, obj in groupby(sorted(write_var, key=lambda x: str(x)), lambda x: str(x))
@ -1614,7 +1611,7 @@ class Function(SourceMapping, metaclass=ABCMeta): # pylint: disable=too-many-pu
read_var = [x.variables_read_as_expression for x in self.nodes]
read_var = [x for x in read_var if x]
read_var = [item for sublist in read_var for item in sublist]
# Remove dupplicate if they share the same string representation
# Remove duplicate if they share the same string representation
read_var = [
next(obj)
for i, obj in groupby(sorted(read_var, key=lambda x: str(x)), lambda x: str(x))
@ -1624,7 +1621,7 @@ class Function(SourceMapping, metaclass=ABCMeta): # pylint: disable=too-many-pu
read_var = [x.variables_read for x in self.nodes]
read_var = [x for x in read_var if x]
read_var = [item for sublist in read_var for item in sublist]
# Remove dupplicate if they share the same string representation
# Remove duplicate if they share the same string representation
read_var = [
next(obj)
for i, obj in groupby(sorted(read_var, key=lambda x: str(x)), lambda x: str(x))

@ -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]:

@ -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

@ -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)

@ -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)

@ -35,7 +35,7 @@ class ReusedBaseConstructor(AbstractDetector):
ARGUMENT = "reused-constructor"
HELP = "Reused base constructor"
IMPACT = DetectorClassification.MEDIUM
# The confidence is medium, because prior Solidity 0.4.22, we cant differentiate
# The confidence is medium, because prior Solidity 0.4.22, we can't differentiate
# contract C is A() {
# to
# contract C is A {

@ -16,7 +16,7 @@ class NamingConvention(AbstractDetector):
Exceptions:
- Allow constant variables name/symbol/decimals to be lowercase (ERC20)
- Allow '_' at the beggining of the mixed_case match for private variables and unused parameters
- Allow '_' at the beginning of the mixed_case match for private variables and unused parameters
- Ignore echidna properties (functions with names starting 'echidna_' or 'crytic_'
"""

@ -80,7 +80,7 @@ def _explore(
for ir in node.irs:
if isinstance(ir, Assignment):
if ir.rvalue in divisions:
# Avoid dupplicate. We dont use set so we keep the order of the nodes
# Avoid duplicate. We dont use set so we keep the order of the nodes
if node not in divisions[ir.rvalue]: # type: ignore
divisions[ir.lvalue] = divisions[ir.rvalue] + [node] # type: ignore
else:
@ -94,7 +94,7 @@ def _explore(
nodes = []
for r in mul_arguments:
if not isinstance(r, Constant) and (r in divisions):
# Dont add node already present to avoid dupplicate
# Dont add node already present to avoid duplicate
# We dont use set to keep the order of the nodes
if node in divisions[r]:
nodes += [n for n in divisions[r] if n not in nodes]

@ -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

@ -46,8 +46,18 @@ def detect_unused(contract: Contract) -> Optional[List[StateVariable]]:
variables_used = [item for sublist in variables_used for item in sublist]
variables_used = list(set(variables_used + array_candidates))
# If the contract is abstract, only consider private variables as other visibilities may be used in dependencies
if contract.is_abstract:
return [
x
for x in contract.state_variables
if x not in variables_used and x.visibility == "private"
]
# Return the variables unused that are not public
return [x for x in contract.variables if x not in variables_used and x.visibility != "public"]
return [
x for x in contract.state_variables if x not in variables_used and x.visibility != "public"
]
class UnusedStateVars(AbstractDetector):
@ -71,7 +81,7 @@ class UnusedStateVars(AbstractDetector):
"""Detect unused state variables"""
results = []
for c in self.compilation_unit.contracts_derived:
if c.is_signature_only():
if c.is_signature_only() or c.is_library:
continue
unusedVars = detect_unused(c)
if unusedVars:

@ -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 += "}"

@ -1,7 +1,12 @@
"""
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,
@ -9,6 +14,9 @@ from slither.analyses.evm import (
from slither.utils.colors import blue, green, magenta, red
logger: logging.Logger = logging.getLogger("EVMPrinter")
def _extract_evm_info(slither):
"""
Extract evm information for all derived contracts using evm_cfg_builder
@ -24,6 +32,16 @@ def _extract_evm_info(slither):
contract_bytecode_runtime = contract.file_scope.bytecode_runtime(
contract.compilation_unit.crytic_compile_compilation_unit, contract.name
)
if not contract_bytecode_runtime:
logger.info(
"Contract %s (abstract: %r) has no bytecode runtime, skipping. ",
contract.name,
contract.is_abstract,
)
evm_info["empty", contract.name] = True
continue
contract_srcmap_runtime = contract.file_scope.srcmap_runtime(
contract.compilation_unit.crytic_compile_compilation_unit, contract.name
)
@ -62,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
@ -80,53 +125,31 @@ class PrinterEVM(AbstractPrinter):
for contract in self.slither.contracts_derived:
txt += blue(f"Contract {contract.name}\n")
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 = {}
if evm_info.get(("empty", contract.name), False):
txt += "\tempty contract\n"
continue
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)

@ -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()

@ -482,7 +482,7 @@ def propagate_type_and_convert_call(result: List[Operation], node: "Node") -> Li
call_data.remove(ins.variable)
if isinstance(ins, Argument):
# In case of dupplicate arguments we overwrite the value
# In case of duplicate arguments we overwrite the value
# This can happen because of addr.call.value(1).value(2)
if ins.get_type() in [ArgumentType.GAS]:
calls_gas[ins.call_id] = ins.argument

@ -593,7 +593,7 @@ class ContractSolc(CallerContextExpression):
def analyze_constant_state_variables(self) -> None:
for var_parser in self._variables_parser:
if var_parser.underlying_variable.is_constant:
# cant parse constant expression based on function calls
# can't parse constant expression based on function calls
try:
var_parser.analyze(self)
except (VariableNotFound, KeyError) as e:

@ -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.

@ -59,7 +59,7 @@ def _get_all_covered_kspec_functions(target: str) -> Set[Tuple[str, str]]:
def _get_slither_functions(
slither: SlitherCompilationUnit,
) -> Dict[Tuple[str, str], Union[FunctionContract, StateVariable]]:
# Use contract == contract_declarer to avoid dupplicate
# Use contract == contract_declarer to avoid duplicate
all_functions_declared: List[Union[FunctionContract, StateVariable]] = [
f
for f in slither.functions

@ -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,
@ -75,13 +76,6 @@ defaults_flag_in_config = {
**DEFAULTS_FLAG_IN_CONFIG_CRYTIC_COMPILE,
}
deprecated_flags = {
"fail_pedantic": True,
"fail_low": False,
"fail_medium": False,
"fail_high": False,
}
def read_config_file(args: argparse.Namespace) -> None:
# No config file was provided as an argument
@ -98,12 +92,6 @@ def read_config_file(args: argparse.Namespace) -> None:
with open(args.config_file, encoding="utf8") as f:
config = json.load(f)
for key, elem in config.items():
if key in deprecated_flags:
logger.info(
yellow(f"{args.config_file} has a deprecated key: {key} : {elem}")
)
migrate_config_options(args, key, elem)
continue
if key not in defaults_flag_in_config:
logger.info(
yellow(f"{args.config_file} has an unknown key: {key} : {elem}")
@ -118,28 +106,6 @@ def read_config_file(args: argparse.Namespace) -> None:
logger.error(yellow("Falling back to the default settings..."))
def migrate_config_options(args: argparse.Namespace, key: str, elem):
if key.startswith("fail_") and getattr(args, "fail_on") == defaults_flag_in_config["fail_on"]:
if key == "fail_pedantic":
pedantic_setting = elem
fail_on = FailOnLevel.PEDANTIC if pedantic_setting else FailOnLevel.NONE
setattr(args, "fail_on", fail_on)
logger.info(f"Migrating fail_pedantic: {pedantic_setting} as fail_on: {fail_on.value}")
elif key == "fail_low" and elem is True:
logger.info("Migrating fail_low: true -> fail_on: low")
setattr(args, "fail_on", FailOnLevel.LOW)
elif key == "fail_medium" and elem is True:
logger.info("Migrating fail_medium: true -> fail_on: medium")
setattr(args, "fail_on", FailOnLevel.MEDIUM)
elif key == "fail_high" and elem is True:
logger.info("Migrating fail_high: true -> fail_on: high")
setattr(args, "fail_on", FailOnLevel.HIGH)
else:
logger.warning(yellow(f"Key {key} was deprecated but no migration was provided"))
def output_to_markdown(
detector_classes: List[Type[AbstractDetector]],
printer_classes: List[Type[AbstractPrinter]],

@ -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)

@ -2,6 +2,6 @@
"detectors_to_run": "all",
"exclude_informational": true,
"exclude_low": true,
"fail_pedantic": false,
"fail_on": "none",
"filter_paths": "libs|src/ReentrancyMock.sol"
}

@ -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)

@ -1,6 +1,6 @@
The following unused import(s) in tests/e2e/detectors/test_data/unused-imports/0.8.16/B.sol should be removed:
-import "./A.sol"; (tests/e2e/detectors/test_data/unused-imports/0.8.16/B.sol#4)
The following unused import(s) in tests/e2e/detectors/test_data/unused-imports/0.8.16/C.sol should be removed:
The following unused import(s) in tests/e2e/detectors/test_data/unused-imports/0.8.16/C.sol should be removed:
-import "./B.sol"; (tests/e2e/detectors/test_data/unused-imports/0.8.16/C.sol#4)
The following unused import(s) in tests/e2e/detectors/test_data/unused-imports/0.8.16/B.sol should be removed:
-import "./A.sol"; (tests/e2e/detectors/test_data/unused-imports/0.8.16/B.sol#4)

@ -4,5 +4,9 @@ A.unused4 (tests/e2e/detectors/test_data/unused-state/0.7.6/unused_state.sol#7)
A.unused2 (tests/e2e/detectors/test_data/unused-state/0.7.6/unused_state.sol#5) is never used in B (tests/e2e/detectors/test_data/unused-state/0.7.6/unused_state.sol#11-16)
H.bad1 (tests/e2e/detectors/test_data/unused-state/0.7.6/unused_state.sol#38) is never used in I (tests/e2e/detectors/test_data/unused-state/0.7.6/unused_state.sol#41-46)
F.bad1 (tests/e2e/detectors/test_data/unused-state/0.7.6/unused_state.sol#27) is never used in F (tests/e2e/detectors/test_data/unused-state/0.7.6/unused_state.sol#26-33)
A.unused3 (tests/e2e/detectors/test_data/unused-state/0.7.6/unused_state.sol#6) is never used in B (tests/e2e/detectors/test_data/unused-state/0.7.6/unused_state.sol#11-16)

@ -1,7 +0,0 @@
contract Similar {
function f() public returns (uint) {
uint testVariable = 1;
uint textVariable = 2;
return testVariable + textVariable;
}
}

@ -1,7 +0,0 @@
contract Similar {
function f() public returns (uint) {
uint testVariable = 1;
uint textVariable = 2;
return testVariable + textVariable;
}
}

@ -1,7 +0,0 @@
contract Similar {
function f() public returns (uint) {
uint testVariable = 1;
uint textVariable = 2;
return testVariable + textVariable;
}
}

@ -1,7 +0,0 @@
contract Similar {
function f() public returns (uint) {
uint testVariable = 1;
uint textVariable = 2;
return testVariable + textVariable;
}
}

@ -0,0 +1,15 @@
import "./Initializable.sol";
contract AnyInitializer is Initializable {
address owner;
function anyName() external initializer {
require(owner == address(0));
owner = msg.sender;
}
function kill() external {
require(msg.sender == owner);
selfdestruct(owner);
}
}

@ -1,5 +1,9 @@
contract Initializable{
modifier initializer() {
_;
}
}
contract Initializable {
modifier initializer() {
_;
}
modifier reinitializer(uint64 version) {
_;
}
}

@ -0,0 +1,15 @@
import "./Initializable.sol";
contract Reinitializer is Initializable {
address owner;
function initialize() external reinitializer(2) {
require(owner == address(0));
owner = msg.sender;
}
function kill() external {
require(msg.sender == owner);
selfdestruct(owner);
}
}

@ -0,0 +1,15 @@
import "./Initializable.sol";
contract AnyInitializer is Initializable {
address payable owner;
function anyName() external initializer {
require(owner == address(0));
owner = msg.sender;
}
function kill() external {
require(msg.sender == owner);
selfdestruct(owner);
}
}

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

Loading…
Cancel
Save