From 114dc2eecaff3262472645f19f961971ee448de5 Mon Sep 17 00:00:00 2001 From: Alexis Date: Mon, 8 Apr 2024 11:43:42 +0200 Subject: [PATCH 01/17] Fix #2266 where the type of functions when used as RValues was not properly printed. --- slither/slithir/operations/assignment.py | 13 +++++++++++-- .../test_data/test_printer_slithir/bug-2266.sol | 13 +++++++++++++ tests/e2e/printers/test_printers.py | 16 ++++++++++++++++ 3 files changed, 40 insertions(+), 2 deletions(-) create mode 100644 tests/e2e/printers/test_data/test_printer_slithir/bug-2266.sol diff --git a/slither/slithir/operations/assignment.py b/slither/slithir/operations/assignment.py index 1f29ceb7b..ab6637faa 100644 --- a/slither/slithir/operations/assignment.py +++ b/slither/slithir/operations/assignment.py @@ -45,10 +45,19 @@ class Assignment(OperationWithLValue): def __str__(self) -> str: lvalue = self.lvalue + + # When rvalues are functions, we want to properly display their return type + # Fix: https://github.com/crytic/slither/issues/2266 + if isinstance(self.rvalue.type, list): + rvalue_type = ",".join(f"{rvalue_type}" for rvalue_type in self.rvalue.type) + else: + rvalue_type = f"{self.rvalue.type}" + assert lvalue if lvalue and isinstance(lvalue, ReferenceVariable): points = lvalue.points_to while isinstance(points, ReferenceVariable): points = points.points_to - return f"{lvalue}({lvalue.type}) (->{points}) := {self.rvalue}({self.rvalue.type})" - return f"{lvalue}({lvalue.type}) := {self.rvalue}({self.rvalue.type})" + return f"{lvalue}({lvalue.type}) (->{points}) := {self.rvalue}({rvalue_type})" + + return f"{lvalue}({lvalue.type}) := {self.rvalue}({rvalue_type})" diff --git a/tests/e2e/printers/test_data/test_printer_slithir/bug-2266.sol b/tests/e2e/printers/test_data/test_printer_slithir/bug-2266.sol new file mode 100644 index 000000000..5c11a2914 --- /dev/null +++ b/tests/e2e/printers/test_data/test_printer_slithir/bug-2266.sol @@ -0,0 +1,13 @@ +pragma solidity ^0.8.0; + +contract A { + function add(uint256 a, uint256 b) public returns (uint256) { + return a + b; + } +} + +contract B is A { + function assignFunction() public { + function(uint256, uint256) returns (uint256) myFunction = super.add; + } +} \ No newline at end of file diff --git a/tests/e2e/printers/test_printers.py b/tests/e2e/printers/test_printers.py index 26429d338..b7cd4d3b6 100644 --- a/tests/e2e/printers/test_printers.py +++ b/tests/e2e/printers/test_printers.py @@ -7,6 +7,7 @@ from crytic_compile.platform.solc_standard_json import SolcStandardJson from slither import Slither from slither.printers.inheritance.inheritance_graph import PrinterInheritanceGraph +from slither.printers.summary.slithir import PrinterSlithIR TEST_DATA_DIR = Path(__file__).resolve().parent / "test_data" @@ -34,3 +35,18 @@ def test_inheritance_printer(solc_binary_path) -> None: assert counter["B -> A"] == 2 assert counter["C -> A"] == 1 + + +def test_slithir_printer(solc_binary_path) -> None: + solc_path = solc_binary_path("0.8.0") + standard_json = SolcStandardJson() + standard_json.add_source_file( + Path(TEST_DATA_DIR, "test_printer_slithir", "bug-2266.sol").as_posix() + ) + compilation = CryticCompile(standard_json, solc=solc_path) + slither = Slither(compilation) + + printer = PrinterSlithIR(slither, logger=None) + output = printer.output("test_printer_slithir.dot") + + assert "slither.core.solidity_types" not in output.data["description"] From f3f4b2084c053c37099a4f56021aeeb5cd76d9c4 Mon Sep 17 00:00:00 2001 From: dm Date: Wed, 5 Jun 2024 16:37:48 +0200 Subject: [PATCH 02/17] Update tests/e2e/printers/test_printers.py Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- tests/e2e/printers/test_printers.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/e2e/printers/test_printers.py b/tests/e2e/printers/test_printers.py index fc7a22378..aa5d7f8a4 100644 --- a/tests/e2e/printers/test_printers.py +++ b/tests/e2e/printers/test_printers.py @@ -35,7 +35,6 @@ def test_inheritance_printer(solc_binary_path) -> None: assert counter["B -> A"] == 2 assert counter["C -> A"] == 1 - # Let also test the include/exclude interface behavior # Check that the interface is not included assert "MyInterfaceX" not in content From ecca8da74ec9cee207b2bbce2dc87a8e3adbfeea Mon Sep 17 00:00:00 2001 From: Alexis Date: Tue, 11 Jun 2024 12:05:50 +0200 Subject: [PATCH 03/17] Improve performances of offsets references. --- slither/core/slither_core.py | 134 +++++++++++------- slither/core/source_mapping/source_mapping.py | 6 +- 2 files changed, 87 insertions(+), 53 deletions(-) diff --git a/slither/core/slither_core.py b/slither/core/slither_core.py index 1206e564b..f5e36ace3 100644 --- a/slither/core/slither_core.py +++ b/slither/core/slither_core.py @@ -8,7 +8,7 @@ import pathlib import posixpath import re from collections import defaultdict -from typing import Optional, Dict, List, Set, Union, Tuple +from typing import Optional, Dict, List, Set, Union, Tuple, TypeVar from crytic_compile import CryticCompile from crytic_compile.utils.naming import Filename @@ -88,6 +88,7 @@ class SlitherCore(Context): self._contracts: List[Contract] = [] self._contracts_derived: List[Contract] = [] + self._offset_to_min_offset: Optional[Dict[Filename, Dict[int, Set[int]]]] = None self._offset_to_objects: Optional[Dict[Filename, Dict[int, Set[SourceMapping]]]] = None self._offset_to_references: Optional[Dict[Filename, Dict[int, Set[Source]]]] = None self._offset_to_implementations: Optional[Dict[Filename, Dict[int, Set[Source]]]] = None @@ -195,69 +196,70 @@ class SlitherCore(Context): for f in c.functions: f.cfg_to_dot(os.path.join(d, f"{c.name}.{f.name}.dot")) - def offset_to_objects(self, filename_str: str, offset: int) -> Set[SourceMapping]: - if self._offset_to_objects is None: - self._compute_offsets_to_ref_impl_decl() - filename: Filename = self.crytic_compile.filename_lookup(filename_str) - return self._offset_to_objects[filename][offset] - def _compute_offsets_from_thing(self, thing: SourceMapping): definition = get_definition(thing, self.crytic_compile) references = get_references(thing) implementations = get_all_implementations(thing, self.contracts) + # Create the offset mapping for offset in range(definition.start, definition.end + 1): - if ( - isinstance(thing, (TopLevel, Contract)) - or ( - isinstance(thing, FunctionContract) - and thing.contract_declarer == thing.contract - ) - or (isinstance(thing, ContractLevel) and not isinstance(thing, FunctionContract)) - ): + self._offset_to_min_offset[definition.filename][offset].add(definition.start) - self._offset_to_objects[definition.filename][offset].add(thing) + is_declared_function = ( + isinstance(thing, FunctionContract) and thing.contract_declarer == thing.contract + ) - self._offset_to_definitions[definition.filename][offset].add(definition) - self._offset_to_implementations[definition.filename][offset].update(implementations) - self._offset_to_references[definition.filename][offset] |= set(references) + should_add_to_objects = ( + isinstance(thing, (TopLevel, Contract)) + or is_declared_function + or (isinstance(thing, ContractLevel) and not isinstance(thing, FunctionContract)) + ) + + if should_add_to_objects: + self._offset_to_objects[definition.filename][definition.start].add(thing) + + self._offset_to_definitions[definition.filename][definition.start].add(definition) + self._offset_to_implementations[definition.filename][definition.start].update( + implementations + ) + self._offset_to_references[definition.filename][definition.start] |= set(references) + + # For references + should_add_to_objects = ( + isinstance(thing, TopLevel) + or is_declared_function + or (isinstance(thing, ContractLevel) and not isinstance(thing, FunctionContract)) + ) for ref in references: for offset in range(ref.start, ref.end + 1): - is_declared_function = ( - isinstance(thing, FunctionContract) - and thing.contract_declarer == thing.contract - ) + self._offset_to_min_offset[definition.filename][offset].add(ref.start) + + if should_add_to_objects: + self._offset_to_objects[definition.filename][ref.start].add(thing) + + if is_declared_function: + # Only show the nearest lexical definition for declared contract-level functions if ( - isinstance(thing, TopLevel) - or is_declared_function - or ( - isinstance(thing, ContractLevel) and not isinstance(thing, FunctionContract) - ) + thing.contract.source_mapping.start + < ref.start + < thing.contract.source_mapping.end ): - self._offset_to_objects[definition.filename][offset].add(thing) - - if is_declared_function: - # Only show the nearest lexical definition for declared contract-level functions - if ( - thing.contract.source_mapping.start - < offset - < thing.contract.source_mapping.end - ): - self._offset_to_definitions[ref.filename][offset].add(definition) + self._offset_to_definitions[ref.filename][ref.start].add(definition) - else: - self._offset_to_definitions[ref.filename][offset].add(definition) + else: + self._offset_to_definitions[ref.filename][ref.start].add(definition) - self._offset_to_implementations[ref.filename][offset].update(implementations) - self._offset_to_references[ref.filename][offset] |= set(references) + self._offset_to_implementations[ref.filename][ref.start].update(implementations) + self._offset_to_references[ref.filename][ref.start] |= set(references) def _compute_offsets_to_ref_impl_decl(self): # pylint: disable=too-many-branches self._offset_to_references = defaultdict(lambda: defaultdict(lambda: set())) self._offset_to_definitions = defaultdict(lambda: defaultdict(lambda: set())) self._offset_to_implementations = defaultdict(lambda: defaultdict(lambda: set())) self._offset_to_objects = defaultdict(lambda: defaultdict(lambda: set())) + self._offset_to_min_offset = defaultdict(lambda: defaultdict(lambda: set())) for compilation_unit in self._compilation_units: for contract in compilation_unit.contracts: @@ -308,23 +310,59 @@ class SlitherCore(Context): for pragma in compilation_unit.pragma_directives: self._compute_offsets_from_thing(pragma) + T = TypeVar("T", Source, SourceMapping) + + def _get_offset( + self, mapping: Dict[Filename, Dict[int, Set[T]]], filename_str: str, offset: int + ) -> Set[T]: + """Get the Source/SourceMapping referenced by the offset. + + For performance reasons, references are only stored once at the lowest offset. + It uses the _offset_to_min_offset mapping to retrieve the correct offsets. + As multiple definitions can be related to the same offset, we retrieve all of them. + + :param mapping: Mapping to search for (objects. references, ...) + :param filename_str: Filename to consider + :param offset: Look-up offset + :raises IndexError: When the start offset is not found + :return: The corresponding set of Source/SourceMapping + """ + filename: Filename = self.crytic_compile.filename_lookup(filename_str) + + start_offsets = self._offset_to_min_offset[filename][offset] + if not start_offsets: + msg = f"Unable to find reference for offset {offset}" + raise IndexError(msg) + + results = set() + for start_offset in start_offsets: + results |= mapping[filename][start_offset] + + return results + def offset_to_references(self, filename_str: str, offset: int) -> Set[Source]: if self._offset_to_references is None: self._compute_offsets_to_ref_impl_decl() - filename: Filename = self.crytic_compile.filename_lookup(filename_str) - return self._offset_to_references[filename][offset] + + return self._get_offset(self._offset_to_references, filename_str, offset) def offset_to_implementations(self, filename_str: str, offset: int) -> Set[Source]: if self._offset_to_implementations is None: self._compute_offsets_to_ref_impl_decl() - filename: Filename = self.crytic_compile.filename_lookup(filename_str) - return self._offset_to_implementations[filename][offset] + + return self._get_offset(self._offset_to_implementations, filename_str, offset) def offset_to_definitions(self, filename_str: str, offset: int) -> Set[Source]: if self._offset_to_definitions is None: self._compute_offsets_to_ref_impl_decl() - filename: Filename = self.crytic_compile.filename_lookup(filename_str) - return self._offset_to_definitions[filename][offset] + + return self._get_offset(self._offset_to_definitions, filename_str, offset) + + def offset_to_objects(self, filename_str: str, offset: int) -> Set[SourceMapping]: + if self._offset_to_objects is None: + self._compute_offsets_to_ref_impl_decl() + + return self._get_offset(self._offset_to_objects, filename_str, offset) # endregion ################################################################################### diff --git a/slither/core/source_mapping/source_mapping.py b/slither/core/source_mapping/source_mapping.py index 41841f1e8..355aa5538 100644 --- a/slither/core/source_mapping/source_mapping.py +++ b/slither/core/source_mapping/source_mapping.py @@ -112,12 +112,8 @@ class Source: try: return ( self.start == other.start - and self.length == other.length - and self.filename == other.filename + and self.filename.relative == other.filename.relative and self.is_dependency == other.is_dependency - and self.lines == other.lines - and self.starting_column == other.starting_column - and self.ending_column == other.ending_column and self.end == other.end ) except AttributeError: From bacd8c0222af135e43b91f3c8dd564b8e6cdf416 Mon Sep 17 00:00:00 2001 From: Feist Josselin Date: Fri, 28 Jun 2024 11:14:41 +0200 Subject: [PATCH 04/17] Update FUNDING.json --- FUNDING.json | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/FUNDING.json b/FUNDING.json index bbda1e452..5caad61f5 100644 --- a/FUNDING.json +++ b/FUNDING.json @@ -4,4 +4,9 @@ "ownedBy": "0xc44F30Be3eBBEfdDBB5a85168710b4f0e18f4Ff0" } } + "drips": { + "ethereum": { + "ownedBy": "0x5e2BA02F62bD4efa939e3B80955bBC21d015DbA0" + } + } } From 9a5b4f5cff9db6475c5badff18f9882180d5f344 Mon Sep 17 00:00:00 2001 From: Feist Josselin Date: Fri, 28 Jun 2024 11:15:05 +0200 Subject: [PATCH 05/17] Update FUNDING.json --- FUNDING.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/FUNDING.json b/FUNDING.json index 5caad61f5..189cf2f95 100644 --- a/FUNDING.json +++ b/FUNDING.json @@ -3,7 +3,7 @@ "op-mainnet": { "ownedBy": "0xc44F30Be3eBBEfdDBB5a85168710b4f0e18f4Ff0" } - } + }, "drips": { "ethereum": { "ownedBy": "0x5e2BA02F62bD4efa939e3B80955bBC21d015DbA0" From fda26fa865f33c6bab66a4257bc17ce4ff87e7b2 Mon Sep 17 00:00:00 2001 From: Daniel Bast <2790401+dbast@users.noreply.github.com> Date: Fri, 2 Aug 2024 19:18:21 +0200 Subject: [PATCH 06/17] Enable running slither as pre-commit hook --- .pre-commit-hooks.yaml | 7 +++++++ README.md | 7 +++++++ 2 files changed, 14 insertions(+) create mode 100644 .pre-commit-hooks.yaml diff --git a/.pre-commit-hooks.yaml b/.pre-commit-hooks.yaml new file mode 100644 index 000000000..0f1b2fbee --- /dev/null +++ b/.pre-commit-hooks.yaml @@ -0,0 +1,7 @@ +- id: slither + name: Slither + description: Run Slither on your project + entry: slither . + pass_filenames: false + language: python + files: \.sol$ diff --git a/README.md b/README.md index 515d6a9f7..5dc7de20f 100644 --- a/README.md +++ b/README.md @@ -102,6 +102,13 @@ docker run -it -v /home/share:/share trailofbits/eth-security-toolbox ### Integration * For GitHub action integration, use [slither-action](https://github.com/marketplace/actions/slither-action). +* For pre-commit integratio, use (replace `$GIT_TAG` with real tag) + ``` + - repo: https://github.com/crytic/slither + rev: $GIT_TAG + hooks: + - slither + ``` * 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`) From f008437703aca24f4437dee458929c130e7d2cce Mon Sep 17 00:00:00 2001 From: Daniel Bast <2790401+dbast@users.noreply.github.com> Date: Fri, 2 Aug 2024 20:58:22 +0200 Subject: [PATCH 07/17] Specify language for code block in readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5dc7de20f..c7a466f80 100644 --- a/README.md +++ b/README.md @@ -103,7 +103,7 @@ docker run -it -v /home/share:/share trailofbits/eth-security-toolbox * For GitHub action integration, use [slither-action](https://github.com/marketplace/actions/slither-action). * For pre-commit integratio, use (replace `$GIT_TAG` with real tag) - ``` + ```YAML - repo: https://github.com/crytic/slither rev: $GIT_TAG hooks: From ee59976d68bb5ecc8cc352c4956a69c249280c23 Mon Sep 17 00:00:00 2001 From: Daniel Bast <2790401+dbast@users.noreply.github.com> Date: Sun, 11 Aug 2024 08:18:40 +0200 Subject: [PATCH 08/17] Specify folder via args to be easier to overwrite/configure --- .pre-commit-hooks.yaml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.pre-commit-hooks.yaml b/.pre-commit-hooks.yaml index 0f1b2fbee..d561ca429 100644 --- a/.pre-commit-hooks.yaml +++ b/.pre-commit-hooks.yaml @@ -1,7 +1,9 @@ - id: slither name: Slither description: Run Slither on your project - entry: slither . + entry: slither + args: + - . pass_filenames: false language: python files: \.sol$ From 177fd1a02edce46b4416958851aa8d0ae9848301 Mon Sep 17 00:00:00 2001 From: Daniel Bast <2790401+dbast@users.noreply.github.com> Date: Tue, 13 Aug 2024 17:33:23 +0200 Subject: [PATCH 09/17] README.md aktualisieren MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Emilio López <2642849+elopez@users.noreply.github.com> --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c7a466f80..a85d85369 100644 --- a/README.md +++ b/README.md @@ -102,7 +102,7 @@ docker run -it -v /home/share:/share trailofbits/eth-security-toolbox ### Integration * For GitHub action integration, use [slither-action](https://github.com/marketplace/actions/slither-action). -* For pre-commit integratio, use (replace `$GIT_TAG` with real tag) +* For pre-commit integration, use (replace `$GIT_TAG` with real tag) ```YAML - repo: https://github.com/crytic/slither rev: $GIT_TAG From d3dadee6e08922fbe68ac742d134be8dcb9cbc19 Mon Sep 17 00:00:00 2001 From: Daniel Bast <2790401+dbast@users.noreply.github.com> Date: Sat, 17 Aug 2024 22:54:48 +0200 Subject: [PATCH 10/17] Fix --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a85d85369..660f4f8e8 100644 --- a/README.md +++ b/README.md @@ -107,7 +107,7 @@ docker run -it -v /home/share:/share trailofbits/eth-security-toolbox - repo: https://github.com/crytic/slither rev: $GIT_TAG hooks: - - slither + - id: slither ``` * 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`) From aeeb2d368802844733671e35200b30b5f5bdcf5c Mon Sep 17 00:00:00 2001 From: alpharush <0xalpharush@protonmail.com> Date: Fri, 23 Aug 2024 08:20:00 -0500 Subject: [PATCH 11/17] prepare 0.10.4 release --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index e5739804c..ef9d81f20 100644 --- a/setup.py +++ b/setup.py @@ -8,7 +8,7 @@ setup( description="Slither is a Solidity and Vyper static analysis framework written in Python 3.", url="https://github.com/crytic/slither", author="Trail of Bits", - version="0.10.3", + version="0.10.4", packages=find_packages(), python_requires=">=3.8", install_requires=[ From 7bfa87ad76e39f7a046b20a7342e1e4c8239ddce Mon Sep 17 00:00:00 2001 From: Simone Date: Mon, 9 Sep 2024 10:38:28 +0200 Subject: [PATCH 12/17] Add support custom errors in require --- .../core/declarations/solidity_variables.py | 1 + .../require-error.sol-0.8.27-compact.zip | Bin 0 -> 3685 bytes .../require-error.sol-0.8.27-compact.json | 5 +++++ .../solc_parsing/test_data/require-error.sol | 20 ++++++++++++++++++ 4 files changed, 26 insertions(+) create mode 100644 tests/e2e/solc_parsing/test_data/compile/require-error.sol-0.8.27-compact.zip create mode 100644 tests/e2e/solc_parsing/test_data/expected/require-error.sol-0.8.27-compact.json create mode 100644 tests/e2e/solc_parsing/test_data/require-error.sol diff --git a/slither/core/declarations/solidity_variables.py b/slither/core/declarations/solidity_variables.py index 8094ab7c3..ce8477ab2 100644 --- a/slither/core/declarations/solidity_variables.py +++ b/slither/core/declarations/solidity_variables.py @@ -50,6 +50,7 @@ SOLIDITY_FUNCTIONS: Dict[str, List[str]] = { "assert(bool)": [], "require(bool)": [], "require(bool,string)": [], + "require(bool,error)": [], # Solidity 0.8.26 via-ir and Solidity >= 0.8.27 "revert()": [], "revert(string)": [], "revert ": [], diff --git a/tests/e2e/solc_parsing/test_data/compile/require-error.sol-0.8.27-compact.zip b/tests/e2e/solc_parsing/test_data/compile/require-error.sol-0.8.27-compact.zip new file mode 100644 index 0000000000000000000000000000000000000000..63aa223b37ffa0e9d4e552aa106161a54fd79272 GIT binary patch literal 3685 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 literal 0 HcmV?d00001 diff --git a/tests/e2e/solc_parsing/test_data/expected/require-error.sol-0.8.27-compact.json b/tests/e2e/solc_parsing/test_data/expected/require-error.sol-0.8.27-compact.json new file mode 100644 index 000000000..3c3089c04 --- /dev/null +++ b/tests/e2e/solc_parsing/test_data/expected/require-error.sol-0.8.27-compact.json @@ -0,0 +1,5 @@ +{ + "TestToken": { + "transferWithRequireError(address,uint256)": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: EXPRESSION 1\n\"];\n1->2;\n2[label=\"Node Type: EXPRESSION 2\n\"];\n2->3;\n3[label=\"Node Type: EXPRESSION 3\n\"];\n}\n" + } +} \ No newline at end of file diff --git a/tests/e2e/solc_parsing/test_data/require-error.sol b/tests/e2e/solc_parsing/test_data/require-error.sol new file mode 100644 index 000000000..97c8ecac4 --- /dev/null +++ b/tests/e2e/solc_parsing/test_data/require-error.sol @@ -0,0 +1,20 @@ +pragma solidity 0.8.27; + +/// Insufficient balance for transfer. Needed `required` but only +/// `available` available. +/// @param available balance available. +/// @param required requested amount to transfer. +error InsufficientBalance(uint256 available, uint256 required); + +contract TestToken { + mapping(address => uint) balance; + function transferWithRequireError(address to, uint256 amount) public { + require( + balance[msg.sender] >= amount, + InsufficientBalance(balance[msg.sender], amount) + ); + balance[msg.sender] -= amount; + balance[to] += amount; + } + // ... +} From 5d55bc5752070f28330acafe890100b48b59aff0 Mon Sep 17 00:00:00 2001 From: Simone Date: Mon, 9 Sep 2024 10:44:29 +0200 Subject: [PATCH 13/17] Add test --- tests/e2e/solc_parsing/test_ast_parsing.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/e2e/solc_parsing/test_ast_parsing.py b/tests/e2e/solc_parsing/test_ast_parsing.py index ca3872f8c..6ec7b6fbd 100644 --- a/tests/e2e/solc_parsing/test_ast_parsing.py +++ b/tests/e2e/solc_parsing/test_ast_parsing.py @@ -475,6 +475,7 @@ ALL_TESTS = [ Test("solidity-0.8.24.sol", ["0.8.24"], solc_args="--evm-version cancun"), Test("scope/inherited_function_scope.sol", ["0.8.24"]), Test("using_for_global_user_defined_operator_1.sol", ["0.8.24"]), + Test("require-error.sol", ["0.8.27"]), ] # create the output folder if needed try: From 109c31adcf0b31f4a45c1ce63ab97db63496844e Mon Sep 17 00:00:00 2001 From: Simone Date: Wed, 11 Sep 2024 14:01:59 +0200 Subject: [PATCH 14/17] Add require with error --- slither/core/cfg/node.py | 3 ++- slither/printers/summary/require_calls.py | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/slither/core/cfg/node.py b/slither/core/cfg/node.py index 87d0e16a2..f600d0f43 100644 --- a/slither/core/cfg/node.py +++ b/slither/core/cfg/node.py @@ -529,7 +529,8 @@ class Node(SourceMapping): # pylint: disable=too-many-public-methods bool: True if the node has a require or assert call """ return any( - c.name in ["require(bool)", "require(bool,string)", "assert(bool)"] + c.name + in ["require(bool)", "require(bool,string)", "require(bool,error)", "assert(bool)"] for c in self.internal_calls ) diff --git a/slither/printers/summary/require_calls.py b/slither/printers/summary/require_calls.py index 7823de160..ae79e9ed6 100644 --- a/slither/printers/summary/require_calls.py +++ b/slither/printers/summary/require_calls.py @@ -11,6 +11,7 @@ require_or_assert = [ SolidityFunction("assert(bool)"), SolidityFunction("require(bool)"), SolidityFunction("require(bool,string)"), + SolidityFunction("require(bool,error)"), ] From 2c792b2b73c6c1fbbf5464bd1f9fc8ccedf0c0bf Mon Sep 17 00:00:00 2001 From: alpharush <0xalpharush@protonmail.com> Date: Wed, 11 Sep 2024 10:25:38 -0500 Subject: [PATCH 15/17] fix: breaking change in upload artifact that ignores hidden files --- .github/actions/upload-coverage/action.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/actions/upload-coverage/action.yml b/.github/actions/upload-coverage/action.yml index 02fb82f65..541b93111 100644 --- a/.github/actions/upload-coverage/action.yml +++ b/.github/actions/upload-coverage/action.yml @@ -27,4 +27,5 @@ runs: path: | .coverage.* *.lcov - if-no-files-found: ignore \ No newline at end of file + if-no-files-found: ignore + include-hidden-files: true From 584d64c73fff7e3d7f08d952725ed4645bcc8897 Mon Sep 17 00:00:00 2001 From: Hamdi Allam Date: Wed, 25 Sep 2024 14:43:01 -0700 Subject: [PATCH 16/17] add event as value member --- slither/slithir/operations/member.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/slither/slithir/operations/member.py b/slither/slithir/operations/member.py index 0942813cf..193f6f177 100644 --- a/slither/slithir/operations/member.py +++ b/slither/slithir/operations/member.py @@ -1,5 +1,5 @@ from typing import List, Union -from slither.core.declarations import Contract, Function +from slither.core.declarations import Contract, Function, Event from slither.core.declarations.custom_error import CustomError from slither.core.declarations.enum import Enum from slither.core.declarations.solidity_import_placeholder import SolidityImportPlaceHolder @@ -33,14 +33,14 @@ class Member(OperationWithLValue): # Can be an ElementaryType because of bytes.concat, string.concat assert is_valid_rvalue(variable_left) or isinstance( variable_left, - (Contract, Enum, Function, CustomError, SolidityImportPlaceHolder, ElementaryType), + (Contract, Enum, Function, Event, CustomError, SolidityImportPlaceHolder, ElementaryType), ) assert isinstance(variable_right, Constant) assert isinstance(result, ReferenceVariable) super().__init__() self._variable_left: Union[ - RVALUE, Contract, Enum, Function, CustomError, SolidityImportPlaceHolder, ElementaryType + RVALUE, Contract, Enum, Function, Event, CustomError, SolidityImportPlaceHolder, ElementaryType ] = variable_left self._variable_right = variable_right self._lvalue = result From 2075e6f1e9f7f5ba14364db256b2e6a3517629ec Mon Sep 17 00:00:00 2001 From: alpharush <0xalpharush@protonmail.com> Date: Thu, 26 Sep 2024 18:02:35 -0500 Subject: [PATCH 17/17] fmt --- slither/slithir/operations/member.py | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/slither/slithir/operations/member.py b/slither/slithir/operations/member.py index 193f6f177..55979572c 100644 --- a/slither/slithir/operations/member.py +++ b/slither/slithir/operations/member.py @@ -33,14 +33,29 @@ class Member(OperationWithLValue): # Can be an ElementaryType because of bytes.concat, string.concat assert is_valid_rvalue(variable_left) or isinstance( variable_left, - (Contract, Enum, Function, Event, CustomError, SolidityImportPlaceHolder, ElementaryType), + ( + Contract, + Enum, + Function, + Event, + CustomError, + SolidityImportPlaceHolder, + ElementaryType, + ), ) assert isinstance(variable_right, Constant) assert isinstance(result, ReferenceVariable) super().__init__() self._variable_left: Union[ - RVALUE, Contract, Enum, Function, Event, CustomError, SolidityImportPlaceHolder, ElementaryType + RVALUE, + Contract, + Enum, + Function, + Event, + CustomError, + SolidityImportPlaceHolder, + ElementaryType, ] = variable_left self._variable_right = variable_right self._lvalue = result