Skip to content

Commit

Permalink
Merge pull request #2576 from crytic/dev-chainlink-feed-detector
Browse files Browse the repository at this point in the history
Add Chainlink feed registry detector
  • Loading branch information
montyly authored Oct 10, 2024
2 parents 570840d + 8b226d0 commit d1d78de
Show file tree
Hide file tree
Showing 6 changed files with 148 additions and 0 deletions.
1 change: 1 addition & 0 deletions slither/detectors/all_detectors.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@
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 .functions.pyth_deprecated_functions import PythDeprecatedFunctions
from .functions.optimism_deprecation import OptimismDeprecation

Expand Down
102 changes: 102 additions & 0 deletions slither/detectors/functions/chainlink_feed_registry.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
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
Original file line number Diff line number Diff line change
@@ -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)

Original file line number Diff line number Diff line change
@@ -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);
}


}
Binary file not shown.
5 changes: 5 additions & 0 deletions tests/e2e/detectors/test_detectors.py
Original file line number Diff line number Diff line change
Expand Up @@ -1714,6 +1714,11 @@ def id_test(test_item: Test):
"out_of_order_retryable.sol",
"0.8.20",
),
Test(
all_detectors.ChainlinkFeedRegistry,
"chainlink_feed_registry.sol",
"0.8.20",
),
Test(
all_detectors.PythDeprecatedFunctions,
"pyth_deprecated_functions.sol",
Expand Down

0 comments on commit d1d78de

Please sign in to comment.