From 8eb6d5c00f9737f2537ba57773941095db555d07 Mon Sep 17 00:00:00 2001 From: Josselin Date: Fri, 17 Apr 2020 13:32:22 +0200 Subject: [PATCH] block.timestamp detector: - Add support for "now" (close #430) - Clean detector and use type annotation --- .../detectors/operations/block_timestamp.py | 79 ++++++++++--------- 1 file changed, 41 insertions(+), 38 deletions(-) diff --git a/slither/detectors/operations/block_timestamp.py b/slither/detectors/operations/block_timestamp.py index 6dc76ae50..8cb90a20f 100644 --- a/slither/detectors/operations/block_timestamp.py +++ b/slither/detectors/operations/block_timestamp.py @@ -2,15 +2,51 @@ Module detecting dangerous use of block.timestamp """ -from slither.core.declarations import Function -from slither.analyses.data_dependency.data_dependency import is_tainted, is_dependent -from slither.core.declarations.solidity_variables import (SolidityFunction, - SolidityVariableComposed) +from typing import List, Tuple + +from slither.analyses.data_dependency.data_dependency import is_dependent +from slither.core.cfg.node import Node +from slither.core.declarations import Function, Contract +from slither.core.declarations.solidity_variables import (SolidityVariableComposed, SolidityVariable) from slither.detectors.abstract_detector import (AbstractDetector, DetectorClassification) from slither.slithir.operations import Binary, BinaryType +def _timestamp(func: Function) -> List[Node]: + ret = set() + for node in func.nodes: + if node.contains_require_or_assert(): + for var in node.variables_read: + if is_dependent(var, SolidityVariableComposed('block.timestamp'), func.contract): + ret.add(node) + if is_dependent(var, SolidityVariable('now'), func.contract): + ret.add(node) + for ir in node.irs: + if isinstance(ir, Binary) and BinaryType.return_bool(ir.type): + for var in ir.read: + if is_dependent(var, SolidityVariableComposed('block.timestamp'), func.contract): + ret.add(node) + if is_dependent(var, SolidityVariable('now'), func.contract): + ret.add(node) + return list(ret) + + +def _detect_dangerous_timestamp(contract: Contract) -> List[Tuple[Function, List[Node]]]: + """ + Args: + contract (Contract) + Returns: + list((Function), (list (Node))) + """ + ret = [] + for f in [f for f in contract.functions if f.contract_declarer == contract]: + nodes = _timestamp(f) + if nodes: + ret.append((f, nodes)) + return ret + + class Timestamp(AbstractDetector): """ """ @@ -22,51 +58,18 @@ class Timestamp(AbstractDetector): WIKI = 'https://github.com/crytic/slither/wiki/Detector-Documentation#block-timestamp' - WIKI_TITLE = 'Block timestamp' WIKI_DESCRIPTION = 'Dangerous usage of `block.timestamp`. `block.timestamp` can be manipulated by miners.' WIKI_EXPLOIT_SCENARIO = '''"Bob's contract relies on `block.timestamp` for its randomness. Eve is a miner and manipulates `block.timestamp` to exploit Bob's contract.''' WIKI_RECOMMENDATION = 'Avoid relying on `block.timestamp`.' - def timestamp(self, func): - """ - """ - - ret = set() - for node in func.nodes: - if node.contains_require_or_assert(): - for var in node.variables_read: - if is_dependent(var, SolidityVariableComposed('block.timestamp'), func.contract): - ret.add(node) - for ir in node.irs: - if isinstance(ir, Binary) and BinaryType.return_bool(ir.type): - for var in ir.read: - if is_dependent(var, SolidityVariableComposed('block.timestamp'), func.contract): - ret.add(node) - return list(ret) - - - def detect_dangerous_timestamp(self, contract): - """ - Args: - contract (Contract) - Returns: - list((Function), (list (Node))) - """ - ret = [] - for f in [f for f in contract.functions if f.contract_declarer == contract]: - nodes = self.timestamp(f) - if nodes: - ret.append((f, nodes)) - return ret - def _detect(self): """ """ results = [] for c in self.contracts: - dangerous_timestamp = self.detect_dangerous_timestamp(c) + dangerous_timestamp = _detect_dangerous_timestamp(c) for (func, nodes) in dangerous_timestamp: info = [func, " uses timestamp for comparisons\n"]