From a2d88b80cddb28ab6cae987a8856374973a716ef Mon Sep 17 00:00:00 2001 From: alpharush <0xalpharush@protonmail.com> Date: Wed, 1 Nov 2023 12:29:31 -0500 Subject: [PATCH 01/12] add support for send builtin --- .../core/declarations/solidity_variables.py | 1 + .../expressions/expression_parsing.py | 15 ++++++++++-- ...ast_parsing__vyper_cfgir_builtins_c__0.txt | 24 +++++++++---------- ..._vyper_cfgir_builtins_test_builtins__0.txt | 8 +++++++ tests/e2e/vyper_parsing/test_data/builtins.vy | 1 + 5 files changed, 35 insertions(+), 14 deletions(-) diff --git a/slither/core/declarations/solidity_variables.py b/slither/core/declarations/solidity_variables.py index 7c81266bf..7bcf2b2b2 100644 --- a/slither/core/declarations/solidity_variables.py +++ b/slither/core/declarations/solidity_variables.py @@ -114,6 +114,7 @@ SOLIDITY_FUNCTIONS: Dict[str, List[str]] = { "_abi_encode()": [], "slice()": [], "uint2str()": ["string"], + "send()": [], } diff --git a/slither/vyper_parsing/expressions/expression_parsing.py b/slither/vyper_parsing/expressions/expression_parsing.py index d51467fab..71eb56631 100644 --- a/slither/vyper_parsing/expressions/expression_parsing.py +++ b/slither/vyper_parsing/expressions/expression_parsing.py @@ -153,10 +153,10 @@ def parse_expression( parsed_expr.set_offset(expression.src, caller_context.compilation_unit) return parsed_expr + # `raw_call` and `send` are treated specially in order to force `extract_tmp_call` to treat this as a `HighLevelCall` which will be converted + # to a `LowLevelCall` by `convert_to_low_level`. This is an artifact of the late conversion of Solidity... if called.value.name == "raw_call()": args = [parse_expression(a, caller_context) for a in expression.args] - # This is treated specially in order to force `extract_tmp_call` to treat this as a `HighLevelCall` which will be converted - # to a `LowLevelCall` by `convert_to_low_level`. This is an artifact of the late conversion of Solidity... call = CallExpression( MemberAccess("raw_call", "tuple(bool,bytes32)", args[0]), args[1:], @@ -183,6 +183,17 @@ def parse_expression( return call + if called.value.name == "send()": + args = [parse_expression(a, caller_context) for a in expression.args] + call = CallExpression( + MemberAccess("send", "tuple()", args[0]), + args[1:], + "tuple()", + ) + call.set_offset(expression.src, caller_context.compilation_unit) + + return call + if expression.args and isinstance(expression.args[0], VyDict): arguments = [] for val in expression.args[0].values: diff --git a/tests/e2e/vyper_parsing/snapshots/ast_parsing__vyper_cfgir_builtins_c__0.txt b/tests/e2e/vyper_parsing/snapshots/ast_parsing__vyper_cfgir_builtins_c__0.txt index 1f973fcdb..572b0d927 100644 --- a/tests/e2e/vyper_parsing/snapshots/ast_parsing__vyper_cfgir_builtins_c__0.txt +++ b/tests/e2e/vyper_parsing/snapshots/ast_parsing__vyper_cfgir_builtins_c__0.txt @@ -16,12 +16,12 @@ EXPRESSION: user_shares.append(1) IRs: -REF_1 -> LENGTH user_shares -TMP_3(uint256) := REF_1(uint256) -TMP_4(uint256) = TMP_3 (c)+ 1 -REF_1(uint256) (->user_shares) := TMP_4(uint256) -REF_2(uint256) -> user_shares[TMP_3] -REF_2(uint256) (->user_shares) := 1(uint256)"]; +REF_2 -> LENGTH user_shares +TMP_4(uint256) := REF_2(uint256) +TMP_5(uint256) = TMP_4 (c)+ 1 +REF_2(uint256) (->user_shares) := TMP_5(uint256) +REF_3(uint256) -> user_shares[TMP_4] +REF_3(uint256) (->user_shares) := 1(uint256)"]; 2->3; 3[label="Node Type: EXPRESSION 3 @@ -29,10 +29,10 @@ EXPRESSION: user_shares.pop() IRs: -REF_4 -> LENGTH user_shares -TMP_6(uint256) = REF_4 (c)- 1 -REF_5(uint256) -> user_shares[TMP_6] -REF_5 = delete REF_5 -REF_6 -> LENGTH user_shares -REF_6(uint256) (->user_shares) := TMP_6(uint256)"]; +REF_5 -> LENGTH user_shares +TMP_7(uint256) = REF_5 (c)- 1 +REF_6(uint256) -> user_shares[TMP_7] +REF_6 = delete REF_6 +REF_7 -> LENGTH user_shares +REF_7(uint256) (->user_shares) := TMP_7(uint256)"]; } diff --git a/tests/e2e/vyper_parsing/snapshots/ast_parsing__vyper_cfgir_builtins_test_builtins__0.txt b/tests/e2e/vyper_parsing/snapshots/ast_parsing__vyper_cfgir_builtins_test_builtins__0.txt index 4719d9926..9da3718f0 100644 --- a/tests/e2e/vyper_parsing/snapshots/ast_parsing__vyper_cfgir_builtins_test_builtins__0.txt +++ b/tests/e2e/vyper_parsing/snapshots/ast_parsing__vyper_cfgir_builtins_test_builtins__0.txt @@ -115,4 +115,12 @@ x = self.balance IRs: x(uint256) := self.balance(uint256)"]; +14->15; +15[label="Node Type: EXPRESSION 15 + +EXPRESSION: +msg.sender.send(x) + +IRs: +TMP_2 = SEND dest:msg.sender value:x"]; } diff --git a/tests/e2e/vyper_parsing/test_data/builtins.vy b/tests/e2e/vyper_parsing/test_data/builtins.vy index 4c4a72927..a49816a09 100644 --- a/tests/e2e/vyper_parsing/test_data/builtins.vy +++ b/tests/e2e/vyper_parsing/test_data/builtins.vy @@ -16,6 +16,7 @@ def test_builtins(): m: address = tx.origin n: uint256 = tx.gasprice x: uint256 = self.balance + send(msg.sender, x) @external def c(x: uint256): From 0c10e27341b730263fc6b043c305f7e24180e2c6 Mon Sep 17 00:00:00 2001 From: alpharush <0xalpharush@protonmail.com> Date: Fri, 1 Mar 2024 16:51:00 -0600 Subject: [PATCH 02/12] add support for python 3.12 --- .github/workflows/ci.yml | 2 +- .github/workflows/test.yml | 2 +- slither/__main__.py | 13 ++++++++++--- slither/utils/output.py | 4 ++-- 4 files changed, 14 insertions(+), 7 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7972c96e7..5eab77239 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -26,7 +26,7 @@ jobs: fail-fast: false matrix: os: ["ubuntu-latest", "windows-2022"] - python: ${{ (github.event_name == 'pull_request' && fromJSON('["3.8", "3.11"]')) || fromJSON('["3.8", "3.9", "3.10", "3.11"]') }} + python: ${{ (github.event_name == 'pull_request' && fromJSON('["3.8", "3.12"]')) || fromJSON('["3.8", "3.9", "3.10", "3.12"]') }} type: ["cli", "dapp", "data_dependency", diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 892e20d15..0e291ec44 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -25,7 +25,7 @@ jobs: matrix: os: ["ubuntu-latest", "windows-2022"] type: ["unit", "integration", "tool"] - python: ${{ (github.event_name == 'pull_request' && fromJSON('["3.8", "3.11"]')) || fromJSON('["3.8", "3.9", "3.10", "3.11"]') }} + python: ${{ (github.event_name == 'pull_request' && fromJSON('["3.8", "3.12"]')) || fromJSON('["3.8", "3.9", "3.10", "3.12"]') }} steps: - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python }} diff --git a/slither/__main__.py b/slither/__main__.py index ee1428967..7b2c2ba8b 100644 --- a/slither/__main__.py +++ b/slither/__main__.py @@ -10,9 +10,9 @@ import os import pstats import sys import traceback +from importlib import metadata from typing import Tuple, Optional, List, Dict, Type, Union, Any, Sequence -from pkg_resources import iter_entry_points, require from crytic_compile import cryticparser, CryticCompile from crytic_compile.platform.standard import generate_standard_export @@ -166,7 +166,14 @@ def get_detectors_and_printers() -> Tuple[ printers = [p for p in printers_ if inspect.isclass(p) and issubclass(p, AbstractPrinter)] # Handle plugins! - for entry_point in iter_entry_points(group="slither_analyzer.plugin", name=None): + if sys.version_info >= (3, 10): + entry_points = metadata.entry_points(group="slither_analyzer.plugin") + else: + from pkg_resources import iter_entry_points # pylint: disable=import-outside-toplevel + + entry_points = iter_entry_points(group="slither_analyzer.plugin", name=None) + + for entry_point in entry_points: make_plugin = entry_point.load() plugin_detectors, plugin_printers = make_plugin() @@ -298,7 +305,7 @@ def parse_args( parser.add_argument( "--version", help="displays the current version", - version=require("slither-analyzer")[0].version, + version=metadata.version("slither-analyzer"), action="version", ) diff --git a/slither/utils/output.py b/slither/utils/output.py index 4a91ca9b9..176b250e3 100644 --- a/slither/utils/output.py +++ b/slither/utils/output.py @@ -4,10 +4,10 @@ import logging import os import zipfile from collections import OrderedDict +from importlib import metadata from typing import Tuple, Optional, Dict, List, Union, Any, TYPE_CHECKING, Type from zipfile import ZipFile -from pkg_resources import require from slither.core.cfg.node import Node from slither.core.declarations import ( @@ -161,7 +161,7 @@ def output_to_sarif( "driver": { "name": "Slither", "informationUri": "https://github.com/crytic/slither", - "version": require("slither-analyzer")[0].version, + "version": metadata.version("slither-analyzer"), "rules": [], } }, From d24347b446532907efada270866efe918d012b22 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 4 Mar 2024 22:59:06 +0000 Subject: [PATCH 03/12] Bump pypa/gh-action-pypi-publish from 1.8.11 to 1.8.12 Bumps [pypa/gh-action-pypi-publish](https://github.com/pypa/gh-action-pypi-publish) from 1.8.11 to 1.8.12. - [Release notes](https://github.com/pypa/gh-action-pypi-publish/releases) - [Commits](https://github.com/pypa/gh-action-pypi-publish/compare/v1.8.11...v1.8.12) --- updated-dependencies: - dependency-name: pypa/gh-action-pypi-publish dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/publish.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 234e2eb05..40e3936a2 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -44,7 +44,7 @@ jobs: path: dist/ - name: publish - uses: pypa/gh-action-pypi-publish@v1.8.11 + uses: pypa/gh-action-pypi-publish@v1.8.12 - name: sign uses: sigstore/gh-action-sigstore-python@v2.1.1 From 3d1742fae6fc4bbc1405436107b309b35822a7d1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 11 Mar 2024 22:31:05 +0000 Subject: [PATCH 04/12] Bump pypa/gh-action-pypi-publish from 1.8.12 to 1.8.14 Bumps [pypa/gh-action-pypi-publish](https://github.com/pypa/gh-action-pypi-publish) from 1.8.12 to 1.8.14. - [Release notes](https://github.com/pypa/gh-action-pypi-publish/releases) - [Commits](https://github.com/pypa/gh-action-pypi-publish/compare/v1.8.12...v1.8.14) --- updated-dependencies: - dependency-name: pypa/gh-action-pypi-publish dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/publish.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 40e3936a2..031d1a376 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -44,7 +44,7 @@ jobs: path: dist/ - name: publish - uses: pypa/gh-action-pypi-publish@v1.8.12 + uses: pypa/gh-action-pypi-publish@v1.8.14 - name: sign uses: sigstore/gh-action-sigstore-python@v2.1.1 From 0e1713bf55b85ad3ed8b805a519ae6c7f7d46ebb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 11 Mar 2024 22:31:13 +0000 Subject: [PATCH 05/12] Bump cachix/install-nix-action from 25 to 26 Bumps [cachix/install-nix-action](https://github.com/cachix/install-nix-action) from 25 to 26. - [Release notes](https://github.com/cachix/install-nix-action/releases) - [Commits](https://github.com/cachix/install-nix-action/compare/v25...v26) --- updated-dependencies: - dependency-name: cachix/install-nix-action dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7972c96e7..6dca59bac 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -67,7 +67,7 @@ jobs: - name: Set up nix if: matrix.type == 'dapp' - uses: cachix/install-nix-action@v25 + uses: cachix/install-nix-action@v26 - name: Set up cachix if: matrix.type == 'dapp' From 134cadf989ba6f63c8ee4082900f37439cd42e44 Mon Sep 17 00:00:00 2001 From: Simone Date: Mon, 18 Mar 2024 14:32:31 +0100 Subject: [PATCH 06/12] Fix parsing of events --- slither/core/scope/scope.py | 6 +- slither/solc_parsing/declarations/contract.py | 4 +- .../{event.py => event_contract.py} | 20 ++--- .../declarations/event_top_level.py | 75 ++++++++++++++++++ .../solc_parsing/expressions/find_variable.py | 10 ++- .../slither_compilation_unit_solc.py | 17 +++- .../solidity_types/type_parsing.py | 11 ++- .../event-top-level.sol-0.8.22-compact.zip | Bin 2022 -> 3325 bytes .../test_data/event-top-level.sol | 10 +++ .../event-top-level.sol-0.8.22-compact.json | 3 +- 10 files changed, 133 insertions(+), 23 deletions(-) rename slither/solc_parsing/declarations/{event.py => event_contract.py} (73%) create mode 100644 slither/solc_parsing/declarations/event_top_level.py diff --git a/slither/core/scope/scope.py b/slither/core/scope/scope.py index 784b17cb2..99fe00b25 100644 --- a/slither/core/scope/scope.py +++ b/slither/core/scope/scope.py @@ -36,11 +36,11 @@ class FileScope: # So we simplify the logic and have the scope fields all populated self.custom_errors: Set[CustomErrorTopLevel] = set() self.enums: Dict[str, EnumTopLevel] = {} - self.events: Dict[str, EventTopLevel] = {} # Functions is a list instead of a dict # Because we parse the function signature later on # So we simplify the logic and have the scope fields all populated self.functions: Set[FunctionTopLevel] = set() + self.events: Set[EventTopLevel] = set() self.using_for_directives: Set[UsingForTopLevel] = set() self.imports: Set[Import] = set() self.pragmas: Set[Pragma] = set() @@ -76,8 +76,8 @@ class FileScope: if not _dict_contain(new_scope.enums, self.enums): self.enums.update(new_scope.enums) learn_something = True - if not _dict_contain(new_scope.events, self.events): - self.events.update(new_scope.events) + if not new_scope.events.issubset(self.events): + self.events |= new_scope.events learn_something = True if not new_scope.functions.issubset(self.functions): self.functions |= new_scope.functions diff --git a/slither/solc_parsing/declarations/contract.py b/slither/solc_parsing/declarations/contract.py index f0696d557..b27cce798 100644 --- a/slither/solc_parsing/declarations/contract.py +++ b/slither/solc_parsing/declarations/contract.py @@ -16,7 +16,7 @@ from slither.core.solidity_types import ElementaryType, TypeAliasContract from slither.core.variables.state_variable import StateVariable from slither.solc_parsing.declarations.caller_context import CallerContextExpression from slither.solc_parsing.declarations.custom_error import CustomErrorSolc -from slither.solc_parsing.declarations.event import EventSolc +from slither.solc_parsing.declarations.event_contract import EventContractSolc from slither.solc_parsing.declarations.function import FunctionSolc from slither.solc_parsing.declarations.modifier import ModifierSolc from slither.solc_parsing.declarations.structure_contract import StructureContractSolc @@ -751,7 +751,7 @@ class ContractSolc(CallerContextExpression): event.set_contract(self._contract) event.set_offset(event_to_parse["src"], self._contract.compilation_unit) - event_parser = EventSolc(event, event_to_parse, self._slither_parser) # type: ignore + event_parser = EventContractSolc(event, event_to_parse, self) # type: ignore event_parser.analyze() # type: ignore self._contract.events_as_dict[event.full_name] = event except (VariableNotFound, KeyError) as e: diff --git a/slither/solc_parsing/declarations/event.py b/slither/solc_parsing/declarations/event_contract.py similarity index 73% rename from slither/solc_parsing/declarations/event.py rename to slither/solc_parsing/declarations/event_contract.py index 4a7d62389..6af45ba52 100644 --- a/slither/solc_parsing/declarations/event.py +++ b/slither/solc_parsing/declarations/event_contract.py @@ -1,27 +1,27 @@ """ - Event module + EventContract module """ from typing import TYPE_CHECKING, Dict from slither.core.variables.event_variable import EventVariable from slither.solc_parsing.variables.event_variable import EventVariableSolc -from slither.core.declarations.event import Event +from slither.core.declarations.event_contract import EventContract if TYPE_CHECKING: - from slither.solc_parsing.slither_compilation_unit_solc import SlitherCompilationUnitSolc + from slither.solc_parsing.declarations.contract import ContractSolc -class EventSolc: +class EventContractSolc: """ - Event class + EventContract class """ def __init__( - self, event: Event, event_data: Dict, slither_parser: "SlitherCompilationUnitSolc" + self, event: EventContract, event_data: Dict, contract_parser: "ContractSolc" ) -> None: self._event = event - self._slither_parser = slither_parser + self._contract_parser = contract_parser if self.is_compact_ast: self._event.name = event_data["name"] @@ -42,16 +42,16 @@ class EventSolc: @property def is_compact_ast(self) -> bool: - return self._slither_parser.is_compact_ast + return self._contract_parser.is_compact_ast def analyze(self) -> None: for elem_to_parse in self._elemsNotParsed: elem = EventVariable() # Todo: check if the source offset is always here if "src" in elem_to_parse: - elem.set_offset(elem_to_parse["src"], self._slither_parser.compilation_unit) + elem.set_offset(elem_to_parse["src"], self._contract_parser.compilation_unit) elem_parser = EventVariableSolc(elem, elem_to_parse) - elem_parser.analyze(self._slither_parser) + elem_parser.analyze(self._contract_parser) self._event.elems.append(elem) diff --git a/slither/solc_parsing/declarations/event_top_level.py b/slither/solc_parsing/declarations/event_top_level.py new file mode 100644 index 000000000..9b6b676c7 --- /dev/null +++ b/slither/solc_parsing/declarations/event_top_level.py @@ -0,0 +1,75 @@ +""" + EventTopLevel module +""" +from typing import TYPE_CHECKING, Dict + +from slither.core.declarations.event_top_level import EventTopLevel +from slither.core.variables.event_variable import EventVariable +from slither.core.compilation_unit import SlitherCompilationUnit +from slither.solc_parsing.variables.event_variable import EventVariableSolc +from slither.solc_parsing.declarations.caller_context import CallerContextExpression + +if TYPE_CHECKING: + from slither.solc_parsing.slither_compilation_unit_solc import SlitherCompilationUnitSolc + + +class EventTopLevelSolc(CallerContextExpression): + """ + EventTopLevel class + """ + + def __init__( + self, event: EventTopLevel, event_data: Dict, slither_parser: "SlitherCompilationUnitSolc" + ) -> None: + + self._event = event + self._slither_parser = slither_parser + + if self.is_compact_ast: + self._event.name = event_data["name"] + elems = event_data["parameters"] + assert elems["nodeType"] == "ParameterList" + self._elemsNotParsed = elems["parameters"] + else: + self._event.name = event_data["attributes"]["name"] + for elem in event_data["children"]: + # From Solidity 0.6.3 to 0.6.10 (included) + # Comment above a event might be added in the children + # of an event for the legacy ast + if elem["name"] == "ParameterList": + if "children" in elem: + self._elemsNotParsed = elem["children"] + else: + self._elemsNotParsed = [] + + def analyze(self) -> None: + for elem_to_parse in self._elemsNotParsed: + elem = EventVariable() + # Todo: check if the source offset is always here + if "src" in elem_to_parse: + elem.set_offset(elem_to_parse["src"], self._slither_parser.compilation_unit) + elem_parser = EventVariableSolc(elem, elem_to_parse) + elem_parser.analyze(self) + + self._event.elems.append(elem) + + self._elemsNotParsed = [] + + @property + def is_compact_ast(self) -> bool: + return self._slither_parser.is_compact_ast + + @property + def compilation_unit(self) -> SlitherCompilationUnit: + return self._slither_parser.compilation_unit + + def get_key(self) -> str: + return self._slither_parser.get_key() + + @property + def slither_parser(self) -> "SlitherCompilationUnitSolc": + return self._slither_parser + + @property + def underlying_event(self) -> EventTopLevel: + return self._event diff --git a/slither/solc_parsing/expressions/find_variable.py b/slither/solc_parsing/expressions/find_variable.py index e7fa99521..404bcdee5 100644 --- a/slither/solc_parsing/expressions/find_variable.py +++ b/slither/solc_parsing/expressions/find_variable.py @@ -134,8 +134,9 @@ def find_top_level( if var_name in scope.enums: return scope.enums[var_name], False - if var_name in scope.events: - return scope.events[var_name], False + for event in scope.events: + if var_name == event.full_name: + return event, False for import_directive in scope.imports: if import_directive.alias == var_name: @@ -268,6 +269,7 @@ def _find_variable_init( from slither.solc_parsing.declarations.function import FunctionSolc from slither.solc_parsing.declarations.structure_top_level import StructureTopLevelSolc from slither.solc_parsing.variables.top_level_variable import TopLevelVariableSolc + from slither.solc_parsing.declarations.event_top_level import EventTopLevelSolc from slither.solc_parsing.declarations.custom_error import CustomErrorSolc direct_contracts: List[Contract] @@ -311,6 +313,10 @@ def _find_variable_init( direct_contracts = [] direct_functions_parser = [] scope = caller_context.underlying_variable.file_scope + elif isinstance(caller_context, EventTopLevelSolc): + direct_contracts = [] + direct_functions_parser = [] + scope = caller_context.underlying_event.file_scope elif isinstance(caller_context, CustomErrorSolc): if caller_context.contract_parser: direct_contracts = [caller_context.contract_parser.underlying_contract] diff --git a/slither/solc_parsing/slither_compilation_unit_solc.py b/slither/solc_parsing/slither_compilation_unit_solc.py index ee28a7bf5..02d830702 100644 --- a/slither/solc_parsing/slither_compilation_unit_solc.py +++ b/slither/solc_parsing/slither_compilation_unit_solc.py @@ -24,7 +24,7 @@ from slither.solc_parsing.declarations.caller_context import CallerContextExpres from slither.solc_parsing.declarations.contract import ContractSolc from slither.solc_parsing.declarations.custom_error import CustomErrorSolc from slither.solc_parsing.declarations.function import FunctionSolc -from slither.solc_parsing.declarations.event import EventSolc +from slither.solc_parsing.declarations.event_top_level import EventTopLevelSolc from slither.solc_parsing.declarations.structure_top_level import StructureTopLevelSolc from slither.solc_parsing.declarations.using_for_top_level import UsingForTopLevelSolc from slither.solc_parsing.exceptions import VariableNotFound @@ -90,6 +90,7 @@ class SlitherCompilationUnitSolc(CallerContextExpression): self._variables_top_level_parser: List[TopLevelVariableSolc] = [] self._functions_top_level_parser: List[FunctionSolc] = [] self._using_for_top_level_parser: List[UsingForTopLevelSolc] = [] + self._events_top_level_parser: List[EventTopLevelSolc] = [] self._all_functions_and_modifier_parser: List[FunctionSolc] = [] self._top_level_contracts_counter = 0 @@ -353,9 +354,9 @@ class SlitherCompilationUnitSolc(CallerContextExpression): event = EventTopLevel(scope) event.set_offset(top_level_data["src"], self._compilation_unit) - event_parser = EventSolc(event, top_level_data, self) # type: ignore - event_parser.analyze() # type: ignore - scope.events[event.full_name] = event + event_parser = EventTopLevelSolc(event, top_level_data, self) # type: ignore + self._events_top_level_parser.append(event_parser) + scope.events.add(event) self._compilation_unit.events_top_level.append(event) else: @@ -612,6 +613,7 @@ Please rename it, this name is reserved for Slither's internals""" self._analyze_top_level_variables() self._analyze_top_level_structures() + self._analyze_top_level_events() # Start with the contracts without inheritance # Analyze a contract only if all its fathers @@ -722,6 +724,13 @@ Please rename it, this name is reserved for Slither's internals""" except (VariableNotFound, KeyError) as e: raise SlitherException(f"Missing {e} during variable analyze") from e + def _analyze_top_level_events(self) -> None: + try: + for event in self._events_top_level_parser: + event.analyze() + except (VariableNotFound, KeyError) as e: + raise SlitherException(f"Missing event {e} during top level event analyze") from e + def _analyze_params_top_level_function(self) -> None: for func_parser in self._functions_top_level_parser: func_parser.analyze_params() diff --git a/slither/solc_parsing/solidity_types/type_parsing.py b/slither/solc_parsing/solidity_types/type_parsing.py index 82c1ac392..06a91f911 100644 --- a/slither/solc_parsing/solidity_types/type_parsing.py +++ b/slither/solc_parsing/solidity_types/type_parsing.py @@ -232,6 +232,7 @@ def parse_type( from slither.solc_parsing.declarations.structure_top_level import StructureTopLevelSolc from slither.solc_parsing.slither_compilation_unit_solc import SlitherCompilationUnitSolc from slither.solc_parsing.variables.top_level_variable import TopLevelVariableSolc + from slither.solc_parsing.declarations.event_top_level import EventTopLevelSolc sl: "SlitherCompilationUnit" renaming: Dict[str, str] @@ -266,7 +267,13 @@ def parse_type( functions = [] elif isinstance( caller_context, - (StructureTopLevelSolc, CustomErrorSolc, TopLevelVariableSolc, UsingForTopLevelSolc), + ( + StructureTopLevelSolc, + CustomErrorSolc, + TopLevelVariableSolc, + UsingForTopLevelSolc, + EventTopLevelSolc, + ), ): if isinstance(caller_context, StructureTopLevelSolc): scope = caller_context.underlying_structure.file_scope @@ -274,6 +281,8 @@ def parse_type( scope = caller_context.underlying_variable.file_scope elif isinstance(caller_context, UsingForTopLevelSolc): scope = caller_context.underlying_using_for.file_scope + elif isinstance(caller_context, EventTopLevelSolc): + scope = caller_context.underlying_event.file_scope else: assert isinstance(caller_context, CustomErrorSolc) custom_error = caller_context.underlying_custom_error diff --git a/tests/e2e/solc_parsing/test_data/compile/event-top-level.sol-0.8.22-compact.zip b/tests/e2e/solc_parsing/test_data/compile/event-top-level.sol-0.8.22-compact.zip index ed82f32b17350e7c70788a8d84e0d02dcbdd0bba..4845562904d655f7a0e5f976f0700974d92f66ea 100644 GIT binary patch delta 3040 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 diff --git a/tests/e2e/solc_parsing/test_data/event-top-level.sol b/tests/e2e/solc_parsing/test_data/event-top-level.sol index fa64e1bf7..81ac94f7b 100644 --- a/tests/e2e/solc_parsing/test_data/event-top-level.sol +++ b/tests/e2e/solc_parsing/test_data/event-top-level.sol @@ -1,7 +1,17 @@ event MyEvent(uint256 a); +uint256 constant A = 3; +event MyEvent2(uint8[A]); + contract T { + type MyType is uint256; + event MyCustomEvent(MyType mytype); + function a() public { emit MyEvent(2); } + + function b() public { + emit MyEvent2([1,2,3]); + } } diff --git a/tests/e2e/solc_parsing/test_data/expected/event-top-level.sol-0.8.22-compact.json b/tests/e2e/solc_parsing/test_data/expected/event-top-level.sol-0.8.22-compact.json index 58c6a3ab6..68dff297f 100644 --- a/tests/e2e/solc_parsing/test_data/expected/event-top-level.sol-0.8.22-compact.json +++ b/tests/e2e/solc_parsing/test_data/expected/event-top-level.sol-0.8.22-compact.json @@ -1,5 +1,6 @@ { "T": { - "a()": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: EXPRESSION 1\n\"];\n}\n" + "a()": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: EXPRESSION 1\n\"];\n}\n", + "b()": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: EXPRESSION 1\n\"];\n}\n" } } \ No newline at end of file From 3f2d189a7e734b59f7d34fc9a6dc20a385c21f8f Mon Sep 17 00:00:00 2001 From: alpharush <0xalpharush@protonmail.com> Date: Mon, 18 Mar 2024 11:37:48 -0500 Subject: [PATCH 07/12] Create issue-metrics.yml --- .github/workflows/issue-metrics.yml | 40 +++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 .github/workflows/issue-metrics.yml diff --git a/.github/workflows/issue-metrics.yml b/.github/workflows/issue-metrics.yml new file mode 100644 index 000000000..288160ca6 --- /dev/null +++ b/.github/workflows/issue-metrics.yml @@ -0,0 +1,40 @@ +name: Monthly issue metrics +on: + workflow_dispatch: + schedule: + - cron: '3 2 1 * *' + +permissions: + issues: write + pull-requests: read + +jobs: + build: + name: issue metrics + runs-on: ubuntu-latest + steps: + - name: Get dates for last month + shell: bash + run: | + # Calculate the first day of the previous month + first_day=$(date -d "last month" +%Y-%m-01) + + # Calculate the last day of the previous month + last_day=$(date -d "$first_day +1 month -1 day" +%Y-%m-%d) + + #Set an environment variable with the date range + echo "$first_day..$last_day" + echo "last_month=$first_day..$last_day" >> "$GITHUB_ENV" + + - name: Run issue-metrics tool + uses: github/issue-metrics@v2 + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + SEARCH_QUERY: 'repo:crytic/slither is:issue created:${{ env.last_month }} -reason:"not planned" -reason:"duplicate"' + + - name: Create issue + uses: peter-evans/create-issue-from-file@v5 + with: + title: Monthly issue metrics report + token: ${{ secrets.GITHUB_TOKEN }} + content-filepath: ./issue_metrics.md From 69ece6da84ee50b2f2c4b61c151af394a7c26690 Mon Sep 17 00:00:00 2001 From: alpharush <0xalpharush@protonmail.com> Date: Wed, 20 Mar 2024 11:03:27 -0500 Subject: [PATCH 08/12] fix: support aliases for NewContract operation --- slither/slithir/convert.py | 7 +++---- slither/slithir/operations/new_contract.py | 14 +++++++------- .../expressions/expression_parsing.py | 17 +++-------------- tests/e2e/solc_parsing/test_ast_parsing.py | 2 ++ .../aliasing/alias-symbol-NewContract.sol | 8 ++++++++ .../aliasing/alias-unit-NewContract.sol | 9 +++++++++ ...s-symbol-NewContract.sol-0.8.19-compact.zip | Bin 0 -> 2042 bytes ...ias-unit-NewContract.sol-0.8.19-compact.zip | Bin 0 -> 2017 bytes ...-symbol-NewContract.sol-0.8.19-compact.json | 6 ++++++ ...as-unit-NewContract.sol-0.8.19-compact.json | 6 ++++++ 10 files changed, 44 insertions(+), 25 deletions(-) create mode 100644 tests/e2e/solc_parsing/test_data/aliasing/alias-symbol-NewContract.sol create mode 100644 tests/e2e/solc_parsing/test_data/aliasing/alias-unit-NewContract.sol create mode 100644 tests/e2e/solc_parsing/test_data/compile/aliasing/alias-symbol-NewContract.sol-0.8.19-compact.zip create mode 100644 tests/e2e/solc_parsing/test_data/compile/aliasing/alias-unit-NewContract.sol-0.8.19-compact.zip create mode 100644 tests/e2e/solc_parsing/test_data/expected/aliasing/alias-symbol-NewContract.sol-0.8.19-compact.json create mode 100644 tests/e2e/solc_parsing/test_data/expected/aliasing/alias-unit-NewContract.sol-0.8.19-compact.json diff --git a/slither/slithir/convert.py b/slither/slithir/convert.py index 170df8cba..a89b0c371 100644 --- a/slither/slithir/convert.py +++ b/slither/slithir/convert.py @@ -871,9 +871,7 @@ def propagate_types(ir: Operation, node: "Node"): # pylint: disable=too-many-lo elif isinstance(ir, NewArray): ir.lvalue.set_type(ir.array_type) elif isinstance(ir, NewContract): - contract = node.file_scope.get_contract_from_name(ir.contract_name) - assert contract - ir.lvalue.set_type(UserDefinedType(contract)) + ir.lvalue.set_type(ir.contract_name) elif isinstance(ir, NewElementaryType): ir.lvalue.set_type(ir.type) elif isinstance(ir, NewStructure): @@ -1164,7 +1162,7 @@ def extract_tmp_call(ins: TmpCall, contract: Optional[Contract]) -> Union[Call, return n if isinstance(ins.ori, TmpNewContract): - op = NewContract(Constant(ins.ori.contract_name), ins.lvalue) + op = NewContract(ins.ori.contract_name, ins.lvalue) op.set_expression(ins.expression) op.call_id = ins.call_id if ins.call_value: @@ -1719,6 +1717,7 @@ def convert_type_of_high_and_internal_level_call( Returns: Potential new IR """ + func = None if isinstance(ir, InternalCall): candidates: List[Function] diff --git a/slither/slithir/operations/new_contract.py b/slither/slithir/operations/new_contract.py index 9ed00b1ac..928cbd013 100644 --- a/slither/slithir/operations/new_contract.py +++ b/slither/slithir/operations/new_contract.py @@ -3,6 +3,7 @@ from typing import Optional, Any, List, Union from slither.core.declarations import Function from slither.core.declarations.contract import Contract from slither.core.variables import Variable +from slither.core.solidity_types import UserDefinedType from slither.slithir.operations import Call, OperationWithLValue from slither.slithir.utils.utils import is_valid_lvalue from slither.slithir.variables.constant import Constant @@ -13,7 +14,7 @@ from slither.slithir.variables.temporary_ssa import TemporaryVariableSSA class NewContract(Call, OperationWithLValue): # pylint: disable=too-many-instance-attributes def __init__( self, - contract_name: Constant, + contract_name: UserDefinedType, lvalue: Union[TemporaryVariableSSA, TemporaryVariable], names: Optional[List[str]] = None, ) -> None: @@ -23,7 +24,9 @@ class NewContract(Call, OperationWithLValue): # pylint: disable=too-many-instan For calls of the form f({argName1 : arg1, ...}), the names of parameters listed in call order. Otherwise, None. """ - assert isinstance(contract_name, Constant) + assert isinstance( + contract_name.type, Contract + ), f"contract_name is {contract_name} of type {type(contract_name)}" assert is_valid_lvalue(lvalue) super().__init__(names=names) self._contract_name = contract_name @@ -58,7 +61,7 @@ class NewContract(Call, OperationWithLValue): # pylint: disable=too-many-instan self._call_salt = s @property - def contract_name(self) -> Constant: + def contract_name(self) -> UserDefinedType: return self._contract_name @property @@ -69,10 +72,7 @@ class NewContract(Call, OperationWithLValue): # pylint: disable=too-many-instan @property def contract_created(self) -> Contract: - contract_name = self.contract_name - contract_instance = self.node.file_scope.get_contract_from_name(contract_name) - assert contract_instance - return contract_instance + return self.contract_name.type ################################################################################### ################################################################################### diff --git a/slither/solc_parsing/expressions/expression_parsing.py b/slither/solc_parsing/expressions/expression_parsing.py index 4100d16ad..4991984ff 100644 --- a/slither/solc_parsing/expressions/expression_parsing.py +++ b/slither/solc_parsing/expressions/expression_parsing.py @@ -614,20 +614,9 @@ def parse_expression(expression: Dict, caller_context: CallerContextExpression) assert type_name[caller_context.get_key()] == "UserDefinedTypeName" - if is_compact_ast: - - # Changed introduced in Solidity 0.8 - # see https://github.com/crytic/slither/issues/794 - - # TODO explore more the changes introduced in 0.8 and the usage of pathNode/IdentifierPath - if "name" not in type_name: - assert "pathNode" in type_name and "name" in type_name["pathNode"] - contract_name = type_name["pathNode"]["name"] - else: - contract_name = type_name["name"] - else: - contract_name = type_name["attributes"]["name"] - new = NewContract(contract_name) + contract_type = parse_type(type_name, caller_context) + assert isinstance(contract_type, UserDefinedType) + new = NewContract(contract_type) new.set_offset(src, caller_context.compilation_unit) return new diff --git a/tests/e2e/solc_parsing/test_ast_parsing.py b/tests/e2e/solc_parsing/test_ast_parsing.py index 522d49cce..96346bf36 100644 --- a/tests/e2e/solc_parsing/test_ast_parsing.py +++ b/tests/e2e/solc_parsing/test_ast_parsing.py @@ -467,6 +467,8 @@ ALL_TESTS = [ ), Test("user_defined_operators-0.8.19.sol", ["0.8.19"]), Test("aliasing/main.sol", ["0.8.19"]), + Test("aliasing/alias-unit-NewContract.sol", ["0.8.19"]), + Test("aliasing/alias-symbol-NewContract.sol", ["0.8.19"]), Test("type-aliases.sol", ["0.8.19"]), Test("enum-max-min.sol", ["0.8.19"]), Test("event-top-level.sol", ["0.8.22"]), diff --git a/tests/e2e/solc_parsing/test_data/aliasing/alias-symbol-NewContract.sol b/tests/e2e/solc_parsing/test_data/aliasing/alias-symbol-NewContract.sol new file mode 100644 index 000000000..1f545461b --- /dev/null +++ b/tests/e2e/solc_parsing/test_data/aliasing/alias-symbol-NewContract.sol @@ -0,0 +1,8 @@ +import {MyContract as MyAliasedContract} from "./MyContract.sol"; + +contract Test { +MyAliasedContract c; + constructor() { + c = new MyAliasedContract(); + } +} diff --git a/tests/e2e/solc_parsing/test_data/aliasing/alias-unit-NewContract.sol b/tests/e2e/solc_parsing/test_data/aliasing/alias-unit-NewContract.sol new file mode 100644 index 000000000..f46693d09 --- /dev/null +++ b/tests/e2e/solc_parsing/test_data/aliasing/alias-unit-NewContract.sol @@ -0,0 +1,9 @@ + +import "./MyContract.sol" as MyAliasedContract; + +contract Test { +MyAliasedContract.MyContract c; + constructor() { + c = new MyAliasedContract.MyContract(); + } +} \ No newline at end of file diff --git a/tests/e2e/solc_parsing/test_data/compile/aliasing/alias-symbol-NewContract.sol-0.8.19-compact.zip b/tests/e2e/solc_parsing/test_data/compile/aliasing/alias-symbol-NewContract.sol-0.8.19-compact.zip new file mode 100644 index 0000000000000000000000000000000000000000..b630fb8c4908a276b2fcf8d904d8210c308e6052 GIT binary patch literal 2042 zcmb8wX&}>$0|)S*W6oSli9{(6PoaiQG0YXq8m3S}!)QZuENX_yF=s-^)f6h{pFBde z%vqWvxi)enOp7^kl!u<4|NH-o|M&ai`|A7pV{a=YYzhbh(tw={$z}R+9OkSj0Ms1= zfHMF9VEysf5bcmV0iHyEZ8R>-f_Re@jP)Yvgb@8baJK`A!6XlCu-6TIC{BkELcA$1 z3P{2?@S>f9nN39_)K*BvsIP6R-Y8B`y`CWTHA6^7Q!5W1QlR_@DkbO~Ko}i5pR#FckE0aL z#IahJ18GIXUYl8H#feF%xcLjuWMXT!{6}XzspESVW$D=9Bg3?}6~yTZ{-^}prjyLK zXmxV8D3smzcm|XCso2(fF=fHoql~xZt#(@7feo(^f?gSu$#6F4FZkA0+Z`nF)ge&A zj_#tbcv6()UHn!%nYIq^-lzSFr7Qi*ZmyDW(Rmlsxa(;VnRMwzKCd2jCA|PDT5Psc zCY1FJ(6fUXh$wPHTla+g6t(6rcqc(*gihX3G4i>QZ}YTay(g-DD_oa%!DHRhrk@BF zx3Cwm(zlu%D_3BXW)kvp23~zoJ48rQa2Htc@fm=!_3Q=}+BvTU3bZw@_mNL&Cadv> zz+$pR6pf=Do)LicO@xRC&9v*3Fw+!UYA27gfX8LZ#%4pn4Y zpQWH1e*NCZMdS3V^GnxQL4pY7@8Ea?HcGp{qXd866~BKVOd zYcfdfZp<1rjN2U~rRc?QTMEdzdPU!*__dMP7tsC}_;4+(V((gmmHu`%Vh?mh!!PV% zO$f#UBDp!2pDm8Cm?uK$KCxa#sP)UIV4cg*+*eaVm+gi2awgN6 z5a|q`yu`+M{*wkY4K9|9l!u`v)!edndK8b>h*H1y=)1A}m>z9)h__1HoKx#gR`sQ? zYo6`=RoD1=DJ)h~860Yf9XroRzV+U6+b4)|jwa6tXUD6wY}jgTQBm<#sj|K&U4J$V zE4^xX*}ho*!lI~)VC|C^)m(!^>kB_31Dj0;QehuT+z{&h_9vl##b86)zW<$hvbcp9yW7 zXHUX`yEbmu%8#98KO`4VIqH{x?R_Nc4hb8F)_ThviQfAY?6Kf2?|s?&`QdOoWb86# z?4n6AJ=)UsulKud_2bklc} z=ud!a+pqM1y9T<&Gt$=_DjbT2+&(~z->0>iozFc~!&p)&>FXUno{D!%Bev{@l`QqC zmZafc=ICbE1_q_^Pr2e?fJ;s-ZJgIK2Vzam2iOPzdA?0xcq@pr*dWy=F^aD}n&Y@> zaT^#f+goVHR7I9-`*H6EzS|`22V3|F`OK`X#^P3_%_Sxf+o$bAuWLj=AjH^Jp@y=T6Or^)+jN8SUNMYVCx_24|UBG`Zy&HQW;qmtR z(UBeX6#A?|&Z(;z@czw<-RS%H4jD2ZZ8|^D;w!8)-!P{Ukz;K6lYY*3ejuq>@aCxE zmnNwgTuRipdU|rCpLEBM4Y{)$oha@Kda-(6`y@HJJC*lThtbXtoHsnsp57gCDFb|@ z?r7;88ujgC6zld;ejZskG*appiC-W4aDJssJZoxPl)6!eZ5L2QkPvPL?b32Nq5uwb4Y_diHEXK&j#TWt3`>LtwanowFWo4A*c@n{A5Qe3lfP(Ck}OnH44| zBAX<-hl|oH`<_?x1f@{AZ?TD9?(e-i7~jj-Kf8KoUcBSo+e~$+@>fhL;MMe zK08Odw#0@$zzYs`>UE%HW`LLKv0gy@rCWK86jh0 z-}bY{mk(kL;>HuKP(vQ7_+|n!^UFl@#<-|W?(?d;uPa)(O_O^zOoR*!0?u1r#(kc| zUfsRl$m7u_j7{g*2RI?wQk z#RaNil__QStJcNqyuGb}psCRRcKA2N{|!~(-}!%4wzma|{QDF5UH9J`|66zf_y^vO B(9Hk< literal 0 HcmV?d00001 diff --git a/tests/e2e/solc_parsing/test_data/compile/aliasing/alias-unit-NewContract.sol-0.8.19-compact.zip b/tests/e2e/solc_parsing/test_data/compile/aliasing/alias-unit-NewContract.sol-0.8.19-compact.zip new file mode 100644 index 0000000000000000000000000000000000000000..da511363567951259cbb5339e57a6a614a6bb07c GIT binary patch literal 2017 zcmb7_c|6mN1ING2=6Iy#jH1bvvgJst91XEYuGUkp&3!$Nq%AfqIWu=71~}k#7?MNv`gJp1wDNy%2sx0$x-I z3^)To6aW|{B>3p<=miw?b=4Xic-o4;q4rKGmU>me_}BoEC9CrTeK3}m>4F`&3qS)_ zWM-d0n`t~$jI;*Ryst5cv`im0Kq1uGaamDivWMluwgPB=2pJPG`*0~F!aAX;J=IHQ}|{!+?y1#WIq)U1~PXZMG0E;g3KabvC1iFtIp`Z zLLKzm-Y{EBETb(6w{jzG3n_Y=y4m4biC-)`P!&)xl*==WOAB}Fl|LH1sh_K!Z@uOX z67K9mgw=kU@tszO{DG7(_BYI9KF_(})!P%C{v4BZx5ZGP(>6^j93`G_cY}nRugCN$ zAapKJG2sn+K`q&%H7fq+Drtk6M3ujo_I^>AbT2FKs)dg%kZ1l+Eq)44|%Vfy|N=P!N9I(!e z_`I=mRCU#3t%P=`Z}un3A=t)4eFIhIPOUu7lmX9*XqR3cVMH*ZzR2Moe7IA9j=-)# zf0_KUSeAWj=UFPZwiQ06*ht8}=zY${^3YzE=73WCZk7`Ja^JJfke#GgDHnZA)QYL) z$JC35*!Pw~Hifp4SHsPd>2!#3IchVqY9vqiiP*KS*qYE}YOD%f*xRbO{wK`DKq_*j z0-3;Yv*4GYP30c5CpJp%CoHUr3HqaNU1w7`86)TL6TF706Ooj~1z5?!`PQ6Wi9!YT zn%3j|?};jLN6%goqU;Iuf=<8CV1@RMmk3^Z+Ptor)-T%Xh<&qnc@P(>-IkYX^0ym# z?mBuRjpWI3IbAZ(GWE0*lhs%sASA?u4U1hPWWOEfR)WBpcq=~)ZWAX&*Ac{zDs~&# z=QBWTkBuE*H+iVv$2Za5U%5@$m~lR22EG8+!vr^)q4hc_FH*m5gM3-6ti$(3-h9$S zL&yfC&-%92`LhcILu~bz1K(Cp^m?b@fw@857iO~jWl_lWiNu%PPmo^QG9Tp!Ghk>u zkD8`jZN8vcrOeXJmfm7>f}56*vVsK8ZnB!;fpx#fRwL6Fm~N?{r3jpk1k-+!c&oOw zBwMh|v^}0*OVfNrWMD$MNyHzta8;z!Ks39b9K9H`RA%YaR(*;Vc7Z!ZoNRTJ6pANo zA1^jhh?&+sz+JAuToYP9IlEoD?X)F)rYjx!b&o%4dUncsCjTDL#Y`&bI{F(gE_l8-G?gunM4o8+Gg)B(c5WQk%DgjqZAEMeQNUO zVh!+2XOo%&9)>72`sb$oA3$M`gwf*b$c7o9ifqI)UrY0y(k)lQlx2J}mackh@=p5p zPOI;Sw|NZ(eLrUIy5t;*d{X*TxfWqzI~=F1?!lpuH@Pw-W+d8?|b-L?h+JAY@}-Z!{NAz zrUI(gq-CFGNi!*VXAGAS6)>SGg;`|KXA!AI2ag!pmfg?op{t)#K%I@EJ~foQ)+-*H z>ygNs4wnwgg#U6I{j-km1dXYQuc(DN@n^bM*UmqaqqE^T(gnP6m^P&ed7l748=JICZ$`l-7OJ)bfcNt6?xguUJ}Q@058S!zs}KXqT_ zuk6*%Ki^EnYmY{?4=HHgB<(*!^vwt*bGYS-hSJOPWp1G4j09B8ZlP5Wndwl0J3&FJ z@`P`<%!y~G!;*yLoqKNwh-(bQL|8JVRC0h_z1J0}qK{b74B_`SE_AiUiHOuYTGV!% z4)W@8GalP9RygB!OHZ&{C6&akWqohyqze_;w~Q8n`6K#RD65XnsLnil;PHv49`q)< zGMpl+1cvZLDx+`m%GD&aozlD3TrqLeAd>%BF@hxH+V42IH|LIQxnd1;\n1[label=\"Node Type: EXPRESSION 1\n\"];\n}\n" + } +} \ No newline at end of file diff --git a/tests/e2e/solc_parsing/test_data/expected/aliasing/alias-unit-NewContract.sol-0.8.19-compact.json b/tests/e2e/solc_parsing/test_data/expected/aliasing/alias-unit-NewContract.sol-0.8.19-compact.json new file mode 100644 index 000000000..b0ce25611 --- /dev/null +++ b/tests/e2e/solc_parsing/test_data/expected/aliasing/alias-unit-NewContract.sol-0.8.19-compact.json @@ -0,0 +1,6 @@ +{ + "MyContract": {}, + "Test": { + "constructor()": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: EXPRESSION 1\n\"];\n}\n" + } +} \ No newline at end of file From 294850f2f128853b73784caa18399db4c7a7b87e Mon Sep 17 00:00:00 2001 From: alpharush <0xalpharush@protonmail.com> Date: Thu, 21 Mar 2024 22:17:04 -0500 Subject: [PATCH 09/12] add missing file --- tests/e2e/solc_parsing/test_data/aliasing/MyContract.sol | 1 + 1 file changed, 1 insertion(+) create mode 100644 tests/e2e/solc_parsing/test_data/aliasing/MyContract.sol diff --git a/tests/e2e/solc_parsing/test_data/aliasing/MyContract.sol b/tests/e2e/solc_parsing/test_data/aliasing/MyContract.sol new file mode 100644 index 000000000..ab7b85a10 --- /dev/null +++ b/tests/e2e/solc_parsing/test_data/aliasing/MyContract.sol @@ -0,0 +1 @@ +contract MyContract {} \ No newline at end of file From 609a5e1d96dac66ea3ac20097a45281315562e7e Mon Sep 17 00:00:00 2001 From: alpharush <0xalpharush@protonmail.com> Date: Fri, 22 Mar 2024 15:35:08 -0500 Subject: [PATCH 10/12] add missing python versions to matrix --- .github/workflows/ci.yml | 2 +- .github/workflows/doctor.yml | 2 +- .github/workflows/test.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5eab77239..1e530fcf6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -26,7 +26,7 @@ jobs: fail-fast: false matrix: os: ["ubuntu-latest", "windows-2022"] - python: ${{ (github.event_name == 'pull_request' && fromJSON('["3.8", "3.12"]')) || fromJSON('["3.8", "3.9", "3.10", "3.12"]') }} + python: ${{ (github.event_name == 'pull_request' && fromJSON('["3.8", "3.12"]')) || fromJSON('["3.8", "3.9", "3.10", "3.11", "3.12"]') }} type: ["cli", "dapp", "data_dependency", diff --git a/.github/workflows/doctor.yml b/.github/workflows/doctor.yml index 555452871..e6b7a580b 100644 --- a/.github/workflows/doctor.yml +++ b/.github/workflows/doctor.yml @@ -23,7 +23,7 @@ jobs: fail-fast: false matrix: os: ["ubuntu-latest", "windows-2022"] - python: ["3.8", "3.9", "3.10", "3.11"] + python: ["3.8", "3.9", "3.10", "3.11", "3.12"] exclude: # strange failure - os: windows-2022 diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 0e291ec44..2087bd4d8 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -25,7 +25,7 @@ jobs: matrix: os: ["ubuntu-latest", "windows-2022"] type: ["unit", "integration", "tool"] - python: ${{ (github.event_name == 'pull_request' && fromJSON('["3.8", "3.12"]')) || fromJSON('["3.8", "3.9", "3.10", "3.12"]') }} + python: ${{ (github.event_name == 'pull_request' && fromJSON('["3.8", "3.12"]')) || fromJSON('["3.8", "3.9", "3.10", "3.11", "3.12"]') }} steps: - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python }} From 20b32df52eb9ade38a0d1c32bb8737ef0e4d6a5b Mon Sep 17 00:00:00 2001 From: alpharush <0xalpharush@protonmail.com> Date: Fri, 22 Mar 2024 15:37:32 -0500 Subject: [PATCH 11/12] use ValueError instead of broad exception --- slither/__main__.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/slither/__main__.py b/slither/__main__.py index 7b2c2ba8b..caaef5730 100644 --- a/slither/__main__.py +++ b/slither/__main__.py @@ -180,12 +180,12 @@ def get_detectors_and_printers() -> Tuple[ detector = None if not all(issubclass(detector, AbstractDetector) for detector in plugin_detectors): - raise Exception( + raise ValueError( f"Error when loading plugin {entry_point}, {detector} is not a detector" ) printer = None if not all(issubclass(printer, AbstractPrinter) for printer in plugin_printers): - raise Exception(f"Error when loading plugin {entry_point}, {printer} is not a printer") + raise ValueError(f"Error when loading plugin {entry_point}, {printer} is not a printer") # We convert those to lists in case someone returns a tuple detectors += list(plugin_detectors) @@ -215,7 +215,7 @@ def choose_detectors( if detector in detectors: detectors_to_run.append(detectors[detector]) else: - raise Exception(f"Error: {detector} is not a detector") + raise ValueError(f"Error: {detector} is not a detector") detectors_to_run = sorted(detectors_to_run, key=lambda x: x.IMPACT) return detectors_to_run @@ -263,7 +263,7 @@ def choose_printers( if printer in printers: printers_to_run.append(printers[printer]) else: - raise Exception(f"Error: {printer} is not a printer") + raise ValueError(f"Error: {printer} is not a printer") return printers_to_run @@ -655,7 +655,7 @@ def parse_args( args.json_types = set(args.json_types.split(",")) # type:ignore for json_type in args.json_types: if json_type not in JSON_OUTPUT_TYPES: - raise Exception(f'Error: "{json_type}" is not a valid JSON result output type.') + raise ValueError(f'Error: "{json_type}" is not a valid JSON result output type.') return args From 0b0f155779aae8cdca515f555babc0ce3f31ce41 Mon Sep 17 00:00:00 2001 From: Ngalim Siregar Date: Thu, 28 Mar 2024 22:31:12 +0700 Subject: [PATCH 12/12] Implement pytest parameterize on test_implicit_returns (#2350) --- tests/unit/slithir/test_implicit_returns.py | 124 ++++++++++---------- 1 file changed, 62 insertions(+), 62 deletions(-) diff --git a/tests/unit/slithir/test_implicit_returns.py b/tests/unit/slithir/test_implicit_returns.py index 2e72a010e..551f86918 100644 --- a/tests/unit/slithir/test_implicit_returns.py +++ b/tests/unit/slithir/test_implicit_returns.py @@ -10,7 +10,8 @@ from slither.slithir.operations import ( ) -def test_with_explicit_return(slither_from_solidity_source) -> None: +@pytest.mark.parametrize("legacy", [True, False]) +def test_with_explicit_return(slither_from_solidity_source, legacy) -> None: source = """ contract Contract { function foo(int x) public returns (int y) { @@ -22,31 +23,31 @@ def test_with_explicit_return(slither_from_solidity_source) -> None: } } """ - for legacy in [True, False]: - with slither_from_solidity_source(source, legacy=legacy) as slither: - c: Contract = slither.get_contract_from_name("Contract")[0] - f: Function = c.functions[0] - node_if: Node = f.nodes[1] - node_true = node_if.son_true - node_false = node_if.son_false - assert node_true.type == NodeType.RETURN - assert isinstance(node_true.irs[0], Return) - assert node_true.irs[0].values[0] == f.get_local_variable_from_name("x") - assert len(node_true.sons) == 0 - node_end_if = node_false.sons[0] - assert node_end_if.type == NodeType.ENDIF - assert node_end_if.sons[0].type == NodeType.RETURN - node_ret = node_end_if.sons[0] - assert isinstance(node_ret.irs[0], Return) - assert node_ret.irs[0].values[0] == f.get_local_variable_from_name("y") + with slither_from_solidity_source(source, legacy=legacy) as slither: + c: Contract = slither.get_contract_from_name("Contract")[0] + f: Function = c.functions[0] + node_if: Node = f.nodes[1] + node_true = node_if.son_true + node_false = node_if.son_false + assert node_true.type == NodeType.RETURN + assert isinstance(node_true.irs[0], Return) + assert node_true.irs[0].values[0] == f.get_local_variable_from_name("x") + assert len(node_true.sons) == 0 + node_end_if = node_false.sons[0] + assert node_end_if.type == NodeType.ENDIF + assert node_end_if.sons[0].type == NodeType.RETURN + node_ret = node_end_if.sons[0] + assert isinstance(node_ret.irs[0], Return) + assert node_ret.irs[0].values[0] == f.get_local_variable_from_name("y") -def test_return_multiple_with_struct(slither_from_solidity_source) -> None: +@pytest.mark.parametrize("legacy", [True, False]) +def test_return_multiple_with_struct(slither_from_solidity_source, legacy) -> None: source = """ struct St { uint256 value; } - + contract Contract { function foo(St memory x) public returns (St memory y, uint256 z) { z = x.value; @@ -54,16 +55,15 @@ def test_return_multiple_with_struct(slither_from_solidity_source) -> None: } } """ - for legacy in [True, False]: - with slither_from_solidity_source(source, legacy=legacy) as slither: - c: Contract = slither.get_contract_from_name("Contract")[0] - f: Function = c.functions[0] - assert len(f.nodes) == 4 - node = f.nodes[3] - assert node.type == NodeType.RETURN - assert isinstance(node.irs[0], Return) - assert node.irs[0].values[0] == f.get_local_variable_from_name("y") - assert node.irs[0].values[1] == f.get_local_variable_from_name("z") + with slither_from_solidity_source(source, legacy=legacy) as slither: + c: Contract = slither.get_contract_from_name("Contract")[0] + f: Function = c.functions[0] + assert len(f.nodes) == 4 + node = f.nodes[3] + assert node.type == NodeType.RETURN + assert isinstance(node.irs[0], Return) + assert node.irs[0].values[0] == f.get_local_variable_from_name("y") + assert node.irs[0].values[1] == f.get_local_variable_from_name("z") def test_nested_ifs_with_loop_legacy(slither_from_solidity_source) -> None: @@ -149,7 +149,8 @@ def test_nested_ifs_with_loop_compact(slither_from_solidity_source) -> None: @pytest.mark.xfail # Explicit returns inside assembly are currently not parsed as return nodes -def test_assembly_switch_cases(slither_from_solidity_source): +@pytest.mark.parametrize("legacy", [True, False]) +def test_assembly_switch_cases(slither_from_solidity_source, legacy): source = """ contract Contract { function foo(uint a) public returns (uint x) { @@ -164,28 +165,28 @@ def test_assembly_switch_cases(slither_from_solidity_source): } } """ - for legacy in [True, False]: - with slither_from_solidity_source(source, solc_version="0.8.0", legacy=legacy) as slither: - c: Contract = slither.get_contract_from_name("Contract")[0] - f = c.functions[0] - if legacy: - node = f.nodes[2] - assert node.type == NodeType.RETURN - assert isinstance(node.irs[0], Return) - assert node.irs[0].values[0] == f.get_local_variable_from_name("x") - else: - node_end_if = f.nodes[5] - assert node_end_if.sons[0].type == NodeType.RETURN - node_implicit = node_end_if.sons[0] - assert isinstance(node_implicit.irs[0], Return) - assert node_implicit.irs[0].values[0] == f.get_local_variable_from_name("x") - # This part will fail until issue #1927 is fixed - node_explicit = f.nodes[10] - assert node_explicit.type == NodeType.RETURN - assert len(node_explicit.sons) == 0 + with slither_from_solidity_source(source, solc_version="0.8.0", legacy=legacy) as slither: + c: Contract = slither.get_contract_from_name("Contract")[0] + f = c.functions[0] + if legacy: + node = f.nodes[2] + assert node.type == NodeType.RETURN + assert isinstance(node.irs[0], Return) + assert node.irs[0].values[0] == f.get_local_variable_from_name("x") + else: + node_end_if = f.nodes[5] + assert node_end_if.sons[0].type == NodeType.RETURN + node_implicit = node_end_if.sons[0] + assert isinstance(node_implicit.irs[0], Return) + assert node_implicit.irs[0].values[0] == f.get_local_variable_from_name("x") + # This part will fail until issue #1927 is fixed + node_explicit = f.nodes[10] + assert node_explicit.type == NodeType.RETURN + assert len(node_explicit.sons) == 0 -def test_issue_1846_ternary_in_ternary(slither_from_solidity_source): +@pytest.mark.parametrize("legacy", [True, False]) +def test_issue_1846_ternary_in_ternary(slither_from_solidity_source, legacy): source = """ contract Contract { function foo(uint x) public returns (uint y) { @@ -193,14 +194,13 @@ def test_issue_1846_ternary_in_ternary(slither_from_solidity_source): } } """ - for legacy in [True, False]: - with slither_from_solidity_source(source, legacy=legacy) as slither: - c: Contract = slither.get_contract_from_name("Contract")[0] - f = c.functions[0] - node_end_if = f.nodes[3] - assert node_end_if.type == NodeType.ENDIF - assert len(node_end_if.sons) == 1 - node_ret = node_end_if.sons[0] - assert node_ret.type == NodeType.RETURN - assert isinstance(node_ret.irs[0], Return) - assert node_ret.irs[0].values[0] == f.get_local_variable_from_name("y") + with slither_from_solidity_source(source, legacy=legacy) as slither: + c: Contract = slither.get_contract_from_name("Contract")[0] + f = c.functions[0] + node_end_if = f.nodes[3] + assert node_end_if.type == NodeType.ENDIF + assert len(node_end_if.sons) == 1 + node_ret = node_end_if.sons[0] + assert node_ret.type == NodeType.RETURN + assert isinstance(node_ret.irs[0], Return) + assert node_ret.irs[0].values[0] == f.get_local_variable_from_name("y")