From bae46ba9cec65a87e8bb686850f7463e72448ca1 Mon Sep 17 00:00:00 2001 From: Simone Date: Tue, 8 Oct 2024 13:56:18 +0200 Subject: [PATCH] Add Pyth deprecated functions detector --- slither/detectors/all_detectors.py | 1 + .../functions/pyth_deprecated_functions.py | 73 ++++++++++++++++++ ..._8_20_pyth_deprecated_functions_sol__0.txt | 3 + .../0.8.20/pyth_deprecated_functions.sol | 35 +++++++++ .../pyth_deprecated_functions.sol-0.8.20.zip | Bin 0 -> 4367 bytes tests/e2e/detectors/test_detectors.py | 5 ++ 6 files changed, 117 insertions(+) create mode 100644 slither/detectors/functions/pyth_deprecated_functions.py create mode 100644 tests/e2e/detectors/snapshots/detectors__detector_PythDeprecatedFunctions_0_8_20_pyth_deprecated_functions_sol__0.txt create mode 100644 tests/e2e/detectors/test_data/pyth-deprecated-functions/0.8.20/pyth_deprecated_functions.sol create mode 100644 tests/e2e/detectors/test_data/pyth-deprecated-functions/0.8.20/pyth_deprecated_functions.sol-0.8.20.zip diff --git a/slither/detectors/all_detectors.py b/slither/detectors/all_detectors.py index 44a168c2b..6f1f94433 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.pyth_deprecated_functions import PythDeprecatedFunctions # from .statements.unused_import import UnusedImport diff --git a/slither/detectors/functions/pyth_deprecated_functions.py b/slither/detectors/functions/pyth_deprecated_functions.py new file mode 100644 index 000000000..87cff9181 --- /dev/null +++ b/slither/detectors/functions/pyth_deprecated_functions.py @@ -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 diff --git a/tests/e2e/detectors/snapshots/detectors__detector_PythDeprecatedFunctions_0_8_20_pyth_deprecated_functions_sol__0.txt b/tests/e2e/detectors/snapshots/detectors__detector_PythDeprecatedFunctions_0_8_20_pyth_deprecated_functions_sol__0.txt new file mode 100644 index 000000000..4cc23d213 --- /dev/null +++ b/tests/e2e/detectors/snapshots/detectors__detector_PythDeprecatedFunctions_0_8_20_pyth_deprecated_functions_sol__0.txt @@ -0,0 +1,3 @@ +The following Pyth deprecated function is used + - price = pyth.getPrice(priceId) (tests/e2e/detectors/test_data/pyth-deprecated-functions/0.8.20/pyth_deprecated_functions.sol#23) + diff --git a/tests/e2e/detectors/test_data/pyth-deprecated-functions/0.8.20/pyth_deprecated_functions.sol b/tests/e2e/detectors/test_data/pyth-deprecated-functions/0.8.20/pyth_deprecated_functions.sol new file mode 100644 index 000000000..dc8130db5 --- /dev/null +++ b/tests/e2e/detectors/test_data/pyth-deprecated-functions/0.8.20/pyth_deprecated_functions.sol @@ -0,0 +1,35 @@ + +// Fake Pyth interface +interface IPyth { + function getPrice(bytes32 id) external returns (uint256 price); + function notDeprecated(bytes32 id) external returns (uint256 price); +} + +interface INotPyth { + function getPrice(bytes32 id) external returns (uint256 price); +} + +contract C { + + IPyth pyth; + INotPyth notPyth; + + constructor(IPyth _pyth, INotPyth _notPyth) { + pyth = _pyth; + notPyth = _notPyth; + } + + function Deprecated(bytes32 priceId) public { + uint256 price = pyth.getPrice(priceId); + } + + function notDeprecated(bytes32 priceId) public { + uint256 price = pyth.notDeprecated(priceId); + } + + function notPythCall(bytes32 priceId) public { + uint256 price = notPyth.getPrice(priceId); + } + + +} diff --git a/tests/e2e/detectors/test_data/pyth-deprecated-functions/0.8.20/pyth_deprecated_functions.sol-0.8.20.zip b/tests/e2e/detectors/test_data/pyth-deprecated-functions/0.8.20/pyth_deprecated_functions.sol-0.8.20.zip new file mode 100644 index 0000000000000000000000000000000000000000..258a28c938effe4b95c819b04d6e7629b1eb21d7 GIT binary patch literal 4367 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 literal 0 HcmV?d00001 diff --git a/tests/e2e/detectors/test_detectors.py b/tests/e2e/detectors/test_detectors.py index 2c6a5f55a..e30dc962d 100644 --- a/tests/e2e/detectors/test_detectors.py +++ b/tests/e2e/detectors/test_detectors.py @@ -1714,6 +1714,11 @@ def id_test(test_item: Test): "out_of_order_retryable.sol", "0.8.20", ), + Test( + all_detectors.PythDeprecatedFunctions, + "pyth_deprecated_functions.sol", + "0.8.20", + ), # Test( # all_detectors.UnusedImport, # "ConstantContractLevelUsedInContractTest.sol",