mirror of https://github.com/crytic/slither
parent
eb1a848283
commit
d49ea4475d
@ -0,0 +1,96 @@ |
||||
""" |
||||
Module detecting send to arbitrary address |
||||
|
||||
To avoid FP, it does not report: |
||||
- If msg.sender is used as index (withdraw situation) |
||||
- If the function is protected |
||||
- If the value sent is msg.value (repay situation) |
||||
- If there is a call to transferFrom |
||||
|
||||
TODO: dont report if the value is tainted by msg.value |
||||
""" |
||||
from slither.core.declarations import Function |
||||
from slither.analyses.taint.all_variables import is_tainted as is_tainted_from_inputs |
||||
from slither.analyses.taint.specific_variable import is_tainted |
||||
from slither.analyses.taint.specific_variable import \ |
||||
run_taint as run_taint_variable |
||||
from slither.core.declarations.solidity_variables import (SolidityFunction, |
||||
SolidityVariableComposed) |
||||
from slither.detectors.abstract_detector import (AbstractDetector, |
||||
DetectorClassification) |
||||
from slither.slithir.operations import Binary, BinaryType |
||||
|
||||
|
||||
class Timestamp(AbstractDetector): |
||||
""" |
||||
""" |
||||
|
||||
ARGUMENT = 'timestamp' |
||||
HELP = 'Dangerous usage of `block.timestamp`' |
||||
IMPACT = DetectorClassification.LOW |
||||
CONFIDENCE = DetectorClassification.MEDIUM |
||||
|
||||
WIKI = 'https://github.com/trailofbits/slither/wiki/Vulnerabilities-Description#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_tainted(var, SolidityVariableComposed('block.timestamp')): |
||||
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_tainted(var, SolidityVariableComposed('block.timestamp')): |
||||
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 == contract]: |
||||
nodes = self.timestamp(f) |
||||
if nodes: |
||||
ret.append((f, nodes)) |
||||
return ret |
||||
|
||||
def detect(self): |
||||
""" |
||||
""" |
||||
results = [] |
||||
|
||||
# Taint block.timestamp |
||||
taint = SolidityVariableComposed('block.timestamp') |
||||
run_taint_variable(self.slither, taint) |
||||
|
||||
for c in self.contracts: |
||||
dangerous_timestamp = self.detect_dangerous_timestamp(c) |
||||
for (func, nodes) in dangerous_timestamp: |
||||
|
||||
info = "{}.{} ({}) uses timestamp for comparisons\n" |
||||
info = info.format(func.contract.name, |
||||
func.name, |
||||
func.source_mapping_str) |
||||
info += '\tDangerous comparisons:\n' |
||||
for node in nodes: |
||||
info += '\t- {} ({})\n'.format(node.expression, node.source_mapping_str) |
||||
|
||||
self.log(info) |
||||
|
||||
|
||||
json = self.generate_json_result(info) |
||||
self.add_function_to_json(func, json) |
||||
self.add_nodes_to_json(nodes, json) |
||||
results.append(json) |
||||
|
||||
return results |
@ -0,0 +1 @@ |
||||
[{"check": "timestamp", "impact": "Low", "confidence": "Medium", "description": "Timestamp.bad0 (tests/timestamp.sol#4-6) uses timestamp for comparisons\n\tDangerous comparisons:\n\t- require(bool)(block.timestamp == 0) (tests/timestamp.sol#5)\n", "function": {"name": "bad0", "source_mapping": {"start": 47, "length": 70, "filename": "tests/timestamp.sol", "lines": [4, 5, 6]}, "contract": {"name": "Timestamp", "source_mapping": {"start": 0, "length": 402, "filename": "tests/timestamp.sol", "lines": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]}}}, "expressions": [{"expression": "require(bool)(block.timestamp == 0)", "source_mapping": {"start": 81, "length": 29, "filename": "tests/timestamp.sol", "lines": [5]}}]}, {"check": "timestamp", "impact": "Low", "confidence": "Medium", "description": "Timestamp.bad1 (tests/timestamp.sol#8-11) uses timestamp for comparisons\n\tDangerous comparisons:\n\t- require(bool)(time == 0) (tests/timestamp.sol#10)\n", "function": {"name": "bad1", "source_mapping": {"start": 126, "length": 96, "filename": "tests/timestamp.sol", "lines": [8, 9, 10, 11]}, "contract": {"name": "Timestamp", "source_mapping": {"start": 0, "length": 402, "filename": "tests/timestamp.sol", "lines": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]}}}, "expressions": [{"expression": "require(bool)(time == 0)", "source_mapping": {"start": 197, "length": 18, "filename": "tests/timestamp.sol", "lines": [10]}}]}, {"check": "timestamp", "impact": "Low", "confidence": "Medium", "description": "Timestamp.bad2 (tests/timestamp.sol#13-15) uses timestamp for comparisons\n\tDangerous comparisons:\n\t- block.timestamp > 0 (tests/timestamp.sol#14)\n", "function": {"name": "bad2", "source_mapping": {"start": 231, "length": 79, "filename": "tests/timestamp.sol", "lines": [13, 14, 15]}, "contract": {"name": "Timestamp", "source_mapping": {"start": 0, "length": 402, "filename": "tests/timestamp.sol", "lines": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]}}}, "expressions": [{"expression": "block.timestamp > 0", "source_mapping": {"start": 279, "length": 24, "filename": "tests/timestamp.sol", "lines": [14]}}]}] |
@ -0,0 +1,21 @@ |
||||
contract Timestamp{ |
||||
event Time(uint); |
||||
|
||||
function bad0() external{ |
||||
require(block.timestamp == 0); |
||||
} |
||||
|
||||
function bad1() external{ |
||||
uint time = block.timestamp; |
||||
require(time == 0); |
||||
} |
||||
|
||||
function bad2() external returns(bool){ |
||||
return block.timestamp>0; |
||||
} |
||||
|
||||
function good() external returns(uint){ |
||||
emit Time(block.timestamp); |
||||
} |
||||
} |
||||
|
Loading…
Reference in new issue