From 2cbbb706ac80bc2e0ed27e2d728749013670072b Mon Sep 17 00:00:00 2001 From: webthethird Date: Tue, 20 Dec 2022 09:33:15 -0600 Subject: [PATCH 1/8] Handle custom upgradeability comments for contract definition --- slither/solc_parsing/declarations/contract.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/slither/solc_parsing/declarations/contract.py b/slither/solc_parsing/declarations/contract.py index b7f938d1d..ed39bd1b6 100644 --- a/slither/solc_parsing/declarations/contract.py +++ b/slither/solc_parsing/declarations/contract.py @@ -699,6 +699,16 @@ class ContractSolc(CallerContextExpression): self._usingForNotParsed = [] self._customErrorParsed = [] + def _handle_comment(self, attributes: Dict): + if "documentation" in attributes and "text" in attributes["documentation"]: + candidates = attributes["documentation"]["text"].replace("\n", ",").split(",") + + for candidate in candidates: + if "@custom:security isProxy" in candidate: + self._contract._is_upgradeable_proxy = True + if "@custom:security isUpgradeable" in candidate: + self._contract._is_upgradeable = True + # endregion ################################################################################### ################################################################################### From 61c67d88011587a1b2cadf32323146e295288627 Mon Sep 17 00:00:00 2001 From: webthethird Date: Tue, 20 Dec 2022 09:49:34 -0600 Subject: [PATCH 2/8] Add `upgradeable_version` property to Contract class --- slither/core/declarations/contract.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/slither/core/declarations/contract.py b/slither/core/declarations/contract.py index eb2ac9a2e..dc77eb866 100644 --- a/slither/core/declarations/contract.py +++ b/slither/core/declarations/contract.py @@ -88,6 +88,7 @@ class Contract(SourceMapping): # pylint: disable=too-many-public-methods self._is_upgradeable: Optional[bool] = None self._is_upgradeable_proxy: Optional[bool] = None + self._upgradeable_version: Optional[str] = None self.is_top_level = False # heavily used, so no @property @@ -1246,6 +1247,14 @@ class Contract(SourceMapping): # pylint: disable=too-many-public-methods return self._is_upgradeable_proxy return self._is_upgradeable_proxy + @property + def upgradeable_version(self) -> Optional[str]: + return self._upgradeable_version + + @upgradeable_version.setter + def upgradeable_version(self, version_name: str): + self._upgradeable_version = version_name + # endregion ################################################################################### ################################################################################### From f1e653fb186cb9c6a65dc8d054bd0d9f1c8d7cfd Mon Sep 17 00:00:00 2001 From: webthethird Date: Tue, 20 Dec 2022 10:12:47 -0600 Subject: [PATCH 3/8] Call `handle_comment` in init method --- slither/solc_parsing/declarations/contract.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/slither/solc_parsing/declarations/contract.py b/slither/solc_parsing/declarations/contract.py index ed39bd1b6..5a51a142e 100644 --- a/slither/solc_parsing/declarations/contract.py +++ b/slither/solc_parsing/declarations/contract.py @@ -65,8 +65,10 @@ class ContractSolc(CallerContextExpression): # Export info if self.is_compact_ast: self._contract.name = self._data["name"] + self._handle_comment(self._data) else: self._contract.name = self._data["attributes"][self.get_key()] + self._handle_comment(self._data["attributes"]) self._contract.id = self._data["id"] From 5b14dae8b6694ebee28a5b7fce69a543a1ea0803 Mon Sep 17 00:00:00 2001 From: webthethird Date: Tue, 20 Dec 2022 10:15:29 -0600 Subject: [PATCH 4/8] Parse version name from custom comment --- slither/solc_parsing/declarations/contract.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/slither/solc_parsing/declarations/contract.py b/slither/solc_parsing/declarations/contract.py index 5a51a142e..05bd9551d 100644 --- a/slither/solc_parsing/declarations/contract.py +++ b/slither/solc_parsing/declarations/contract.py @@ -1,4 +1,5 @@ import logging +import re from typing import List, Dict, Callable, TYPE_CHECKING, Union, Set from slither.core.declarations import Modifier, Event, EnumContract, StructureContract, Function @@ -711,6 +712,12 @@ class ContractSolc(CallerContextExpression): if "@custom:security isUpgradeable" in candidate: self._contract._is_upgradeable = True + version_name = re.search( + r'@custom:version name="([\w, .]*)"', candidate + ) + if version_name: + self._contract.upgradeable_version = version_name.group(1) + # endregion ################################################################################### ################################################################################### From afced5cc929abf4563f6df1b252320e7b4c42cbd Mon Sep 17 00:00:00 2001 From: webthethird Date: Tue, 20 Dec 2022 14:35:03 -0600 Subject: [PATCH 5/8] Fix test errors caused by `attributes["documentation"] = None"` --- slither/solc_parsing/declarations/contract.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/slither/solc_parsing/declarations/contract.py b/slither/solc_parsing/declarations/contract.py index 05bd9551d..124e5b9c2 100644 --- a/slither/solc_parsing/declarations/contract.py +++ b/slither/solc_parsing/declarations/contract.py @@ -703,7 +703,11 @@ class ContractSolc(CallerContextExpression): self._customErrorParsed = [] def _handle_comment(self, attributes: Dict): - if "documentation" in attributes and "text" in attributes["documentation"]: + if ( + "documentation" in attributes + and attributes["documentation"] is not None + and "text" in attributes["documentation"] + ): candidates = attributes["documentation"]["text"].replace("\n", ",").split(",") for candidate in candidates: From 5458a614cf91f6cd337046342efdba335bde3662 Mon Sep 17 00:00:00 2001 From: webthethird Date: Tue, 20 Dec 2022 14:47:43 -0600 Subject: [PATCH 6/8] Fix formatting --- slither/solc_parsing/declarations/contract.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/slither/solc_parsing/declarations/contract.py b/slither/solc_parsing/declarations/contract.py index 124e5b9c2..4e8a91560 100644 --- a/slither/solc_parsing/declarations/contract.py +++ b/slither/solc_parsing/declarations/contract.py @@ -704,9 +704,9 @@ class ContractSolc(CallerContextExpression): def _handle_comment(self, attributes: Dict): if ( - "documentation" in attributes - and attributes["documentation"] is not None - and "text" in attributes["documentation"] + "documentation" in attributes + and attributes["documentation"] is not None + and "text" in attributes["documentation"] ): candidates = attributes["documentation"]["text"].replace("\n", ",").split(",") @@ -716,9 +716,7 @@ class ContractSolc(CallerContextExpression): if "@custom:security isUpgradeable" in candidate: self._contract._is_upgradeable = True - version_name = re.search( - r'@custom:version name="([\w, .]*)"', candidate - ) + version_name = re.search(r'@custom:version name="([\w, .]*)"', candidate) if version_name: self._contract.upgradeable_version = version_name.group(1) From c44cf18831ceb789b5131b6722b90b80667a6640 Mon Sep 17 00:00:00 2001 From: webthethird Date: Tue, 20 Dec 2022 15:31:55 -0600 Subject: [PATCH 7/8] Add setters for properties --- slither/core/declarations/contract.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/slither/core/declarations/contract.py b/slither/core/declarations/contract.py index dc77eb866..a90e2591e 100644 --- a/slither/core/declarations/contract.py +++ b/slither/core/declarations/contract.py @@ -1222,6 +1222,10 @@ class Contract(SourceMapping): # pylint: disable=too-many-public-methods break return self._is_upgradeable + @is_upgradeable.setter + def is_upgradeable(self, upgradeable: bool): + self._is_upgradeable = upgradeable + @property def is_upgradeable_proxy(self) -> bool: from slither.core.cfg.node import NodeType @@ -1247,6 +1251,10 @@ class Contract(SourceMapping): # pylint: disable=too-many-public-methods return self._is_upgradeable_proxy return self._is_upgradeable_proxy + @is_upgradeable_proxy.setter + def is_upgradeable_proxy(self, upgradeable_proxy: bool): + self._is_upgradeable_proxy = upgradeable_proxy + @property def upgradeable_version(self) -> Optional[str]: return self._upgradeable_version From 762806c52f2a825ca3dfcfcdd8f3a9c23c922765 Mon Sep 17 00:00:00 2001 From: webthethird Date: Tue, 20 Dec 2022 15:32:29 -0600 Subject: [PATCH 8/8] Fix access to protected member --- slither/solc_parsing/declarations/contract.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/slither/solc_parsing/declarations/contract.py b/slither/solc_parsing/declarations/contract.py index 4e8a91560..1766e7617 100644 --- a/slither/solc_parsing/declarations/contract.py +++ b/slither/solc_parsing/declarations/contract.py @@ -712,9 +712,9 @@ class ContractSolc(CallerContextExpression): for candidate in candidates: if "@custom:security isProxy" in candidate: - self._contract._is_upgradeable_proxy = True + self._contract.is_upgradeable_proxy = True if "@custom:security isUpgradeable" in candidate: - self._contract._is_upgradeable = True + self._contract.is_upgradeable = True version_name = re.search(r'@custom:version name="([\w, .]*)"', candidate) if version_name: