diff --git a/.coderabbit.yaml b/.coderabbit.yaml new file mode 100644 index 0000000000..c1e63d0c0e --- /dev/null +++ b/.coderabbit.yaml @@ -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 \ No newline at end of file diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index afead53368..7d2ba83d0d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -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 diff --git a/.gitignore b/.gitignore index 4441c03d18..a895c22ee1 100644 --- a/.gitignore +++ b/.gitignore @@ -114,4 +114,7 @@ test_artifacts/ crytic-export/ # Auto-generated Github pages docs -docs/ \ No newline at end of file +docs/ + +# slither.db.json +slither.db.json diff --git a/README.md b/README.md index 558aa72229..515d6a9f7d 100644 --- a/README.md +++ b/README.md @@ -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/). diff --git a/plugin_example/setup.py b/plugin_example/setup.py index 1bc065394a..908ad51da2 100644 --- a/plugin_example/setup.py +++ b/plugin_example/setup.py @@ -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", }, diff --git a/plugin_example/slither_my_plugin/detectors/__init__.py b/plugin_example/slither_my_plugin/detectors/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/plugin_example/slither_my_plugin/detectors/example.py b/plugin_example/slither_my_plugin/detectors/example.py index 2f10cb518a..edf2d382ad 100644 --- a/plugin_example/slither_my_plugin/detectors/example.py +++ b/plugin_example/slither_my_plugin/detectors/example.py @@ -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): diff --git a/scripts/ci_test.sh b/scripts/ci_test.sh deleted file mode 100755 index 6696a4e890..0000000000 --- a/scripts/ci_test.sh +++ /dev/null @@ -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 -} - diff --git a/scripts/json_diff.py b/scripts/json_diff.py deleted file mode 100644 index 9422f6b8df..0000000000 --- a/scripts/json_diff.py +++ /dev/null @@ -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)) diff --git a/setup.py b/setup.py index 873edd3fe2..a669b82a30 100644 --- a/setup.py +++ b/setup.py @@ -8,7 +8,7 @@ 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=[ diff --git a/slither/__main__.py b/slither/__main__.py index caaef5730b..886d392c0d 100644 --- a/slither/__main__.py +++ b/slither/__main__.py @@ -11,10 +11,10 @@ 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", diff --git a/slither/analyses/evm/convert.py b/slither/analyses/evm/convert.py index 5e8c4a1280..fe294798e5 100644 --- a/slither/analyses/evm/convert.py +++ b/slither/analyses/evm/convert.py @@ -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 diff --git a/slither/core/declarations/contract.py b/slither/core/declarations/contract.py index fe617b3313..3f97a33ed2 100644 --- a/slither/core/declarations/contract.py +++ b/slither/core/declarations/contract.py @@ -1372,8 +1372,6 @@ def update_read_write_using_ssa(self) -> None: 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: diff --git a/slither/core/declarations/custom_error.py b/slither/core/declarations/custom_error.py index 234873eaca..6e2cf142ff 100644 --- a/slither/core/declarations/custom_error.py +++ b/slither/core/declarations/custom_error.py @@ -17,6 +17,7 @@ def __init__(self, compilation_unit: "SlitherCompilationUnit") -> None: self._solidity_signature: Optional[str] = None self._full_name: Optional[str] = None + self._pattern = "error" @property def name(self) -> str: diff --git a/slither/core/declarations/function.py b/slither/core/declarations/function.py index 958a7d2199..6e8968dfb2 100644 --- a/slither/core/declarations/function.py +++ b/slither/core/declarations/function.py @@ -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 @@ def _analyze_read_write(self) -> None: 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 @@ def _analyze_read_write(self) -> None: 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 @@ def _analyze_read_write(self) -> None: 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 @@ def _analyze_read_write(self) -> None: 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)) diff --git a/slither/core/declarations/import_directive.py b/slither/core/declarations/import_directive.py index 19ea2cff96..440b09e9cd 100644 --- a/slither/core/declarations/import_directive.py +++ b/slither/core/declarations/import_directive.py @@ -16,6 +16,8 @@ def __init__(self, filename: Path, scope: "FileScope") -> None: # Map local name -> original name self.renaming: Dict[str, str] = {} + self._pattern = "import" + @property def filename(self) -> str: """ diff --git a/slither/core/declarations/pragma_directive.py b/slither/core/declarations/pragma_directive.py index cd790d5a47..90c1da2ddf 100644 --- a/slither/core/declarations/pragma_directive.py +++ b/slither/core/declarations/pragma_directive.py @@ -11,6 +11,7 @@ def __init__(self, directive: List[str], scope: "FileScope") -> None: super().__init__() self._directive = directive self.scope: "FileScope" = scope + self._pattern = "pragma" @property def directive(self) -> List[str]: diff --git a/slither/core/expressions/identifier.py b/slither/core/expressions/identifier.py index 5cd29a9f5d..493620ab18 100644 --- a/slither/core/expressions/identifier.py +++ b/slither/core/expressions/identifier.py @@ -78,3 +78,6 @@ def value( def __str__(self) -> str: return str(self._value) + + def expression(self): + return self diff --git a/slither/core/slither_core.py b/slither/core/slither_core.py index 8eca260fac..1206e564bc 100644 --- a/slither/core/slither_core.py +++ b/slither/core/slither_core.py @@ -524,6 +524,7 @@ def write_results_to_hide(self) -> None: 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): """ diff --git a/slither/core/solidity_types/elementary_type.py b/slither/core/solidity_types/elementary_type.py index a9f45c8d81..61729b06a2 100644 --- a/slither/core/solidity_types/elementary_type.py +++ b/slither/core/solidity_types/elementary_type.py @@ -225,4 +225,4 @@ def __eq__(self, other: Any) -> bool: return self.type == other.type def __hash__(self) -> int: - return hash(str(self)) + return hash(self._type) diff --git a/slither/core/solidity_types/type_alias.py b/slither/core/solidity_types/type_alias.py index ead9b5394f..c22cd257ef 100644 --- a/slither/core/solidity_types/type_alias.py +++ b/slither/core/solidity_types/type_alias.py @@ -15,6 +15,7 @@ def __init__(self, underlying_type: ElementaryType, name: str) -> None: super().__init__() self.name = name self.underlying_type = underlying_type + self._pattern = "type" @property def type(self) -> ElementaryType: diff --git a/slither/core/source_mapping/source_mapping.py b/slither/core/source_mapping/source_mapping.py index fceab78559..41841f1e80 100644 --- a/slither/core/source_mapping/source_mapping.py +++ b/slither/core/source_mapping/source_mapping.py @@ -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 @@ def __str__(self) -> str: 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 @@ def add_reference_from_raw_source( ) -> 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 diff --git a/slither/detectors/all_detectors.py b/slither/detectors/all_detectors.py index ff1c352c31..7c54844316 100644 --- a/slither/detectors/all_detectors.py +++ b/slither/detectors/all_detectors.py @@ -57,7 +57,6 @@ 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 diff --git a/slither/detectors/attributes/constant_pragma.py b/slither/detectors/attributes/constant_pragma.py index 44a2cb791c..e391f1d56d 100644 --- a/slither/detectors/attributes/constant_pragma.py +++ b/slither/detectors/attributes/constant_pragma.py @@ -36,21 +36,19 @@ def _detect(self) -> List[Output]: 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) diff --git a/slither/detectors/attributes/incorrect_solc.py b/slither/detectors/attributes/incorrect_solc.py index 56ff13315d..532a964934 100644 --- a/slither/detectors/attributes/incorrect_solc.py +++ b/slither/detectors/attributes/incorrect_solc.py @@ -115,16 +115,18 @@ def _detect(self) -> List[Output]: 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) diff --git a/slither/detectors/compiler_bugs/reused_base_constructor.py b/slither/detectors/compiler_bugs/reused_base_constructor.py index 73bd410c79..4764dc6419 100644 --- a/slither/detectors/compiler_bugs/reused_base_constructor.py +++ b/slither/detectors/compiler_bugs/reused_base_constructor.py @@ -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 { diff --git a/slither/detectors/naming_convention/naming_convention.py b/slither/detectors/naming_convention/naming_convention.py index 827cfe2468..ab43318f11 100644 --- a/slither/detectors/naming_convention/naming_convention.py +++ b/slither/detectors/naming_convention/naming_convention.py @@ -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_' """ diff --git a/slither/detectors/statements/divide_before_multiply.py b/slither/detectors/statements/divide_before_multiply.py index 1f6ccd87e1..e33477135d 100644 --- a/slither/detectors/statements/divide_before_multiply.py +++ b/slither/detectors/statements/divide_before_multiply.py @@ -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] diff --git a/slither/detectors/statements/unprotected_upgradeable.py b/slither/detectors/statements/unprotected_upgradeable.py index 30e6300f17..d25aff187d 100644 --- a/slither/detectors/statements/unprotected_upgradeable.py +++ b/slither/detectors/statements/unprotected_upgradeable.py @@ -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) + ], + ) ) diff --git a/slither/detectors/statements/unused_import.py b/slither/detectors/statements/unused_import.py index fe29a91d8f..d3447dcd81 100644 --- a/slither/detectors/statements/unused_import.py +++ b/slither/detectors/statements/unused_import.py @@ -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 @@ def _detect(self) -> List[Output]: 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 diff --git a/slither/detectors/variables/similar_variables.py b/slither/detectors/variables/similar_variables.py deleted file mode 100644 index dccaf09c49..0000000000 --- a/slither/detectors/variables/similar_variables.py +++ /dev/null @@ -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 diff --git a/slither/detectors/variables/unused_state_variables.py b/slither/detectors/variables/unused_state_variables.py index 830ca34caa..0fb068fda4 100644 --- a/slither/detectors/variables/unused_state_variables.py +++ b/slither/detectors/variables/unused_state_variables.py @@ -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 @@ def _detect(self) -> List[Output]: """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: diff --git a/slither/printers/inheritance/inheritance_graph.py b/slither/printers/inheritance/inheritance_graph.py index a16ce273af..54b525c774 100644 --- a/slither/printers/inheritance/inheritance_graph.py +++ b/slither/printers/inheritance/inheritance_graph.py @@ -98,12 +98,21 @@ def _summary(self, contract): """ 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 @@ def _summary(self, contract): 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 @@ def output(self, filename): 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 += "}" diff --git a/slither/printers/summary/evm.py b/slither/printers/summary/evm.py index 0e5ce58d78..b834eb6eb8 100644 --- a/slither/printers/summary/evm.py +++ b/slither/printers/summary/evm.py @@ -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.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 @@ def output(self, _filename): 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) diff --git a/slither/slither.py b/slither/slither.py index 0f22185353..7adc0694ca 100644 --- a/slither/slither.py +++ b/slither/slither.py @@ -196,10 +196,12 @@ def __init__(self, target: Union[str, CryticCompile], **kwargs) -> None: 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() diff --git a/slither/slithir/convert.py b/slither/slithir/convert.py index b8ce460bff..7d8aa543bf 100644 --- a/slither/slithir/convert.py +++ b/slither/slithir/convert.py @@ -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 diff --git a/slither/solc_parsing/declarations/contract.py b/slither/solc_parsing/declarations/contract.py index ebab0e6df4..1ccdc57602 100644 --- a/slither/solc_parsing/declarations/contract.py +++ b/slither/solc_parsing/declarations/contract.py @@ -593,7 +593,7 @@ def _analyze_params_elements( # pylint: disable=too-many-arguments,too-many-loc 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: diff --git a/slither/solc_parsing/expressions/expression_parsing.py b/slither/solc_parsing/expressions/expression_parsing.py index 4991984ff7..afa5c367b2 100644 --- a/slither/solc_parsing/expressions/expression_parsing.py +++ b/slither/solc_parsing/expressions/expression_parsing.py @@ -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] + ")" diff --git a/slither/solc_parsing/expressions/find_variable.py b/slither/solc_parsing/expressions/find_variable.py index 2d8fa58f60..4ea1239e52 100644 --- a/slither/solc_parsing/expressions/find_variable.py +++ b/slither/solc_parsing/expressions/find_variable.py @@ -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 = [] diff --git a/slither/tools/documentation/README.md b/slither/tools/documentation/README.md index b4b3e6a76b..218f2e91d2 100644 --- a/slither/tools/documentation/README.md +++ b/slither/tools/documentation/README.md @@ -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. diff --git a/slither/tools/kspec_coverage/analysis.py b/slither/tools/kspec_coverage/analysis.py index 763939e251..a2ae236606 100755 --- a/slither/tools/kspec_coverage/analysis.py +++ b/slither/tools/kspec_coverage/analysis.py @@ -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 diff --git a/slither/utils/command_line.py b/slither/utils/command_line.py index f03ced8345..f5b9ab4527 100644 --- a/slither/utils/command_line.py +++ b/slither/utils/command_line.py @@ -48,6 +48,7 @@ class FailOnLevel(enum.Enum): "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 @@ class FailOnLevel(enum.Enum): **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]], diff --git a/slither/utils/source_mapping.py b/slither/utils/source_mapping.py index 9bf772894e..180c842f72 100644 --- a/slither/utils/source_mapping.py +++ b/slither/utils/source_mapping.py @@ -2,37 +2,17 @@ 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[ diff --git a/slither/visitors/expression/expression.py b/slither/visitors/expression/expression.py index 41886a1023..83dd1be51a 100644 --- a/slither/visitors/expression/expression.py +++ b/slither/visitors/expression/expression.py @@ -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.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 @@ def expression(self) -> Expression: # 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) diff --git a/slither/visitors/expression/read_var.py b/slither/visitors/expression/read_var.py index a0efdde618..9619cfff9b 100644 --- a/slither/visitors/expression/read_var.py +++ b/slither/visitors/expression/read_var.py @@ -50,8 +50,8 @@ def result(self) -> List[Expression]: 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) diff --git a/slither/visitors/slithir/expression_to_slithir.py b/slither/visitors/slithir/expression_to_slithir.py index 0f91f9bd16..1d68336bd6 100644 --- a/slither/visitors/slithir/expression_to_slithir.py +++ b/slither/visitors/slithir/expression_to_slithir.py @@ -188,7 +188,7 @@ def _post_assignement_operation(self, expression: AssignmentOperation) -> None: 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 @@ def _post_index_access(self, expression: IndexAccess) -> None: 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) diff --git a/slither/vyper_parsing/declarations/contract.py b/slither/vyper_parsing/declarations/contract.py index 2acd43e0fa..64fab1c549 100644 --- a/slither/vyper_parsing/declarations/contract.py +++ b/slither/vyper_parsing/declarations/contract.py @@ -84,7 +84,7 @@ def _parse_contract_items(self) -> None: 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 = { diff --git a/tests/e2e/compilation/test_data/test_change/README.md b/tests/e2e/compilation/test_data/test_change/README.md new file mode 100644 index 0000000000..d541e925ad --- /dev/null +++ b/tests/e2e/compilation/test_data/test_change/README.md @@ -0,0 +1,3 @@ +# Foundry + +To init this test case, run `forge install --no-commit --no-git foundry-rs/forge-std` diff --git a/tests/e2e/compilation/test_data/test_change/foundry.toml b/tests/e2e/compilation/test_data/test_change/foundry.toml new file mode 100644 index 0000000000..25b918f9c9 --- /dev/null +++ b/tests/e2e/compilation/test_data/test_change/foundry.toml @@ -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 diff --git a/tests/e2e/compilation/test_data/test_change/src/Counter.sol b/tests/e2e/compilation/test_data/test_change/src/Counter.sol new file mode 100644 index 0000000000..1ca2a93194 --- /dev/null +++ b/tests/e2e/compilation/test_data/test_change/src/Counter.sol @@ -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 +} \ No newline at end of file diff --git a/tests/e2e/compilation/test_diagnostic.py b/tests/e2e/compilation/test_diagnostic.py new file mode 100644 index 0000000000..3aa8071094 --- /dev/null +++ b/tests/e2e/compilation/test_diagnostic.py @@ -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) diff --git a/tests/e2e/config/test_path_filtering/slither.config.json b/tests/e2e/config/test_path_filtering/slither.config.json index 95aac92b06..0c365eac75 100644 --- a/tests/e2e/config/test_path_filtering/slither.config.json +++ b/tests/e2e/config/test_path_filtering/slither.config.json @@ -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" } diff --git a/tests/e2e/detectors/snapshots/detectors__detector_ConstantPragma_0_4_25_pragma_0_4_25_sol__0.txt b/tests/e2e/detectors/snapshots/detectors__detector_ConstantPragma_0_4_25_pragma_0_4_25_sol__0.txt index 717ca87499..971bb214db 100644 --- a/tests/e2e/detectors/snapshots/detectors__detector_ConstantPragma_0_4_25_pragma_0_4_25_sol__0.txt +++ b/tests/e2e/detectors/snapshots/detectors__detector_ConstantPragma_0_4_25_pragma_0_4_25_sol__0.txt @@ -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) diff --git a/tests/e2e/detectors/snapshots/detectors__detector_ConstantPragma_0_5_16_pragma_0_5_16_sol__0.txt b/tests/e2e/detectors/snapshots/detectors__detector_ConstantPragma_0_5_16_pragma_0_5_16_sol__0.txt index ae5543f3d5..cfb1d98196 100644 --- a/tests/e2e/detectors/snapshots/detectors__detector_ConstantPragma_0_5_16_pragma_0_5_16_sol__0.txt +++ b/tests/e2e/detectors/snapshots/detectors__detector_ConstantPragma_0_5_16_pragma_0_5_16_sol__0.txt @@ -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) diff --git a/tests/e2e/detectors/snapshots/detectors__detector_ConstantPragma_0_6_11_pragma_0_6_11_sol__0.txt b/tests/e2e/detectors/snapshots/detectors__detector_ConstantPragma_0_6_11_pragma_0_6_11_sol__0.txt index f8ccb74bb9..647e535f1a 100644 --- a/tests/e2e/detectors/snapshots/detectors__detector_ConstantPragma_0_6_11_pragma_0_6_11_sol__0.txt +++ b/tests/e2e/detectors/snapshots/detectors__detector_ConstantPragma_0_6_11_pragma_0_6_11_sol__0.txt @@ -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) diff --git a/tests/e2e/detectors/snapshots/detectors__detector_ConstantPragma_0_7_6_pragma_0_7_6_sol__0.txt b/tests/e2e/detectors/snapshots/detectors__detector_ConstantPragma_0_7_6_pragma_0_7_6_sol__0.txt index 4c1a821a3d..6b3309a768 100644 --- a/tests/e2e/detectors/snapshots/detectors__detector_ConstantPragma_0_7_6_pragma_0_7_6_sol__0.txt +++ b/tests/e2e/detectors/snapshots/detectors__detector_ConstantPragma_0_7_6_pragma_0_7_6_sol__0.txt @@ -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) diff --git a/tests/e2e/detectors/snapshots/detectors__detector_IncorrectSolc_0_4_25_static_sol__0.txt b/tests/e2e/detectors/snapshots/detectors__detector_IncorrectSolc_0_4_25_static_sol__0.txt index 0a37cfba03..75c7b031da 100644 --- a/tests/e2e/detectors/snapshots/detectors__detector_IncorrectSolc_0_4_25_static_sol__0.txt +++ b/tests/e2e/detectors/snapshots/detectors__detector_IncorrectSolc_0_4_25_static_sol__0.txt @@ -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. diff --git a/tests/e2e/detectors/snapshots/detectors__detector_IncorrectSolc_0_5_14_static_sol__0.txt b/tests/e2e/detectors/snapshots/detectors__detector_IncorrectSolc_0_5_14_static_sol__0.txt index 7a65a5925d..9ae939c707 100644 --- a/tests/e2e/detectors/snapshots/detectors__detector_IncorrectSolc_0_5_14_static_sol__0.txt +++ b/tests/e2e/detectors/snapshots/detectors__detector_IncorrectSolc_0_5_14_static_sol__0.txt @@ -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. diff --git a/tests/e2e/detectors/snapshots/detectors__detector_IncorrectSolc_0_5_16_dynamic_1_sol__0.txt b/tests/e2e/detectors/snapshots/detectors__detector_IncorrectSolc_0_5_16_dynamic_1_sol__0.txt index 442355e38c..826d1d357a 100644 --- a/tests/e2e/detectors/snapshots/detectors__detector_IncorrectSolc_0_5_16_dynamic_1_sol__0.txt +++ b/tests/e2e/detectors/snapshots/detectors__detector_IncorrectSolc_0_5_16_dynamic_1_sol__0.txt @@ -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. diff --git a/tests/e2e/detectors/snapshots/detectors__detector_IncorrectSolc_0_5_16_dynamic_2_sol__0.txt b/tests/e2e/detectors/snapshots/detectors__detector_IncorrectSolc_0_5_16_dynamic_2_sol__0.txt index 9cde284b52..b4cf6391e1 100644 --- a/tests/e2e/detectors/snapshots/detectors__detector_IncorrectSolc_0_5_16_dynamic_2_sol__0.txt +++ b/tests/e2e/detectors/snapshots/detectors__detector_IncorrectSolc_0_5_16_dynamic_2_sol__0.txt @@ -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. diff --git a/tests/e2e/detectors/snapshots/detectors__detector_IncorrectSolc_0_5_16_static_sol__0.txt b/tests/e2e/detectors/snapshots/detectors__detector_IncorrectSolc_0_5_16_static_sol__0.txt index 15f233fe73..36088f7dab 100644 --- a/tests/e2e/detectors/snapshots/detectors__detector_IncorrectSolc_0_5_16_static_sol__0.txt +++ b/tests/e2e/detectors/snapshots/detectors__detector_IncorrectSolc_0_5_16_static_sol__0.txt @@ -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. diff --git a/tests/e2e/detectors/snapshots/detectors__detector_IncorrectSolc_0_6_10_static_sol__0.txt b/tests/e2e/detectors/snapshots/detectors__detector_IncorrectSolc_0_6_10_static_sol__0.txt index de41146274..48301062fe 100644 --- a/tests/e2e/detectors/snapshots/detectors__detector_IncorrectSolc_0_6_10_static_sol__0.txt +++ b/tests/e2e/detectors/snapshots/detectors__detector_IncorrectSolc_0_6_10_static_sol__0.txt @@ -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) diff --git a/tests/e2e/detectors/snapshots/detectors__detector_IncorrectSolc_0_6_11_dynamic_1_sol__0.txt b/tests/e2e/detectors/snapshots/detectors__detector_IncorrectSolc_0_6_11_dynamic_1_sol__0.txt index 626683d0ee..88bc6b0cdb 100644 --- a/tests/e2e/detectors/snapshots/detectors__detector_IncorrectSolc_0_6_11_dynamic_1_sol__0.txt +++ b/tests/e2e/detectors/snapshots/detectors__detector_IncorrectSolc_0_6_11_dynamic_1_sol__0.txt @@ -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. diff --git a/tests/e2e/detectors/snapshots/detectors__detector_IncorrectSolc_0_6_11_dynamic_2_sol__0.txt b/tests/e2e/detectors/snapshots/detectors__detector_IncorrectSolc_0_6_11_dynamic_2_sol__0.txt index 2142313f28..d7959d2cff 100644 --- a/tests/e2e/detectors/snapshots/detectors__detector_IncorrectSolc_0_6_11_dynamic_2_sol__0.txt +++ b/tests/e2e/detectors/snapshots/detectors__detector_IncorrectSolc_0_6_11_dynamic_2_sol__0.txt @@ -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. diff --git a/tests/e2e/detectors/snapshots/detectors__detector_IncorrectSolc_0_6_11_static_sol__0.txt b/tests/e2e/detectors/snapshots/detectors__detector_IncorrectSolc_0_6_11_static_sol__0.txt index ef330a1bbb..f5f5638bc7 100644 --- a/tests/e2e/detectors/snapshots/detectors__detector_IncorrectSolc_0_6_11_static_sol__0.txt +++ b/tests/e2e/detectors/snapshots/detectors__detector_IncorrectSolc_0_6_11_static_sol__0.txt @@ -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. diff --git a/tests/e2e/detectors/snapshots/detectors__detector_IncorrectSolc_0_7_4_static_sol__0.txt b/tests/e2e/detectors/snapshots/detectors__detector_IncorrectSolc_0_7_4_static_sol__0.txt index 052d8cb271..06a5f98497 100644 --- a/tests/e2e/detectors/snapshots/detectors__detector_IncorrectSolc_0_7_4_static_sol__0.txt +++ b/tests/e2e/detectors/snapshots/detectors__detector_IncorrectSolc_0_7_4_static_sol__0.txt @@ -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) diff --git a/tests/e2e/detectors/snapshots/detectors__detector_IncorrectSolc_0_7_6_dynamic_1_sol__0.txt b/tests/e2e/detectors/snapshots/detectors__detector_IncorrectSolc_0_7_6_dynamic_1_sol__0.txt index 0a67e6f3ea..2749ed44c2 100644 --- a/tests/e2e/detectors/snapshots/detectors__detector_IncorrectSolc_0_7_6_dynamic_1_sol__0.txt +++ b/tests/e2e/detectors/snapshots/detectors__detector_IncorrectSolc_0_7_6_dynamic_1_sol__0.txt @@ -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. diff --git a/tests/e2e/detectors/snapshots/detectors__detector_IncorrectSolc_0_7_6_dynamic_2_sol__0.txt b/tests/e2e/detectors/snapshots/detectors__detector_IncorrectSolc_0_7_6_dynamic_2_sol__0.txt index ae3793886c..84413fbc48 100644 --- a/tests/e2e/detectors/snapshots/detectors__detector_IncorrectSolc_0_7_6_dynamic_2_sol__0.txt +++ b/tests/e2e/detectors/snapshots/detectors__detector_IncorrectSolc_0_7_6_dynamic_2_sol__0.txt @@ -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) + diff --git a/tests/e2e/detectors/snapshots/detectors__detector_IncorrectSolc_0_7_6_static_sol__0.txt b/tests/e2e/detectors/snapshots/detectors__detector_IncorrectSolc_0_7_6_static_sol__0.txt index 70bc412b75..b12f1d1f45 100644 --- a/tests/e2e/detectors/snapshots/detectors__detector_IncorrectSolc_0_7_6_static_sol__0.txt +++ b/tests/e2e/detectors/snapshots/detectors__detector_IncorrectSolc_0_7_6_static_sol__0.txt @@ -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. diff --git a/tests/e2e/detectors/snapshots/detectors__detector_SimilarVarsDetection_0_4_25_similar_variables_sol__0.txt b/tests/e2e/detectors/snapshots/detectors__detector_SimilarVarsDetection_0_4_25_similar_variables_sol__0.txt deleted file mode 100644 index 7f6fa4da1f..0000000000 --- a/tests/e2e/detectors/snapshots/detectors__detector_SimilarVarsDetection_0_4_25_similar_variables_sol__0.txt +++ /dev/null @@ -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) - diff --git a/tests/e2e/detectors/snapshots/detectors__detector_SimilarVarsDetection_0_5_16_similar_variables_sol__0.txt b/tests/e2e/detectors/snapshots/detectors__detector_SimilarVarsDetection_0_5_16_similar_variables_sol__0.txt deleted file mode 100644 index 70b5c329b1..0000000000 --- a/tests/e2e/detectors/snapshots/detectors__detector_SimilarVarsDetection_0_5_16_similar_variables_sol__0.txt +++ /dev/null @@ -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) - diff --git a/tests/e2e/detectors/snapshots/detectors__detector_SimilarVarsDetection_0_6_11_similar_variables_sol__0.txt b/tests/e2e/detectors/snapshots/detectors__detector_SimilarVarsDetection_0_6_11_similar_variables_sol__0.txt deleted file mode 100644 index efb92b5aa8..0000000000 --- a/tests/e2e/detectors/snapshots/detectors__detector_SimilarVarsDetection_0_6_11_similar_variables_sol__0.txt +++ /dev/null @@ -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) - diff --git a/tests/e2e/detectors/snapshots/detectors__detector_SimilarVarsDetection_0_7_6_similar_variables_sol__0.txt b/tests/e2e/detectors/snapshots/detectors__detector_SimilarVarsDetection_0_7_6_similar_variables_sol__0.txt deleted file mode 100644 index 67d4823289..0000000000 --- a/tests/e2e/detectors/snapshots/detectors__detector_SimilarVarsDetection_0_7_6_similar_variables_sol__0.txt +++ /dev/null @@ -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) - diff --git a/tests/e2e/detectors/snapshots/detectors__detector_UnprotectedUpgradeable_0_4_25_AnyInitializer_sol__0.txt b/tests/e2e/detectors/snapshots/detectors__detector_UnprotectedUpgradeable_0_4_25_AnyInitializer_sol__0.txt new file mode 100644 index 0000000000..1ca788ced8 --- /dev/null +++ b/tests/e2e/detectors/snapshots/detectors__detector_UnprotectedUpgradeable_0_4_25_AnyInitializer_sol__0.txt @@ -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) diff --git a/tests/e2e/detectors/snapshots/detectors__detector_UnprotectedUpgradeable_0_4_25_Reinitializer_sol__0.txt b/tests/e2e/detectors/snapshots/detectors__detector_UnprotectedUpgradeable_0_4_25_Reinitializer_sol__0.txt new file mode 100644 index 0000000000..79cc306fea --- /dev/null +++ b/tests/e2e/detectors/snapshots/detectors__detector_UnprotectedUpgradeable_0_4_25_Reinitializer_sol__0.txt @@ -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) diff --git a/tests/e2e/detectors/snapshots/detectors__detector_UnprotectedUpgradeable_0_5_16_AnyInitializer_sol__0.txt b/tests/e2e/detectors/snapshots/detectors__detector_UnprotectedUpgradeable_0_5_16_AnyInitializer_sol__0.txt new file mode 100644 index 0000000000..36309ced37 --- /dev/null +++ b/tests/e2e/detectors/snapshots/detectors__detector_UnprotectedUpgradeable_0_5_16_AnyInitializer_sol__0.txt @@ -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) diff --git a/tests/e2e/detectors/snapshots/detectors__detector_UnprotectedUpgradeable_0_5_16_Reinitializer_sol__0.txt b/tests/e2e/detectors/snapshots/detectors__detector_UnprotectedUpgradeable_0_5_16_Reinitializer_sol__0.txt new file mode 100644 index 0000000000..99eac307d9 --- /dev/null +++ b/tests/e2e/detectors/snapshots/detectors__detector_UnprotectedUpgradeable_0_5_16_Reinitializer_sol__0.txt @@ -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) diff --git a/tests/e2e/detectors/snapshots/detectors__detector_UnprotectedUpgradeable_0_6_11_AnyInitializer_sol__0.txt b/tests/e2e/detectors/snapshots/detectors__detector_UnprotectedUpgradeable_0_6_11_AnyInitializer_sol__0.txt new file mode 100644 index 0000000000..dc9612b837 --- /dev/null +++ b/tests/e2e/detectors/snapshots/detectors__detector_UnprotectedUpgradeable_0_6_11_AnyInitializer_sol__0.txt @@ -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) diff --git a/tests/e2e/detectors/snapshots/detectors__detector_UnprotectedUpgradeable_0_6_11_Reinitializer_sol__0.txt b/tests/e2e/detectors/snapshots/detectors__detector_UnprotectedUpgradeable_0_6_11_Reinitializer_sol__0.txt new file mode 100644 index 0000000000..2708424239 --- /dev/null +++ b/tests/e2e/detectors/snapshots/detectors__detector_UnprotectedUpgradeable_0_6_11_Reinitializer_sol__0.txt @@ -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) diff --git a/tests/e2e/detectors/snapshots/detectors__detector_UnprotectedUpgradeable_0_7_6_AnyInitializer_sol__0.txt b/tests/e2e/detectors/snapshots/detectors__detector_UnprotectedUpgradeable_0_7_6_AnyInitializer_sol__0.txt new file mode 100644 index 0000000000..5a4ccf71af --- /dev/null +++ b/tests/e2e/detectors/snapshots/detectors__detector_UnprotectedUpgradeable_0_7_6_AnyInitializer_sol__0.txt @@ -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) diff --git a/tests/e2e/detectors/snapshots/detectors__detector_UnprotectedUpgradeable_0_7_6_Reinitializer_sol__0.txt b/tests/e2e/detectors/snapshots/detectors__detector_UnprotectedUpgradeable_0_7_6_Reinitializer_sol__0.txt new file mode 100644 index 0000000000..f79c9c662d --- /dev/null +++ b/tests/e2e/detectors/snapshots/detectors__detector_UnprotectedUpgradeable_0_7_6_Reinitializer_sol__0.txt @@ -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) diff --git a/tests/e2e/detectors/snapshots/detectors__detector_UnprotectedUpgradeable_0_8_15_AnyInitializer_sol__0.txt b/tests/e2e/detectors/snapshots/detectors__detector_UnprotectedUpgradeable_0_8_15_AnyInitializer_sol__0.txt new file mode 100644 index 0000000000..fb08aab174 --- /dev/null +++ b/tests/e2e/detectors/snapshots/detectors__detector_UnprotectedUpgradeable_0_8_15_AnyInitializer_sol__0.txt @@ -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) diff --git a/tests/e2e/detectors/snapshots/detectors__detector_UnprotectedUpgradeable_0_8_15_Reinitializer_sol__0.txt b/tests/e2e/detectors/snapshots/detectors__detector_UnprotectedUpgradeable_0_8_15_Reinitializer_sol__0.txt new file mode 100644 index 0000000000..2eb7d5feea --- /dev/null +++ b/tests/e2e/detectors/snapshots/detectors__detector_UnprotectedUpgradeable_0_8_15_Reinitializer_sol__0.txt @@ -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) diff --git a/tests/e2e/detectors/snapshots/detectors__detector_UnusedImport_0_8_16_C_sol__0.txt b/tests/e2e/detectors/snapshots/detectors__detector_UnusedImport_0_8_16_C_sol__0.txt index f5556857be..df4d95471d 100644 --- a/tests/e2e/detectors/snapshots/detectors__detector_UnusedImport_0_8_16_C_sol__0.txt +++ b/tests/e2e/detectors/snapshots/detectors__detector_UnusedImport_0_8_16_C_sol__0.txt @@ -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) + diff --git a/tests/e2e/detectors/snapshots/detectors__detector_UnusedStateVars_0_7_6_unused_state_sol__0.txt b/tests/e2e/detectors/snapshots/detectors__detector_UnusedStateVars_0_7_6_unused_state_sol__0.txt index 39c7ed13e8..6e89528837 100644 --- a/tests/e2e/detectors/snapshots/detectors__detector_UnusedStateVars_0_7_6_unused_state_sol__0.txt +++ b/tests/e2e/detectors/snapshots/detectors__detector_UnusedStateVars_0_7_6_unused_state_sol__0.txt @@ -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) diff --git a/tests/e2e/detectors/test_data/similar-names/0.4.25/similar_variables.sol b/tests/e2e/detectors/test_data/similar-names/0.4.25/similar_variables.sol deleted file mode 100644 index 57f9698675..0000000000 --- a/tests/e2e/detectors/test_data/similar-names/0.4.25/similar_variables.sol +++ /dev/null @@ -1,7 +0,0 @@ -contract Similar { - function f() public returns (uint) { - uint testVariable = 1; - uint textVariable = 2; - return testVariable + textVariable; - } -} \ No newline at end of file diff --git a/tests/e2e/detectors/test_data/similar-names/0.4.25/similar_variables.sol-0.4.25.zip b/tests/e2e/detectors/test_data/similar-names/0.4.25/similar_variables.sol-0.4.25.zip deleted file mode 100644 index b91c8f6ffe..0000000000 Binary files a/tests/e2e/detectors/test_data/similar-names/0.4.25/similar_variables.sol-0.4.25.zip and /dev/null differ diff --git a/tests/e2e/detectors/test_data/similar-names/0.5.16/similar_variables.sol b/tests/e2e/detectors/test_data/similar-names/0.5.16/similar_variables.sol deleted file mode 100644 index 57f9698675..0000000000 --- a/tests/e2e/detectors/test_data/similar-names/0.5.16/similar_variables.sol +++ /dev/null @@ -1,7 +0,0 @@ -contract Similar { - function f() public returns (uint) { - uint testVariable = 1; - uint textVariable = 2; - return testVariable + textVariable; - } -} \ No newline at end of file diff --git a/tests/e2e/detectors/test_data/similar-names/0.5.16/similar_variables.sol-0.5.16.zip b/tests/e2e/detectors/test_data/similar-names/0.5.16/similar_variables.sol-0.5.16.zip deleted file mode 100644 index d547748aab..0000000000 Binary files a/tests/e2e/detectors/test_data/similar-names/0.5.16/similar_variables.sol-0.5.16.zip and /dev/null differ diff --git a/tests/e2e/detectors/test_data/similar-names/0.6.11/similar_variables.sol b/tests/e2e/detectors/test_data/similar-names/0.6.11/similar_variables.sol deleted file mode 100644 index 57f9698675..0000000000 --- a/tests/e2e/detectors/test_data/similar-names/0.6.11/similar_variables.sol +++ /dev/null @@ -1,7 +0,0 @@ -contract Similar { - function f() public returns (uint) { - uint testVariable = 1; - uint textVariable = 2; - return testVariable + textVariable; - } -} \ No newline at end of file diff --git a/tests/e2e/detectors/test_data/similar-names/0.6.11/similar_variables.sol-0.6.11.zip b/tests/e2e/detectors/test_data/similar-names/0.6.11/similar_variables.sol-0.6.11.zip deleted file mode 100644 index 87dd6c4a68..0000000000 Binary files a/tests/e2e/detectors/test_data/similar-names/0.6.11/similar_variables.sol-0.6.11.zip and /dev/null differ diff --git a/tests/e2e/detectors/test_data/similar-names/0.7.6/similar_variables.sol b/tests/e2e/detectors/test_data/similar-names/0.7.6/similar_variables.sol deleted file mode 100644 index 57f9698675..0000000000 --- a/tests/e2e/detectors/test_data/similar-names/0.7.6/similar_variables.sol +++ /dev/null @@ -1,7 +0,0 @@ -contract Similar { - function f() public returns (uint) { - uint testVariable = 1; - uint textVariable = 2; - return testVariable + textVariable; - } -} \ No newline at end of file diff --git a/tests/e2e/detectors/test_data/similar-names/0.7.6/similar_variables.sol-0.7.6.zip b/tests/e2e/detectors/test_data/similar-names/0.7.6/similar_variables.sol-0.7.6.zip deleted file mode 100644 index 5db2e6d7db..0000000000 Binary files a/tests/e2e/detectors/test_data/similar-names/0.7.6/similar_variables.sol-0.7.6.zip and /dev/null differ diff --git a/tests/e2e/detectors/test_data/unprotected-upgrade/0.4.25/AnyInitializer.sol b/tests/e2e/detectors/test_data/unprotected-upgrade/0.4.25/AnyInitializer.sol new file mode 100644 index 0000000000..9ad63bcdf1 --- /dev/null +++ b/tests/e2e/detectors/test_data/unprotected-upgrade/0.4.25/AnyInitializer.sol @@ -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); + } +} diff --git a/tests/e2e/detectors/test_data/unprotected-upgrade/0.4.25/AnyInitializer.sol-0.4.25.zip b/tests/e2e/detectors/test_data/unprotected-upgrade/0.4.25/AnyInitializer.sol-0.4.25.zip new file mode 100644 index 0000000000..143c2bc524 Binary files /dev/null and b/tests/e2e/detectors/test_data/unprotected-upgrade/0.4.25/AnyInitializer.sol-0.4.25.zip differ diff --git a/tests/e2e/detectors/test_data/unprotected-upgrade/0.4.25/Initializable.sol b/tests/e2e/detectors/test_data/unprotected-upgrade/0.4.25/Initializable.sol index 779a0e87c9..c19577db34 100644 --- a/tests/e2e/detectors/test_data/unprotected-upgrade/0.4.25/Initializable.sol +++ b/tests/e2e/detectors/test_data/unprotected-upgrade/0.4.25/Initializable.sol @@ -1,5 +1,9 @@ -contract Initializable{ - modifier initializer() { - _; - } -} \ No newline at end of file +contract Initializable { + modifier initializer() { + _; + } + + modifier reinitializer(uint64 version) { + _; + } +} diff --git a/tests/e2e/detectors/test_data/unprotected-upgrade/0.4.25/Reinitializer.sol b/tests/e2e/detectors/test_data/unprotected-upgrade/0.4.25/Reinitializer.sol new file mode 100644 index 0000000000..6786d15210 --- /dev/null +++ b/tests/e2e/detectors/test_data/unprotected-upgrade/0.4.25/Reinitializer.sol @@ -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); + } +} diff --git a/tests/e2e/detectors/test_data/unprotected-upgrade/0.4.25/Reinitializer.sol-0.4.25.zip b/tests/e2e/detectors/test_data/unprotected-upgrade/0.4.25/Reinitializer.sol-0.4.25.zip new file mode 100644 index 0000000000..054a9f79f4 Binary files /dev/null and b/tests/e2e/detectors/test_data/unprotected-upgrade/0.4.25/Reinitializer.sol-0.4.25.zip differ diff --git a/tests/e2e/detectors/test_data/unprotected-upgrade/0.5.16/AnyInitializer.sol b/tests/e2e/detectors/test_data/unprotected-upgrade/0.5.16/AnyInitializer.sol new file mode 100644 index 0000000000..2afa430964 --- /dev/null +++ b/tests/e2e/detectors/test_data/unprotected-upgrade/0.5.16/AnyInitializer.sol @@ -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); + } +} diff --git a/tests/e2e/detectors/test_data/unprotected-upgrade/0.5.16/AnyInitializer.sol-0.5.16.zip b/tests/e2e/detectors/test_data/unprotected-upgrade/0.5.16/AnyInitializer.sol-0.5.16.zip new file mode 100644 index 0000000000..eafa90e247 Binary files /dev/null and b/tests/e2e/detectors/test_data/unprotected-upgrade/0.5.16/AnyInitializer.sol-0.5.16.zip differ diff --git a/tests/e2e/detectors/test_data/unprotected-upgrade/0.5.16/Initializable.sol b/tests/e2e/detectors/test_data/unprotected-upgrade/0.5.16/Initializable.sol index 779a0e87c9..c19577db34 100644 --- a/tests/e2e/detectors/test_data/unprotected-upgrade/0.5.16/Initializable.sol +++ b/tests/e2e/detectors/test_data/unprotected-upgrade/0.5.16/Initializable.sol @@ -1,5 +1,9 @@ -contract Initializable{ - modifier initializer() { - _; - } -} \ No newline at end of file +contract Initializable { + modifier initializer() { + _; + } + + modifier reinitializer(uint64 version) { + _; + } +} diff --git a/tests/e2e/detectors/test_data/unprotected-upgrade/0.5.16/Reinitializer.sol b/tests/e2e/detectors/test_data/unprotected-upgrade/0.5.16/Reinitializer.sol new file mode 100644 index 0000000000..cdf587c494 --- /dev/null +++ b/tests/e2e/detectors/test_data/unprotected-upgrade/0.5.16/Reinitializer.sol @@ -0,0 +1,15 @@ +import "./Initializable.sol"; + +contract Reinitializer is Initializable { + address payable owner; + + function initialize() external reinitializer(2) { + require(owner == address(0)); + owner = msg.sender; + } + + function kill() external { + require(msg.sender == owner); + selfdestruct(owner); + } +} diff --git a/tests/e2e/detectors/test_data/unprotected-upgrade/0.5.16/Reinitializer.sol-0.5.16.zip b/tests/e2e/detectors/test_data/unprotected-upgrade/0.5.16/Reinitializer.sol-0.5.16.zip new file mode 100644 index 0000000000..0f8b1e9847 Binary files /dev/null and b/tests/e2e/detectors/test_data/unprotected-upgrade/0.5.16/Reinitializer.sol-0.5.16.zip differ diff --git a/tests/e2e/detectors/test_data/unprotected-upgrade/0.6.11/AnyInitializer.sol b/tests/e2e/detectors/test_data/unprotected-upgrade/0.6.11/AnyInitializer.sol new file mode 100644 index 0000000000..bccbbc3ea2 --- /dev/null +++ b/tests/e2e/detectors/test_data/unprotected-upgrade/0.6.11/AnyInitializer.sol @@ -0,0 +1,15 @@ +import "./Initializable.sol"; + +contract AnyInitializer is Initializable { + address payable owner; + + function anyName() external initializer { + require(owner == address(0)); + owner = payable(msg.sender); + } + + function kill() external { + require(msg.sender == owner); + selfdestruct(owner); + } +} diff --git a/tests/e2e/detectors/test_data/unprotected-upgrade/0.6.11/AnyInitializer.sol-0.6.11.zip b/tests/e2e/detectors/test_data/unprotected-upgrade/0.6.11/AnyInitializer.sol-0.6.11.zip new file mode 100644 index 0000000000..b2608ec199 Binary files /dev/null and b/tests/e2e/detectors/test_data/unprotected-upgrade/0.6.11/AnyInitializer.sol-0.6.11.zip differ diff --git a/tests/e2e/detectors/test_data/unprotected-upgrade/0.6.11/Initializable.sol b/tests/e2e/detectors/test_data/unprotected-upgrade/0.6.11/Initializable.sol index 779a0e87c9..c19577db34 100644 --- a/tests/e2e/detectors/test_data/unprotected-upgrade/0.6.11/Initializable.sol +++ b/tests/e2e/detectors/test_data/unprotected-upgrade/0.6.11/Initializable.sol @@ -1,5 +1,9 @@ -contract Initializable{ - modifier initializer() { - _; - } -} \ No newline at end of file +contract Initializable { + modifier initializer() { + _; + } + + modifier reinitializer(uint64 version) { + _; + } +} diff --git a/tests/e2e/detectors/test_data/unprotected-upgrade/0.6.11/Reinitializer.sol b/tests/e2e/detectors/test_data/unprotected-upgrade/0.6.11/Reinitializer.sol new file mode 100644 index 0000000000..d8a81a4cea --- /dev/null +++ b/tests/e2e/detectors/test_data/unprotected-upgrade/0.6.11/Reinitializer.sol @@ -0,0 +1,15 @@ +import "./Initializable.sol"; + +contract Reinitializer is Initializable { + address payable owner; + + function initialize() external reinitializer(2) { + require(owner == address(0)); + owner = payable(msg.sender); + } + + function kill() external { + require(msg.sender == owner); + selfdestruct(owner); + } +} diff --git a/tests/e2e/detectors/test_data/unprotected-upgrade/0.6.11/Reinitializer.sol-0.6.11.zip b/tests/e2e/detectors/test_data/unprotected-upgrade/0.6.11/Reinitializer.sol-0.6.11.zip new file mode 100644 index 0000000000..a74cdfdf4d Binary files /dev/null and b/tests/e2e/detectors/test_data/unprotected-upgrade/0.6.11/Reinitializer.sol-0.6.11.zip differ diff --git a/tests/e2e/detectors/test_data/unprotected-upgrade/0.7.6/AnyInitializer.sol b/tests/e2e/detectors/test_data/unprotected-upgrade/0.7.6/AnyInitializer.sol new file mode 100644 index 0000000000..bccbbc3ea2 --- /dev/null +++ b/tests/e2e/detectors/test_data/unprotected-upgrade/0.7.6/AnyInitializer.sol @@ -0,0 +1,15 @@ +import "./Initializable.sol"; + +contract AnyInitializer is Initializable { + address payable owner; + + function anyName() external initializer { + require(owner == address(0)); + owner = payable(msg.sender); + } + + function kill() external { + require(msg.sender == owner); + selfdestruct(owner); + } +} diff --git a/tests/e2e/detectors/test_data/unprotected-upgrade/0.7.6/AnyInitializer.sol-0.7.6.zip b/tests/e2e/detectors/test_data/unprotected-upgrade/0.7.6/AnyInitializer.sol-0.7.6.zip new file mode 100644 index 0000000000..578572ecb3 Binary files /dev/null and b/tests/e2e/detectors/test_data/unprotected-upgrade/0.7.6/AnyInitializer.sol-0.7.6.zip differ diff --git a/tests/e2e/detectors/test_data/unprotected-upgrade/0.7.6/Initializable.sol b/tests/e2e/detectors/test_data/unprotected-upgrade/0.7.6/Initializable.sol index cff401bae7..a095b72274 100644 --- a/tests/e2e/detectors/test_data/unprotected-upgrade/0.7.6/Initializable.sol +++ b/tests/e2e/detectors/test_data/unprotected-upgrade/0.7.6/Initializable.sol @@ -1,4 +1,4 @@ -contract Initializable{ +contract Initializable { uint8 private _initialized; bool private _initializing; @@ -6,10 +6,14 @@ contract Initializable{ _; } + modifier reinitializer(uint64 version) { + _; + } + function _disableInitializers() internal virtual { require(!_initializing, "Initializable: contract is initializing"); if (_initialized < type(uint8).max) { _initialized = type(uint8).max; } } -} \ No newline at end of file +} diff --git a/tests/e2e/detectors/test_data/unprotected-upgrade/0.7.6/Reinitializer.sol b/tests/e2e/detectors/test_data/unprotected-upgrade/0.7.6/Reinitializer.sol new file mode 100644 index 0000000000..d8a81a4cea --- /dev/null +++ b/tests/e2e/detectors/test_data/unprotected-upgrade/0.7.6/Reinitializer.sol @@ -0,0 +1,15 @@ +import "./Initializable.sol"; + +contract Reinitializer is Initializable { + address payable owner; + + function initialize() external reinitializer(2) { + require(owner == address(0)); + owner = payable(msg.sender); + } + + function kill() external { + require(msg.sender == owner); + selfdestruct(owner); + } +} diff --git a/tests/e2e/detectors/test_data/unprotected-upgrade/0.7.6/Reinitializer.sol-0.7.6.zip b/tests/e2e/detectors/test_data/unprotected-upgrade/0.7.6/Reinitializer.sol-0.7.6.zip new file mode 100644 index 0000000000..bbf0f580b5 Binary files /dev/null and b/tests/e2e/detectors/test_data/unprotected-upgrade/0.7.6/Reinitializer.sol-0.7.6.zip differ diff --git a/tests/e2e/detectors/test_data/unprotected-upgrade/0.8.15/AnyInitializer.sol b/tests/e2e/detectors/test_data/unprotected-upgrade/0.8.15/AnyInitializer.sol new file mode 100644 index 0000000000..bccbbc3ea2 --- /dev/null +++ b/tests/e2e/detectors/test_data/unprotected-upgrade/0.8.15/AnyInitializer.sol @@ -0,0 +1,15 @@ +import "./Initializable.sol"; + +contract AnyInitializer is Initializable { + address payable owner; + + function anyName() external initializer { + require(owner == address(0)); + owner = payable(msg.sender); + } + + function kill() external { + require(msg.sender == owner); + selfdestruct(owner); + } +} diff --git a/tests/e2e/detectors/test_data/unprotected-upgrade/0.8.15/AnyInitializer.sol-0.8.15.zip b/tests/e2e/detectors/test_data/unprotected-upgrade/0.8.15/AnyInitializer.sol-0.8.15.zip new file mode 100644 index 0000000000..9da08b61b0 Binary files /dev/null and b/tests/e2e/detectors/test_data/unprotected-upgrade/0.8.15/AnyInitializer.sol-0.8.15.zip differ diff --git a/tests/e2e/detectors/test_data/unprotected-upgrade/0.8.15/Initializable.sol b/tests/e2e/detectors/test_data/unprotected-upgrade/0.8.15/Initializable.sol index 4fd7561c84..a095b72274 100644 --- a/tests/e2e/detectors/test_data/unprotected-upgrade/0.8.15/Initializable.sol +++ b/tests/e2e/detectors/test_data/unprotected-upgrade/0.8.15/Initializable.sol @@ -6,10 +6,14 @@ contract Initializable { _; } + modifier reinitializer(uint64 version) { + _; + } + function _disableInitializers() internal virtual { require(!_initializing, "Initializable: contract is initializing"); if (_initialized < type(uint8).max) { _initialized = type(uint8).max; } } -} \ No newline at end of file +} diff --git a/tests/e2e/detectors/test_data/unprotected-upgrade/0.8.15/Reinitializer.sol b/tests/e2e/detectors/test_data/unprotected-upgrade/0.8.15/Reinitializer.sol new file mode 100644 index 0000000000..d8a81a4cea --- /dev/null +++ b/tests/e2e/detectors/test_data/unprotected-upgrade/0.8.15/Reinitializer.sol @@ -0,0 +1,15 @@ +import "./Initializable.sol"; + +contract Reinitializer is Initializable { + address payable owner; + + function initialize() external reinitializer(2) { + require(owner == address(0)); + owner = payable(msg.sender); + } + + function kill() external { + require(msg.sender == owner); + selfdestruct(owner); + } +} diff --git a/tests/e2e/detectors/test_data/unprotected-upgrade/0.8.15/Reinitializer.sol-0.8.15.zip b/tests/e2e/detectors/test_data/unprotected-upgrade/0.8.15/Reinitializer.sol-0.8.15.zip new file mode 100644 index 0000000000..e58f71c44b Binary files /dev/null and b/tests/e2e/detectors/test_data/unprotected-upgrade/0.8.15/Reinitializer.sol-0.8.15.zip differ diff --git a/tests/e2e/detectors/test_data/unused-state/0.7.6/unused_state.sol b/tests/e2e/detectors/test_data/unused-state/0.7.6/unused_state.sol index b603f88755..9e4f7ec6f5 100644 --- a/tests/e2e/detectors/test_data/unused-state/0.7.6/unused_state.sol +++ b/tests/e2e/detectors/test_data/unused-state/0.7.6/unused_state.sol @@ -14,3 +14,32 @@ contract B is A{ used = address(0); } } + +library C { + uint internal constant good = 0x00; // other contract can access this constant + function c() public pure returns (uint){ + return 0; + } + +} + +abstract contract F { + uint private bad1 = 0x00; + uint private good1 = 0x00; + uint internal good2 = 0x00; + function use() external returns (uint){ + return good1; + } +} + +abstract contract H { + uint private good1 = 0x00; + uint internal good2 = 0x00; + uint internal bad1 = 0x00; +} + +contract I is H { + function use2() external returns (uint){ + return good2; + } +} \ No newline at end of file diff --git a/tests/e2e/detectors/test_data/unused-state/0.7.6/unused_state.sol-0.7.6.zip b/tests/e2e/detectors/test_data/unused-state/0.7.6/unused_state.sol-0.7.6.zip index 2feb3c2f6d..13cab662f3 100644 Binary files a/tests/e2e/detectors/test_data/unused-state/0.7.6/unused_state.sol-0.7.6.zip and b/tests/e2e/detectors/test_data/unused-state/0.7.6/unused_state.sol-0.7.6.zip differ diff --git a/tests/e2e/detectors/test_detectors.py b/tests/e2e/detectors/test_detectors.py index 5604b57dd6..299f2ea031 100644 --- a/tests/e2e/detectors/test_detectors.py +++ b/tests/e2e/detectors/test_detectors.py @@ -938,6 +938,16 @@ def id_test(test_item: Test): "whitelisted.sol", "0.4.25", ), + Test( + all_detectors.UnprotectedUpgradeable, + "Reinitializer.sol", + "0.4.25", + ), + Test( + all_detectors.UnprotectedUpgradeable, + "AnyInitializer.sol", + "0.4.25", + ), Test( all_detectors.UnprotectedUpgradeable, "Buggy.sol", @@ -953,6 +963,16 @@ def id_test(test_item: Test): "whitelisted.sol", "0.5.16", ), + Test( + all_detectors.UnprotectedUpgradeable, + "Reinitializer.sol", + "0.5.16", + ), + Test( + all_detectors.UnprotectedUpgradeable, + "AnyInitializer.sol", + "0.5.16", + ), Test( all_detectors.UnprotectedUpgradeable, "Buggy.sol", @@ -968,6 +988,16 @@ def id_test(test_item: Test): "whitelisted.sol", "0.6.11", ), + Test( + all_detectors.UnprotectedUpgradeable, + "Reinitializer.sol", + "0.6.11", + ), + Test( + all_detectors.UnprotectedUpgradeable, + "AnyInitializer.sol", + "0.6.11", + ), Test( all_detectors.UnprotectedUpgradeable, "Buggy.sol", @@ -978,6 +1008,16 @@ def id_test(test_item: Test): "Fixed.sol", "0.7.6", ), + Test( + all_detectors.UnprotectedUpgradeable, + "Reinitializer.sol", + "0.7.6", + ), + Test( + all_detectors.UnprotectedUpgradeable, + "AnyInitializer.sol", + "0.7.6", + ), Test( all_detectors.UnprotectedUpgradeable, "whitelisted.sol", @@ -998,6 +1038,16 @@ def id_test(test_item: Test): "whitelisted.sol", "0.8.15", ), + Test( + all_detectors.UnprotectedUpgradeable, + "Reinitializer.sol", + "0.8.15", + ), + Test( + all_detectors.UnprotectedUpgradeable, + "AnyInitializer.sol", + "0.8.15", + ), Test( all_detectors.ABIEncoderV2Array, "storage_ABIEncoderV2_array.sol", @@ -1403,26 +1453,6 @@ def id_test(test_item: Test): "type_based_tautology.sol", "0.7.6", ), - Test( - all_detectors.SimilarVarsDetection, - "similar_variables.sol", - "0.4.25", - ), - Test( - all_detectors.SimilarVarsDetection, - "similar_variables.sol", - "0.5.16", - ), - Test( - all_detectors.SimilarVarsDetection, - "similar_variables.sol", - "0.6.11", - ), - Test( - all_detectors.SimilarVarsDetection, - "similar_variables.sol", - "0.7.6", - ), Test( all_detectors.MsgValueInLoop, "msg_value_loop.sol", diff --git a/tests/e2e/printers/test_data/test_contract_names/C.sol b/tests/e2e/printers/test_data/test_contract_names/C.sol index 90bc35df39..d6ba9b5c15 100644 --- a/tests/e2e/printers/test_data/test_contract_names/C.sol +++ b/tests/e2e/printers/test_data/test_contract_names/C.sol @@ -1,7 +1,21 @@ import "./A.sol"; -contract C is A { +interface MyInterfaceX { + function count() external view returns (uint256); + + function increment() external; +} + +contract C is A, MyInterfaceX { function c_main() public pure { a_main(); } + + function count() external view override returns (uint256){ + return 1; + } + + function increment() external override { + + } } diff --git a/tests/e2e/printers/test_printers.py b/tests/e2e/printers/test_printers.py index 26429d3381..3dea8b74a4 100644 --- a/tests/e2e/printers/test_printers.py +++ b/tests/e2e/printers/test_printers.py @@ -34,3 +34,15 @@ def test_inheritance_printer(solc_binary_path) -> None: assert counter["B -> A"] == 2 assert counter["C -> A"] == 1 + + # Lets also test the include/exclude interface behavior + # Check that the interface is not included + assert "MyInterfaceX" not in content + + slither.include_interfaces = True + output = printer.output("test_printer.dot") + content = output.elements[0]["name"]["content"] + assert "MyInterfaceX" in content + + # Remove test generated files + Path("test_printer.dot").unlink(missing_ok=True) diff --git a/tests/e2e/solc_parsing/test_data/compile/user_defined_value_type/abi-decode-fixed-array.sol-0.8.10-compact.zip b/tests/e2e/solc_parsing/test_data/compile/user_defined_value_type/abi-decode-fixed-array.sol-0.8.10-compact.zip index b65e511efb..6c43440c91 100644 Binary files a/tests/e2e/solc_parsing/test_data/compile/user_defined_value_type/abi-decode-fixed-array.sol-0.8.10-compact.zip and b/tests/e2e/solc_parsing/test_data/compile/user_defined_value_type/abi-decode-fixed-array.sol-0.8.10-compact.zip differ diff --git a/tests/e2e/solc_parsing/test_data/compile/user_defined_value_type/abi-decode-fixed-array.sol-0.8.11-compact.zip b/tests/e2e/solc_parsing/test_data/compile/user_defined_value_type/abi-decode-fixed-array.sol-0.8.11-compact.zip index c9a78fceaf..6c8b090357 100644 Binary files a/tests/e2e/solc_parsing/test_data/compile/user_defined_value_type/abi-decode-fixed-array.sol-0.8.11-compact.zip and b/tests/e2e/solc_parsing/test_data/compile/user_defined_value_type/abi-decode-fixed-array.sol-0.8.11-compact.zip differ diff --git a/tests/e2e/solc_parsing/test_data/compile/user_defined_value_type/abi-decode-fixed-array.sol-0.8.12-compact.zip b/tests/e2e/solc_parsing/test_data/compile/user_defined_value_type/abi-decode-fixed-array.sol-0.8.12-compact.zip index 1b69e8e3d5..bdc1f1bfde 100644 Binary files a/tests/e2e/solc_parsing/test_data/compile/user_defined_value_type/abi-decode-fixed-array.sol-0.8.12-compact.zip and b/tests/e2e/solc_parsing/test_data/compile/user_defined_value_type/abi-decode-fixed-array.sol-0.8.12-compact.zip differ diff --git a/tests/e2e/solc_parsing/test_data/compile/user_defined_value_type/abi-decode-fixed-array.sol-0.8.13-compact.zip b/tests/e2e/solc_parsing/test_data/compile/user_defined_value_type/abi-decode-fixed-array.sol-0.8.13-compact.zip index 5661f465da..1d2d91bad4 100644 Binary files a/tests/e2e/solc_parsing/test_data/compile/user_defined_value_type/abi-decode-fixed-array.sol-0.8.13-compact.zip and b/tests/e2e/solc_parsing/test_data/compile/user_defined_value_type/abi-decode-fixed-array.sol-0.8.13-compact.zip differ diff --git a/tests/e2e/solc_parsing/test_data/compile/user_defined_value_type/abi-decode-fixed-array.sol-0.8.14-compact.zip b/tests/e2e/solc_parsing/test_data/compile/user_defined_value_type/abi-decode-fixed-array.sol-0.8.14-compact.zip index cf49507691..3188827199 100644 Binary files a/tests/e2e/solc_parsing/test_data/compile/user_defined_value_type/abi-decode-fixed-array.sol-0.8.14-compact.zip and b/tests/e2e/solc_parsing/test_data/compile/user_defined_value_type/abi-decode-fixed-array.sol-0.8.14-compact.zip differ diff --git a/tests/e2e/solc_parsing/test_data/compile/user_defined_value_type/abi-decode-fixed-array.sol-0.8.15-compact.zip b/tests/e2e/solc_parsing/test_data/compile/user_defined_value_type/abi-decode-fixed-array.sol-0.8.15-compact.zip index 16236fd228..c4adafc8e2 100644 Binary files a/tests/e2e/solc_parsing/test_data/compile/user_defined_value_type/abi-decode-fixed-array.sol-0.8.15-compact.zip and b/tests/e2e/solc_parsing/test_data/compile/user_defined_value_type/abi-decode-fixed-array.sol-0.8.15-compact.zip differ diff --git a/tests/e2e/solc_parsing/test_data/compile/user_defined_value_type/abi-decode-fixed-array.sol-0.8.8-compact.zip b/tests/e2e/solc_parsing/test_data/compile/user_defined_value_type/abi-decode-fixed-array.sol-0.8.8-compact.zip index d574a7cb84..4c6db0a424 100644 Binary files a/tests/e2e/solc_parsing/test_data/compile/user_defined_value_type/abi-decode-fixed-array.sol-0.8.8-compact.zip and b/tests/e2e/solc_parsing/test_data/compile/user_defined_value_type/abi-decode-fixed-array.sol-0.8.8-compact.zip differ diff --git a/tests/e2e/solc_parsing/test_data/expected/user_defined_value_type/abi-decode-fixed-array.sol-0.8.10-compact.json b/tests/e2e/solc_parsing/test_data/expected/user_defined_value_type/abi-decode-fixed-array.sol-0.8.10-compact.json index 070b687bd8..b040e9ab4d 100644 --- a/tests/e2e/solc_parsing/test_data/expected/user_defined_value_type/abi-decode-fixed-array.sol-0.8.10-compact.json +++ b/tests/e2e/solc_parsing/test_data/expected/user_defined_value_type/abi-decode-fixed-array.sol-0.8.10-compact.json @@ -2,6 +2,7 @@ "I": {}, "C": { "test_decode_interface_array(bytes)": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: EXPRESSION 1\n\"];\n}\n", - "test_decode_enum_array(bytes)": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: EXPRESSION 1\n\"];\n}\n" + "test_decode_enum_array(bytes)": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: EXPRESSION 1\n\"];\n}\n", + "test_decode_struct_array(bytes)": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: NEW VARIABLE 1\n\"];\n}\n" } } \ No newline at end of file diff --git a/tests/e2e/solc_parsing/test_data/expected/user_defined_value_type/abi-decode-fixed-array.sol-0.8.11-compact.json b/tests/e2e/solc_parsing/test_data/expected/user_defined_value_type/abi-decode-fixed-array.sol-0.8.11-compact.json index 070b687bd8..b040e9ab4d 100644 --- a/tests/e2e/solc_parsing/test_data/expected/user_defined_value_type/abi-decode-fixed-array.sol-0.8.11-compact.json +++ b/tests/e2e/solc_parsing/test_data/expected/user_defined_value_type/abi-decode-fixed-array.sol-0.8.11-compact.json @@ -2,6 +2,7 @@ "I": {}, "C": { "test_decode_interface_array(bytes)": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: EXPRESSION 1\n\"];\n}\n", - "test_decode_enum_array(bytes)": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: EXPRESSION 1\n\"];\n}\n" + "test_decode_enum_array(bytes)": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: EXPRESSION 1\n\"];\n}\n", + "test_decode_struct_array(bytes)": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: NEW VARIABLE 1\n\"];\n}\n" } } \ No newline at end of file diff --git a/tests/e2e/solc_parsing/test_data/expected/user_defined_value_type/abi-decode-fixed-array.sol-0.8.12-compact.json b/tests/e2e/solc_parsing/test_data/expected/user_defined_value_type/abi-decode-fixed-array.sol-0.8.12-compact.json index 070b687bd8..b040e9ab4d 100644 --- a/tests/e2e/solc_parsing/test_data/expected/user_defined_value_type/abi-decode-fixed-array.sol-0.8.12-compact.json +++ b/tests/e2e/solc_parsing/test_data/expected/user_defined_value_type/abi-decode-fixed-array.sol-0.8.12-compact.json @@ -2,6 +2,7 @@ "I": {}, "C": { "test_decode_interface_array(bytes)": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: EXPRESSION 1\n\"];\n}\n", - "test_decode_enum_array(bytes)": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: EXPRESSION 1\n\"];\n}\n" + "test_decode_enum_array(bytes)": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: EXPRESSION 1\n\"];\n}\n", + "test_decode_struct_array(bytes)": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: NEW VARIABLE 1\n\"];\n}\n" } } \ No newline at end of file diff --git a/tests/e2e/solc_parsing/test_data/expected/user_defined_value_type/abi-decode-fixed-array.sol-0.8.13-compact.json b/tests/e2e/solc_parsing/test_data/expected/user_defined_value_type/abi-decode-fixed-array.sol-0.8.13-compact.json index 070b687bd8..b040e9ab4d 100644 --- a/tests/e2e/solc_parsing/test_data/expected/user_defined_value_type/abi-decode-fixed-array.sol-0.8.13-compact.json +++ b/tests/e2e/solc_parsing/test_data/expected/user_defined_value_type/abi-decode-fixed-array.sol-0.8.13-compact.json @@ -2,6 +2,7 @@ "I": {}, "C": { "test_decode_interface_array(bytes)": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: EXPRESSION 1\n\"];\n}\n", - "test_decode_enum_array(bytes)": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: EXPRESSION 1\n\"];\n}\n" + "test_decode_enum_array(bytes)": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: EXPRESSION 1\n\"];\n}\n", + "test_decode_struct_array(bytes)": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: NEW VARIABLE 1\n\"];\n}\n" } } \ No newline at end of file diff --git a/tests/e2e/solc_parsing/test_data/expected/user_defined_value_type/abi-decode-fixed-array.sol-0.8.14-compact.json b/tests/e2e/solc_parsing/test_data/expected/user_defined_value_type/abi-decode-fixed-array.sol-0.8.14-compact.json index 070b687bd8..b040e9ab4d 100644 --- a/tests/e2e/solc_parsing/test_data/expected/user_defined_value_type/abi-decode-fixed-array.sol-0.8.14-compact.json +++ b/tests/e2e/solc_parsing/test_data/expected/user_defined_value_type/abi-decode-fixed-array.sol-0.8.14-compact.json @@ -2,6 +2,7 @@ "I": {}, "C": { "test_decode_interface_array(bytes)": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: EXPRESSION 1\n\"];\n}\n", - "test_decode_enum_array(bytes)": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: EXPRESSION 1\n\"];\n}\n" + "test_decode_enum_array(bytes)": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: EXPRESSION 1\n\"];\n}\n", + "test_decode_struct_array(bytes)": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: NEW VARIABLE 1\n\"];\n}\n" } } \ No newline at end of file diff --git a/tests/e2e/solc_parsing/test_data/expected/user_defined_value_type/abi-decode-fixed-array.sol-0.8.15-compact.json b/tests/e2e/solc_parsing/test_data/expected/user_defined_value_type/abi-decode-fixed-array.sol-0.8.15-compact.json index 070b687bd8..b040e9ab4d 100644 --- a/tests/e2e/solc_parsing/test_data/expected/user_defined_value_type/abi-decode-fixed-array.sol-0.8.15-compact.json +++ b/tests/e2e/solc_parsing/test_data/expected/user_defined_value_type/abi-decode-fixed-array.sol-0.8.15-compact.json @@ -2,6 +2,7 @@ "I": {}, "C": { "test_decode_interface_array(bytes)": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: EXPRESSION 1\n\"];\n}\n", - "test_decode_enum_array(bytes)": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: EXPRESSION 1\n\"];\n}\n" + "test_decode_enum_array(bytes)": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: EXPRESSION 1\n\"];\n}\n", + "test_decode_struct_array(bytes)": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: NEW VARIABLE 1\n\"];\n}\n" } } \ No newline at end of file diff --git a/tests/e2e/solc_parsing/test_data/expected/user_defined_value_type/abi-decode-fixed-array.sol-0.8.8-compact.json b/tests/e2e/solc_parsing/test_data/expected/user_defined_value_type/abi-decode-fixed-array.sol-0.8.8-compact.json index 070b687bd8..b040e9ab4d 100644 --- a/tests/e2e/solc_parsing/test_data/expected/user_defined_value_type/abi-decode-fixed-array.sol-0.8.8-compact.json +++ b/tests/e2e/solc_parsing/test_data/expected/user_defined_value_type/abi-decode-fixed-array.sol-0.8.8-compact.json @@ -2,6 +2,7 @@ "I": {}, "C": { "test_decode_interface_array(bytes)": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: EXPRESSION 1\n\"];\n}\n", - "test_decode_enum_array(bytes)": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: EXPRESSION 1\n\"];\n}\n" + "test_decode_enum_array(bytes)": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: EXPRESSION 1\n\"];\n}\n", + "test_decode_struct_array(bytes)": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: NEW VARIABLE 1\n\"];\n}\n" } } \ No newline at end of file diff --git a/tests/e2e/solc_parsing/test_data/user_defined_value_type/abi-decode-fixed-array.sol b/tests/e2e/solc_parsing/test_data/user_defined_value_type/abi-decode-fixed-array.sol index 14a59b5a2a..d5bddddc29 100644 --- a/tests/e2e/solc_parsing/test_data/user_defined_value_type/abi-decode-fixed-array.sol +++ b/tests/e2e/solc_parsing/test_data/user_defined_value_type/abi-decode-fixed-array.sol @@ -1,10 +1,11 @@ interface I {} enum A {a,b} - +struct S {uint256 a;} contract C { I[6] interfaceArray; A[6] enumArray; + function test_decode_interface_array(bytes memory data) public { interfaceArray = abi.decode(data, (I[6])); } @@ -13,4 +14,8 @@ contract C { enumArray = abi.decode(data, (A[6])); } + function test_decode_struct_array(bytes memory data) public { + S[6] memory structArray = abi.decode(data, (S[6])); + } + } diff --git a/tests/e2e/test_cli.py b/tests/e2e/test_cli.py new file mode 100644 index 0000000000..72e0441d6a --- /dev/null +++ b/tests/e2e/test_cli.py @@ -0,0 +1,20 @@ +import sys +import tempfile +import pytest + +from slither.__main__ import main_impl + + +def test_cli_exit_on_invalid_compilation_file(caplog): + + with tempfile.NamedTemporaryFile("w") as f: + f.write("pragma solidity ^0.10000.0;") + + sys.argv = ["slither", f.name] + with pytest.raises(SystemExit) as pytest_wrapped_e: + main_impl([], []) + + assert pytest_wrapped_e.type == SystemExit + assert pytest_wrapped_e.value.code == 2 + + assert caplog.record_tuples[0] == ("Slither", 40, "Unable to compile all targets.") diff --git a/tests/unit/core/test_data/scope_with_renaming/core/MainContract.sol b/tests/unit/core/test_data/scope_with_renaming/core/MainContract.sol new file mode 100644 index 0000000000..5eb92aa2fd --- /dev/null +++ b/tests/unit/core/test_data/scope_with_renaming/core/MainContract.sol @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.13; + +import { + ParentContract +} from "./ParentContract.sol"; + +import { + MainErrors as Errors +} from "./../errors/MainErrors.sol"; + + +contract MainContract is ParentContract { + + + function functionWithMainError1(uint256 a, uint256 b) external pure returns (uint256) { + if (a == b) { + revert Errors.MainError1(); + } + // Add some arithmetic operations here + return a + b; + } + + function functionWithMainError2(uint256 a, uint256 b) external pure returns (uint256) { + if (a < b) { + revert Errors.MainError2(); + } + // Add some arithmetic operations here + return a - b; + } + + function functionWithMainError3(uint256 a, uint256 b) external pure returns (uint256) { + if (b == 0) { + revert Errors.MainError3(); + } + // Add some arithmetic operations here + return a * b; + } + + +} diff --git a/tests/unit/core/test_data/scope_with_renaming/core/ParentContract.sol b/tests/unit/core/test_data/scope_with_renaming/core/ParentContract.sol new file mode 100644 index 0000000000..9ca32a1a6f --- /dev/null +++ b/tests/unit/core/test_data/scope_with_renaming/core/ParentContract.sol @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.13; + + +import { + AccessControlErrors as Errors +} from "../errors/ParentContractErrors.sol"; + + +contract ParentContract { + + + function functionWithAccessControlErrors1(uint256 a, uint256 b) external pure returns (uint256) { + if (a == b) { + revert Errors.AccessControlErrors1(); + } + // Add some arithmetic operations here + return a + b; + } + + function functionWithAccessControlErrors2(uint256 a, uint256 b) external pure returns (uint256) { + if (a < b) { + revert Errors.AccessControlErrors2(); + } + // Add some arithmetic operations here + return a - b; + } + + +} diff --git a/tests/unit/core/test_data/scope_with_renaming/errors/MainErrors.sol b/tests/unit/core/test_data/scope_with_renaming/errors/MainErrors.sol new file mode 100644 index 0000000000..e7db5c5c38 --- /dev/null +++ b/tests/unit/core/test_data/scope_with_renaming/errors/MainErrors.sol @@ -0,0 +1,9 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.13; + +// TODO: remove unused errors +library MainErrors { + error MainError1(); + error MainError2(); + error MainError3(); +} diff --git a/tests/unit/core/test_data/scope_with_renaming/errors/ParentContractErrors.sol b/tests/unit/core/test_data/scope_with_renaming/errors/ParentContractErrors.sol new file mode 100644 index 0000000000..38ce16bdd2 --- /dev/null +++ b/tests/unit/core/test_data/scope_with_renaming/errors/ParentContractErrors.sol @@ -0,0 +1,7 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.13; + +library AccessControlErrors { + error AccessControlErrors1(); + error AccessControlErrors2(); +} diff --git a/tests/unit/core/test_scope_with_renaming.py b/tests/unit/core/test_scope_with_renaming.py new file mode 100644 index 0000000000..6984cfaa42 --- /dev/null +++ b/tests/unit/core/test_scope_with_renaming.py @@ -0,0 +1,17 @@ +from pathlib import Path +from crytic_compile import CryticCompile +from crytic_compile.platform.solc_standard_json import SolcStandardJson + +from slither import Slither + +TEST_DATA_DIR = Path(__file__).resolve().parent / "test_data" +SCOPE_RENAMING_TEST_DATA_DIR = Path(TEST_DATA_DIR, "scope_with_renaming") + +# https://github.com/crytic/slither/issues/2454 +def test_find_variable_scope_with_renaming(solc_binary_path) -> None: + solc_path = solc_binary_path("0.8.24") + standard_json = SolcStandardJson() + for source_file in SCOPE_RENAMING_TEST_DATA_DIR.rglob("**/*.sol"): + standard_json.add_source_file(Path(source_file).as_posix()) + compilation = CryticCompile(standard_json, solc=solc_path) + Slither(compilation, disallow_partial=True) diff --git a/tests/unit/core/test_source_mapping.py b/tests/unit/core/test_source_mapping.py index 298a192d5f..2a1bbc5510 100644 --- a/tests/unit/core/test_source_mapping.py +++ b/tests/unit/core/test_source_mapping.py @@ -58,7 +58,7 @@ def test_source_mapping_inheritance(solc_binary_path, solc_version): assert {(x.start, x.end) for x in slither.offset_to_implementations(file, 203)} == {(193, 230)} # Offset 93 is the call to f() in A.test() - # This can lead to three differents functions, depending on the current contract's context + # This can lead to three different functions, depending on the current contract's context functions = slither.offset_to_objects(file, 93) print(functions) assert len(functions) == 3 diff --git a/tests/unit/core/test_using_for.py b/tests/unit/core/test_using_for.py index 4d7634c5c9..656fc3a357 100644 --- a/tests/unit/core/test_using_for.py +++ b/tests/unit/core/test_using_for.py @@ -17,7 +17,7 @@ def test_using_for_global_collision(solc_binary_path) -> None: for source_file in Path(USING_FOR_TEST_DATA_DIR, "using_for_global_collision").rglob("*.sol"): standard_json.add_source_file(Path(source_file).as_posix()) compilation = CryticCompile(standard_json, solc=solc_path) - sl = Slither(compilation) + sl = Slither(compilation, disallow_partial=True) _run_all_detectors(sl) diff --git a/tests/unit/slithir/test_data/ternary_expressions.sol b/tests/unit/slithir/test_data/ternary_expressions.sol index 1ccd51d34d..43ba9b9cb2 100644 --- a/tests/unit/slithir/test_data/ternary_expressions.sol +++ b/tests/unit/slithir/test_data/ternary_expressions.sol @@ -6,7 +6,7 @@ contract C { // TODO // 1) support variable declarations //uint min = 1 > 0 ? 1 : 2; - // 2) suppory ternary index range access + // 2) support ternary index range access // function e(bool cond, bytes calldata x) external { // bytes memory a = x[cond ? 1 : 2 :]; // } diff --git a/tests/unit/slithir/test_ssa_generation.py b/tests/unit/slithir/test_ssa_generation.py index 1a709c2f78..1358fa957c 100644 --- a/tests/unit/slithir/test_ssa_generation.py +++ b/tests/unit/slithir/test_ssa_generation.py @@ -502,7 +502,7 @@ def test_storage_refers_to(slither_from_solidity_source): When a phi-node is created, ensure refers_to is propagated to the phi-node. Assignments also propagate refers_to. Whenever a ReferenceVariable is the destination of an assignment (e.g. s.v = 10) - below, create additional versions of the variables it refers to record that a a + below, create additional versions of the variables it refers to record that a write was made. In the current implementation, this is referenced by phis. """ source = """ @@ -550,7 +550,7 @@ def test_storage_refers_to(slither_from_solidity_source): entryphi = [x for x in phinodes if x.lvalue in sphi.lvalue.refers_to] assert len(entryphi) == 2 - # The remaining two phis are the ones recording that write through ReferenceVariable occured + # The remaining two phis are the ones recording that write through ReferenceVariable occurred for ephi in entryphi: phinodes.remove(ephi) phinodes.remove(sphi)