Merge branch 'master' into external-publications

pull/1738/head
Feist Josselin 2 years ago
commit ed4eadaf60
  1. 55
      .github/DISCUSSION_TEMPLATE/trouble_with_installation.yml
  2. 2
      .github/ISSUE_TEMPLATE/bug_report.yml
  3. 6
      .github/workflows/IR.yml
  4. 15
      .github/workflows/black.yml
  5. 8
      .github/workflows/ci.yml
  6. 8
      .github/workflows/detectors.yml
  7. 4
      .github/workflows/docker.yml
  8. 6
      .github/workflows/doctor.yml
  9. 6
      .github/workflows/features.yml
  10. 14
      .github/workflows/linter.yml
  11. 8
      .github/workflows/parser.yml
  12. 4
      .github/workflows/pip-audit.yml
  13. 22
      .github/workflows/pylint.yml
  14. 6
      .github/workflows/read_storage.yml
  15. 24
      README.md
  16. 7
      scripts/ci_test_etherscan.sh
  17. 28
      scripts/ci_test_upgradability.sh
  18. 5
      setup.py
  19. 16
      slither/__main__.py
  20. 138
      slither/core/cfg/node.py
  21. 2
      slither/core/cfg/scope.py
  22. 4
      slither/core/children/child_contract.py
  23. 2
      slither/core/children/child_event.py
  24. 9
      slither/core/children/child_expression.py
  25. 4
      slither/core/children/child_inheritance.py
  26. 4
      slither/core/children/child_node.py
  27. 4
      slither/core/children/child_structure.py
  28. 14
      slither/core/compilation_unit.py
  29. 1
      slither/core/declarations/__init__.py
  30. 46
      slither/core/declarations/contract.py
  31. 8
      slither/core/declarations/custom_error.py
  32. 4
      slither/core/declarations/enum.py
  33. 4
      slither/core/declarations/enum_top_level.py
  34. 4
      slither/core/declarations/event.py
  35. 65
      slither/core/declarations/function.py
  36. 12
      slither/core/declarations/function_contract.py
  37. 9
      slither/core/declarations/function_top_level.py
  38. 2
      slither/core/declarations/import_directive.py
  39. 4
      slither/core/declarations/pragma_directive.py
  40. 8
      slither/core/declarations/solidity_import_placeholder.py
  41. 36
      slither/core/declarations/solidity_variables.py
  42. 14
      slither/core/declarations/structure.py
  43. 2
      slither/core/declarations/structure_top_level.py
  44. 4
      slither/core/declarations/using_for_top_level.py
  45. 12
      slither/core/dominators/utils.py
  46. 2
      slither/core/expressions/assignment_operation.py
  47. 6
      slither/core/expressions/call_expression.py
  48. 19
      slither/core/expressions/conditional_expression.py
  49. 5
      slither/core/expressions/elementary_type_name_expression.py
  50. 13
      slither/core/expressions/index_access.py
  51. 2
      slither/core/expressions/literal.py
  52. 4
      slither/core/expressions/member_access.py
  53. 11
      slither/core/expressions/new_array.py
  54. 4
      slither/core/expressions/new_contract.py
  55. 21
      slither/core/expressions/type_conversion.py
  56. 20
      slither/core/expressions/unary_operation.py
  57. 2
      slither/core/scope/scope.py
  58. 14
      slither/core/slither_core.py
  59. 23
      slither/core/solidity_types/array_type.py
  60. 6
      slither/core/solidity_types/elementary_type.py
  61. 6
      slither/core/solidity_types/function_type.py
  62. 16
      slither/core/solidity_types/mapping_type.py
  63. 16
      slither/core/solidity_types/type_alias.py
  64. 7
      slither/core/solidity_types/type_information.py
  65. 14
      slither/core/solidity_types/user_defined_type.py
  66. 40
      slither/core/source_mapping/source_mapping.py
  67. 2
      slither/core/variables/__init__.py
  68. 2
      slither/core/variables/event_variable.py
  69. 2
      slither/core/variables/local_variable_init_from_tuple.py
  70. 2
      slither/core/variables/state_variable.py
  71. 2
      slither/core/variables/top_level_variable.py
  72. 2
      slither/core/variables/variable.py
  73. 4
      slither/detectors/abstract_detector.py
  74. 1
      slither/detectors/all_detectors.py
  75. 7
      slither/detectors/assembly/shift_parameter_mixup.py
  76. 4
      slither/detectors/attributes/const_functions_asm.py
  77. 4
      slither/detectors/attributes/const_functions_state.py
  78. 4
      slither/detectors/attributes/constant_pragma.py
  79. 9
      slither/detectors/attributes/incorrect_solc.py
  80. 7
      slither/detectors/attributes/locked_ether.py
  81. 12
      slither/detectors/attributes/unimplemented_interface.py
  82. 19
      slither/detectors/compiler_bugs/array_by_reference.py
  83. 9
      slither/detectors/compiler_bugs/enum_conversion.py
  84. 5
      slither/detectors/compiler_bugs/multiple_constructor_schemes.py
  85. 9
      slither/detectors/compiler_bugs/public_mapping_nested.py
  86. 18
      slither/detectors/compiler_bugs/reused_base_constructor.py
  87. 12
      slither/detectors/compiler_bugs/storage_ABIEncoderV2_array.py
  88. 4
      slither/detectors/compiler_bugs/storage_signed_integer_array.py
  89. 17
      slither/detectors/compiler_bugs/uninitialized_function_ptr_in_constructor.py
  90. 15
      slither/detectors/erc/erc20/arbitrary_send_erc20.py
  91. 11
      slither/detectors/erc/erc20/incorrect_erc20_interface.py
  92. 12
      slither/detectors/erc/incorrect_erc721_interface.py
  93. 11
      slither/detectors/erc/unindexed_event_parameters.py
  94. 5
      slither/detectors/examples/backdoor.py
  95. 12
      slither/detectors/functions/arbitrary_send_eth.py
  96. 10
      slither/detectors/functions/codex.py
  97. 50
      slither/detectors/functions/cyclomatic_complexity.py
  98. 3
      slither/detectors/functions/dead_code.py
  99. 11
      slither/detectors/functions/modifier.py
  100. 3
      slither/detectors/functions/permit_domain_signature_collision.py
  101. Some files were not shown because too many files have changed in this diff Show More

@ -0,0 +1,55 @@
---
body:
-
attributes:
value: |
Please check the issues tab to avoid duplicates.
Thanks for taking the time to fill out this bug report!
type: markdown
-
attributes:
label: "What operating system are you using?"
id: os
type: textarea
validations:
required: true
-
attributes:
label: "How did you install slither?"
description: |
For example, using git or python's pip.
id: install-method
type: textarea
validations:
required: true
- type: dropdown
id: python
attributes:
label: Do you have python added to your $PATH?
multiple: true
options:
- "Yes"
- "No"
- "Not sure"
- type: dropdown
id: solc
attributes:
label: Do you have solc-select installed?
multiple: true
options:
- "Yes"
- "No"
-
attributes:
description: |
Please copy and paste any relevant log output. This
will be automatically formatted into code, so no need for backticks.
render: shell
label: "Output of running `slither-doctor .`:"
id: logs
type: textarea
description: "Get help troubleshooting slither installation"
labels:
- installation-help
name: "Trouble with Installing Slither"
title: "[Installation-Help]: "

@ -4,6 +4,8 @@ body:
attributes:
value: |
Please check the issues tab to avoid duplicates.
If you are having difficulty installing slither,
please head over to the "Discussions" page.
Thanks for taking the time to fill out this bug report!
type: markdown
-

@ -13,6 +13,10 @@ on:
# run CI every day even if no PRs/merges occur
- cron: '0 12 * * *'
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
build:
name: IR tests
@ -24,7 +28,7 @@ jobs:
steps:
- name: Checkout Code
uses: actions/checkout@v2
uses: actions/checkout@v3
- name: Set up Python 3.8
uses: actions/setup-python@v3

@ -1,5 +1,5 @@
---
name: Lint Code Base
name: Run black
defaults:
run:
@ -9,18 +9,27 @@ defaults:
on:
pull_request:
branches: [master, dev]
paths:
- "**/*.py"
schedule:
# run CI every day even if no PRs/merges occur
- cron: '0 12 * * *'
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
build:
name: Lint Code Base
name: Black
runs-on: ubuntu-latest
steps:
- name: Checkout Code
uses: actions/checkout@v2
uses: actions/checkout@v3
with:
# Full git history is needed to get a proper list of changed files within `super-linter`
fetch-depth: 0
- name: Set up Python 3.8
uses: actions/setup-python@v3

@ -15,6 +15,10 @@ on:
# run CI every day even if no PRs/merges occur
- cron: '0 12 * * *'
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
tests:
runs-on: ${{ matrix.os }}
@ -47,7 +51,7 @@ jobs:
- os: windows-2022
type: truffle
steps:
- uses: actions/checkout@v1
- uses: actions/checkout@v3
- name: Set up Python 3.8
uses: actions/setup-python@v3
with:
@ -63,7 +67,7 @@ jobs:
- name: Set up nix
if: matrix.type == 'dapp'
uses: cachix/install-nix-action@v16
uses: cachix/install-nix-action@v20
- name: Set up cachix
if: matrix.type == 'dapp'

@ -13,6 +13,10 @@ on:
# run CI every day even if no PRs/merges occur
- cron: '0 12 * * *'
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
build:
name: Detectors tests
@ -24,7 +28,7 @@ jobs:
steps:
- name: Checkout Code
uses: actions/checkout@v2
uses: actions/checkout@v3
- name: Set up Python 3.8
uses: actions/setup-python@v3
@ -38,4 +42,4 @@ jobs:
solc-select use 0.7.3 --always-install
- name: Test with pytest
run: |
pytest tests/test_detectors.py
pytest tests/test_detectors.py

@ -8,6 +8,10 @@ on:
tags:
- '*'
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
docker:
runs-on: ubuntu-latest

@ -1,5 +1,5 @@
---
name: CI
name: Doctor
defaults:
run:
@ -12,6 +12,10 @@ on:
- 'slither/tools/doctor/**'
- '.github/workflows/doctor.yml'
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
slither-doctor:
runs-on: ${{ matrix.os }}

@ -13,6 +13,10 @@ on:
# run CI every day even if no PRs/merges occur
- cron: '0 12 * * *'
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
build:
name: Features tests
@ -24,7 +28,7 @@ jobs:
steps:
- name: Checkout Code
uses: actions/checkout@v2
uses: actions/checkout@v3
- name: Set up Python 3.8
uses: actions/setup-python@v3

@ -9,18 +9,28 @@ defaults:
on:
pull_request:
branches: [master, dev]
paths:
- "**/*.py"
schedule:
# run CI every day even if no PRs/merges occur
- cron: '0 12 * * *'
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
build:
name: Lint Code Base
name: Pylint
runs-on: ubuntu-latest
steps:
- name: Checkout Code
uses: actions/checkout@v2
uses: actions/checkout@v3
with:
# Full git history is needed to get a proper list of changed files within `super-linter`
fetch-depth: 0
- name: Set up Python 3.8
uses: actions/setup-python@v3

@ -13,6 +13,10 @@ on:
# run CI every day even if no PRs/merges occur
- cron: '0 12 * * *'
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
build:
name: Parser tests
@ -24,7 +28,7 @@ jobs:
steps:
- name: Checkout Code
uses: actions/checkout@v2
uses: actions/checkout@v3
- name: Set up Python 3.8
uses: actions/setup-python@v3
@ -42,4 +46,4 @@ jobs:
- name: Test with pytest
run: |
pytest tests/test_ast_parsing.py
pytest tests/test_ast_parsing.py -n auto

@ -8,6 +8,10 @@ on:
branches: [ dev, master ]
schedule: [ cron: "0 7 * * 2" ]
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
audit:
runs-on: ubuntu-latest

@ -1,5 +1,5 @@
---
name: Lint Code Base
name: Run pylint
defaults:
run:
@ -9,9 +9,10 @@ defaults:
on:
pull_request:
branches: [master, dev]
schedule:
# run CI every day even if no PRs/merges occur
- cron: '0 12 * * *'
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
build:
@ -20,7 +21,10 @@ jobs:
steps:
- name: Checkout Code
uses: actions/checkout@v2
uses: actions/checkout@v3
with:
# Full git history is needed to get a proper list of changed files within `super-linter`
fetch-depth: 0
- name: Set up Python 3.8
uses: actions/setup-python@v3
@ -36,9 +40,11 @@ jobs:
uses: github/super-linter/slim@v4.9.2
if: always()
env:
# run linter on everything to catch preexisting problems
VALIDATE_ALL_CODEBASE: true
DEFAULT_BRANCH: master
# Run linters only on new files for pylint to speed up the CI
VALIDATE_ALL_CODEBASE: false
# Compare against the base branch
# This is only accessible on PR
DEFAULT_BRANCH: ${{ github.base_ref }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# Run only pylint
VALIDATE_PYTHON: true

@ -13,13 +13,17 @@ on:
# run CI every day even if no PRs/merges occur
- cron: '0 12 * * *'
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
build:
name: Test slither-read-storage
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- name: Setup node
uses: actions/setup-node@v2
with:

@ -1,7 +1,7 @@
# Slither, the Solidity source analyzer
<img src="./logo.png" alt="Logo" width="500"/>
[![Build Status](https://img.shields.io/github/workflow/status/crytic/slither/CI/master)](https://github.com/crytic/slither/actions?query=workflow%3ACI)
[![Build Status](https://img.shields.io/github/actions/workflow/status/crytic/slither/ci.yml?branch=master)](https://github.com/crytic/slither/actions?query=workflow%3ACI)
[![Slack Status](https://empireslacking.herokuapp.com/badge.svg)](https://empireslacking.herokuapp.com)
[![PyPI version](https://badge.fury.io/py/slither-analyzer.svg)](https://badge.fury.io/py/slither-analyzer)
@ -10,10 +10,10 @@ Slither is a Solidity static analysis framework written in Python3. It runs a su
- [Features](#features)
- [Usage](#usage)
- [How to Install](#how-to-install)
- [API Documentation](#api-documentation)
- [Detectors](#detectors)
- [Printers](#printers)
- [Tools](#tools)
- [API Documentation](#api-documentation)
- [Getting Help](#getting-help)
- [FAQ](#faq)
- [Publications](#publications)
@ -44,16 +44,10 @@ However, you can run Slither on a single file that does not import dependencies:
slither tests/uninitialized.sol
```
### Integration
- For GitHub action integration, use [slither-action](https://github.com/marketplace/actions/slither-action).
- To generate a Markdown report, use `slither [target] --checklist`.
- To generate a Markdown with GitHub source code highlighting, use `slither [target] --checklist --markdown-root https://github.com/ORG/REPO/blob/COMMIT/` (replace `ORG`, `REPO`, `COMMIT`)
## How to install
Slither requires Python 3.8+ and [solc](https://github.com/ethereum/solidity/), the Solidity compiler. We recommend using [solc-select](https://github.com/crytic/solc-select) to conveniently switch between solc versions, but it is not required. For additional configuration, see the [usage](https://github.com/trailofbits/slither/wiki/Usage) documentation.
Slither requires Python 3.8+.
If you're **not** going to use one of the [supported compilation frameworks](https://github.com/crytic/crytic-compile), you need [solc](https://github.com/ethereum/solidity/), the Solidity compiler; we recommend using [solc-select](https://github.com/crytic/solc-select) to conveniently switch between solc versions.
### Using Pip
@ -84,10 +78,10 @@ To share a directory in the container:
docker run -it -v /home/share:/share trailofbits/eth-security-toolbox
```
## API Documentation
Documentation on Slither's internals is available [here](https://crytic.github.io/slither/slither.html).
### Integration
- For GitHub action integration, use [slither-action](https://github.com/marketplace/actions/slither-action).
- To generate a Markdown report, use `slither [target] --checklist`.
- To generate a Markdown with GitHub source code highlighting, use `slither [target] --checklist --markdown-root https://github.com/ORG/REPO/blob/COMMIT/` (replace `ORG`, `REPO`, `COMMIT`)
## Detectors
@ -214,6 +208,8 @@ See the [Tool documentation](https://github.com/crytic/slither/wiki/Tool-Documen
[Contact us](https://www.trailofbits.com/contact/) to get help on building custom tools.
## API Documentation
Documentation on Slither's internals is available [here](https://crytic.github.io/slither/slither.html).
## Getting Help

@ -17,12 +17,5 @@ if [ "$GITHUB_ETHERSCAN" = "" ]; then
sleep $(( ( RANDOM % 5 ) + 1 ))s
fi
echo "::group::Etherscan rinkeby"
if ! slither rinkeby:0xFe05820C5A92D9bc906D4A46F662dbeba794d3b7 --etherscan-apikey "$GITHUB_ETHERSCAN" --no-fail-pedantic; then
echo "Etherscan rinkeby test failed"
exit 1
fi
echo "::endgroup::"
exit 0

@ -155,6 +155,32 @@ then
exit 255
fi
slither-check-upgradeability "$DIR_TESTS/contractV1_struct.sol" ContractV1 --new-contract-filename "$DIR_TESTS/contractV2_struct.sol" --new-contract-name ContractV2 > test_12.txt 2>&1
DIFF=$(diff test_12.txt "$DIR_TESTS/test_12.txt")
if [ "$DIFF" != "" ]
then
echo "slither-check-upgradeability 12 failed"
cat test_12.txt
echo ""
cat "$DIR_TESTS/test_12.txt"
echo ""
echo "$DIFF"
exit 255
fi
slither-check-upgradeability "$DIR_TESTS/contractV1_struct.sol" ContractV1 --new-contract-filename "$DIR_TESTS/contractV2_struct_bug.sol" --new-contract-name ContractV2 > test_13.txt 2>&1
DIFF=$(diff test_13.txt "$DIR_TESTS/test_13.txt")
if [ "$DIFF" != "" ]
then
echo "slither-check-upgradeability 13 failed"
cat test_13.txt
echo ""
cat "$DIR_TESTS/test_13.txt"
echo ""
echo "$DIFF"
exit 255
fi
rm test_1.txt
rm test_2.txt
rm test_3.txt
@ -166,3 +192,5 @@ rm test_8.txt
rm test_9.txt
rm test_10.txt
rm test_11.txt
rm test_12.txt
rm test_13.txt

@ -15,8 +15,8 @@ setup(
"packaging",
"prettytable>=0.7.2",
"pycryptodome>=3.4.6",
"crytic-compile>=0.3.0",
# "crytic-compile@git+https://github.com/crytic/crytic-compile.git@master#egg=crytic-compile",
# "crytic-compile>=0.3.0",
"crytic-compile@git+https://github.com/crytic/crytic-compile.git@master#egg=crytic-compile",
],
extras_require={
"dev": [
@ -24,6 +24,7 @@ setup(
"pylint==2.13.4",
"pytest",
"pytest-cov",
"pytest-xdist",
"deepdiff",
"numpy",
"solc-select>=v1.0.0b1",

@ -25,7 +25,13 @@ from slither.printers import all_printers
from slither.printers.abstract_printer import AbstractPrinter
from slither.slither import Slither
from slither.utils import codex
from slither.utils.output import output_to_json, output_to_zip, output_to_sarif, ZIP_TYPES_ACCEPTED
from slither.utils.output import (
output_to_json,
output_to_zip,
output_to_sarif,
ZIP_TYPES_ACCEPTED,
Output,
)
from slither.utils.output_capture import StandardOutputCapture
from slither.utils.colors import red, set_colorization_enabled
from slither.utils.command_line import (
@ -112,7 +118,7 @@ def _process(
slither: Slither,
detector_classes: List[Type[AbstractDetector]],
printer_classes: List[Type[AbstractPrinter]],
) -> Tuple[Slither, List[Dict], List[Dict], int]:
) -> Tuple[Slither, List[Dict], List[Output], int]:
for detector_cls in detector_classes:
slither.register_detector(detector_cls)
@ -125,9 +131,9 @@ def _process(
results_printers = []
if not printer_classes:
detector_results = slither.run_detectors()
detector_results = [x for x in detector_results if x] # remove empty results
detector_results = [item for sublist in detector_results for item in sublist] # flatten
detector_resultss = slither.run_detectors()
detector_resultss = [x for x in detector_resultss if x] # remove empty results
detector_results = [item for sublist in detector_resultss for item in sublist] # flatten
results_detectors.extend(detector_results)
else:

@ -4,16 +4,19 @@
from enum import Enum
from typing import Optional, List, Set, Dict, Tuple, Union, TYPE_CHECKING
from slither.all_exceptions import SlitherException
from slither.core.children.child_function import ChildFunction
from slither.core.declarations import Contract, Function
from slither.core.declarations.solidity_variables import (
SolidityVariable,
SolidityFunction,
)
from slither.core.expressions.expression import Expression
from slither.core.solidity_types import ElementaryType
from slither.core.source_mapping.source_mapping import SourceMapping
from slither.core.variables.local_variable import LocalVariable
from slither.core.variables.state_variable import StateVariable
from slither.core.variables.variable import Variable
from slither.core.solidity_types import ElementaryType
from slither.slithir.convert import convert_expression
from slither.slithir.operations import (
HighLevelCall,
@ -38,10 +41,6 @@ from slither.slithir.variables import (
TemporaryVariable,
TupleVariable,
)
from slither.all_exceptions import SlitherException
from slither.core.declarations import Contract, Function
from slither.core.expressions.expression import Expression
if TYPE_CHECKING:
from slither.slithir.variables.variable import SlithIRVariable
@ -66,80 +65,41 @@ if TYPE_CHECKING:
class NodeType(Enum):
ENTRYPOINT = 0x0 # no expression
ENTRYPOINT = "ENTRY_POINT" # no expression
# Node with expression
# Nodes that may have an expression
EXPRESSION = 0x10 # normal case
RETURN = 0x11 # RETURN may contain an expression
IF = 0x12
VARIABLE = 0x13 # Declaration of variable
ASSEMBLY = 0x14
IFLOOP = 0x15
EXPRESSION = "EXPRESSION" # normal case
RETURN = "RETURN" # RETURN may contain an expression
IF = "IF"
VARIABLE = "NEW VARIABLE" # Variable declaration
ASSEMBLY = "INLINE ASM"
IFLOOP = "IF_LOOP"
# Merging nodes
# Nodes where control flow merges
# Can have phi IR operation
ENDIF = 0x50 # ENDIF node source mapping points to the if/else body
STARTLOOP = 0x51 # STARTLOOP node source mapping points to the entire loop body
ENDLOOP = 0x52 # ENDLOOP node source mapping points to the entire loop body
ENDIF = "END_IF" # ENDIF node source mapping points to the if/else "body"
STARTLOOP = "BEGIN_LOOP" # STARTLOOP node source mapping points to the entire loop "body"
ENDLOOP = "END_LOOP" # ENDLOOP node source mapping points to the entire loop "body"
# Below the nodes have no expression
# But are used to expression CFG structure
# Below the nodes do not have an expression but are used to expression CFG structure.
# Absorbing node
THROW = 0x20
THROW = "THROW"
# Loop related nodes
BREAK = 0x31
CONTINUE = 0x32
BREAK = "BREAK"
CONTINUE = "CONTINUE"
# Only modifier node
PLACEHOLDER = 0x40
PLACEHOLDER = "_"
TRY = 0x41
CATCH = 0x42
TRY = "TRY"
CATCH = "CATCH"
# Node not related to the CFG
# Use for state variable declaration
OTHER_ENTRYPOINT = 0x60
# @staticmethod
def __str__(self):
if self == NodeType.ENTRYPOINT:
return "ENTRY_POINT"
if self == NodeType.EXPRESSION:
return "EXPRESSION"
if self == NodeType.RETURN:
return "RETURN"
if self == NodeType.IF:
return "IF"
if self == NodeType.VARIABLE:
return "NEW VARIABLE"
if self == NodeType.ASSEMBLY:
return "INLINE ASM"
if self == NodeType.IFLOOP:
return "IF_LOOP"
if self == NodeType.THROW:
return "THROW"
if self == NodeType.BREAK:
return "BREAK"
if self == NodeType.CONTINUE:
return "CONTINUE"
if self == NodeType.PLACEHOLDER:
return "_"
if self == NodeType.TRY:
return "TRY"
if self == NodeType.CATCH:
return "CATCH"
if self == NodeType.ENDIF:
return "END_IF"
if self == NodeType.STARTLOOP:
return "BEGIN_LOOP"
if self == NodeType.ENDLOOP:
return "END_LOOP"
if self == NodeType.OTHER_ENTRYPOINT:
return "OTHER_ENTRYPOINT"
return f"Unknown type {hex(self.value)}"
OTHER_ENTRYPOINT = "OTHER_ENTRYPOINT"
# endregion
@ -158,7 +118,7 @@ class Node(SourceMapping, ChildFunction): # pylint: disable=too-many-public-met
node_id: int,
scope: Union["Scope", "Function"],
file_scope: "FileScope",
):
) -> None:
super().__init__()
self._node_type = node_type
@ -513,11 +473,11 @@ class Node(SourceMapping, ChildFunction): # pylint: disable=too-many-public-met
"""
return self._expression
def add_expression(self, expression: Expression, bypass_verif_empty: bool = False):
def add_expression(self, expression: Expression, bypass_verif_empty: bool = False) -> None:
assert self._expression is None or bypass_verif_empty
self._expression = expression
def add_variable_declaration(self, var: LocalVariable):
def add_variable_declaration(self, var: LocalVariable) -> None:
assert self._variable_declaration is None
self._variable_declaration = var
if var.expression:
@ -550,7 +510,7 @@ class Node(SourceMapping, ChildFunction): # pylint: disable=too-many-public-met
for c in self.internal_calls
)
def contains_if(self, include_loop=True) -> bool:
def contains_if(self, include_loop: bool = True) -> bool:
"""
Check if the node is a IF node
Returns:
@ -560,7 +520,7 @@ class Node(SourceMapping, ChildFunction): # pylint: disable=too-many-public-met
return self.type in [NodeType.IF, NodeType.IFLOOP]
return self.type == NodeType.IF
def is_conditional(self, include_loop=True) -> bool:
def is_conditional(self, include_loop: bool = True) -> bool:
"""
Check if the node is a conditional node
A conditional node is either a IF or a require/assert or a RETURN bool
@ -589,7 +549,7 @@ class Node(SourceMapping, ChildFunction): # pylint: disable=too-many-public-met
def inline_asm(self) -> Optional[Union[str, Dict]]:
return self._asm_source_code
def add_inline_asm(self, asm: Union[str, Dict]):
def add_inline_asm(self, asm: Union[str, Dict]) -> None:
self._asm_source_code = asm
# endregion
@ -599,7 +559,7 @@ class Node(SourceMapping, ChildFunction): # pylint: disable=too-many-public-met
###################################################################################
###################################################################################
def add_father(self, father: "Node"):
def add_father(self, father: "Node") -> None:
"""Add a father node
Args:
@ -624,7 +584,7 @@ class Node(SourceMapping, ChildFunction): # pylint: disable=too-many-public-met
"""
return list(self._fathers)
def remove_father(self, father: "Node"):
def remove_father(self, father: "Node") -> None:
"""Remove the father node. Do nothing if the node is not a father
Args:
@ -632,7 +592,7 @@ class Node(SourceMapping, ChildFunction): # pylint: disable=too-many-public-met
"""
self._fathers = [x for x in self._fathers if x.node_id != father.node_id]
def remove_son(self, son: "Node"):
def remove_son(self, son: "Node") -> None:
"""Remove the son node. Do nothing if the node is not a son
Args:
@ -640,7 +600,7 @@ class Node(SourceMapping, ChildFunction): # pylint: disable=too-many-public-met
"""
self._sons = [x for x in self._sons if x.node_id != son.node_id]
def add_son(self, son: "Node"):
def add_son(self, son: "Node") -> None:
"""Add a son node
Args:
@ -648,7 +608,7 @@ class Node(SourceMapping, ChildFunction): # pylint: disable=too-many-public-met
"""
self._sons.append(son)
def set_sons(self, sons: List["Node"]):
def set_sons(self, sons: List["Node"]) -> None:
"""Set the son nodes
Args:
@ -706,14 +666,14 @@ class Node(SourceMapping, ChildFunction): # pylint: disable=too-many-public-met
def irs_ssa(self, irs):
self._irs_ssa = irs
def add_ssa_ir(self, ir: Operation):
def add_ssa_ir(self, ir: Operation) -> None:
"""
Use to place phi operation
"""
ir.set_node(self)
self._irs_ssa.append(ir)
def slithir_generation(self):
def slithir_generation(self) -> None:
if self.expression:
expression = self.expression
self._irs = convert_expression(expression, self)
@ -730,11 +690,11 @@ class Node(SourceMapping, ChildFunction): # pylint: disable=too-many-public-met
return self._all_slithir_operations
@staticmethod
def _is_non_slithir_var(var: Variable):
def _is_non_slithir_var(var: Variable) -> bool:
return not isinstance(var, (Constant, ReferenceVariable, TemporaryVariable, TupleVariable))
@staticmethod
def _is_valid_slithir_var(var: Variable):
def _is_valid_slithir_var(var: Variable) -> bool:
return isinstance(var, (ReferenceVariable, TemporaryVariable, TupleVariable))
# endregion
@ -785,7 +745,7 @@ class Node(SourceMapping, ChildFunction): # pylint: disable=too-many-public-met
self._dominance_frontier = doms
@property
def dominator_successors(self):
def dominator_successors(self) -> Set["Node"]:
return self._dom_successors
@property
@ -827,14 +787,14 @@ class Node(SourceMapping, ChildFunction): # pylint: disable=too-many-public-met
# def phi_origin_member_variables(self) -> Dict[str, Tuple[MemberVariable, Set["Node"]]]:
# return self._phi_origins_member_variables
def add_phi_origin_local_variable(self, variable: LocalVariable, node: "Node"):
def add_phi_origin_local_variable(self, variable: LocalVariable, node: "Node") -> None:
if variable.name not in self._phi_origins_local_variables:
self._phi_origins_local_variables[variable.name] = (variable, set())
(v, nodes) = self._phi_origins_local_variables[variable.name]
assert v == variable
nodes.add(node)
def add_phi_origin_state_variable(self, variable: StateVariable, node: "Node"):
def add_phi_origin_state_variable(self, variable: StateVariable, node: "Node") -> None:
if variable.canonical_name not in self._phi_origins_state_variables:
self._phi_origins_state_variables[variable.canonical_name] = (
variable,
@ -858,7 +818,7 @@ class Node(SourceMapping, ChildFunction): # pylint: disable=too-many-public-met
###################################################################################
###################################################################################
def _find_read_write_call(self): # pylint: disable=too-many-statements
def _find_read_write_call(self) -> None: # pylint: disable=too-many-statements
for ir in self.irs:
@ -934,7 +894,7 @@ class Node(SourceMapping, ChildFunction): # pylint: disable=too-many-public-met
self._low_level_calls = list(set(self._low_level_calls))
@staticmethod
def _convert_ssa(v: Variable):
def _convert_ssa(v: Variable) -> Optional[Union[StateVariable, LocalVariable]]:
if isinstance(v, StateIRVariable):
contract = v.contract
non_ssa_var = contract.get_state_variable_from_name(v.name)
@ -944,7 +904,7 @@ class Node(SourceMapping, ChildFunction): # pylint: disable=too-many-public-met
non_ssa_var = function.get_local_variable_from_name(v.name)
return non_ssa_var
def update_read_write_using_ssa(self):
def update_read_write_using_ssa(self) -> None:
if not self.expression:
return
for ir in self.irs_ssa:
@ -1008,13 +968,13 @@ class Node(SourceMapping, ChildFunction): # pylint: disable=too-many-public-met
###################################################################################
###################################################################################
def __str__(self):
def __str__(self) -> str:
additional_info = ""
if self.expression:
additional_info += " " + str(self.expression)
elif self.variable_declaration:
additional_info += " " + str(self.variable_declaration)
txt = str(self._node_type) + additional_info
txt = self._node_type.value + additional_info
return txt
@ -1026,12 +986,12 @@ class Node(SourceMapping, ChildFunction): # pylint: disable=too-many-public-met
###################################################################################
def link_nodes(node1: Node, node2: Node):
def link_nodes(node1: Node, node2: Node) -> None:
node1.add_son(node2)
node2.add_father(node1)
def insert_node(origin: Node, node_inserted: Node):
def insert_node(origin: Node, node_inserted: Node) -> None:
sons = origin.sons
link_nodes(origin, node_inserted)
for son in sons:

@ -7,7 +7,7 @@ if TYPE_CHECKING:
# pylint: disable=too-few-public-methods
class Scope:
def __init__(self, is_checked: bool, is_yul: bool, scope: Union["Scope", "Function"]):
def __init__(self, is_checked: bool, is_yul: bool, scope: Union["Scope", "Function"]) -> None:
self.nodes: List["Node"] = []
self.is_checked = is_checked
self.is_yul = is_yul

@ -7,11 +7,11 @@ if TYPE_CHECKING:
class ChildContract(SourceMapping):
def __init__(self):
def __init__(self) -> None:
super().__init__()
self._contract = None
def set_contract(self, contract: "Contract"):
def set_contract(self, contract: "Contract") -> None:
self._contract = contract
@property

@ -5,7 +5,7 @@ if TYPE_CHECKING:
class ChildEvent:
def __init__(self):
def __init__(self) -> None:
super().__init__()
self._event = None

@ -1,17 +1,18 @@
from typing import TYPE_CHECKING
from typing import TYPE_CHECKING, Union
if TYPE_CHECKING:
from slither.core.expressions.expression import Expression
from slither.slithir.operations import Operation
class ChildExpression:
def __init__(self):
def __init__(self) -> None:
super().__init__()
self._expression = None
def set_expression(self, expression: "Expression"):
def set_expression(self, expression: Union["Expression", "Operation"]) -> None:
self._expression = expression
@property
def expression(self) -> "Expression":
def expression(self) -> Union["Expression", "Operation"]:
return self._expression

@ -5,11 +5,11 @@ if TYPE_CHECKING:
class ChildInheritance:
def __init__(self):
def __init__(self) -> None:
super().__init__()
self._contract_declarer = None
def set_contract_declarer(self, contract: "Contract"):
def set_contract_declarer(self, contract: "Contract") -> None:
self._contract_declarer = contract
@property

@ -7,11 +7,11 @@ if TYPE_CHECKING:
class ChildNode:
def __init__(self):
def __init__(self) -> None:
super().__init__()
self._node = None
def set_node(self, node: "Node"):
def set_node(self, node: "Node") -> None:
self._node = node
@property

@ -5,11 +5,11 @@ if TYPE_CHECKING:
class ChildStructure:
def __init__(self):
def __init__(self) -> None:
super().__init__()
self._structure = None
def set_structure(self, structure: "Structure"):
def set_structure(self, structure: "Structure") -> None:
self._structure = structure
@property

@ -16,10 +16,10 @@ from slither.core.declarations import (
from slither.core.declarations.custom_error import CustomError
from slither.core.declarations.enum_top_level import EnumTopLevel
from slither.core.declarations.function_top_level import FunctionTopLevel
from slither.core.declarations.using_for_top_level import UsingForTopLevel
from slither.core.declarations.structure_top_level import StructureTopLevel
from slither.core.solidity_types.type_alias import TypeAliasTopLevel
from slither.core.declarations.using_for_top_level import UsingForTopLevel
from slither.core.scope.scope import FileScope
from slither.core.solidity_types.type_alias import TypeAliasTopLevel
from slither.core.variables.state_variable import StateVariable
from slither.core.variables.top_level_variable import TopLevelVariable
from slither.slithir.operations import InternalCall
@ -31,7 +31,7 @@ if TYPE_CHECKING:
# pylint: disable=too-many-instance-attributes,too-many-public-methods
class SlitherCompilationUnit(Context):
def __init__(self, core: "SlitherCore", crytic_compilation_unit: CompilationUnit):
def __init__(self, core: "SlitherCore", crytic_compilation_unit: CompilationUnit) -> None:
super().__init__()
self._core = core
@ -150,21 +150,21 @@ class SlitherCompilationUnit(Context):
def functions(self) -> List[Function]:
return list(self._all_functions)
def add_function(self, func: Function):
def add_function(self, func: Function) -> None:
self._all_functions.add(func)
@property
def modifiers(self) -> List[Modifier]:
return list(self._all_modifiers)
def add_modifier(self, modif: Modifier):
def add_modifier(self, modif: Modifier) -> None:
self._all_modifiers.add(modif)
@property
def functions_and_modifiers(self) -> List[Function]:
return self.functions + self.modifiers
def propagate_function_calls(self):
def propagate_function_calls(self) -> None:
for f in self.functions_and_modifiers:
for node in f.nodes:
for ir in node.irs_ssa:
@ -256,7 +256,7 @@ class SlitherCompilationUnit(Context):
###################################################################################
###################################################################################
def compute_storage_layout(self):
def compute_storage_layout(self) -> None:
for contract in self.contracts_derived:
self._storage_layouts[contract.name] = {}

@ -17,3 +17,4 @@ from .structure_contract import StructureContract
from .structure_top_level import StructureTopLevel
from .function_contract import FunctionContract
from .function_top_level import FunctionTopLevel
from .custom_error_contract import CustomErrorContract

@ -4,7 +4,7 @@
import logging
from collections import defaultdict
from pathlib import Path
from typing import Optional, List, Dict, Callable, Tuple, TYPE_CHECKING, Union, Set
from typing import Optional, List, Dict, Callable, Tuple, TYPE_CHECKING, Union, Set, Any
from crytic_compile.platform import Type as PlatformType
@ -38,13 +38,13 @@ if TYPE_CHECKING:
EnumContract,
StructureContract,
FunctionContract,
CustomErrorContract,
)
from slither.slithir.variables.variable import SlithIRVariable
from slither.core.variables.variable import Variable
from slither.core.variables.state_variable import StateVariable
from slither.core.variables import Variable, StateVariable
from slither.core.compilation_unit import SlitherCompilationUnit
from slither.core.declarations.custom_error_contract import CustomErrorContract
from slither.core.scope.scope import FileScope
from slither.core.cfg.node import Node
LOGGER = logging.getLogger("Contract")
@ -55,7 +55,7 @@ class Contract(SourceMapping): # pylint: disable=too-many-public-methods
Contract class
"""
def __init__(self, compilation_unit: "SlitherCompilationUnit", scope: "FileScope"):
def __init__(self, compilation_unit: "SlitherCompilationUnit", scope: "FileScope") -> None:
super().__init__()
self._name: Optional[str] = None
@ -366,7 +366,7 @@ class Contract(SourceMapping): # pylint: disable=too-many-public-methods
"""
return list(self._variables_ordered)
def add_variables_ordered(self, new_vars: List["StateVariable"]):
def add_variables_ordered(self, new_vars: List["StateVariable"]) -> None:
self._variables_ordered += new_vars
@property
@ -534,7 +534,7 @@ class Contract(SourceMapping): # pylint: disable=too-many-public-methods
def add_function(self, func: "FunctionContract"):
self._functions[func.canonical_name] = func
def set_functions(self, functions: Dict[str, "FunctionContract"]):
def set_functions(self, functions: Dict[str, "FunctionContract"]) -> None:
"""
Set the functions
@ -578,7 +578,7 @@ class Contract(SourceMapping): # pylint: disable=too-many-public-methods
def available_modifiers_as_dict(self) -> Dict[str, "Modifier"]:
return {m.full_name: m for m in self._modifiers.values() if not m.is_shadowed}
def set_modifiers(self, modifiers: Dict[str, "Modifier"]):
def set_modifiers(self, modifiers: Dict[str, "Modifier"]) -> None:
"""
Set the modifiers
@ -688,7 +688,7 @@ class Contract(SourceMapping): # pylint: disable=too-many-public-methods
inheritance: List["Contract"],
immediate_inheritance: List["Contract"],
called_base_constructor_contracts: List["Contract"],
):
) -> None:
self._inheritance = inheritance
self._immediate_inheritance = immediate_inheritance
self._explicit_base_constructor_calls = called_base_constructor_contracts
@ -803,23 +803,25 @@ class Contract(SourceMapping): # pylint: disable=too-many-public-methods
"""
return next((v for v in self.state_variables if v.name == canonical_name), None)
def get_structure_from_name(self, structure_name: str) -> Optional["Structure"]:
def get_structure_from_name(self, structure_name: str) -> Optional["StructureContract"]:
"""
Return a structure from a name
Args:
structure_name (str): name of the structure
Returns:
Structure
StructureContract
"""
return next((st for st in self.structures if st.name == structure_name), None)
def get_structure_from_canonical_name(self, structure_name: str) -> Optional["Structure"]:
def get_structure_from_canonical_name(
self, structure_name: str
) -> Optional["StructureContract"]:
"""
Return a structure from a canonical name
Args:
structure_name (str): canonical name of the structure
Returns:
Structure
StructureContract
"""
return next((st for st in self.structures if st.canonical_name == structure_name), None)
@ -1216,7 +1218,7 @@ class Contract(SourceMapping): # pylint: disable=too-many-public-methods
###################################################################################
###################################################################################
def update_read_write_using_ssa(self):
def update_read_write_using_ssa(self) -> None:
for function in self.functions + self.modifiers:
function.update_read_write_using_ssa()
@ -1311,7 +1313,7 @@ class Contract(SourceMapping): # pylint: disable=too-many-public-methods
def is_incorrectly_constructed(self, incorrect: bool):
self._is_incorrectly_parsed = incorrect
def add_constructor_variables(self):
def add_constructor_variables(self) -> None:
from slither.core.declarations.function_contract import FunctionContract
if self.state_variables:
@ -1380,7 +1382,7 @@ class Contract(SourceMapping): # pylint: disable=too-many-public-methods
def _create_node(
self, func: Function, counter: int, variable: "Variable", scope: Union[Scope, Function]
):
) -> "Node":
from slither.core.cfg.node import Node, NodeType
from slither.core.expressions import (
AssignmentOperationType,
@ -1412,7 +1414,7 @@ class Contract(SourceMapping): # pylint: disable=too-many-public-methods
###################################################################################
###################################################################################
def convert_expression_to_slithir_ssa(self):
def convert_expression_to_slithir_ssa(self) -> None:
"""
Assume generate_slithir_and_analyze was called on all functions
@ -1437,7 +1439,7 @@ class Contract(SourceMapping): # pylint: disable=too-many-public-methods
for func in self.functions + self.modifiers:
func.generate_slithir_ssa(all_ssa_state_variables_instances)
def fix_phi(self):
def fix_phi(self) -> None:
last_state_variables_instances = {}
initial_state_variables_instances = {}
for v in self._initial_state_variables:
@ -1459,20 +1461,20 @@ class Contract(SourceMapping): # pylint: disable=too-many-public-methods
###################################################################################
###################################################################################
def __eq__(self, other):
def __eq__(self, other: SourceMapping) -> bool:
if isinstance(other, str):
return other == self.name
return NotImplemented
def __neq__(self, other):
def __neq__(self, other: Any) -> bool:
if isinstance(other, str):
return other != self.name
return NotImplemented
def __str__(self):
def __str__(self) -> str:
return self.name
def __hash__(self):
def __hash__(self) -> int:
return self._id
# endregion

@ -9,7 +9,7 @@ if TYPE_CHECKING:
class CustomError(SourceMapping):
def __init__(self, compilation_unit: "SlitherCompilationUnit"):
def __init__(self, compilation_unit: "SlitherCompilationUnit") -> None:
super().__init__()
self._name: str = ""
self._parameters: List[LocalVariable] = []
@ -30,7 +30,7 @@ class CustomError(SourceMapping):
def parameters(self) -> List[LocalVariable]:
return self._parameters
def add_parameters(self, p: "LocalVariable"):
def add_parameters(self, p: "LocalVariable") -> None:
self._parameters.append(p)
@property
@ -42,7 +42,7 @@ class CustomError(SourceMapping):
###################################################################################
@staticmethod
def _convert_type_for_solidity_signature(t: Optional[Union[Type, List[Type]]]):
def _convert_type_for_solidity_signature(t: Optional[Union[Type, List[Type]]]) -> str:
# pylint: disable=import-outside-toplevel
from slither.core.declarations import Contract
@ -92,5 +92,5 @@ class CustomError(SourceMapping):
###################################################################################
###################################################################################
def __str__(self):
def __str__(self) -> str:
return "revert " + self.solidity_signature

@ -4,7 +4,7 @@ from slither.core.source_mapping.source_mapping import SourceMapping
class Enum(SourceMapping):
def __init__(self, name: str, canonical_name: str, values: List[str]):
def __init__(self, name: str, canonical_name: str, values: List[str]) -> None:
super().__init__()
self._name = name
self._canonical_name = canonical_name
@ -33,5 +33,5 @@ class Enum(SourceMapping):
def max(self) -> int:
return self._max
def __str__(self):
def __str__(self) -> str:
return self.name

@ -8,6 +8,8 @@ if TYPE_CHECKING:
class EnumTopLevel(Enum, TopLevel):
def __init__(self, name: str, canonical_name: str, values: List[str], scope: "FileScope"):
def __init__(
self, name: str, canonical_name: str, values: List[str], scope: "FileScope"
) -> None:
super().__init__(name, canonical_name, values)
self.file_scope: "FileScope" = scope

@ -9,7 +9,7 @@ if TYPE_CHECKING:
class Event(ChildContract, SourceMapping):
def __init__(self):
def __init__(self) -> None:
super().__init__()
self._name = None
self._elems: List[EventVariable] = []
@ -59,5 +59,5 @@ class Event(ChildContract, SourceMapping):
"""
return self.contract == contract
def __str__(self):
def __str__(self) -> str:
return self.name

@ -6,7 +6,7 @@ from abc import abstractmethod, ABCMeta
from collections import namedtuple
from enum import Enum
from itertools import groupby
from typing import Dict, TYPE_CHECKING, List, Optional, Set, Union, Callable, Tuple
from typing import Any, Dict, TYPE_CHECKING, List, Optional, Set, Union, Callable, Tuple
from slither.core.cfg.scope import Scope
from slither.core.declarations.solidity_variables import (
@ -27,6 +27,7 @@ from slither.core.variables.state_variable import StateVariable
from slither.utils.type import convert_type_for_solidity_signature_to_string
from slither.utils.utils import unroll
# pylint: disable=import-outside-toplevel,too-many-instance-attributes,too-many-statements,too-many-lines
if TYPE_CHECKING:
@ -45,6 +46,8 @@ if TYPE_CHECKING:
from slither.slithir.operations import Operation
from slither.core.compilation_unit import SlitherCompilationUnit
from slither.core.scope.scope import FileScope
from slither.slithir.variables.state_variable import StateIRVariable
from slither.core.declarations.function_contract import FunctionContract
LOGGER = logging.getLogger("Function")
ReacheableNode = namedtuple("ReacheableNode", ["node", "ir"])
@ -56,7 +59,7 @@ class ModifierStatements:
modifier: Union["Contract", "Function"],
entry_point: "Node",
nodes: List["Node"],
):
) -> None:
self._modifier = modifier
self._entry_point = entry_point
self._nodes = nodes
@ -116,7 +119,7 @@ class Function(SourceMapping, metaclass=ABCMeta): # pylint: disable=too-many-pu
Function class
"""
def __init__(self, compilation_unit: "SlitherCompilationUnit"):
def __init__(self, compilation_unit: "SlitherCompilationUnit") -> None:
super().__init__()
self._internal_scope: List[str] = []
self._name: Optional[str] = None
@ -295,7 +298,7 @@ class Function(SourceMapping, metaclass=ABCMeta): # pylint: disable=too-many-pu
def contains_assembly(self, c: bool):
self._contains_assembly = c
def can_reenter(self, callstack=None) -> bool:
def can_reenter(self, callstack: Optional[List["FunctionContract"]] = None) -> bool:
"""
Check if the function can re-enter
Follow internal calls.
@ -370,7 +373,7 @@ class Function(SourceMapping, metaclass=ABCMeta): # pylint: disable=too-many-pu
###################################################################################
###################################################################################
def set_function_type(self, t: FunctionType):
def set_function_type(self, t: FunctionType) -> None:
assert isinstance(t, FunctionType)
self._function_type = t
@ -455,7 +458,7 @@ class Function(SourceMapping, metaclass=ABCMeta): # pylint: disable=too-many-pu
def visibility(self, v: str):
self._visibility = v
def set_visibility(self, v: str):
def set_visibility(self, v: str) -> None:
self._visibility = v
@property
@ -554,7 +557,7 @@ class Function(SourceMapping, metaclass=ABCMeta): # pylint: disable=too-many-pu
def entry_point(self, node: "Node"):
self._entry_point = node
def add_node(self, node: "Node"):
def add_node(self, node: "Node") -> None:
if not self._entry_point:
self._entry_point = node
self._nodes.append(node)
@ -598,7 +601,7 @@ class Function(SourceMapping, metaclass=ABCMeta): # pylint: disable=too-many-pu
"""
return list(self._parameters)
def add_parameters(self, p: "LocalVariable"):
def add_parameters(self, p: "LocalVariable") -> None:
self._parameters.append(p)
@property
@ -608,7 +611,7 @@ class Function(SourceMapping, metaclass=ABCMeta): # pylint: disable=too-many-pu
"""
return list(self._parameters_ssa)
def add_parameter_ssa(self, var: "LocalIRVariable"):
def add_parameter_ssa(self, var: "LocalIRVariable") -> None:
self._parameters_ssa.append(var)
def parameters_src(self) -> SourceMapping:
@ -651,7 +654,7 @@ class Function(SourceMapping, metaclass=ABCMeta): # pylint: disable=too-many-pu
"""
return list(self._returns)
def add_return(self, r: "LocalVariable"):
def add_return(self, r: "LocalVariable") -> None:
self._returns.append(r)
@property
@ -661,7 +664,7 @@ class Function(SourceMapping, metaclass=ABCMeta): # pylint: disable=too-many-pu
"""
return list(self._returns_ssa)
def add_return_ssa(self, var: "LocalIRVariable"):
def add_return_ssa(self, var: "LocalIRVariable") -> None:
self._returns_ssa.append(var)
# endregion
@ -680,7 +683,7 @@ class Function(SourceMapping, metaclass=ABCMeta): # pylint: disable=too-many-pu
"""
return [c.modifier for c in self._modifiers]
def add_modifier(self, modif: "ModifierStatements"):
def add_modifier(self, modif: "ModifierStatements") -> None:
self._modifiers.append(modif)
@property
@ -714,7 +717,7 @@ class Function(SourceMapping, metaclass=ABCMeta): # pylint: disable=too-many-pu
# This is a list of contracts internally, so we convert it to a list of constructor functions.
return list(self._explicit_base_constructor_calls)
def add_explicit_base_constructor_calls_statements(self, modif: ModifierStatements):
def add_explicit_base_constructor_calls_statements(self, modif: ModifierStatements) -> None:
self._explicit_base_constructor_calls.append(modif)
# endregion
@ -1057,7 +1060,7 @@ class Function(SourceMapping, metaclass=ABCMeta): # pylint: disable=too-many-pu
self._all_reachable_from_functions = functions
return self._all_reachable_from_functions
def add_reachable_from_node(self, n: "Node", ir: "Operation"):
def add_reachable_from_node(self, n: "Node", ir: "Operation") -> None:
self._reachable_from_nodes.add(ReacheableNode(n, ir))
self._reachable_from_functions.add(n.function)
@ -1068,7 +1071,7 @@ class Function(SourceMapping, metaclass=ABCMeta): # pylint: disable=too-many-pu
###################################################################################
###################################################################################
def _explore_functions(self, f_new_values: Callable[["Function"], List]):
def _explore_functions(self, f_new_values: Callable[["Function"], List]) -> List[Any]:
values = f_new_values(self)
explored = [self]
to_explore = [
@ -1218,11 +1221,13 @@ class Function(SourceMapping, metaclass=ABCMeta): # pylint: disable=too-many-pu
func: "Function",
f: Callable[["Node"], List[SolidityVariable]],
include_loop: bool,
):
) -> List[Any]:
ret = [f(n) for n in func.nodes if n.is_conditional(include_loop)]
return [item for sublist in ret for item in sublist]
def all_conditional_solidity_variables_read(self, include_loop=True) -> List[SolidityVariable]:
def all_conditional_solidity_variables_read(
self, include_loop: bool = True
) -> List[SolidityVariable]:
"""
Return the Soldiity variables directly used in a condtion
@ -1258,7 +1263,9 @@ class Function(SourceMapping, metaclass=ABCMeta): # pylint: disable=too-many-pu
return [var for var in ret if isinstance(var, SolidityVariable)]
@staticmethod
def _explore_func_nodes(func: "Function", f: Callable[["Node"], List[SolidityVariable]]):
def _explore_func_nodes(
func: "Function", f: Callable[["Node"], List[SolidityVariable]]
) -> List[Union[Any, SolidityVariableComposed]]:
ret = [f(n) for n in func.nodes]
return [item for sublist in ret for item in sublist]
@ -1367,7 +1374,7 @@ class Function(SourceMapping, metaclass=ABCMeta): # pylint: disable=too-many-pu
with open(filename, "w", encoding="utf8") as f:
f.write(content)
def slithir_cfg_to_dot_str(self, skip_expressions=False) -> str:
def slithir_cfg_to_dot_str(self, skip_expressions: bool = False) -> str:
"""
Export the CFG to a DOT format. The nodes includes the Solidity expressions and the IRs
:return: the DOT content
@ -1378,7 +1385,7 @@ class Function(SourceMapping, metaclass=ABCMeta): # pylint: disable=too-many-pu
content = ""
content += "digraph{\n"
for node in self.nodes:
label = f"Node Type: {str(node.type)} {node.node_id}\n"
label = f"Node Type: {node.type.value} {node.node_id}\n"
if node.expression and not skip_expressions:
label += f"\nEXPRESSION:\n{node.expression}\n"
if node.irs and not skip_expressions:
@ -1512,7 +1519,7 @@ class Function(SourceMapping, metaclass=ABCMeta): # pylint: disable=too-many-pu
###################################################################################
###################################################################################
def _analyze_read_write(self):
def _analyze_read_write(self) -> None:
"""Compute variables read/written/..."""
write_var = [x.variables_written_as_expression for x in self.nodes]
write_var = [x for x in write_var if x]
@ -1570,7 +1577,7 @@ class Function(SourceMapping, metaclass=ABCMeta): # pylint: disable=too-many-pu
slithir_variables = [x for x in slithir_variables if x]
self._slithir_variables = [item for sublist in slithir_variables for item in sublist]
def _analyze_calls(self):
def _analyze_calls(self) -> None:
calls = [x.calls_as_expression for x in self.nodes]
calls = [x for x in calls if x]
calls = [item for sublist in calls for item in sublist]
@ -1702,7 +1709,7 @@ class Function(SourceMapping, metaclass=ABCMeta): # pylint: disable=too-many-pu
return self._get_last_ssa_variable_instances(target_state=False, target_local=True)
@staticmethod
def _unchange_phi(ir: "Operation"):
def _unchange_phi(ir: "Operation") -> bool:
from slither.slithir.operations import Phi, PhiCallback
if not isinstance(ir, (Phi, PhiCallback)) or len(ir.rvalues) > 1:
@ -1711,7 +1718,11 @@ class Function(SourceMapping, metaclass=ABCMeta): # pylint: disable=too-many-pu
return True
return ir.rvalues[0] == ir.lvalue
def fix_phi(self, last_state_variables_instances, initial_state_variables_instances):
def fix_phi(
self,
last_state_variables_instances: Dict[str, List["StateIRVariable"]],
initial_state_variables_instances: Dict[str, "StateIRVariable"],
) -> None:
from slither.slithir.operations import InternalCall, PhiCallback
from slither.slithir.variables import Constant, StateIRVariable
@ -1745,7 +1756,7 @@ class Function(SourceMapping, metaclass=ABCMeta): # pylint: disable=too-many-pu
node.irs_ssa = [ir for ir in node.irs_ssa if not self._unchange_phi(ir)]
def generate_slithir_and_analyze(self):
def generate_slithir_and_analyze(self) -> None:
for node in self.nodes:
node.slithir_generation()
@ -1756,7 +1767,7 @@ class Function(SourceMapping, metaclass=ABCMeta): # pylint: disable=too-many-pu
def generate_slithir_ssa(self, all_ssa_state_variables_instances):
pass
def update_read_write_using_ssa(self):
def update_read_write_using_ssa(self) -> None:
for node in self.nodes:
node.update_read_write_using_ssa()
self._analyze_read_write()
@ -1767,7 +1778,7 @@ class Function(SourceMapping, metaclass=ABCMeta): # pylint: disable=too-many-pu
###################################################################################
###################################################################################
def __str__(self):
def __str__(self) -> str:
return self.name
# endregion

@ -1,17 +1,20 @@
"""
Function module
"""
from typing import TYPE_CHECKING, List, Tuple
from typing import Dict, TYPE_CHECKING, List, Tuple
from slither.core.children.child_contract import ChildContract
from slither.core.children.child_inheritance import ChildInheritance
from slither.core.declarations import Function
from slither.utils.code_complexity import compute_cyclomatic_complexity
# pylint: disable=import-outside-toplevel,too-many-instance-attributes,too-many-statements,too-many-lines
if TYPE_CHECKING:
from slither.core.declarations import Contract
from slither.core.scope.scope import FileScope
from slither.slithir.variables.state_variable import StateIRVariable
class FunctionContract(Function, ChildContract, ChildInheritance):
@ -71,7 +74,7 @@ class FunctionContract(Function, ChildContract, ChildInheritance):
def get_summary(
self,
) -> Tuple[str, str, str, List[str], List[str], List[str], List[str], List[str]]:
) -> Tuple[str, str, str, List[str], List[str], List[str], List[str], List[str], int]:
"""
Return the function summary
Returns:
@ -87,6 +90,7 @@ class FunctionContract(Function, ChildContract, ChildInheritance):
[str(x) for x in self.state_variables_written],
[str(x) for x in self.internal_calls],
[str(x) for x in self.external_calls_as_expressions],
compute_cyclomatic_complexity(self),
)
# endregion
@ -96,7 +100,9 @@ class FunctionContract(Function, ChildContract, ChildInheritance):
###################################################################################
###################################################################################
def generate_slithir_ssa(self, all_ssa_state_variables_instances):
def generate_slithir_ssa(
self, all_ssa_state_variables_instances: Dict[str, "StateIRVariable"]
) -> None:
from slither.slithir.utils.ssa import add_ssa_ir, transform_slithir_vars_to_ssa
from slither.core.dominators.utils import (
compute_dominance_frontier,

@ -1,7 +1,7 @@
"""
Function module
"""
from typing import List, Tuple, TYPE_CHECKING
from typing import Dict, List, Tuple, TYPE_CHECKING
from slither.core.declarations import Function
from slither.core.declarations.top_level import TopLevel
@ -9,10 +9,11 @@ from slither.core.declarations.top_level import TopLevel
if TYPE_CHECKING:
from slither.core.compilation_unit import SlitherCompilationUnit
from slither.core.scope.scope import FileScope
from slither.slithir.variables.state_variable import StateIRVariable
class FunctionTopLevel(Function, TopLevel):
def __init__(self, compilation_unit: "SlitherCompilationUnit", scope: "FileScope"):
def __init__(self, compilation_unit: "SlitherCompilationUnit", scope: "FileScope") -> None:
super().__init__(compilation_unit)
self._scope: "FileScope" = scope
@ -78,7 +79,9 @@ class FunctionTopLevel(Function, TopLevel):
###################################################################################
###################################################################################
def generate_slithir_ssa(self, all_ssa_state_variables_instances):
def generate_slithir_ssa(
self, all_ssa_state_variables_instances: Dict[str, "StateIRVariable"]
) -> None:
# pylint: disable=import-outside-toplevel
from slither.slithir.utils.ssa import add_ssa_ir, transform_slithir_vars_to_ssa
from slither.core.dominators.utils import (

@ -8,7 +8,7 @@ if TYPE_CHECKING:
class Import(SourceMapping):
def __init__(self, filename: Path, scope: "FileScope"):
def __init__(self, filename: Path, scope: "FileScope") -> None:
super().__init__()
self._filename: Path = filename
self._alias: Optional[str] = None

@ -7,7 +7,7 @@ if TYPE_CHECKING:
class Pragma(SourceMapping):
def __init__(self, directive: List[str], scope: "FileScope"):
def __init__(self, directive: List[str], scope: "FileScope") -> None:
super().__init__()
self._directive = directive
self.scope: "FileScope" = scope
@ -39,5 +39,5 @@ class Pragma(SourceMapping):
return self._directive[0] == "experimental" and self._directive[1] == "ABIEncoderV2"
return False
def __str__(self):
def __str__(self) -> str:
return "pragma " + "".join(self.directive)

@ -1,7 +1,11 @@
"""
Special variable to model import with renaming
"""
from typing import Union
from slither.core.declarations import Import
from slither.core.declarations.contract import Contract
from slither.core.declarations.solidity_variables import SolidityVariable
from slither.core.solidity_types import ElementaryType
from slither.core.variables.variable import Variable
@ -13,7 +17,7 @@ class SolidityImportPlaceHolder(Variable):
In the long term we should remove this and better integrate import aliases
"""
def __init__(self, import_directive: Import):
def __init__(self, import_directive: Import) -> None:
super().__init__()
assert import_directive.alias is not None
self._import_directive = import_directive
@ -27,7 +31,7 @@ class SolidityImportPlaceHolder(Variable):
def type(self) -> ElementaryType:
return ElementaryType("string")
def __eq__(self, other):
def __eq__(self, other: Union[Contract, SolidityVariable]) -> bool:
return (
self.__class__ == other.__class__
and self._import_directive.filename == self._import_directive.filename

@ -1,13 +1,11 @@
# https://solidity.readthedocs.io/en/v0.4.24/units-and-global-variables.html
from typing import List, Dict, Union, TYPE_CHECKING
from typing import List, Dict, Union, Any
from slither.core.declarations.custom_error import CustomError
from slither.core.solidity_types import ElementaryType, TypeInformation
from slither.core.source_mapping.source_mapping import SourceMapping
from slither.exceptions import SlitherException
if TYPE_CHECKING:
pass
SOLIDITY_VARIABLES = {
"now": "uint256",
@ -98,13 +96,13 @@ def solidity_function_signature(name):
class SolidityVariable(SourceMapping):
def __init__(self, name: str):
def __init__(self, name: str) -> None:
super().__init__()
self._check_name(name)
self._name = name
# dev function, will be removed once the code is stable
def _check_name(self, name: str): # pylint: disable=no-self-use
def _check_name(self, name: str) -> None: # pylint: disable=no-self-use
assert name in SOLIDITY_VARIABLES or name.endswith(("_slot", "_offset"))
@property
@ -124,18 +122,18 @@ class SolidityVariable(SourceMapping):
def type(self) -> ElementaryType:
return ElementaryType(SOLIDITY_VARIABLES[self.name])
def __str__(self):
def __str__(self) -> str:
return self._name
def __eq__(self, other):
def __eq__(self, other: SourceMapping) -> bool:
return self.__class__ == other.__class__ and self.name == other.name
def __hash__(self):
def __hash__(self) -> int:
return hash(self.name)
class SolidityVariableComposed(SolidityVariable):
def _check_name(self, name: str):
def _check_name(self, name: str) -> None:
assert name in SOLIDITY_VARIABLES_COMPOSED
@property
@ -146,13 +144,13 @@ class SolidityVariableComposed(SolidityVariable):
def type(self) -> ElementaryType:
return ElementaryType(SOLIDITY_VARIABLES_COMPOSED[self.name])
def __str__(self):
def __str__(self) -> str:
return self._name
def __eq__(self, other):
def __eq__(self, other: Any) -> bool:
return self.__class__ == other.__class__ and self.name == other.name
def __hash__(self):
def __hash__(self) -> int:
return hash(self.name)
@ -162,7 +160,7 @@ class SolidityFunction(SourceMapping):
# https://solidity.readthedocs.io/en/latest/units-and-global-variables.html#type-information
# As a result, we set return_type during the Ir conversion
def __init__(self, name: str):
def __init__(self, name: str) -> None:
super().__init__()
assert name in SOLIDITY_FUNCTIONS
self._name = name
@ -187,28 +185,28 @@ class SolidityFunction(SourceMapping):
def return_type(self, r: List[Union[TypeInformation, ElementaryType]]):
self._return_type = r
def __str__(self):
def __str__(self) -> str:
return self._name
def __eq__(self, other):
def __eq__(self, other: "SolidityFunction") -> bool:
return self.__class__ == other.__class__ and self.name == other.name
def __hash__(self):
def __hash__(self) -> int:
return hash(self.name)
class SolidityCustomRevert(SolidityFunction):
def __init__(self, custom_error: CustomError): # pylint: disable=super-init-not-called
def __init__(self, custom_error: CustomError) -> None: # pylint: disable=super-init-not-called
self._name = "revert " + custom_error.solidity_signature
self._custom_error = custom_error
self._return_type: List[Union[TypeInformation, ElementaryType]] = []
def __eq__(self, other):
def __eq__(self, other: Union["SolidityCustomRevert", SolidityFunction]) -> bool:
return (
self.__class__ == other.__class__
and self.name == other.name
and self._custom_error == other._custom_error
)
def __hash__(self):
def __hash__(self) -> int:
return hash(hash(self.name) + hash(self._custom_error))

@ -51,3 +51,17 @@ class Structure(SourceMapping):
def __str__(self) -> str:
return self.name
def __eq__(self, other) -> bool:
if not isinstance(other, Structure):
return False
if len(self.elems) != len(other.elems):
return False
for idx, elem in enumerate(self.elems_ordered):
other_elem = other.elems_ordered[idx]
if str(other_elem.type) != str(elem.type) or other_elem.name != elem.name:
return False
return self.name == other.name
def __hash__(self):
return hash(self.name)

@ -9,6 +9,6 @@ if TYPE_CHECKING:
class StructureTopLevel(Structure, TopLevel):
def __init__(self, compilation_unit: "SlitherCompilationUnit", scope: "FileScope"):
def __init__(self, compilation_unit: "SlitherCompilationUnit", scope: "FileScope") -> None:
super().__init__(compilation_unit)
self.file_scope: "FileScope" = scope

@ -8,11 +8,11 @@ if TYPE_CHECKING:
class UsingForTopLevel(TopLevel):
def __init__(self, scope: "FileScope"):
def __init__(self, scope: "FileScope") -> None:
super().__init__()
self._using_for: Dict[Union[str, Type], List[Type]] = {}
self.file_scope: "FileScope" = scope
@property
def using_for(self) -> Dict[Type, List[Type]]:
def using_for(self) -> Dict[Union[str, Type], List[Type]]:
return self._using_for

@ -1,4 +1,4 @@
from typing import List, TYPE_CHECKING
from typing import Set, List, TYPE_CHECKING
from slither.core.cfg.node import NodeType
@ -6,7 +6,7 @@ if TYPE_CHECKING:
from slither.core.cfg.node import Node
def intersection_predecessor(node: "Node"):
def intersection_predecessor(node: "Node") -> Set["Node"]:
if not node.fathers:
return set()
ret = node.fathers[0].dominators
@ -15,7 +15,7 @@ def intersection_predecessor(node: "Node"):
return ret
def _compute_dominators(nodes: List["Node"]):
def _compute_dominators(nodes: List["Node"]) -> None:
changed = True
while changed:
@ -28,7 +28,7 @@ def _compute_dominators(nodes: List["Node"]):
changed = True
def _compute_immediate_dominators(nodes: List["Node"]):
def _compute_immediate_dominators(nodes: List["Node"]) -> None:
for node in nodes:
idom_candidates = set(node.dominators)
idom_candidates.remove(node)
@ -58,7 +58,7 @@ def _compute_immediate_dominators(nodes: List["Node"]):
idom.dominator_successors.add(node)
def compute_dominators(nodes: List["Node"]):
def compute_dominators(nodes: List["Node"]) -> None:
"""
Naive implementation of Cooper, Harvey, Kennedy algo
See 'A Simple,Fast Dominance Algorithm'
@ -74,7 +74,7 @@ def compute_dominators(nodes: List["Node"]):
_compute_immediate_dominators(nodes)
def compute_dominance_frontier(nodes: List["Node"]):
def compute_dominance_frontier(nodes: List["Node"]) -> None:
"""
Naive implementation of Cooper, Harvey, Kennedy algo
See 'A Simple,Fast Dominance Algorithm'

@ -85,7 +85,7 @@ class AssignmentOperation(ExpressionTyped):
right_expression: Expression,
expression_type: AssignmentOperationType,
expression_return_type: Optional["Type"],
):
) -> None:
assert isinstance(left_expression, Expression)
assert isinstance(right_expression, Expression)
super().__init__()

@ -1,10 +1,10 @@
from typing import Optional, List
from typing import Any, Optional, List
from slither.core.expressions.expression import Expression
class CallExpression(Expression): # pylint: disable=too-many-instance-attributes
def __init__(self, called, arguments, type_call):
def __init__(self, called: Expression, arguments: List[Any], type_call: str) -> None:
assert isinstance(called, Expression)
super().__init__()
self._called: Expression = called
@ -53,7 +53,7 @@ class CallExpression(Expression): # pylint: disable=too-many-instance-attribute
def type_call(self) -> str:
return self._type_call
def __str__(self):
def __str__(self) -> str:
txt = str(self._called)
if self.call_gas or self.call_value:
gas = f"gas: {self.call_gas}" if self.call_gas else ""

@ -1,10 +1,23 @@
from typing import List
from typing import Union, List
from .expression import Expression
from slither.core.expressions.binary_operation import BinaryOperation
from slither.core.expressions.expression import Expression
from slither.core.expressions.identifier import Identifier
from slither.core.expressions.literal import Literal
from slither.core.expressions.tuple_expression import TupleExpression
from slither.core.expressions.type_conversion import TypeConversion
from slither.core.expressions.unary_operation import UnaryOperation
class ConditionalExpression(Expression):
def __init__(self, if_expression, then_expression, else_expression):
def __init__(
self,
if_expression: Union[BinaryOperation, Identifier, Literal],
then_expression: Union[
"ConditionalExpression", TypeConversion, Literal, TupleExpression, Identifier
],
else_expression: Union[TupleExpression, UnaryOperation, Identifier, Literal],
) -> None:
assert isinstance(if_expression, Expression)
assert isinstance(then_expression, Expression)
assert isinstance(else_expression, Expression)

@ -3,10 +3,11 @@
"""
from slither.core.expressions.expression import Expression
from slither.core.solidity_types.type import Type
from slither.core.solidity_types.elementary_type import ElementaryType
class ElementaryTypeNameExpression(Expression):
def __init__(self, t):
def __init__(self, t: ElementaryType) -> None:
assert isinstance(t, Type)
super().__init__()
self._type = t
@ -20,5 +21,5 @@ class ElementaryTypeNameExpression(Expression):
assert isinstance(new_type, Type)
self._type = new_type
def __str__(self):
def __str__(self) -> str:
return str(self._type)

@ -1,6 +1,8 @@
from typing import List, TYPE_CHECKING
from typing import Union, List, TYPE_CHECKING
from slither.core.expressions.expression_typed import ExpressionTyped
from slither.core.expressions.identifier import Identifier
from slither.core.expressions.literal import Literal
if TYPE_CHECKING:
@ -9,7 +11,12 @@ if TYPE_CHECKING:
class IndexAccess(ExpressionTyped):
def __init__(self, left_expression, right_expression, index_type):
def __init__(
self,
left_expression: Union["IndexAccess", Identifier],
right_expression: Union[Literal, Identifier],
index_type: str,
) -> None:
super().__init__()
self._expressions = [left_expression, right_expression]
# TODO type of undexAccess is not always a Type
@ -32,5 +39,5 @@ class IndexAccess(ExpressionTyped):
def type(self) -> "Type":
return self._type
def __str__(self):
def __str__(self) -> str:
return str(self.expression_left) + "[" + str(self.expression_right) + "]"

@ -12,7 +12,7 @@ if TYPE_CHECKING:
class Literal(Expression):
def __init__(
self, value: Union[int, str], custom_type: "Type", subdenomination: Optional[str] = None
):
) -> None:
super().__init__()
self._value = value
self._type = custom_type

@ -5,7 +5,7 @@ from slither.core.solidity_types.type import Type
class MemberAccess(ExpressionTyped):
def __init__(self, member_name, member_type, expression):
def __init__(self, member_name: str, member_type: str, expression: Expression) -> None:
# assert isinstance(member_type, Type)
# TODO member_type is not always a Type
assert isinstance(expression, Expression)
@ -26,5 +26,5 @@ class MemberAccess(ExpressionTyped):
def type(self) -> Type:
return self._type
def __str__(self):
def __str__(self) -> str:
return str(self.expression) + "." + self.member_name

@ -1,11 +1,20 @@
from typing import Union, TYPE_CHECKING
from slither.core.expressions.expression import Expression
from slither.core.solidity_types.type import Type
if TYPE_CHECKING:
from slither.core.solidity_types.elementary_type import ElementaryType
from slither.core.solidity_types.type_alias import TypeAliasTopLevel
class NewArray(Expression):
# note: dont conserve the size of the array if provided
def __init__(self, depth, array_type):
def __init__(
self, depth: int, array_type: Union["TypeAliasTopLevel", "ElementaryType"]
) -> None:
super().__init__()
assert isinstance(array_type, Type)
self._depth: int = depth

@ -2,7 +2,7 @@ from slither.core.expressions.expression import Expression
class NewContract(Expression):
def __init__(self, contract_name):
def __init__(self, contract_name: str) -> None:
super().__init__()
self._contract_name: str = contract_name
self._gas = None
@ -29,5 +29,5 @@ class NewContract(Expression):
def call_salt(self, salt):
self._salt = salt
def __str__(self):
def __str__(self) -> str:
return "new " + str(self._contract_name)

@ -1,10 +1,27 @@
from typing import Union, TYPE_CHECKING
from slither.core.expressions.expression_typed import ExpressionTyped
from slither.core.expressions.expression import Expression
from slither.core.solidity_types.type import Type
if TYPE_CHECKING:
from slither.core.expressions.call_expression import CallExpression
from slither.core.expressions.identifier import Identifier
from slither.core.expressions.literal import Literal
from slither.core.expressions.member_access import MemberAccess
from slither.core.solidity_types.elementary_type import ElementaryType
from slither.core.solidity_types.type_alias import TypeAliasContract
from slither.core.solidity_types.user_defined_type import UserDefinedType
class TypeConversion(ExpressionTyped):
def __init__(self, expression, expression_type):
def __init__(
self,
expression: Union[
"MemberAccess", "Literal", "CallExpression", "TypeConversion", "Identifier"
],
expression_type: Union["ElementaryType", "UserDefinedType", "TypeAliasContract"],
) -> None:
super().__init__()
assert isinstance(expression, Expression)
assert isinstance(expression_type, Type)
@ -15,5 +32,5 @@ class TypeConversion(ExpressionTyped):
def expression(self) -> Expression:
return self._expression
def __str__(self):
def __str__(self) -> str:
return str(self.type) + "(" + str(self.expression) + ")"

@ -1,9 +1,15 @@
import logging
from typing import Union
from enum import Enum
from slither.core.expressions.expression_typed import ExpressionTyped
from slither.core.expressions.expression import Expression
from slither.core.exceptions import SlitherCoreError
from slither.core.expressions.identifier import Identifier
from slither.core.expressions.index_access import IndexAccess
from slither.core.expressions.literal import Literal
from slither.core.expressions.tuple_expression import TupleExpression
logger = logging.getLogger("UnaryOperation")
@ -20,7 +26,7 @@ class UnaryOperationType(Enum):
MINUS_PRE = 8 # for stuff like uint(-1)
@staticmethod
def get_type(operation_type, isprefix):
def get_type(operation_type: str, isprefix: bool) -> "UnaryOperationType":
if isprefix:
if operation_type == "!":
return UnaryOperationType.BANG
@ -43,7 +49,7 @@ class UnaryOperationType(Enum):
return UnaryOperationType.MINUSMINUS_POST
raise SlitherCoreError(f"get_type: Unknown operation type {operation_type}")
def __str__(self):
def __str__(self) -> str:
if self == UnaryOperationType.BANG:
return "!"
if self == UnaryOperationType.TILD:
@ -65,7 +71,7 @@ class UnaryOperationType(Enum):
raise SlitherCoreError(f"str: Unknown operation type {self}")
@staticmethod
def is_prefix(operation_type):
def is_prefix(operation_type: "UnaryOperationType") -> bool:
if operation_type in [
UnaryOperationType.BANG,
UnaryOperationType.TILD,
@ -86,7 +92,11 @@ class UnaryOperationType(Enum):
class UnaryOperation(ExpressionTyped):
def __init__(self, expression, expression_type):
def __init__(
self,
expression: Union[Literal, Identifier, IndexAccess, TupleExpression],
expression_type: UnaryOperationType,
) -> None:
assert isinstance(expression, Expression)
super().__init__()
self._expression: Expression = expression
@ -114,7 +124,7 @@ class UnaryOperation(ExpressionTyped):
def is_prefix(self) -> bool:
return UnaryOperationType.is_prefix(self._type)
def __str__(self):
def __str__(self) -> str:
if self.is_prefix:
return str(self.type) + " " + str(self._expression)
return str(self._expression) + " " + str(self.type)

@ -25,7 +25,7 @@ def _dict_contain(d1: Dict, d2: Dict) -> bool:
# pylint: disable=too-many-instance-attributes
class FileScope:
def __init__(self, filename: Filename):
def __init__(self, filename: Filename) -> None:
self.filename = filename
self.accessible_scopes: List[FileScope] = []

@ -8,7 +8,7 @@ import pathlib
import posixpath
import re
from collections import defaultdict
from typing import Optional, Dict, List, Set, Union
from typing import Optional, Dict, List, Set, Union, Tuple
from crytic_compile import CryticCompile
from crytic_compile.utils.naming import Filename
@ -40,7 +40,7 @@ class SlitherCore(Context):
Slither static analyzer
"""
def __init__(self):
def __init__(self) -> None:
super().__init__()
self._filename: Optional[str] = None
@ -73,8 +73,8 @@ class SlitherCore(Context):
# Maps from file to detector name to the start/end ranges for that detector.
# Infinity is used to signal a detector has no end range.
self._ignore_ranges: defaultdict[str, defaultdict[str, List[(int, int)]]] = defaultdict(
lambda: defaultdict(lambda: [])
self._ignore_ranges: Dict[str, Dict[str, List[Tuple[int, ...]]]] = defaultdict(
lambda: defaultdict(lambda: [(-1, -1)])
)
self._compilation_units: List[SlitherCompilationUnit] = []
@ -443,7 +443,7 @@ class SlitherCore(Context):
return True
def load_previous_results(self):
def load_previous_results(self) -> None:
filename = self._previous_results_filename
try:
if os.path.isfile(filename):
@ -456,7 +456,7 @@ class SlitherCore(Context):
except json.decoder.JSONDecodeError:
logger.error(red(f"Impossible to decode {filename}. Consider removing the file"))
def write_results_to_hide(self):
def write_results_to_hide(self) -> None:
if not self._results_to_hide:
return
filename = self._previous_results_filename
@ -464,7 +464,7 @@ class SlitherCore(Context):
results = self._results_to_hide + self._previous_results
json.dump(results, f)
def save_results_to_hide(self, results: List[Dict]):
def save_results_to_hide(self, results: List[Dict]) -> None:
self._results_to_hide += results
def add_path_to_filter(self, path: str):

@ -1,13 +1,24 @@
from typing import Optional, Tuple
from typing import Union, Optional, Tuple, Any, TYPE_CHECKING
from slither.core.expressions import Literal
from slither.core.expressions.expression import Expression
from slither.core.solidity_types.type import Type
from slither.visitors.expression.constants_folding import ConstantFolding
from slither.core.expressions.literal import Literal
if TYPE_CHECKING:
from slither.core.expressions.binary_operation import BinaryOperation
from slither.core.expressions.identifier import Identifier
from slither.core.solidity_types.elementary_type import ElementaryType
from slither.core.solidity_types.function_type import FunctionType
from slither.core.solidity_types.type_alias import TypeAliasTopLevel
class ArrayType(Type):
def __init__(self, t, length):
def __init__(
self,
t: Union["TypeAliasTopLevel", "ArrayType", "FunctionType", "ElementaryType"],
length: Optional[Union["Identifier", Literal, "BinaryOperation", int]],
) -> None:
assert isinstance(t, Type)
if length:
if isinstance(length, int):
@ -56,15 +67,15 @@ class ArrayType(Type):
return elem_size * int(str(self._length_value)), True
return 32, True
def __str__(self):
def __str__(self) -> str:
if self._length:
return str(self._type) + f"[{str(self._length_value)}]"
return str(self._type) + "[]"
def __eq__(self, other):
def __eq__(self, other: Any) -> bool:
if not isinstance(other, ArrayType):
return False
return self._type == other.type and self.length == other.length
def __hash__(self):
def __hash__(self) -> int:
return hash(str(self))

@ -216,13 +216,13 @@ class ElementaryType(Type):
return MaxValues[self.name]
raise SlitherException(f"{self.name} does not have a max value")
def __str__(self):
def __str__(self) -> str:
return self._type
def __eq__(self, other):
def __eq__(self, other) -> bool:
if not isinstance(other, ElementaryType):
return False
return self.type == other.type
def __hash__(self):
def __hash__(self) -> int:
return hash(str(self))

@ -1,4 +1,4 @@
from typing import List, Tuple
from typing import List, Tuple, Any
from slither.core.solidity_types.type import Type
from slither.core.variables.function_type_variable import FunctionTypeVariable
@ -9,7 +9,7 @@ class FunctionType(Type):
self,
params: List[FunctionTypeVariable],
return_values: List[FunctionTypeVariable],
):
) -> None:
assert all(isinstance(x, FunctionTypeVariable) for x in params)
assert all(isinstance(x, FunctionTypeVariable) for x in return_values)
super().__init__()
@ -68,7 +68,7 @@ class FunctionType(Type):
return f"({params}) returns({return_values})"
return f"({params})"
def __eq__(self, other):
def __eq__(self, other: Any) -> bool:
if not isinstance(other, FunctionType):
return False
return self.params == other.params and self.return_values == other.return_values

@ -1,10 +1,18 @@
from typing import Tuple
from typing import Union, Tuple, TYPE_CHECKING
from slither.core.solidity_types.type import Type
if TYPE_CHECKING:
from slither.core.solidity_types.elementary_type import ElementaryType
from slither.core.solidity_types.type_alias import TypeAliasTopLevel
class MappingType(Type):
def __init__(self, type_from, type_to):
def __init__(
self,
type_from: "ElementaryType",
type_to: Union["MappingType", "TypeAliasTopLevel", "ElementaryType"],
) -> None:
assert isinstance(type_from, Type)
assert isinstance(type_to, Type)
super().__init__()
@ -27,7 +35,7 @@ class MappingType(Type):
def is_dynamic(self) -> bool:
return True
def __str__(self):
def __str__(self) -> str:
return f"mapping({str(self._from)} => {str(self._to)})"
def __eq__(self, other):
@ -35,5 +43,5 @@ class MappingType(Type):
return False
return self.type_from == other.type_from and self.type_to == other.type_to
def __hash__(self):
def __hash__(self) -> int:
return hash(str(self))

@ -2,7 +2,7 @@ from typing import TYPE_CHECKING, Tuple
from slither.core.children.child_contract import ChildContract
from slither.core.declarations.top_level import TopLevel
from slither.core.solidity_types import Type
from slither.core.solidity_types import Type, ElementaryType
if TYPE_CHECKING:
from slither.core.declarations import Contract
@ -10,13 +10,13 @@ if TYPE_CHECKING:
class TypeAlias(Type):
def __init__(self, underlying_type: Type, name: str):
def __init__(self, underlying_type: ElementaryType, name: str) -> None:
super().__init__()
self.name = name
self.underlying_type = underlying_type
@property
def type(self) -> Type:
def type(self) -> ElementaryType:
"""
Return the underlying type. Alias for underlying_type
@ -31,7 +31,7 @@ class TypeAlias(Type):
def storage_size(self) -> Tuple[int, bool]:
return self.underlying_type.storage_size
def __hash__(self):
def __hash__(self) -> int:
return hash(str(self))
@property
@ -40,18 +40,18 @@ class TypeAlias(Type):
class TypeAliasTopLevel(TypeAlias, TopLevel):
def __init__(self, underlying_type: Type, name: str, scope: "FileScope"):
def __init__(self, underlying_type: Type, name: str, scope: "FileScope") -> None:
super().__init__(underlying_type, name)
self.file_scope: "FileScope" = scope
def __str__(self):
def __str__(self) -> str:
return self.name
class TypeAliasContract(TypeAlias, ChildContract):
def __init__(self, underlying_type: Type, name: str, contract: "Contract"):
def __init__(self, underlying_type: Type, name: str, contract: "Contract") -> None:
super().__init__(underlying_type, name)
self._contract: "Contract" = contract
def __str__(self):
def __str__(self) -> str:
return self.contract.name + "." + self.name

@ -1,16 +1,17 @@
from typing import TYPE_CHECKING, Tuple
from typing import Union, TYPE_CHECKING, Tuple
from slither.core.solidity_types import ElementaryType
from slither.core.solidity_types.type import Type
if TYPE_CHECKING:
from slither.core.declarations.contract import Contract
from slither.core.declarations.enum import Enum
# Use to model the Type(X) function, which returns an undefined type
# https://solidity.readthedocs.io/en/latest/units-and-global-variables.html#type-information
class TypeInformation(Type):
def __init__(self, c):
def __init__(self, c: Union[ElementaryType, "Contract", "Enum"]) -> None:
# pylint: disable=import-outside-toplevel
from slither.core.declarations.contract import Contract
from slither.core.declarations.enum import Enum
@ -20,7 +21,7 @@ class TypeInformation(Type):
self._type = c
@property
def type(self) -> "Contract":
def type(self) -> Union["Contract", ElementaryType, "Enum"]:
return self._type
@property

@ -1,4 +1,4 @@
from typing import Union, TYPE_CHECKING, Tuple
from typing import Union, TYPE_CHECKING, Tuple, Any
import math
from slither.core.solidity_types.type import Type
@ -11,7 +11,7 @@ if TYPE_CHECKING:
# pylint: disable=import-outside-toplevel
class UserDefinedType(Type):
def __init__(self, t):
def __init__(self, t: Union["Enum", "Contract", "Structure"]) -> None:
from slither.core.declarations.structure import Structure
from slither.core.declarations.enum import Enum
from slither.core.declarations.contract import Contract
@ -62,7 +62,7 @@ class UserDefinedType(Type):
to_log = f"{self} does not have storage size"
raise SlitherException(to_log)
def __str__(self):
def __str__(self) -> str:
from slither.core.declarations.structure_contract import StructureContract
from slither.core.declarations.enum_contract import EnumContract
@ -71,10 +71,14 @@ class UserDefinedType(Type):
return str(type_used.contract) + "." + str(type_used.name)
return str(type_used.name)
def __eq__(self, other):
def __eq__(self, other: Any) -> bool:
from slither.core.declarations.contract import Contract
if not isinstance(other, UserDefinedType):
return False
if isinstance(self.type, Contract) and isinstance(other.type, Contract):
return self.type == other.type.name
return self.type == other.type
def __hash__(self):
def __hash__(self) -> int:
return hash(str(self))

@ -2,8 +2,8 @@ import re
from abc import ABCMeta
from typing import Dict, Union, List, Tuple, TYPE_CHECKING, Optional
from Crypto.Hash import SHA1
from crytic_compile.utils.naming import Filename
from slither.core.context.context import Context
if TYPE_CHECKING:
@ -56,7 +56,7 @@ class Source:
filename_short: str = self.filename.short if self.filename.short else ""
return f"{filename_short}{lines} ({self.starting_column} - {self.ending_column})"
def _get_lines_str(self, line_descr=""):
def _get_lines_str(self, line_descr: str = "") -> str:
# If the compilation unit was not initialized, it means that the set_offset was never called
# on the corresponding object, which should not happen
@ -66,12 +66,36 @@ class Source:
lines = self.lines
if not lines:
lines = ""
elif len(lines) == 1:
lines = f"{line_prefix}{line_descr}{lines[0]}"
else:
lines = f"{line_prefix}{line_descr}{lines[0]}-{line_descr}{lines[-1]}"
return lines
return ""
if len(lines) == 1:
return f"{line_prefix}{line_descr}{lines[0]}"
return f"{line_prefix}{line_descr}{lines[0]}-{line_descr}{lines[-1]}"
@property
def content(self) -> str:
"""
Return the txt content of the Source
Returns:
"""
# If the compilation unit was not initialized, it means that the set_offset was never called
# on the corresponding object, which should not happen
assert self.compilation_unit
return self.compilation_unit.core.source_code[self.filename.absolute][self.start : self.end]
@property
def content_hash(self) -> str:
"""
Return sha1(self.content)
Returns:
"""
h = SHA1.new()
h.update(self.content.encode("utf8"))
return h.hexdigest()
def __str__(self) -> str:
lines = self._get_lines_str()

@ -0,0 +1,2 @@
from .state_variable import StateVariable
from .variable import Variable

@ -3,7 +3,7 @@ from slither.core.children.child_event import ChildEvent
class EventVariable(ChildEvent, Variable):
def __init__(self):
def __init__(self) -> None:
super().__init__()
self._indexed = False

@ -13,7 +13,7 @@ class LocalVariableInitFromTuple(LocalVariable):
"""
def __init__(self):
def __init__(self) -> None:
super().__init__()
self._tuple_index: Optional[int] = None

@ -9,7 +9,7 @@ if TYPE_CHECKING:
class StateVariable(ChildContract, Variable):
def __init__(self):
def __init__(self) -> None:
super().__init__()
self._node_initialization: Optional["Node"] = None

@ -9,7 +9,7 @@ if TYPE_CHECKING:
class TopLevelVariable(TopLevel, Variable):
def __init__(self, scope: "FileScope"):
def __init__(self, scope: "FileScope") -> None:
super().__init__()
self._node_initialization: Optional["Node"] = None
self.file_scope = scope

@ -12,7 +12,7 @@ if TYPE_CHECKING:
# pylint: disable=too-many-instance-attributes
class Variable(SourceMapping):
def __init__(self):
def __init__(self) -> None:
super().__init__()
self._name: Optional[str] = None
self._initial_expression: Optional["Expression"] = None

@ -5,9 +5,9 @@ from typing import Optional, List, TYPE_CHECKING, Dict, Union, Callable
from slither.core.compilation_unit import SlitherCompilationUnit
from slither.core.declarations import Contract
from slither.utils.colors import green, yellow, red
from slither.formatters.exceptions import FormatImpossible
from slither.formatters.utils.patches import apply_patch, create_diff
from slither.utils.colors import green, yellow, red
from slither.utils.comparable_enum import ComparableEnum
from slither.utils.output import Output, SupportedOutput
@ -81,7 +81,7 @@ class AbstractDetector(metaclass=abc.ABCMeta):
def __init__(
self, compilation_unit: SlitherCompilationUnit, slither: "Slither", logger: Logger
):
) -> None:
self.compilation_unit: SlitherCompilationUnit = compilation_unit
self.contracts: List[Contract] = compilation_unit.contracts
self.slither: "Slither" = slither

@ -88,3 +88,4 @@ from .statements.delegatecall_in_loop import DelegatecallInLoop
from .functions.protected_variable import ProtectedVariables
from .functions.permit_domain_signature_collision import DomainSeparatorCollision
from .functions.codex import Codex
from .functions.cyclomatic_complexity import CyclomaticComplexity

@ -1,6 +1,9 @@
from typing import List
from slither.detectors.abstract_detector import AbstractDetector, DetectorClassification
from slither.slithir.operations import Binary, BinaryType
from slither.slithir.variables import Constant
from slither.core.declarations.function_contract import FunctionContract
from slither.utils.output import Output
class ShiftParameterMixup(AbstractDetector):
@ -36,7 +39,7 @@ The shift statement will right-shift the constant 8 by `a` bits"""
WIKI_RECOMMENDATION = "Swap the order of parameters."
def _check_function(self, f):
def _check_function(self, f: FunctionContract) -> List[Output]:
results = []
for node in f.nodes:
@ -52,7 +55,7 @@ The shift statement will right-shift the constant 8 by `a` bits"""
results.append(json)
return results
def _detect(self):
def _detect(self) -> List[Output]:
results = []
for c in self.contracts:
for f in c.functions:

@ -2,12 +2,14 @@
Module detecting constant functions
Recursively check the called functions
"""
from typing import List
from slither.detectors.abstract_detector import (
AbstractDetector,
DetectorClassification,
ALL_SOLC_VERSIONS_04,
)
from slither.formatters.attributes.const_functions import custom_format
from slither.utils.output import Output
class ConstantFunctionsAsm(AbstractDetector):
@ -55,7 +57,7 @@ All the calls to `get` revert, breaking Bob's smart contract execution."""
VULNERABLE_SOLC_VERSIONS = ALL_SOLC_VERSIONS_04
def _detect(self):
def _detect(self) -> List[Output]:
"""Detect the constant function using assembly code
Recursively visit the calls

@ -2,12 +2,14 @@
Module detecting constant functions
Recursively check the called functions
"""
from typing import List
from slither.detectors.abstract_detector import (
AbstractDetector,
DetectorClassification,
ALL_SOLC_VERSIONS_04,
)
from slither.formatters.attributes.const_functions import custom_format
from slither.utils.output import Output
class ConstantFunctionsState(AbstractDetector):
@ -55,7 +57,7 @@ All the calls to `get` revert, breaking Bob's smart contract execution."""
VULNERABLE_SOLC_VERSIONS = ALL_SOLC_VERSIONS_04
def _detect(self):
def _detect(self) -> List[Output]:
"""Detect the constant function changing the state
Recursively visit the calls

@ -1,9 +1,11 @@
"""
Check that the same pragma is used in all the files
"""
from typing import List
from slither.detectors.abstract_detector import AbstractDetector, DetectorClassification
from slither.formatters.attributes.constant_pragma import custom_format
from slither.utils.output import Output
class ConstantPragma(AbstractDetector):
@ -22,7 +24,7 @@ class ConstantPragma(AbstractDetector):
WIKI_DESCRIPTION = "Detect whether different Solidity versions are used."
WIKI_RECOMMENDATION = "Use one Solidity version."
def _detect(self):
def _detect(self) -> List[Output]:
results = []
pragma = self.compilation_unit.pragma_directives
versions = [p.version for p in pragma if p.is_solidity_version]

@ -3,8 +3,11 @@
"""
import re
from typing import List, Optional, Tuple
from slither.detectors.abstract_detector import AbstractDetector, DetectorClassification
from slither.formatters.attributes.incorrect_solc import custom_format
from slither.utils.output import Output
# group:
# 0: ^ > >= < <= (optional)
@ -83,7 +86,7 @@ Consider using the latest version of Solidity for testing."""
"^0.8.8",
]
def _check_version(self, version):
def _check_version(self, version: Tuple[str, str, str, str, str]) -> Optional[str]:
op = version[0]
if op and op not in [">", ">=", "^"]:
return self.LESS_THAN_TXT
@ -96,7 +99,7 @@ Consider using the latest version of Solidity for testing."""
return self.OLD_VERSION_TXT
return None
def _check_pragma(self, version):
def _check_pragma(self, version: str) -> Optional[str]:
if version in self.BUGGY_VERSIONS:
return self.BUGGY_VERSION_TXT
versions = PATTERN.findall(version)
@ -117,7 +120,7 @@ Consider using the latest version of Solidity for testing."""
return self._check_version(version_left)
return self.COMPLEX_PRAGMA_TXT
def _detect(self):
def _detect(self) -> List[Output]:
"""
Detects pragma statements that allow for outdated solc versions.
:return: Returns the relevant JSON data for the findings.

@ -1,7 +1,9 @@
"""
Check if ethers are locked in the contract
"""
from typing import List
from slither.core.declarations.contract import Contract
from slither.detectors.abstract_detector import AbstractDetector, DetectorClassification
from slither.slithir.operations import (
HighLevelCall,
@ -12,6 +14,7 @@ from slither.slithir.operations import (
LibraryCall,
InternalCall,
)
from slither.utils.output import Output
class LockedEther(AbstractDetector): # pylint: disable=too-many-nested-blocks
@ -41,7 +44,7 @@ Every Ether sent to `Locked` will be lost."""
WIKI_RECOMMENDATION = "Remove the payable attribute or add a withdraw function."
@staticmethod
def do_no_send_ether(contract):
def do_no_send_ether(contract: Contract) -> bool:
functions = contract.all_functions_called
to_explore = functions
explored = []
@ -73,7 +76,7 @@ Every Ether sent to `Locked` will be lost."""
return True
def _detect(self):
def _detect(self) -> List[Output]:
results = []
for contract in self.compilation_unit.contracts_derived:

@ -4,8 +4,10 @@ Module detecting unimplemented interfaces
Collect all the interfaces
Check for contracts which implement all interface functions but do not explicitly derive from those interfaces.
"""
from typing import List
from slither.detectors.abstract_detector import AbstractDetector, DetectorClassification
from slither.core.declarations.contract import Contract
from slither.utils.output import Output
class MissingInheritance(AbstractDetector):
@ -42,7 +44,9 @@ contract Something {
WIKI_RECOMMENDATION = "Inherit from the missing interface or contract."
@staticmethod
def detect_unimplemented_interface(contract, interfaces):
def detect_unimplemented_interface(
contract: Contract, interfaces: List[Contract]
) -> List[Contract]:
"""
Detects if contract intends to implement one of the interfaces but does not explicitly do so by deriving from it
:param contract: The contract to check
@ -50,7 +54,7 @@ contract Something {
:return: Interfaces likely intended to implement by the contract
"""
intended_interfaces = []
intended_interfaces: List[Contract] = []
sigs_contract = {f.full_name for f in contract.functions_entry_points}
if not sigs_contract:
@ -111,7 +115,7 @@ contract Something {
return intended_interfaces
def _detect(self):
def _detect(self) -> List[Output]:
"""Detect unimplemented interfaces
Returns:
list: {'contract'}

@ -1,13 +1,17 @@
"""
Detects the passing of arrays located in memory to functions which expect to modify arrays via storage reference.
"""
from typing import List, Set, Tuple, Union
from slither.detectors.abstract_detector import AbstractDetector, DetectorClassification
from slither.core.solidity_types.array_type import ArrayType
from slither.core.variables.state_variable import StateVariable
from slither.core.variables.local_variable import LocalVariable
from slither.slithir.operations.high_level_call import HighLevelCall
from slither.slithir.operations.internal_call import InternalCall
from slither.core.cfg.node import Node
from slither.core.declarations.contract import Contract
from slither.core.declarations.function_contract import FunctionContract
from slither.utils.output import Output
class ArrayByReference(AbstractDetector):
@ -55,7 +59,7 @@ As a result, Bob's usage of the contract is incorrect."""
WIKI_RECOMMENDATION = "Ensure the correct usage of `memory` and `storage` in the function parameters. Make all the locations explicit."
@staticmethod
def get_funcs_modifying_array_params(contracts):
def get_funcs_modifying_array_params(contracts: List[Contract]) -> Set[FunctionContract]:
"""
Obtains a set of functions which take arrays not located in storage as parameters, and writes to them.
:param contracts: The collection of contracts to check functions in.
@ -83,7 +87,14 @@ As a result, Bob's usage of the contract is incorrect."""
return results
@staticmethod
def detect_calls_passing_ref_to_function(contracts, array_modifying_funcs):
def detect_calls_passing_ref_to_function(
contracts: List[Contract], array_modifying_funcs: Set[FunctionContract]
) -> List[
Union[
Tuple[Node, StateVariable, FunctionContract],
Tuple[Node, LocalVariable, FunctionContract],
]
]:
"""
Obtains all calls passing storage arrays by value to a function which cannot write to them successfully.
:param contracts: The collection of contracts to check for problematic calls in.
@ -134,7 +145,7 @@ As a result, Bob's usage of the contract is incorrect."""
results.append((node, arg, ir.function))
return results
def _detect(self):
def _detect(self) -> List[Output]:
"""
Detects passing of arrays located in memory to functions which expect to modify arrays via storage reference.
:return: The JSON results of the detector, which contains the calling_node, affected_argument_variable and

@ -1,7 +1,11 @@
"""
Module detecting dangerous conversion to enum
"""
from typing import List, Tuple
from slither.core.cfg.node import Node
from slither.core.declarations import Contract
from slither.core.source_mapping.source_mapping import SourceMapping
from slither.detectors.abstract_detector import (
AbstractDetector,
DetectorClassification,
@ -9,9 +13,10 @@ from slither.detectors.abstract_detector import (
)
from slither.slithir.operations import TypeConversion
from slither.core.declarations.enum import Enum
from slither.utils.output import Output
def _detect_dangerous_enum_conversions(contract):
def _detect_dangerous_enum_conversions(contract: Contract) -> List[Tuple[Node, SourceMapping]]:
"""Detect dangerous conversion to enum by checking IR
Args:
contract (Contract)
@ -61,7 +66,7 @@ Attackers can trigger unexpected behaviour by calling `bug(1)`."""
VULNERABLE_SOLC_VERSIONS = make_solc_versions(4, 0, 4)
def _detect(self):
def _detect(self) -> List[Output]:
"""Detect dangerous conversion to enum"""
results = []

@ -1,4 +1,7 @@
from typing import List
from slither.detectors.abstract_detector import AbstractDetector, DetectorClassification
from slither.utils.output import Output
class MultipleConstructorSchemes(AbstractDetector):
@ -43,7 +46,7 @@ In Solidity [0.4.22](https://github.com/ethereum/solidity/releases/tag/v0.4.23),
WIKI_RECOMMENDATION = "Only declare one constructor, preferably using the new scheme `constructor(...)` instead of `function <contractName>(...)`."
def _detect(self):
def _detect(self) -> List[Output]:
"""
Detect multiple constructor schemes in the same contract
:return: Returns a list of contract JSON result, where each result contains all constructor definitions.

@ -1,7 +1,7 @@
"""
Module detecting public mappings with nested variables (returns incorrect values prior to 0.5.x)
"""
from typing import Any, List, Union
from slither.detectors.abstract_detector import (
AbstractDetector,
DetectorClassification,
@ -10,9 +10,12 @@ from slither.detectors.abstract_detector import (
from slither.core.solidity_types.mapping_type import MappingType
from slither.core.solidity_types.user_defined_type import UserDefinedType
from slither.core.declarations.structure import Structure
from slither.core.declarations.contract import Contract
from slither.core.variables.state_variable import StateVariable
from slither.utils.output import Output
def detect_public_nested_mappings(contract):
def detect_public_nested_mappings(contract: Contract) -> List[Union[StateVariable, Any]]:
"""
Detect any state variables that are initialized from an immediate function call (prior to constructor run).
:param contract: The contract to detect state variable definitions for.
@ -68,7 +71,7 @@ class PublicMappingNested(AbstractDetector):
VULNERABLE_SOLC_VERSIONS = ALL_SOLC_VERSIONS_04
def _detect(self):
def _detect(self) -> List[Output]:
"""
Detect public mappings with nested variables (returns incorrect values prior to 0.5.x)

@ -1,18 +1,24 @@
"""
Module detecting re-used base constructors in inheritance hierarchy.
"""
from typing import Any, Dict, List, Tuple, Union
from slither.detectors.abstract_detector import (
AbstractDetector,
DetectorClassification,
ALL_SOLC_VERSIONS_04,
)
from slither.core.declarations.contract import Contract
from slither.core.declarations.function_contract import FunctionContract
from slither.utils.output import Output
# Helper: adds explicitly called constructors with arguments to the results lookup.
def _add_constructors_with_args(
base_constructors, called_by_constructor, current_contract, results
):
base_constructors: List[Union[Any, FunctionContract]],
called_by_constructor: bool,
current_contract: Contract,
results: Dict[FunctionContract, List[Tuple[Contract, bool]]],
) -> None:
for explicit_base_constructor in base_constructors:
if len(explicit_base_constructor.parameters) > 0:
if explicit_base_constructor not in results:
@ -77,7 +83,9 @@ The constructor of `A` is called multiple times in `D` and `E`:
VULNERABLE_SOLC_VERSIONS = ALL_SOLC_VERSIONS_04
def _detect_explicitly_called_base_constructors(self, contract):
def _detect_explicitly_called_base_constructors(
self, contract: Contract
) -> Dict[FunctionContract, List[Tuple[Contract, bool]]]:
"""
Detects explicitly calls to base constructors with arguments in the inheritance hierarchy.
:param contract: The contract to detect explicit calls to a base constructor with arguments to.
@ -124,7 +132,7 @@ The constructor of `A` is called multiple times in `D` and `E`:
return results
def _detect(self):
def _detect(self) -> List[Output]:
"""
Detect reused base constructors.
:return: Returns a list of JSON results.

@ -1,7 +1,7 @@
"""
Module detecting ABIEncoderV2 array bug
"""
from typing import List, Set, Tuple
from slither.detectors.abstract_detector import (
AbstractDetector,
DetectorClassification,
@ -16,6 +16,10 @@ from slither.core.declarations.solidity_variables import SolidityFunction
from slither.slithir.operations import EventCall
from slither.slithir.operations import HighLevelCall
from slither.utils.utils import unroll
from slither.core.cfg.node import Node
from slither.core.declarations.contract import Contract
from slither.core.declarations.function_contract import FunctionContract
from slither.utils.output import Output
class ABIEncoderV2Array(AbstractDetector):
@ -55,7 +59,9 @@ contract A {
VULNERABLE_SOLC_VERSIONS = make_solc_versions(4, 7, 25) + make_solc_versions(5, 0, 9)
@staticmethod
def _detect_storage_abiencoderv2_arrays(contract):
def _detect_storage_abiencoderv2_arrays(
contract: Contract,
) -> Set[Tuple[FunctionContract, Node]]:
"""
Detects and returns all nodes with storage-allocated abiencoderv2 arrays of arrays/structs in abi.encode, events or external calls
:param contract: Contract to detect within
@ -98,7 +104,7 @@ contract A {
# Return the resulting set of tuples
return results
def _detect(self):
def _detect(self) -> List[Output]:
"""
Detect ABIEncoderV2 array bug
"""

@ -1,6 +1,7 @@
"""
Module detecting storage signed integer array bug
"""
from typing import List
from slither.detectors.abstract_detector import (
AbstractDetector,
@ -14,6 +15,7 @@ from slither.core.variables.local_variable import LocalVariable
from slither.core.variables.state_variable import StateVariable
from slither.slithir.operations.assignment import Assignment
from slither.slithir.operations.init_array import InitArray
from slither.utils.output import Output
class StorageSignedIntegerArray(AbstractDetector):
@ -108,7 +110,7 @@ contract A {
# Return the resulting set of tuples
return results
def _detect(self):
def _detect(self) -> List[Output]:
"""
Detect storage signed integer array init/assignment
"""

@ -1,7 +1,7 @@
"""
Module detecting uninitialized function pointer calls in constructors
"""
from typing import Any, List, Union
from slither.detectors.abstract_detector import (
AbstractDetector,
DetectorClassification,
@ -10,9 +10,14 @@ from slither.detectors.abstract_detector import (
from slither.slithir.operations import InternalDynamicCall, OperationWithLValue
from slither.slithir.variables import ReferenceVariable
from slither.slithir.variables.variable import SlithIRVariable
from slither.core.cfg.node import Node
from slither.core.declarations.contract import Contract
from slither.core.declarations.function_contract import FunctionContract
from slither.slithir.variables.state_variable import StateIRVariable
from slither.utils.output import Output
def _get_variables_entrance(function):
def _get_variables_entrance(function: FunctionContract) -> List[Union[Any, StateIRVariable]]:
"""
Return the first SSA variables of the function
Catpure the phi operation at the entry point
@ -25,7 +30,7 @@ def _get_variables_entrance(function):
return ret
def _is_vulnerable(node, variables_entrance):
def _is_vulnerable(node: Node, variables_entrance: List[Union[Any, StateIRVariable]]) -> bool:
"""
Vulnerable if an IR ssa:
- It is an internal dynamic call
@ -84,7 +89,9 @@ The call to `a(10)` will lead to unexpected behavior because function pointer `a
VULNERABLE_SOLC_VERSIONS = make_solc_versions(4, 5, 25) + make_solc_versions(5, 0, 8)
@staticmethod
def _detect_uninitialized_function_ptr_in_constructor(contract):
def _detect_uninitialized_function_ptr_in_constructor(
contract: Contract,
) -> List[Union[Any, Node]]:
"""
Detect uninitialized function pointer calls in constructors
:param contract: The contract of interest for detection
@ -99,7 +106,7 @@ The call to `a(10)` will lead to unexpected behavior because function pointer `a
]
return results
def _detect(self):
def _detect(self) -> List[Output]:
"""
Detect uninitialized function pointer calls in constructors of contracts
Returns:

@ -1,16 +1,17 @@
from typing import List
from slither.analyses.data_dependency.data_dependency import is_dependent
from slither.core.cfg.node import Node
from slither.core.compilation_unit import SlitherCompilationUnit
from slither.core.declarations import Contract, Function, SolidityVariableComposed
from slither.core.declarations.solidity_variables import SolidityVariable
from slither.slithir.operations import HighLevelCall, LibraryCall
from slither.core.declarations import Contract, Function, SolidityVariableComposed
from slither.analyses.data_dependency.data_dependency import is_dependent
from slither.core.compilation_unit import SlitherCompilationUnit
class ArbitrarySendErc20:
"""Detects instances where ERC20 can be sent from an arbitrary from address."""
def __init__(self, compilation_unit: SlitherCompilationUnit):
def __init__(self, compilation_unit: SlitherCompilationUnit) -> None:
self._compilation_unit = compilation_unit
self._no_permit_results: List[Node] = []
self._permit_results: List[Node] = []
@ -27,7 +28,7 @@ class ArbitrarySendErc20:
def permit_results(self) -> List[Node]:
return self._permit_results
def _detect_arbitrary_from(self, contract: Contract):
def _detect_arbitrary_from(self, contract: Contract) -> None:
for f in contract.functions:
all_high_level_calls = [
f_called[1].solidity_signature
@ -48,7 +49,7 @@ class ArbitrarySendErc20:
ArbitrarySendErc20._arbitrary_from(f.nodes, self._no_permit_results)
@staticmethod
def _arbitrary_from(nodes: List[Node], results: List[Node]):
def _arbitrary_from(nodes: List[Node], results: List[Node]) -> None:
"""Finds instances of (safe)transferFrom that do not use msg.sender or address(this) as from parameter."""
for node in nodes:
for ir in node.irs:
@ -89,7 +90,7 @@ class ArbitrarySendErc20:
):
results.append(ir.node)
def detect(self):
def detect(self) -> None:
"""Detect transfers that use arbitrary `from` parameter."""
for c in self.compilation_unit.contracts_derived:
self._detect_arbitrary_from(c)

@ -2,7 +2,12 @@
Detect incorrect erc20 interface.
Some contracts do not return a bool on transfer/transferFrom/approve, which may lead to preventing the contract to be used with contracts compiled with recent solc (>0.4.22)
"""
from typing import List, Tuple
from slither.core.declarations.contract import Contract
from slither.core.declarations.function_contract import FunctionContract
from slither.detectors.abstract_detector import AbstractDetector, DetectorClassification
from slither.utils.output import Output
class IncorrectERC20InterfaceDetection(AbstractDetector):
@ -36,7 +41,7 @@ contract Token{
)
@staticmethod
def incorrect_erc20_interface(signature):
def incorrect_erc20_interface(signature: Tuple[str, List[str], List[str]]) -> bool:
(name, parameters, returnVars) = signature
if name == "transfer" and parameters == ["address", "uint256"] and returnVars != ["bool"]:
@ -68,7 +73,7 @@ contract Token{
return False
@staticmethod
def detect_incorrect_erc20_interface(contract):
def detect_incorrect_erc20_interface(contract: Contract) -> List[FunctionContract]:
"""Detect incorrect ERC20 interface
Returns:
@ -93,7 +98,7 @@ contract Token{
return functions
def _detect(self):
def _detect(self) -> List[Output]:
"""Detect incorrect erc20 interface
Returns:

@ -1,7 +1,11 @@
"""
Detect incorrect erc721 interface.
"""
from typing import Any, List, Tuple, Union
from slither.detectors.abstract_detector import AbstractDetector, DetectorClassification
from slither.core.declarations.contract import Contract
from slither.core.declarations.function_contract import FunctionContract
from slither.utils.output import Output
class IncorrectERC721InterfaceDetection(AbstractDetector):
@ -37,7 +41,9 @@ contract Token{
)
@staticmethod
def incorrect_erc721_interface(signature):
def incorrect_erc721_interface(
signature: Union[Tuple[str, List[str], List[str]], Tuple[str, List[str], List[Any]]]
) -> bool:
(name, parameters, returnVars) = signature
# ERC721
@ -83,7 +89,7 @@ contract Token{
return False
@staticmethod
def detect_incorrect_erc721_interface(contract):
def detect_incorrect_erc721_interface(contract: Contract) -> List[Union[FunctionContract, Any]]:
"""Detect incorrect ERC721 interface
Returns:
@ -102,7 +108,7 @@ contract Token{
]
return functions
def _detect(self):
def _detect(self) -> List[Output]:
"""Detect incorrect erc721 interface
Returns:

@ -1,7 +1,12 @@
"""
Detect mistakenly un-indexed ERC20 event parameters
"""
from typing import Any, List, Tuple, Union
from slither.detectors.abstract_detector import AbstractDetector, DetectorClassification
from slither.core.declarations.contract import Contract
from slither.core.declarations.event import Event
from slither.core.variables.event_variable import EventVariable
from slither.utils.output import Output
class UnindexedERC20EventParameters(AbstractDetector):
@ -39,7 +44,9 @@ Failure to include these keywords will exclude the parameter data in the transac
STANDARD_JSON = False
@staticmethod
def detect_erc20_unindexed_event_params(contract):
def detect_erc20_unindexed_event_params(
contract: Contract,
) -> List[Union[Tuple[Event, EventVariable], Any]]:
"""
Detect un-indexed ERC20 event parameters in a given contract.
:param contract: The contract to check ERC20 events for un-indexed parameters in.
@ -68,7 +75,7 @@ Failure to include these keywords will exclude the parameter data in the transac
# Return the results.
return results
def _detect(self):
def _detect(self) -> List[Output]:
"""
Detect un-indexed ERC20 event parameters in all contracts.
"""

@ -1,4 +1,7 @@
from typing import List
from slither.detectors.abstract_detector import AbstractDetector, DetectorClassification
from slither.utils.output import Output
class Backdoor(AbstractDetector):
@ -17,7 +20,7 @@ class Backdoor(AbstractDetector):
WIKI_EXPLOIT_SCENARIO = ".."
WIKI_RECOMMENDATION = ".."
def _detect(self):
def _detect(self) -> List[Output]:
results = []
for contract in self.compilation_unit.contracts_derived:

@ -9,11 +9,12 @@
TODO: dont report if the value is tainted by msg.value
"""
from typing import List
from typing import Any, Tuple, Union, List
from slither.analyses.data_dependency.data_dependency import is_tainted, is_dependent
from slither.core.cfg.node import Node
from slither.core.declarations import Function, Contract
from slither.analyses.data_dependency.data_dependency import is_tainted, is_dependent
from slither.core.declarations.function_contract import FunctionContract
from slither.core.declarations.solidity_variables import (
SolidityFunction,
SolidityVariableComposed,
@ -28,12 +29,11 @@ from slither.slithir.operations import (
Transfer,
)
# pylint: disable=too-many-nested-blocks,too-many-branches
from slither.utils.output import Output
def arbitrary_send(func: Function):
def arbitrary_send(func: Function) -> Union[bool, List[Node]]:
if func.is_protected():
return []
@ -74,7 +74,9 @@ def arbitrary_send(func: Function):
return ret
def detect_arbitrary_send(contract: Contract):
def detect_arbitrary_send(
contract: Contract,
) -> List[Union[Tuple[FunctionContract, List[Node]], Any]]:
"""
Detect arbitrary send
Args:

@ -52,6 +52,10 @@ class Codex(AbstractDetector):
answer = ""
res = {}
if self.slither.codex_organization:
openai_module.organization = self.slither.codex_organization
try:
res = openai_module.Completion.create(
prompt=prompt,
@ -114,11 +118,7 @@ class Codex(AbstractDetector):
):
continue
prompt = f"Analyze this Solidity contract and find the vulnerabilities. If you find any vulnerabilities, begin the response with {VULN_FOUND}\n"
src_mapping = contract.source_mapping
content = contract.compilation_unit.core.source_code[src_mapping.filename.absolute]
start = src_mapping.start
end = src_mapping.start + src_mapping.length
prompt += content[start:end]
prompt += contract.source_mapping.content
answer = self._run_codex(logging_file, prompt)

@ -0,0 +1,50 @@
from typing import List, Tuple
from slither.core.declarations import Function
from slither.detectors.abstract_detector import AbstractDetector, DetectorClassification
from slither.utils.code_complexity import compute_cyclomatic_complexity
from slither.utils.output import Output
def _check_for_high_cc(high_cc_functions: List[Tuple[Function, int]], f: Function) -> None:
cc = compute_cyclomatic_complexity(f)
if cc > 11:
high_cc_functions.append((f, cc))
class CyclomaticComplexity(AbstractDetector):
"""
Detects functions with high (> 11) cyclomatic complexity.
"""
ARGUMENT = "cyclomatic-complexity"
HELP = "Detects functions with high (> 11) cyclomatic complexity"
IMPACT = DetectorClassification.INFORMATIONAL
CONFIDENCE = DetectorClassification.HIGH
WIKI = "https://github.com/crytic/slither/wiki/Detector-Documentation#cyclomatic-complexity"
WIKI_TITLE = "Cyclomatic complexity"
WIKI_DESCRIPTION = "Detects functions with high (> 11) cyclomatic complexity."
WIKI_EXPLOIT_SCENARIO = ""
WIKI_RECOMMENDATION = (
"Reduce cyclomatic complexity by splitting the function into several smaller subroutines."
)
def _detect(self) -> List[Output]:
results = []
high_cc_functions: List[Tuple[Function, int]] = []
f: Function
for c in self.compilation_unit.contracts:
for f in c.functions_declared:
_check_for_high_cc(high_cc_functions, f)
for f in self.compilation_unit.functions_top_level:
_check_for_high_cc(high_cc_functions, f)
for f, cc in high_cc_functions:
info = [f, f" has a high cyclomatic complexity ({cc}).\n"]
res = self.generate_result(info)
results.append(res)
return results

@ -5,6 +5,7 @@ from typing import List, Tuple
from slither.core.declarations import Function, FunctionContract, Contract
from slither.detectors.abstract_detector import AbstractDetector, DetectorClassification
from slither.utils.output import Output
class DeadCode(AbstractDetector):
@ -34,7 +35,7 @@ contract Contract{
WIKI_RECOMMENDATION = "Remove unused functions."
def _detect(self):
def _detect(self) -> List[Output]:
results = []

@ -5,18 +5,19 @@ Note that require()/assert() are not considered here. Even if they
are in the outermost scope, they do not guarantee a revert, so a
default value can still be returned.
"""
from typing import List
from slither.detectors.abstract_detector import AbstractDetector, DetectorClassification
from slither.core.cfg.node import NodeType
from slither.core.cfg.node import Node, NodeType
from slither.utils.output import Output
def is_revert(node):
def is_revert(node: Node) -> bool:
return node.type == NodeType.THROW or any(
c.name in ["revert()", "revert(string"] for c in node.internal_calls
)
def _get_false_son(node):
def _get_false_son(node: Node) -> Node:
"""Select the son node corresponding to a false branch
Following this node stays on the outer scope of the function
"""
@ -60,7 +61,7 @@ If the condition in `myModif` is false, the execution of `get()` will return 0."
WIKI_RECOMMENDATION = "All the paths in a modifier must execute `_` or revert."
def _detect(self):
def _detect(self) -> List[Output]:
results = []
for c in self.contracts:
for mod in c.modifiers:

@ -8,6 +8,7 @@ from slither.core.solidity_types.elementary_type import ElementaryType
from slither.core.variables.state_variable import StateVariable
from slither.detectors.abstract_detector import AbstractDetector, DetectorClassification
from slither.utils.function import get_function_id
from slither.utils.output import Output
class DomainSeparatorCollision(AbstractDetector):
@ -39,7 +40,7 @@ contract Contract{
WIKI_RECOMMENDATION = "Remove or rename the function that collides with DOMAIN_SEPARATOR()."
def _detect(self):
def _detect(self) -> List[Output]:
domain_sig = get_function_id("DOMAIN_SEPARATOR()")
for contract in self.compilation_unit.contracts_derived:
if contract.is_erc20():

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

Loading…
Cancel
Save