-
Notifications
You must be signed in to change notification settings - Fork 979
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'dev' into unused-errors-detector
- Loading branch information
Showing
34 changed files
with
1,451 additions
and
2 deletions.
There are no files selected for viewing
Validating CODEOWNERS rules …
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
78 changes: 78 additions & 0 deletions
78
slither/detectors/functions/gelato_unprotected_randomness.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
from typing import List | ||
|
||
from slither.slithir.operations.internal_call import InternalCall | ||
from slither.detectors.abstract_detector import ( | ||
AbstractDetector, | ||
DetectorClassification, | ||
DETECTOR_INFO, | ||
) | ||
from slither.utils.output import Output | ||
|
||
|
||
class GelatoUnprotectedRandomness(AbstractDetector): | ||
""" | ||
Unprotected Gelato VRF requests | ||
""" | ||
|
||
ARGUMENT = "gelato-unprotected-randomness" | ||
HELP = "Call to _requestRandomness within an unprotected function" | ||
IMPACT = DetectorClassification.MEDIUM | ||
CONFIDENCE = DetectorClassification.MEDIUM | ||
|
||
WIKI = "https://github.com/crytic/slither/wiki/Detector-Documentation#gelato-unprotected-randomness" | ||
|
||
WIKI_TITLE = "Gelato unprotected randomness" | ||
WIKI_DESCRIPTION = "Detect calls to `_requestRandomness` within an unprotected function." | ||
|
||
# region wiki_exploit_scenario | ||
WIKI_EXPLOIT_SCENARIO = """ | ||
```solidity | ||
contract C is GelatoVRFConsumerBase { | ||
function _fulfillRandomness( | ||
uint256 randomness, | ||
uint256, | ||
bytes memory extraData | ||
) internal override { | ||
// Do something with the random number | ||
} | ||
function bad() public { | ||
_requestRandomness(abi.encode(msg.sender)); | ||
} | ||
} | ||
``` | ||
The function `bad` is uprotected and requests randomness.""" | ||
# endregion wiki_exploit_scenario | ||
|
||
WIKI_RECOMMENDATION = ( | ||
"Function that request randomness should be allowed only to authorized users." | ||
) | ||
|
||
def _detect(self) -> List[Output]: | ||
results = [] | ||
|
||
for contract in self.compilation_unit.contracts_derived: | ||
if "GelatoVRFConsumerBase" in [c.name for c in contract.inheritance]: | ||
for function in contract.functions_entry_points: | ||
if not function.is_protected() and ( | ||
nodes_request := [ | ||
ir.node | ||
for ir in function.all_internal_calls() | ||
if isinstance(ir, InternalCall) | ||
and ir.function_name == "_requestRandomness" | ||
] | ||
): | ||
# Sort so output is deterministic | ||
nodes_request.sort(key=lambda x: (x.node_id, x.function.full_name)) | ||
|
||
for node in nodes_request: | ||
info: DETECTOR_INFO = [ | ||
function, | ||
" is unprotected and request randomness from Gelato VRF\n\t- ", | ||
node, | ||
"\n", | ||
] | ||
res = self.generate_result(info) | ||
results.append(res) | ||
|
||
return results |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
from typing import List | ||
|
||
from slither.detectors.abstract_detector import ( | ||
AbstractDetector, | ||
DetectorClassification, | ||
DETECTOR_INFO, | ||
) | ||
from slither.core.cfg.node import Node | ||
from slither.core.variables.variable import Variable | ||
from slither.core.expressions import TypeConversion, Literal | ||
from slither.utils.output import Output | ||
|
||
|
||
class OptimismDeprecation(AbstractDetector): | ||
|
||
ARGUMENT = "optimism-deprecation" | ||
HELP = "Detect when deprecated Optimism predeploy or function is used." | ||
IMPACT = DetectorClassification.LOW | ||
CONFIDENCE = DetectorClassification.HIGH | ||
|
||
WIKI = "https://github.com/crytic/slither/wiki/Detector-Documentation#optimism-deprecation" | ||
|
||
WIKI_TITLE = "Optimism deprecated predeploy or function" | ||
WIKI_DESCRIPTION = "Detect when deprecated Optimism predeploy or function is used." | ||
|
||
# region wiki_exploit_scenario | ||
WIKI_EXPLOIT_SCENARIO = """ | ||
```solidity | ||
interface GasPriceOracle { | ||
function scalar() external view returns (uint256); | ||
} | ||
contract Test { | ||
GasPriceOracle constant OPT_GAS = GasPriceOracle(0x420000000000000000000000000000000000000F); | ||
function a() public { | ||
OPT_GAS.scalar(); | ||
} | ||
} | ||
``` | ||
The call to the `scalar` function of the Optimism GasPriceOracle predeploy always revert. | ||
""" | ||
# endregion wiki_exploit_scenario | ||
|
||
WIKI_RECOMMENDATION = "Do not use the deprecated components." | ||
|
||
def _detect(self) -> List[Output]: | ||
results = [] | ||
|
||
deprecated_predeploys = [ | ||
"0x4200000000000000000000000000000000000000", # LegacyMessagePasser | ||
"0x4200000000000000000000000000000000000001", # L1MessageSender | ||
"0x4200000000000000000000000000000000000002", # DeployerWhitelist | ||
"0x4200000000000000000000000000000000000013", # L1BlockNumber | ||
] | ||
|
||
for contract in self.compilation_unit.contracts_derived: | ||
use_deprecated: List[Node] = [] | ||
|
||
for _, ir in contract.all_high_level_calls: | ||
# To avoid FPs we assume predeploy contracts are always assigned to a constant and typecasted to an interface | ||
# and we check the target address of a high level call. | ||
if ( | ||
isinstance(ir.destination, Variable) | ||
and isinstance(ir.destination.expression, TypeConversion) | ||
and isinstance(ir.destination.expression.expression, Literal) | ||
): | ||
if ir.destination.expression.expression.value in deprecated_predeploys: | ||
use_deprecated.append(ir.node) | ||
|
||
if ( | ||
ir.destination.expression.expression.value | ||
== "0x420000000000000000000000000000000000000F" | ||
and ir.function_name in ("overhead", "scalar", "getL1GasUsed") | ||
): | ||
use_deprecated.append(ir.node) | ||
# Sort so output is deterministic | ||
use_deprecated.sort(key=lambda x: (x.node_id, x.function.full_name)) | ||
if len(use_deprecated) > 0: | ||
info: DETECTOR_INFO = [ | ||
"A deprecated Optimism predeploy or function is used in the ", | ||
contract.name, | ||
" contract.\n", | ||
] | ||
|
||
for node in use_deprecated: | ||
info.extend(["\t - ", node, "\n"]) | ||
|
||
res = self.generate_result(info) | ||
results.append(res) | ||
|
||
return results |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
from typing import List | ||
|
||
from slither.detectors.abstract_detector import ( | ||
AbstractDetector, | ||
DetectorClassification, | ||
DETECTOR_INFO, | ||
) | ||
from slither.utils.output import Output | ||
|
||
|
||
class PythDeprecatedFunctions(AbstractDetector): | ||
""" | ||
Documentation: This detector finds deprecated Pyth function calls | ||
""" | ||
|
||
ARGUMENT = "pyth-deprecated-functions" | ||
HELP = "Detect Pyth deprecated functions" | ||
IMPACT = DetectorClassification.MEDIUM | ||
CONFIDENCE = DetectorClassification.HIGH | ||
|
||
WIKI = "https://github.com/crytic/slither/wiki/Detector-Documentation#pyth-deprecated-functions" | ||
WIKI_TITLE = "Pyth deprecated functions" | ||
WIKI_DESCRIPTION = "Detect when a Pyth deprecated function is used" | ||
WIKI_RECOMMENDATION = ( | ||
"Do not use deprecated Pyth functions. Visit https://api-reference.pyth.network/." | ||
) | ||
|
||
WIKI_EXPLOIT_SCENARIO = """ | ||
```solidity | ||
import "@pythnetwork/pyth-sdk-solidity/IPyth.sol"; | ||
import "@pythnetwork/pyth-sdk-solidity/PythStructs.sol"; | ||
contract C { | ||
IPyth pyth; | ||
constructor(IPyth _pyth) { | ||
pyth = _pyth; | ||
} | ||
function A(bytes32 priceId) public { | ||
PythStructs.Price memory price = pyth.getPrice(priceId); | ||
... | ||
} | ||
} | ||
``` | ||
The function `A` uses the deprecated `getPrice` Pyth function. | ||
""" | ||
|
||
def _detect(self): | ||
DEPRECATED_PYTH_FUNCTIONS = [ | ||
"getValidTimePeriod", | ||
"getEmaPrice", | ||
"getPrice", | ||
] | ||
results: List[Output] = [] | ||
|
||
for contract in self.compilation_unit.contracts_derived: | ||
for target_contract, ir in contract.all_high_level_calls: | ||
if ( | ||
target_contract.name == "IPyth" | ||
and ir.function_name in DEPRECATED_PYTH_FUNCTIONS | ||
): | ||
info: DETECTOR_INFO = [ | ||
"The following Pyth deprecated function is used\n\t- ", | ||
ir.node, | ||
"\n", | ||
] | ||
|
||
res = self.generate_result(info) | ||
results.append(res) | ||
|
||
return results |
Oops, something went wrong.