From 256ca32a8b6737e29d2af92e00a28679331ae866 Mon Sep 17 00:00:00 2001 From: Simone Date: Tue, 19 Sep 2023 15:59:19 +0200 Subject: [PATCH 1/4] Scope imports only correct items --- slither/core/scope/scope.py | 98 ++++++++++++++++++- .../slither_compilation_unit_solc.py | 44 +++++---- 2 files changed, 120 insertions(+), 22 deletions(-) diff --git a/slither/core/scope/scope.py b/slither/core/scope/scope.py index 937a051361..4f8910a52e 100644 --- a/slither/core/scope/scope.py +++ b/slither/core/scope/scope.py @@ -1,4 +1,4 @@ -from typing import List, Any, Dict, Optional, Union, Set, TypeVar, Callable +from typing import List, Any, Dict, Optional, Union, Set, TypeVar, Callable, Tuple from crytic_compile import CompilationUnit from crytic_compile.source_unit import SourceUnit @@ -27,7 +27,7 @@ def _dict_contain(d1: Dict, d2: Dict) -> bool: class FileScope: def __init__(self, filename: Filename) -> None: self.filename = filename - self.accessible_scopes: List[FileScope] = [] + self.accessible_scopes: List[FileScopeToImport] = [] self.contracts: Dict[str, Contract] = {} # Custom error are a list instead of a dict @@ -238,3 +238,97 @@ def __hash__(self) -> int: return hash(self.filename.relative) # endregion + +class FileScopeToImport: + def __init__(self, filescope: FileScope, items_to_import: List[str]) -> None: + self.filescope = filescope + self.items_to_import = items_to_import + + @property + def contracts(self) -> Dict[str, Contract]: + if len(self.items_to_import) != 0: + result = {} + for name, contract in self.filescope.contracts.items(): + if name in self.items_to_import: + result[name] = contract + return result + return self.filescope.contracts + + @property + def custom_errors(self) -> Set[CustomErrorTopLevel]: + if len(self.items_to_import) != 0: + result = set() + for custom_error in self.filescope.custom_errors: + if custom_error.name in self.items_to_import: + result.add(custom_error) + return result + return self.filescope.custom_errors + + @property + def enums(self) -> Dict[str, EnumTopLevel]: + if len(self.items_to_import) != 0: + result = {} + for name, enum in self.filescope.enums.items(): + if name in self.items_to_import: + result[name] = enum + return result + return self.filescope.enums + + @property + def functions(self) -> Set[FunctionTopLevel]: + if len(self.items_to_import) != 0: + result = set() + for function in self.filescope.functions: + if function.name in self.items_to_import: + result.add(function) + return result + return self.filescope.functions + + @property + def using_for_directives(self) -> Set[UsingForTopLevel]: + # TODO check it's correct + if len(self.items_to_import) == 0: + return self.filescope.using_for_directives + return set() + + @property + def imports(self) -> Set[Import]: + # TODO check it's correct + if len(self.items_to_import) == 0: + return self.filescope.imports + return set() + + @property + def pragmas(self) -> Set[Pragma]: + # TODO check it's correct + return self.filescope.pragmas + + @property + def structures(self) -> Dict[str, StructureTopLevel]: + if len(self.items_to_import) != 0: + result = {} + for name, structure in self.filescope.structures.items(): + if name in self.items_to_import: + result[name] = structure + return result + return self.filescope.structures + + @property + def variables(self) -> Dict[str, TopLevelVariable]: + if len(self.items_to_import) != 0: + result = {} + for name, variable in self.filescope.variables.items(): + if name in self.items_to_import: + result[name] = variable + return result + return self.filescope.variables + + @property + def renaming(self) -> Dict[str, str]: + # TODO check it's correct + return self.filescope.renaming + + @property + def type_aliases(self) -> Dict[str, TypeAlias]: + # TODO check it's correct + return self.filescope.type_aliases diff --git a/slither/solc_parsing/slither_compilation_unit_solc.py b/slither/solc_parsing/slither_compilation_unit_solc.py index 2d04a70a63..ae4ce0c060 100644 --- a/slither/solc_parsing/slither_compilation_unit_solc.py +++ b/slither/solc_parsing/slither_compilation_unit_solc.py @@ -15,7 +15,7 @@ from slither.core.declarations.pragma_directive import Pragma from slither.core.declarations.structure_top_level import StructureTopLevel from slither.core.declarations.using_for_top_level import UsingForTopLevel -from slither.core.scope.scope import FileScope +from slither.core.scope.scope import FileScope, FileScopeToImport from slither.core.solidity_types import ElementaryType, TypeAliasTopLevel from slither.core.variables.top_level_variable import TopLevelVariable from slither.exceptions import SlitherException @@ -39,7 +39,7 @@ class InheritanceResolutionError(SlitherException): def _handle_import_aliases( symbol_aliases: Dict, import_directive: Import, scope: FileScope -) -> None: +) -> List[str]: """ Handle the parsing of import aliases @@ -51,24 +51,27 @@ def _handle_import_aliases( Returns: """ + to_import = [] for symbol_alias in symbol_aliases: - if "foreign" in symbol_alias and "local" in symbol_alias: - if isinstance(symbol_alias["foreign"], dict) and "name" in symbol_alias["foreign"]: - - original_name = symbol_alias["foreign"]["name"] - local_name = symbol_alias["local"] - import_directive.renaming[local_name] = original_name - # Assuming that two imports cannot collide in renaming - scope.renaming[local_name] = original_name - - # This path should only be hit for the malformed AST of solc 0.5.12 where - # the foreign identifier cannot be found but is required to resolve the alias. - # see https://github.com/crytic/slither/issues/1319 - elif symbol_alias["local"]: - raise SlitherException( - "Cannot resolve local alias for import directive due to malformed AST. Please upgrade to solc 0.6.0 or higher." - ) + if "foreign" in symbol_alias: + original_name = symbol_alias["foreign"]["name"] + to_import.append(original_name) + if "local" in symbol_alias: + if isinstance(symbol_alias["foreign"], dict) and "name" in symbol_alias["foreign"]: + local_name = symbol_alias["local"] + import_directive.renaming[local_name] = original_name + # Assuming that two imports cannot collide in renaming + scope.renaming[local_name] = original_name + + # This path should only be hit for the malformed AST of solc 0.5.12 where + # the foreign identifier cannot be found but is required to resolve the alias. + # see https://github.com/crytic/slither/issues/1319 + elif symbol_alias["local"]: + raise SlitherException( + "Cannot resolve local alias for import directive due to malformed AST. Please upgrade to solc 0.6.0 or higher." + ) + return to_import class SlitherCompilationUnitSolc(CallerContextExpression): # pylint: disable=no-self-use,too-many-instance-attributes @@ -257,6 +260,7 @@ def parse_top_level_from_loaded_json(self, data_loaded: Dict, filename: str) -> self._using_for_top_level_parser.append(usingFor_parser) elif top_level_data[self.get_key()] == "ImportDirective": + to_import = [] if self.is_compact_ast: import_directive = Import( Path( @@ -270,7 +274,7 @@ def parse_top_level_from_loaded_json(self, data_loaded: Dict, filename: str) -> import_directive.alias = top_level_data["unitAlias"] if "symbolAliases" in top_level_data: symbol_aliases = top_level_data["symbolAliases"] - _handle_import_aliases(symbol_aliases, import_directive, scope) + to_import = _handle_import_aliases(symbol_aliases, import_directive, scope) else: import_directive = Import( Path( @@ -289,7 +293,7 @@ def parse_top_level_from_loaded_json(self, data_loaded: Dict, filename: str) -> self._compilation_unit.import_directives.append(import_directive) get_imported_scope = self.compilation_unit.get_scope(import_directive.filename) - scope.accessible_scopes.append(get_imported_scope) + scope.accessible_scopes.append(FileScopeToImport(get_imported_scope, to_import)) elif top_level_data[self.get_key()] == "StructDefinition": st = StructureTopLevel(self.compilation_unit, scope) From c99d1ca62a9629e18defcd5d7b149fc6861a6109 Mon Sep 17 00:00:00 2001 From: Simone Date: Tue, 20 Feb 2024 12:45:00 +0100 Subject: [PATCH 2/4] Add top level event in FileScopeToImport --- slither/core/scope/scope.py | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/slither/core/scope/scope.py b/slither/core/scope/scope.py index f2c7354963..1c2e6d1a54 100644 --- a/slither/core/scope/scope.py +++ b/slither/core/scope/scope.py @@ -1,4 +1,4 @@ -from typing import List, Any, Dict, Optional, Union, Set, TypeVar, Callable, Tuple +from typing import List, Any, Dict, Optional, Union, Set, TypeVar, Callable from crytic_compile import CompilationUnit from crytic_compile.source_unit import SourceUnit @@ -244,11 +244,12 @@ def __hash__(self) -> int: # endregion + class FileScopeToImport: def __init__(self, filescope: FileScope, items_to_import: List[str]) -> None: self.filescope = filescope self.items_to_import = items_to_import - + @property def contracts(self) -> Dict[str, Contract]: if len(self.items_to_import) != 0: @@ -279,6 +280,16 @@ def enums(self) -> Dict[str, EnumTopLevel]: return result return self.filescope.enums + @property + def events(self) -> Dict[str, EventTopLevel]: + if len(self.items_to_import) != 0: + result = {} + for name, event in self.filescope.events.items(): + if name in self.items_to_import: + result[name] = event + return result + return self.filescope.events + @property def functions(self) -> Set[FunctionTopLevel]: if len(self.items_to_import) != 0: @@ -295,7 +306,7 @@ def using_for_directives(self) -> Set[UsingForTopLevel]: if len(self.items_to_import) == 0: return self.filescope.using_for_directives return set() - + @property def imports(self) -> Set[Import]: # TODO check it's correct From c066ca3a4f99c65e8f23743b4a9cec86f9f54a70 Mon Sep 17 00:00:00 2001 From: Simone Date: Tue, 20 Feb 2024 14:18:01 +0100 Subject: [PATCH 3/4] Refactor to support the broken AST --- .../slither_compilation_unit_solc.py | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/slither/solc_parsing/slither_compilation_unit_solc.py b/slither/solc_parsing/slither_compilation_unit_solc.py index 78944449c2..fada372fa6 100644 --- a/slither/solc_parsing/slither_compilation_unit_solc.py +++ b/slither/solc_parsing/slither_compilation_unit_solc.py @@ -56,25 +56,25 @@ def _handle_import_aliases( to_import = [] for symbol_alias in symbol_aliases: if "foreign" in symbol_alias: - original_name = symbol_alias["foreign"]["name"] - to_import.append(original_name) - if "local" in symbol_alias: - if isinstance(symbol_alias["foreign"], dict) and "name" in symbol_alias["foreign"]: + if isinstance(symbol_alias["foreign"], dict) and "name" in symbol_alias["foreign"]: + original_name = symbol_alias["foreign"]["name"] + to_import.append(original_name) + if "local" in symbol_alias: local_name = symbol_alias["local"] import_directive.renaming[local_name] = original_name # Assuming that two imports cannot collide in renaming scope.renaming[local_name] = original_name - - # This path should only be hit for the malformed AST of solc 0.5.12 where - # the foreign identifier cannot be found but is required to resolve the alias. - # see https://github.com/crytic/slither/issues/1319 - elif symbol_alias["local"]: - raise SlitherException( - "Cannot resolve local alias for import directive due to malformed AST. Please upgrade to solc 0.6.0 or higher." - ) + # This path should only be hit for the malformed AST of solc 0.5.12 where + # the foreign identifier cannot be found but is required to resolve the alias. + # see https://github.com/crytic/slither/issues/1319 + elif symbol_alias["local"]: + raise SlitherException( + "Cannot resolve local alias for import directive due to malformed AST. Please upgrade to solc 0.6.0 or higher." + ) return to_import + class SlitherCompilationUnitSolc(CallerContextExpression): # pylint: disable=no-self-use,too-many-instance-attributes def __init__(self, compilation_unit: SlitherCompilationUnit) -> None: From 970cfca13b073c4787720c333063506f8b4f660b Mon Sep 17 00:00:00 2001 From: Simone Date: Tue, 20 Feb 2024 16:10:27 +0100 Subject: [PATCH 4/4] Correct imports for rename and type aliases --- slither/core/scope/scope.py | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/slither/core/scope/scope.py b/slither/core/scope/scope.py index 1c2e6d1a54..3f91771fbe 100644 --- a/slither/core/scope/scope.py +++ b/slither/core/scope/scope.py @@ -341,10 +341,20 @@ def variables(self) -> Dict[str, TopLevelVariable]: @property def renaming(self) -> Dict[str, str]: - # TODO check it's correct + if len(self.items_to_import) != 0: + result = {} + for name, rename in self.filescope.renaming.items(): + if name in self.items_to_import: + result[name] = rename + return result return self.filescope.renaming @property def type_aliases(self) -> Dict[str, TypeAlias]: - # TODO check it's correct + if len(self.items_to_import) != 0: + result = {} + for name, type_alias in self.filescope.type_aliases.items(): + if name in self.items_to_import: + result[name] = type_alias + return result return self.filescope.type_aliases