Merge branch 'dev' into fix/ir-assignment-2266

pull/2412/head
dm 6 months ago committed by GitHub
commit 9fe27d375f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 4
      .github/workflows/ci.yml
  2. 5
      .gitignore
  3. 7
      README.md
  4. 84
      scripts/ci_test.sh
  5. 27
      scripts/json_diff.py
  6. 81
      slither/__main__.py
  7. 8
      slither/analyses/evm/convert.py
  8. 2
      slither/core/declarations/contract.py
  9. 1
      slither/core/declarations/custom_error.py
  10. 9
      slither/core/declarations/function.py
  11. 2
      slither/core/declarations/import_directive.py
  12. 1
      slither/core/declarations/pragma_directive.py
  13. 3
      slither/core/expressions/identifier.py
  14. 1
      slither/core/slither_core.py
  15. 2
      slither/core/solidity_types/elementary_type.py
  16. 1
      slither/core/solidity_types/type_alias.py
  17. 45
      slither/core/source_mapping/source_mapping.py
  18. 1
      slither/detectors/all_detectors.py
  19. 9
      slither/detectors/statements/unprotected_upgradeable.py
  20. 106
      slither/detectors/variables/similar_variables.py
  21. 20
      slither/printers/inheritance/inheritance_graph.py
  22. 86
      slither/printers/summary/evm.py
  23. 4
      slither/slither.py
  24. 2
      slither/solc_parsing/expressions/find_variable.py
  25. 2
      slither/tools/documentation/README.md
  26. 1
      slither/utils/command_line.py
  27. 28
      slither/utils/source_mapping.py
  28. 85
      slither/visitors/expression/expression.py
  29. 4
      slither/visitors/expression/read_var.py
  30. 6
      slither/visitors/slithir/expression_to_slithir.py
  31. 2
      slither/vyper_parsing/declarations/contract.py
  32. 2
      tests/e2e/detectors/snapshots/detectors__detector_SimilarVarsDetection_0_4_25_similar_variables_sol__0.txt
  33. 2
      tests/e2e/detectors/snapshots/detectors__detector_SimilarVarsDetection_0_5_16_similar_variables_sol__0.txt
  34. 2
      tests/e2e/detectors/snapshots/detectors__detector_SimilarVarsDetection_0_6_11_similar_variables_sol__0.txt
  35. 2
      tests/e2e/detectors/snapshots/detectors__detector_SimilarVarsDetection_0_7_6_similar_variables_sol__0.txt
  36. 1
      tests/e2e/detectors/snapshots/detectors__detector_UnprotectedUpgradeable_0_4_25_AnyInitializer_sol__0.txt
  37. 1
      tests/e2e/detectors/snapshots/detectors__detector_UnprotectedUpgradeable_0_4_25_Reinitializer_sol__0.txt
  38. 1
      tests/e2e/detectors/snapshots/detectors__detector_UnprotectedUpgradeable_0_5_16_AnyInitializer_sol__0.txt
  39. 1
      tests/e2e/detectors/snapshots/detectors__detector_UnprotectedUpgradeable_0_5_16_Reinitializer_sol__0.txt
  40. 1
      tests/e2e/detectors/snapshots/detectors__detector_UnprotectedUpgradeable_0_6_11_AnyInitializer_sol__0.txt
  41. 1
      tests/e2e/detectors/snapshots/detectors__detector_UnprotectedUpgradeable_0_6_11_Reinitializer_sol__0.txt
  42. 1
      tests/e2e/detectors/snapshots/detectors__detector_UnprotectedUpgradeable_0_7_6_AnyInitializer_sol__0.txt
  43. 1
      tests/e2e/detectors/snapshots/detectors__detector_UnprotectedUpgradeable_0_7_6_Reinitializer_sol__0.txt
  44. 1
      tests/e2e/detectors/snapshots/detectors__detector_UnprotectedUpgradeable_0_8_15_AnyInitializer_sol__0.txt
  45. 1
      tests/e2e/detectors/snapshots/detectors__detector_UnprotectedUpgradeable_0_8_15_Reinitializer_sol__0.txt
  46. 7
      tests/e2e/detectors/test_data/similar-names/0.4.25/similar_variables.sol
  47. BIN
      tests/e2e/detectors/test_data/similar-names/0.4.25/similar_variables.sol-0.4.25.zip
  48. 7
      tests/e2e/detectors/test_data/similar-names/0.5.16/similar_variables.sol
  49. BIN
      tests/e2e/detectors/test_data/similar-names/0.5.16/similar_variables.sol-0.5.16.zip
  50. 7
      tests/e2e/detectors/test_data/similar-names/0.6.11/similar_variables.sol
  51. BIN
      tests/e2e/detectors/test_data/similar-names/0.6.11/similar_variables.sol-0.6.11.zip
  52. 7
      tests/e2e/detectors/test_data/similar-names/0.7.6/similar_variables.sol
  53. BIN
      tests/e2e/detectors/test_data/similar-names/0.7.6/similar_variables.sol-0.7.6.zip
  54. 15
      tests/e2e/detectors/test_data/unprotected-upgrade/0.4.25/AnyInitializer.sol
  55. BIN
      tests/e2e/detectors/test_data/unprotected-upgrade/0.4.25/AnyInitializer.sol-0.4.25.zip
  56. 14
      tests/e2e/detectors/test_data/unprotected-upgrade/0.4.25/Initializable.sol
  57. 15
      tests/e2e/detectors/test_data/unprotected-upgrade/0.4.25/Reinitializer.sol
  58. BIN
      tests/e2e/detectors/test_data/unprotected-upgrade/0.4.25/Reinitializer.sol-0.4.25.zip
  59. 15
      tests/e2e/detectors/test_data/unprotected-upgrade/0.5.16/AnyInitializer.sol
  60. BIN
      tests/e2e/detectors/test_data/unprotected-upgrade/0.5.16/AnyInitializer.sol-0.5.16.zip
  61. 14
      tests/e2e/detectors/test_data/unprotected-upgrade/0.5.16/Initializable.sol
  62. 15
      tests/e2e/detectors/test_data/unprotected-upgrade/0.5.16/Reinitializer.sol
  63. BIN
      tests/e2e/detectors/test_data/unprotected-upgrade/0.5.16/Reinitializer.sol-0.5.16.zip
  64. 15
      tests/e2e/detectors/test_data/unprotected-upgrade/0.6.11/AnyInitializer.sol
  65. BIN
      tests/e2e/detectors/test_data/unprotected-upgrade/0.6.11/AnyInitializer.sol-0.6.11.zip
  66. 14
      tests/e2e/detectors/test_data/unprotected-upgrade/0.6.11/Initializable.sol
  67. 15
      tests/e2e/detectors/test_data/unprotected-upgrade/0.6.11/Reinitializer.sol
  68. BIN
      tests/e2e/detectors/test_data/unprotected-upgrade/0.6.11/Reinitializer.sol-0.6.11.zip
  69. 15
      tests/e2e/detectors/test_data/unprotected-upgrade/0.7.6/AnyInitializer.sol
  70. BIN
      tests/e2e/detectors/test_data/unprotected-upgrade/0.7.6/AnyInitializer.sol-0.7.6.zip
  71. 8
      tests/e2e/detectors/test_data/unprotected-upgrade/0.7.6/Initializable.sol
  72. 15
      tests/e2e/detectors/test_data/unprotected-upgrade/0.7.6/Reinitializer.sol
  73. BIN
      tests/e2e/detectors/test_data/unprotected-upgrade/0.7.6/Reinitializer.sol-0.7.6.zip
  74. 15
      tests/e2e/detectors/test_data/unprotected-upgrade/0.8.15/AnyInitializer.sol
  75. BIN
      tests/e2e/detectors/test_data/unprotected-upgrade/0.8.15/AnyInitializer.sol-0.8.15.zip
  76. 6
      tests/e2e/detectors/test_data/unprotected-upgrade/0.8.15/Initializable.sol
  77. 15
      tests/e2e/detectors/test_data/unprotected-upgrade/0.8.15/Reinitializer.sol
  78. BIN
      tests/e2e/detectors/test_data/unprotected-upgrade/0.8.15/Reinitializer.sol-0.8.15.zip
  79. 70
      tests/e2e/detectors/test_detectors.py
  80. 16
      tests/e2e/printers/test_data/test_contract_names/C.sol
  81. 12
      tests/e2e/printers/test_printers.py
  82. BIN
      tests/e2e/solc_parsing/test_data/compile/user_defined_value_type/abi-decode-fixed-array.sol-0.8.10-compact.zip
  83. BIN
      tests/e2e/solc_parsing/test_data/compile/user_defined_value_type/abi-decode-fixed-array.sol-0.8.11-compact.zip
  84. BIN
      tests/e2e/solc_parsing/test_data/compile/user_defined_value_type/abi-decode-fixed-array.sol-0.8.12-compact.zip
  85. BIN
      tests/e2e/solc_parsing/test_data/compile/user_defined_value_type/abi-decode-fixed-array.sol-0.8.13-compact.zip
  86. BIN
      tests/e2e/solc_parsing/test_data/compile/user_defined_value_type/abi-decode-fixed-array.sol-0.8.14-compact.zip
  87. BIN
      tests/e2e/solc_parsing/test_data/compile/user_defined_value_type/abi-decode-fixed-array.sol-0.8.15-compact.zip
  88. BIN
      tests/e2e/solc_parsing/test_data/compile/user_defined_value_type/abi-decode-fixed-array.sol-0.8.8-compact.zip
  89. 3
      tests/e2e/solc_parsing/test_data/expected/user_defined_value_type/abi-decode-fixed-array.sol-0.8.10-compact.json
  90. 3
      tests/e2e/solc_parsing/test_data/expected/user_defined_value_type/abi-decode-fixed-array.sol-0.8.11-compact.json
  91. 3
      tests/e2e/solc_parsing/test_data/expected/user_defined_value_type/abi-decode-fixed-array.sol-0.8.12-compact.json
  92. 3
      tests/e2e/solc_parsing/test_data/expected/user_defined_value_type/abi-decode-fixed-array.sol-0.8.13-compact.json
  93. 3
      tests/e2e/solc_parsing/test_data/expected/user_defined_value_type/abi-decode-fixed-array.sol-0.8.14-compact.json
  94. 3
      tests/e2e/solc_parsing/test_data/expected/user_defined_value_type/abi-decode-fixed-array.sol-0.8.15-compact.json
  95. 3
      tests/e2e/solc_parsing/test_data/expected/user_defined_value_type/abi-decode-fixed-array.sol-0.8.8-compact.json
  96. 7
      tests/e2e/solc_parsing/test_data/user_defined_value_type/abi-decode-fixed-array.sol
  97. 41
      tests/unit/core/test_data/scope_with_renaming/core/MainContract.sol
  98. 30
      tests/unit/core/test_data/scope_with_renaming/core/ParentContract.sol
  99. 9
      tests/unit/core/test_data/scope_with_renaming/errors/MainErrors.sol
  100. 7
      tests/unit/core/test_data/scope_with_renaming/errors/ParentContractErrors.sol
  101. Some files were not shown because too many files have changed in this diff Show More

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

