|
|
@ -1,6 +1,8 @@ |
|
|
|
import abc |
|
|
|
import abc |
|
|
|
import re |
|
|
|
import re |
|
|
|
from typing import Optional, List, TYPE_CHECKING |
|
|
|
from functools import partial |
|
|
|
|
|
|
|
from logging import Logger |
|
|
|
|
|
|
|
from typing import Optional, List, TYPE_CHECKING, Dict, Union, Callable |
|
|
|
|
|
|
|
|
|
|
|
from slither.core.compilation_unit import SlitherCompilationUnit |
|
|
|
from slither.core.compilation_unit import SlitherCompilationUnit |
|
|
|
from slither.core.declarations import Contract |
|
|
|
from slither.core.declarations import Contract |
|
|
@ -8,7 +10,7 @@ from slither.utils.colors import green, yellow, red |
|
|
|
from slither.formatters.exceptions import FormatImpossible |
|
|
|
from slither.formatters.exceptions import FormatImpossible |
|
|
|
from slither.formatters.utils.patches import apply_patch, create_diff |
|
|
|
from slither.formatters.utils.patches import apply_patch, create_diff |
|
|
|
from slither.utils.comparable_enum import ComparableEnum |
|
|
|
from slither.utils.comparable_enum import ComparableEnum |
|
|
|
from slither.utils.output import Output |
|
|
|
from slither.utils.output import Output, SupportedOutput |
|
|
|
|
|
|
|
|
|
|
|
if TYPE_CHECKING: |
|
|
|
if TYPE_CHECKING: |
|
|
|
from slither import Slither |
|
|
|
from slither import Slither |
|
|
@ -25,8 +27,10 @@ class DetectorClassification(ComparableEnum): |
|
|
|
INFORMATIONAL = 3 |
|
|
|
INFORMATIONAL = 3 |
|
|
|
OPTIMIZATION = 4 |
|
|
|
OPTIMIZATION = 4 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
UNIMPLEMENTED = 999 |
|
|
|
|
|
|
|
|
|
|
|
classification_colors = { |
|
|
|
|
|
|
|
|
|
|
|
classification_colors: Dict[DetectorClassification, Callable[[str], str]] = { |
|
|
|
DetectorClassification.INFORMATIONAL: green, |
|
|
|
DetectorClassification.INFORMATIONAL: green, |
|
|
|
DetectorClassification.OPTIMIZATION: green, |
|
|
|
DetectorClassification.OPTIMIZATION: green, |
|
|
|
DetectorClassification.LOW: green, |
|
|
|
DetectorClassification.LOW: green, |
|
|
@ -46,8 +50,8 @@ classification_txt = { |
|
|
|
class AbstractDetector(metaclass=abc.ABCMeta): |
|
|
|
class AbstractDetector(metaclass=abc.ABCMeta): |
|
|
|
ARGUMENT = "" # run the detector with slither.py --ARGUMENT |
|
|
|
ARGUMENT = "" # run the detector with slither.py --ARGUMENT |
|
|
|
HELP = "" # help information |
|
|
|
HELP = "" # help information |
|
|
|
IMPACT: Optional[DetectorClassification] = None |
|
|
|
IMPACT: DetectorClassification = DetectorClassification.UNIMPLEMENTED |
|
|
|
CONFIDENCE: Optional[DetectorClassification] = None |
|
|
|
CONFIDENCE: DetectorClassification = DetectorClassification.UNIMPLEMENTED |
|
|
|
|
|
|
|
|
|
|
|
WIKI = "" |
|
|
|
WIKI = "" |
|
|
|
|
|
|
|
|
|
|
@ -58,7 +62,9 @@ class AbstractDetector(metaclass=abc.ABCMeta): |
|
|
|
|
|
|
|
|
|
|
|
STANDARD_JSON = True |
|
|
|
STANDARD_JSON = True |
|
|
|
|
|
|
|
|
|
|
|
def __init__(self, compilation_unit: SlitherCompilationUnit, slither, logger): |
|
|
|
def __init__( |
|
|
|
|
|
|
|
self, compilation_unit: SlitherCompilationUnit, slither: "Slither", logger: Logger |
|
|
|
|
|
|
|
): |
|
|
|
self.compilation_unit: SlitherCompilationUnit = compilation_unit |
|
|
|
self.compilation_unit: SlitherCompilationUnit = compilation_unit |
|
|
|
self.contracts: List[Contract] = compilation_unit.contracts |
|
|
|
self.contracts: List[Contract] = compilation_unit.contracts |
|
|
|
self.slither: "Slither" = slither |
|
|
|
self.slither: "Slither" = slither |
|
|
@ -130,32 +136,25 @@ class AbstractDetector(metaclass=abc.ABCMeta): |
|
|
|
"CONFIDENCE is not initialized {}".format(self.__class__.__name__) |
|
|
|
"CONFIDENCE is not initialized {}".format(self.__class__.__name__) |
|
|
|
) |
|
|
|
) |
|
|
|
|
|
|
|
|
|
|
|
def _log(self, info): |
|
|
|
def _log(self, info: str) -> None: |
|
|
|
if self.logger: |
|
|
|
if self.logger: |
|
|
|
self.logger.info(self.color(info)) |
|
|
|
self.logger.info(self.color(info)) |
|
|
|
|
|
|
|
|
|
|
|
@abc.abstractmethod |
|
|
|
@abc.abstractmethod |
|
|
|
def _detect(self): |
|
|
|
def _detect(self) -> List[Output]: |
|
|
|
"""TODO Documentation""" |
|
|
|
"""TODO Documentation""" |
|
|
|
return [] |
|
|
|
return [] |
|
|
|
|
|
|
|
|
|
|
|
# pylint: disable=too-many-branches |
|
|
|
# pylint: disable=too-many-branches |
|
|
|
def detect(self): |
|
|
|
def detect(self) -> List[Dict]: |
|
|
|
results = [] |
|
|
|
results: List[Dict] = [] |
|
|
|
# only keep valid result, and remove dupplicate |
|
|
|
# only keep valid result, and remove dupplicate |
|
|
|
# Keep only dictionaries |
|
|
|
# Keep only dictionaries |
|
|
|
for r in [r.data for r in self._detect()]: |
|
|
|
for r in [output.data for output in self._detect()]: |
|
|
|
if self.compilation_unit.core.valid_result(r) and r not in results: |
|
|
|
if self.compilation_unit.core.valid_result(r) and r not in results: |
|
|
|
results.append(r) |
|
|
|
results.append(r) |
|
|
|
if results: |
|
|
|
if results and self.logger: |
|
|
|
if self.logger: |
|
|
|
self._log_result(results) |
|
|
|
info = "\n" |
|
|
|
|
|
|
|
for idx, result in enumerate(results): |
|
|
|
|
|
|
|
if self.slither.triage_mode: |
|
|
|
|
|
|
|
info += "{}: ".format(idx) |
|
|
|
|
|
|
|
info += result["description"] |
|
|
|
|
|
|
|
info += "Reference: {}".format(self.WIKI) |
|
|
|
|
|
|
|
self._log(info) |
|
|
|
|
|
|
|
if self.compilation_unit.core.generate_patches: |
|
|
|
if self.compilation_unit.core.generate_patches: |
|
|
|
for result in results: |
|
|
|
for result in results: |
|
|
|
try: |
|
|
|
try: |
|
|
@ -205,20 +204,24 @@ class AbstractDetector(metaclass=abc.ABCMeta): |
|
|
|
if indexes.endswith("]"): |
|
|
|
if indexes.endswith("]"): |
|
|
|
indexes = indexes[:-1] |
|
|
|
indexes = indexes[:-1] |
|
|
|
try: |
|
|
|
try: |
|
|
|
indexes = [int(i) for i in indexes.split(",")] |
|
|
|
indexes_converted = [int(i) for i in indexes.split(",")] |
|
|
|
self.slither.save_results_to_hide( |
|
|
|
self.slither.save_results_to_hide( |
|
|
|
[r for (idx, r) in enumerate(results) if idx in indexes] |
|
|
|
[r for (idx, r) in enumerate(results) if idx in indexes_converted] |
|
|
|
) |
|
|
|
) |
|
|
|
return [r for (idx, r) in enumerate(results) if idx not in indexes] |
|
|
|
return [r for (idx, r) in enumerate(results) if idx not in indexes_converted] |
|
|
|
except ValueError: |
|
|
|
except ValueError: |
|
|
|
self.logger.error(yellow("Malformed input. Example of valid input: 0,1,2,3")) |
|
|
|
self.logger.error(yellow("Malformed input. Example of valid input: 0,1,2,3")) |
|
|
|
return results |
|
|
|
return results |
|
|
|
|
|
|
|
|
|
|
|
@property |
|
|
|
@property |
|
|
|
def color(self): |
|
|
|
def color(self) -> Callable[[str], str]: |
|
|
|
return classification_colors[self.IMPACT] |
|
|
|
return classification_colors[self.IMPACT] |
|
|
|
|
|
|
|
|
|
|
|
def generate_result(self, info, additional_fields=None): |
|
|
|
def generate_result( |
|
|
|
|
|
|
|
self, |
|
|
|
|
|
|
|
info: Union[str, List[Union[str, SupportedOutput]]], |
|
|
|
|
|
|
|
additional_fields: Optional[Dict] = None, |
|
|
|
|
|
|
|
) -> Output: |
|
|
|
output = Output( |
|
|
|
output = Output( |
|
|
|
info, |
|
|
|
info, |
|
|
|
additional_fields, |
|
|
|
additional_fields, |
|
|
@ -233,6 +236,15 @@ class AbstractDetector(metaclass=abc.ABCMeta): |
|
|
|
return output |
|
|
|
return output |
|
|
|
|
|
|
|
|
|
|
|
@staticmethod |
|
|
|
@staticmethod |
|
|
|
def _format(_compilation_unit: SlitherCompilationUnit, _result): |
|
|
|
def _format(_compilation_unit: SlitherCompilationUnit, _result: Dict) -> None: |
|
|
|
"""Implement format""" |
|
|
|
"""Implement format""" |
|
|
|
return |
|
|
|
return |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _log_result(self, results: List[Dict]) -> None: |
|
|
|
|
|
|
|
info = "\n" |
|
|
|
|
|
|
|
for idx, result in enumerate(results): |
|
|
|
|
|
|
|
if self.slither.triage_mode: |
|
|
|
|
|
|
|
info += "{}: ".format(idx) |
|
|
|
|
|
|
|
info += result["description"] |
|
|
|
|
|
|
|
info += "Reference: {}".format(self.WIKI) |
|
|
|
|
|
|
|
self._log(info) |
|
|
|