From 088b704f3113d45d025bba0341f4181103a191a6 Mon Sep 17 00:00:00 2001 From: Simone Date: Fri, 4 Oct 2024 19:55:24 +0200 Subject: [PATCH 1/2] Add Chainlink feed registry detector --- slither/detectors/all_detectors.py | 1 + .../functions/chainlink_feed_registry.py | 103 ++++++++++++++++++ ..._0_8_20_chainlink_feed_registry_sol__0.txt | 3 + .../0.8.20/chainlink_feed_registry.sol | 37 +++++++ .../chainlink_feed_registry.sol-0.8.20.zip | Bin 0 -> 5853 bytes tests/e2e/detectors/test_detectors.py | 5 + 6 files changed, 149 insertions(+) create mode 100644 slither/detectors/functions/chainlink_feed_registry.py create mode 100644 tests/e2e/detectors/snapshots/detectors__detector_ChainlinkFeedRegistry_0_8_20_chainlink_feed_registry_sol__0.txt create mode 100644 tests/e2e/detectors/test_data/chainlink-feed-registry/0.8.20/chainlink_feed_registry.sol create mode 100644 tests/e2e/detectors/test_data/chainlink-feed-registry/0.8.20/chainlink_feed_registry.sol-0.8.20.zip diff --git a/slither/detectors/all_detectors.py b/slither/detectors/all_detectors.py index 44a168c2b..0b93b9d75 100644 --- a/slither/detectors/all_detectors.py +++ b/slither/detectors/all_detectors.py @@ -97,5 +97,6 @@ from .statements.tautological_compare import TautologicalCompare from .statements.return_bomb import ReturnBomb from .functions.out_of_order_retryable import OutOfOrderRetryable +from .functions.chainlink_feed_registry import ChainlinkFeedRegistry # from .statements.unused_import import UnusedImport diff --git a/slither/detectors/functions/chainlink_feed_registry.py b/slither/detectors/functions/chainlink_feed_registry.py new file mode 100644 index 000000000..951dfb584 --- /dev/null +++ b/slither/detectors/functions/chainlink_feed_registry.py @@ -0,0 +1,103 @@ +from typing import List + +from slither.detectors.abstract_detector import ( + AbstractDetector, + DetectorClassification, + DETECTOR_INFO, +) +from slither.utils.output import Output + + +class ChainlinkFeedRegistry(AbstractDetector): + + ARGUMENT = "chainlink-feed-registry" + HELP = "Detect when chainlink feed registry is used" + IMPACT = DetectorClassification.LOW + CONFIDENCE = DetectorClassification.HIGH + + WIKI = "https://github.com/crytic/slither/wiki/Detector-Documentation#chainlink-feed-registry" + + WIKI_TITLE = "Chainlink Feed Registry usage" + WIKI_DESCRIPTION = "Detect when Chainlink Feed Registry is used. At the moment is only available on Ethereum Mainnet." + + # region wiki_exploit_scenario + WIKI_EXPLOIT_SCENARIO = """ +```solidity +import "chainlink/contracts/src/v0.8/interfaces/FeedRegistryInteface.sol" + +contract A { + FeedRegistryInterface public immutable registry; + + constructor(address _registry) { + registry = _registry; + } + + function getPrice(address base, address quote) public return(uint256) { + (, int256 price,,,) = registry.latestRoundData(base, quote); + // Do price validation + return uint256(price); + } +} +``` +If the contract is deployed on a different chain than Ethereum Mainnet the `getPrice` function will revert. +""" + # endregion wiki_exploit_scenario + + WIKI_RECOMMENDATION = "Do not use Chainlink Feed Registry outside of Ethereum Mainnet." + + def _detect(self) -> List[Output]: + # https://github.com/smartcontractkit/chainlink/blob/8ca41fc8f722accfccccb4b1778db2df8fef5437/contracts/src/v0.8/interfaces/FeedRegistryInterface.sol + registry_functions = [ + "decimals", + "description", + "versiom", + "latestRoundData", + "getRoundData", + "latestAnswer", + "latestTimestamp", + "latestRound", + "getAnswer", + "getTimestamp", + "getFeed", + "getPhaseFeed", + "isFeedEnabled", + "getPhase", + "getRoundFeed", + "getPhaseRange", + "getPreviousRoundId", + "getNextRoundId", + "proposeFeed", + "confirmFeed", + "getProposedFeed", + "proposedGetRoundData", + "proposedLatestRoundData", + "getCurrentPhaseId", + ] + results = [] + + for contract in self.compilation_unit.contracts_derived: + nodes = [] + for target, ir in contract.all_high_level_calls: + if ( + target.name == "FeedRegistryInterface" + and ir.function_name in registry_functions + ): + nodes.append(ir.node) + + # Sort so output is deterministic + nodes.sort(key=lambda x: (x.node_id, x.function.full_name)) + + if len(nodes) > 0: + info: DETECTOR_INFO = [ + "The Chainlink Feed Registry is used in the ", + contract.name, + " contract. It's only available on Ethereum Mainnet, consider to not use it if the contract needs to be deployed on other chains.\n", + ] + + for node in nodes: + info.extend(["\t - ", node, "\n"]) + + res = self.generate_result(info) + results.append(res) + + return results diff --git a/tests/e2e/detectors/snapshots/detectors__detector_ChainlinkFeedRegistry_0_8_20_chainlink_feed_registry_sol__0.txt b/tests/e2e/detectors/snapshots/detectors__detector_ChainlinkFeedRegistry_0_8_20_chainlink_feed_registry_sol__0.txt new file mode 100644 index 000000000..6b7653ed0 --- /dev/null +++ b/tests/e2e/detectors/snapshots/detectors__detector_ChainlinkFeedRegistry_0_8_20_chainlink_feed_registry_sol__0.txt @@ -0,0 +1,3 @@ +The Chainlink Feed Registry is used in the A contract. It's only available on Ethereum Mainnet, consider to not use it if the contract needs to be deployed on other chains. + - (None,price,None,None,None) = registry.latestRoundData(base,quote) (tests/e2e/detectors/test_data/chainlink-feed-registry/0.8.20/chainlink_feed_registry.sol#25) + diff --git a/tests/e2e/detectors/test_data/chainlink-feed-registry/0.8.20/chainlink_feed_registry.sol b/tests/e2e/detectors/test_data/chainlink-feed-registry/0.8.20/chainlink_feed_registry.sol new file mode 100644 index 000000000..cf5d1ad4d --- /dev/null +++ b/tests/e2e/detectors/test_data/chainlink-feed-registry/0.8.20/chainlink_feed_registry.sol @@ -0,0 +1,37 @@ +interface FeedRegistryInterface { + function latestRoundData( + address base, + address quote + ) external view returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound); +} + +interface MyInterface { + function latestRoundData( + address base, + address quote + ) external view returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound); +} + +contract A { + FeedRegistryInterface public immutable registry; + MyInterface public immutable my_interface; + + constructor(FeedRegistryInterface _registry, MyInterface _my_interface) { + registry = _registry; + my_interface = _my_interface; + } + + function getPriceBad(address base, address quote) public returns (uint256) { + (, int256 price,,,) = registry.latestRoundData(base, quote); + // Do price validation + return uint256(price); + } + + function getPriceGood(address base, address quote) public returns (uint256) { + (, int256 price,,,) = my_interface.latestRoundData(base, quote); + // Do price validation + return uint256(price); + } + + +} \ No newline at end of file diff --git a/tests/e2e/detectors/test_data/chainlink-feed-registry/0.8.20/chainlink_feed_registry.sol-0.8.20.zip b/tests/e2e/detectors/test_data/chainlink-feed-registry/0.8.20/chainlink_feed_registry.sol-0.8.20.zip new file mode 100644 index 0000000000000000000000000000000000000000..262ede23f1aed4f717506adaae6be3b1b7dabd49 GIT binary patch literal 5853 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 Date: Fri, 4 Oct 2024 20:00:28 +0200 Subject: [PATCH 2/2] Update slither/detectors/functions/chainlink_feed_registry.py Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- slither/detectors/functions/chainlink_feed_registry.py | 1 - 1 file changed, 1 deletion(-) diff --git a/slither/detectors/functions/chainlink_feed_registry.py b/slither/detectors/functions/chainlink_feed_registry.py index 951dfb584..82ab17424 100644 --- a/slither/detectors/functions/chainlink_feed_registry.py +++ b/slither/detectors/functions/chainlink_feed_registry.py @@ -83,7 +83,6 @@ def _detect(self) -> List[Output]: and ir.function_name in registry_functions ): nodes.append(ir.node) - # Sort so output is deterministic nodes.sort(key=lambda x: (x.node_id, x.function.full_name))