5
.gitignore vendored

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

@ -302,5 +302,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) | - [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) [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) [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/). If you are using Slither on an academic work, consider applying to the [Crytic $10k Research Prize](https://blog.trailofbits.com/2019/11/13/announcing-the-crytic-10k-research-prize/).

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

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

@ -11,7 +11,7 @@ import pstats
import sys import sys
import traceback import traceback
from importlib import metadata from importlib import metadata
from typing import Tuple, Optional, List, Dict, Type, Union, Any, Sequence from typing import Any, Dict, List, Optional, Sequence, Set, Tuple, Type, Union
from crytic_compile import cryticparser, CryticCompile, InvalidCompilation from crytic_compile import cryticparser, CryticCompile, InvalidCompilation
@ -211,44 +211,51 @@ def choose_detectors(
if args.detectors_to_run == "all": if args.detectors_to_run == "all":
detectors_to_run = all_detector_classes 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: else:
for detector in args.detectors_to_run.split(","): detectors_to_run = __include_detectors(
if detector in detectors: set(detectors_to_run), args.detectors_to_run, 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)
return detectors_to_run return detectors_to_run
if args.exclude_optimization: classification_map = {
detectors_to_run = [ DetectorClassification.HIGH: args.exclude_high,
d for d in detectors_to_run if d.IMPACT != DetectorClassification.OPTIMIZATION 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: if args.detectors_to_exclude:
detectors_to_run = [ detectors_to_run = [
d for d in detectors_to_run if d.ARGUMENT not in args.detectors_to_exclude 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 return detectors_to_run
@ -341,6 +348,14 @@ def parse_args(
default=defaults_flag_in_config["printers_to_run"], 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( group_detector.add_argument(
"--list-detectors", "--list-detectors",
help="List available detectors", help="List available detectors",
@ -407,6 +422,14 @@ def parse_args(
default=defaults_flag_in_config["exclude_high"], 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 = group_detector.add_mutually_exclusive_group()
fail_on_group.add_argument( fail_on_group.add_argument(
"--fail-pedantic", "--fail-pedantic",

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

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

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

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

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

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

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

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

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

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

@ -1,5 +1,4 @@
import re import re
from abc import ABCMeta
from typing import Dict, Union, List, Tuple, TYPE_CHECKING, Optional, Any from typing import Dict, Union, List, Tuple, TYPE_CHECKING, Optional, Any
from Crypto.Hash import SHA1 from Crypto.Hash import SHA1
@ -99,21 +98,29 @@ class Source:
return f"{filename_short}{lines}" return f"{filename_short}{lines}"
def __hash__(self) -> int: 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: 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 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( def _compute_line(
@ -183,12 +190,14 @@ def _convert_source_mapping(
return new_source return new_source
class SourceMapping(Context, metaclass=ABCMeta): class SourceMapping(Context):
def __init__(self) -> None: def __init__(self) -> None:
super().__init__() super().__init__()
self.source_mapping: Optional[Source] = None self.source_mapping: Optional[Source] = None
self.references: List[Source] = [] self.references: List[Source] = []
self._pattern: Union[str, None] = None
def set_offset( def set_offset(
self, offset: Union["Source", str], compilation_unit: "SlitherCompilationUnit" self, offset: Union["Source", str], compilation_unit: "SlitherCompilationUnit"
) -> None: ) -> None:
@ -204,3 +213,11 @@ class SourceMapping(Context, metaclass=ABCMeta):
) -> None: ) -> None:
s = _convert_source_mapping(offset, compilation_unit) s = _convert_source_mapping(offset, compilation_unit)
self.references.append(s) self.references.append(s)
@property
def pattern(self) -> str:
if self._pattern is None:
# Add " " to look after the first solidity keyword
return f" {self.name}" # pylint: disable=no-member
return self._pattern

@ -57,7 +57,6 @@ from .slither.name_reused import NameReused
from .functions.unimplemented import UnimplementedFunctionDetection from .functions.unimplemented import UnimplementedFunctionDetection
from .statements.mapping_deletion import MappingDeletionDetection from .statements.mapping_deletion import MappingDeletionDetection
from .statements.array_length_assignment import ArrayLengthAssignment from .statements.array_length_assignment import ArrayLengthAssignment
from .variables.similar_variables import SimilarVarsDetection
from .variables.function_init_state_variables import FunctionInitializedState from .variables.function_init_state_variables import FunctionInitializedState
from .statements.redundant_statements import RedundantStatements from .statements.redundant_statements import RedundantStatements
from .operations.bad_prng import BadPRNG from .operations.bad_prng import BadPRNG

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

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

@ -98,12 +98,21 @@ class PrinterInheritanceGraph(AbstractPrinter):
""" """
ret = "" 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). # 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] immediate_inheritance = contract.immediate_inheritance[0]
ret += f"c{contract.id}_{contract.name} -> c{immediate_inheritance.id}_{immediate_inheritance};\n" ret += f"c{contract.id}_{contract.name} -> c{immediate_inheritance.id}_{immediate_inheritance};\n"
else: 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' ret += f'c{contract.id}_{contract.name} -> c{immediate_inheritance.id}_{immediate_inheritance} [ label="{i + 1}" ];\n'
# Functions # Functions
@ -113,6 +122,7 @@ class PrinterInheritanceGraph(AbstractPrinter):
for f in contract.functions for f in contract.functions
if not f.is_constructor if not f.is_constructor
and not f.is_constructor_variables and not f.is_constructor_variables
and not f.is_virtual
and f.contract_declarer == contract and f.contract_declarer == contract
and f.visibility in visibilities and f.visibility in visibilities
] ]
@ -195,6 +205,12 @@ class PrinterInheritanceGraph(AbstractPrinter):
content = 'digraph "" {\n' content = 'digraph "" {\n'
for c in self.contracts: 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 += self._summary(c) + "\n"
content += "}" content += "}"

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

@ -196,10 +196,12 @@ class Slither(
if printers_to_run == "echidna": if printers_to_run == "echidna":
self.skip_data_dependency = True 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)) self._init_parsing_and_analyses(kwargs.get("skip_analyze", False))
def _init_parsing_and_analyses(self, skip_analyze: bool) -> None: def _init_parsing_and_analyses(self, skip_analyze: bool) -> None:
for parser in self._parsers: for parser in self._parsers:
try: try:
parser.parse_contracts() parser.parse_contracts()

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

@ -1,5 +1,5 @@
# slither-documentation # 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. This tool is experimental. See [solmate documentation](https://github.com/montyly/solmate/pull/1) for an example of usage.

@ -48,6 +48,7 @@ defaults_flag_in_config = {
"detectors_to_run": "all", "detectors_to_run": "all",
"printers_to_run": None, "printers_to_run": None,
"detectors_to_exclude": None, "detectors_to_exclude": None,
"detectors_to_include": None,
"exclude_dependencies": False, "exclude_dependencies": False,
"exclude_informational": False, "exclude_informational": False,
"exclude_optimization": False, "exclude_optimization": False,

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

@ -1,4 +1,5 @@
import logging import logging
from functools import lru_cache
from slither.core.expressions.assignment_operation import AssignmentOperation from slither.core.expressions.assignment_operation import AssignmentOperation
from slither.core.expressions.binary_operation import BinaryOperation from slither.core.expressions.binary_operation import BinaryOperation
@ -16,11 +17,39 @@ from slither.core.expressions.new_elementary_type import NewElementaryType
from slither.core.expressions.tuple_expression import TupleExpression from slither.core.expressions.tuple_expression import TupleExpression
from slither.core.expressions.type_conversion import TypeConversion from slither.core.expressions.type_conversion import TypeConversion
from slither.core.expressions.unary_operation import UnaryOperation 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 from slither.exceptions import SlitherError
logger = logging.getLogger("ExpressionVisitor") 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 # pylint: disable=too-few-public-methods
class ExpressionVisitor: class ExpressionVisitor:
def __init__(self, expression: Expression) -> None: def __init__(self, expression: Expression) -> None:
@ -35,60 +64,16 @@ class ExpressionVisitor:
# visit an expression # visit an expression
# call pre_visit, visit_expression_name, post_visit # call pre_visit, visit_expression_name, post_visit
# pylint: disable=too-many-branches
def _visit_expression(self, expression: Expression) -> None: def _visit_expression(self, expression: Expression) -> None:
self._pre_visit(expression) self._pre_visit(expression)
if isinstance(expression, AssignmentOperation): if expression is not None:
self._visit_assignement_operation(expression) visitor_method = get_visitor_mapping().get(expression.__class__)
if not visitor_method:
elif isinstance(expression, BinaryOperation): raise SlitherError(f"Expression not handled: {expression}")
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)
elif isinstance(expression, TypeConversion): visitor = getattr(self, visitor_method)
self._visit_type_conversion(expression) visitor(expression)
elif isinstance(expression, UnaryOperation):
self._visit_unary_operation(expression)
elif expression is None:
pass
else:
raise SlitherError(f"Expression not handled: {expression}")
self._post_visit(expression) self._post_visit(expression)

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

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

@ -84,7 +84,7 @@ class ContractVyper: # pylint: disable=too-many-instance-attributes
self._structuresNotParsed.append(node) self._structuresNotParsed.append(node)
elif isinstance(node, ImportFrom): elif isinstance(node, ImportFrom):
# TOOD aliases # 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 # https://github.com/vyperlang/vyper/tree/master/vyper/builtins/interfaces
if node.module == "vyper.interfaces": if node.module == "vyper.interfaces":
interfaces = { interfaces = {

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

@ -1,4 +1,4 @@
contract Initializable{ contract Initializable {
uint8 private _initialized; uint8 private _initialized;
bool private _initializing; bool private _initializing;
@ -6,10 +6,14 @@ contract Initializable{
_; _;
} }
modifier reinitializer(uint64 version) {
_;
}
function _disableInitializers() internal virtual { function _disableInitializers() internal virtual {
require(!_initializing, "Initializable: contract is initializing"); require(!_initializing, "Initializable: contract is initializing");
if (_initialized < type(uint8).max) { if (_initialized < type(uint8).max) {
_initialized = type(uint8).max; _initialized = type(uint8).max;
} }
} }
} }

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

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

@ -6,10 +6,14 @@ contract Initializable {
_; _;
} }
modifier reinitializer(uint64 version) {
_;
}
function _disableInitializers() internal virtual { function _disableInitializers() internal virtual {
require(!_initializing, "Initializable: contract is initializing"); require(!_initializing, "Initializable: contract is initializing");
if (_initialized < type(uint8).max) { if (_initialized < type(uint8).max) {
_initialized = type(uint8).max; _initialized = type(uint8).max;
} }
} }
} }

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

@ -938,6 +938,16 @@ ALL_TESTS = [
"whitelisted.sol", "whitelisted.sol",
"0.4.25", "0.4.25",
), ),
Test(
all_detectors.UnprotectedUpgradeable,
"Reinitializer.sol",
"0.4.25",
),
Test(
all_detectors.UnprotectedUpgradeable,
"AnyInitializer.sol",
"0.4.25",
),
Test( Test(
all_detectors.UnprotectedUpgradeable, all_detectors.UnprotectedUpgradeable,
"Buggy.sol", "Buggy.sol",
@ -953,6 +963,16 @@ ALL_TESTS = [
"whitelisted.sol", "whitelisted.sol",
"0.5.16", "0.5.16",
), ),
Test(
all_detectors.UnprotectedUpgradeable,
"Reinitializer.sol",
"0.5.16",
),
Test(
all_detectors.UnprotectedUpgradeable,
"AnyInitializer.sol",
"0.5.16",
),
Test( Test(
all_detectors.UnprotectedUpgradeable, all_detectors.UnprotectedUpgradeable,
"Buggy.sol", "Buggy.sol",
@ -968,6 +988,16 @@ ALL_TESTS = [
"whitelisted.sol", "whitelisted.sol",
"0.6.11", "0.6.11",
), ),
Test(
all_detectors.UnprotectedUpgradeable,
"Reinitializer.sol",
"0.6.11",
),
Test(
all_detectors.UnprotectedUpgradeable,
"AnyInitializer.sol",
"0.6.11",
),
Test( Test(
all_detectors.UnprotectedUpgradeable, all_detectors.UnprotectedUpgradeable,
"Buggy.sol", "Buggy.sol",
@ -978,6 +1008,16 @@ ALL_TESTS = [
"Fixed.sol", "Fixed.sol",
"0.7.6", "0.7.6",
), ),
Test(
all_detectors.UnprotectedUpgradeable,
"Reinitializer.sol",
"0.7.6",
),
Test(
all_detectors.UnprotectedUpgradeable,
"AnyInitializer.sol",
"0.7.6",
),
Test( Test(
all_detectors.UnprotectedUpgradeable, all_detectors.UnprotectedUpgradeable,
"whitelisted.sol", "whitelisted.sol",
@ -998,6 +1038,16 @@ ALL_TESTS = [
"whitelisted.sol", "whitelisted.sol",
"0.8.15", "0.8.15",
), ),
Test(
all_detectors.UnprotectedUpgradeable,
"Reinitializer.sol",
"0.8.15",
),
Test(
all_detectors.UnprotectedUpgradeable,
"AnyInitializer.sol",
"0.8.15",
),
Test( Test(
all_detectors.ABIEncoderV2Array, all_detectors.ABIEncoderV2Array,
"storage_ABIEncoderV2_array.sol", "storage_ABIEncoderV2_array.sol",
@ -1403,26 +1453,6 @@ ALL_TESTS = [
"type_based_tautology.sol", "type_based_tautology.sol",
"0.7.6", "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( Test(
all_detectors.MsgValueInLoop, all_detectors.MsgValueInLoop,
"msg_value_loop.sol", "msg_value_loop.sol",

@ -1,7 +1,21 @@
import "./A.sol"; 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 { function c_main() public pure {
a_main(); a_main();
} }
function count() external view override returns (uint256){
return 1;
}
function increment() external override {
}
} }

@ -35,6 +35,18 @@ def test_inheritance_printer(solc_binary_path) -> None:
assert counter["B -> A"] == 2 assert counter["B -> A"] == 2
assert counter["C -> A"] == 1 assert counter["C -> A"] == 1
# Let 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)
def test_slithir_printer(solc_binary_path) -> None: def test_slithir_printer(solc_binary_path) -> None:

@ -2,6 +2,7 @@
"I": {}, "I": {},
"C": { "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_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"
} }
} }

@ -2,6 +2,7 @@
"I": {}, "I": {},
"C": { "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_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"
} }
} }

@ -2,6 +2,7 @@
"I": {}, "I": {},
"C": { "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_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"
} }
} }

@ -2,6 +2,7 @@
"I": {}, "I": {},
"C": { "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_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"
} }
} }

@ -2,6 +2,7 @@
"I": {}, "I": {},
"C": { "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_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"
} }
} }

@ -2,6 +2,7 @@
"I": {}, "I": {},
"C": { "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_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"
} }
} }

@ -2,6 +2,7 @@
"I": {}, "I": {},
"C": { "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_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"
} }
} }

@ -1,10 +1,11 @@
interface I {} interface I {}
enum A {a,b} enum A {a,b}
struct S {uint256 a;}
contract C { contract C {
I[6] interfaceArray; I[6] interfaceArray;
A[6] enumArray; A[6] enumArray;
function test_decode_interface_array(bytes memory data) public { function test_decode_interface_array(bytes memory data) public {
interfaceArray = abi.decode(data, (I[6])); interfaceArray = abi.decode(data, (I[6]));
} }
@ -13,4 +14,8 @@ contract C {
enumArray = abi.decode(data, (A[6])); enumArray = abi.decode(data, (A[6]));
} }
function test_decode_struct_array(bytes memory data) public {
S[6] memory structArray = abi.decode(data, (S[6]));
}
} }

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

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

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

@ -0,0 +1,7 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;
library AccessControlErrors {
error AccessControlErrors1();
error AccessControlErrors2();
}

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

Loading…
Cancel
Save