Merge pull request #355 from crytic/dev-json

Move json add_ functions from abstractdetectors to json_utils
pull/364/head
Feist Josselin 5 years ago committed by GitHub
commit a578cd66ba
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 220
      slither/detectors/abstract_detector.py
  2. 1
      slither/detectors/attributes/const_functions.py
  3. 1
      slither/detectors/attributes/incorrect_solc.py
  4. 1
      slither/detectors/functions/suicidal.py
  5. 1
      slither/detectors/naming_convention/naming_convention.py
  6. 1
      slither/detectors/operations/unused_return_values.py
  7. 1
      slither/detectors/operations/void_constructor.py
  8. 10
      slither/detectors/reentrancy/reentrancy.py
  9. 8
      slither/detectors/reentrancy/reentrancy_benign.py
  10. 6
      slither/detectors/reentrancy/reentrancy_eth.py
  11. 7
      slither/detectors/reentrancy/reentrancy_read_before_write.py
  12. 5
      slither/detectors/source/rtlo.py
  13. 1
      slither/detectors/statements/controlled_delegatecall.py
  14. 3
      slither/detectors/statements/incorrect_strict_equality.py
  15. 1
      slither/detectors/statements/too_many_digits.py
  16. 1
      slither/detectors/statements/tx_origin.py
  17. 2
      slither/detectors/variables/uninitialized_local_variables.py
  18. 7
      slither/detectors/variables/uninitialized_state_variables.py
  19. 3
      slither/detectors/variables/uninitialized_storage_variables.py
  20. 1
      slither/detectors/variables/unused_state_variables.py
  21. 301
      slither/utils/json_utils.py

@ -1,10 +1,11 @@
import abc
import re
from collections import OrderedDict, defaultdict
from slither.utils.colors import green, yellow, red
from slither.core.source_mapping.source_mapping import SourceMapping
from slither.formatters.exceptions import FormatImpossible
from slither.formatters.utils.patches import apply_patch, create_diff
from slither.utils import json_utils
class IncorrectDetectorInitialization(Exception):
pass
@ -167,177 +168,62 @@ class AbstractDetector(metaclass=abc.ABCMeta):
def color(self):
return classification_colors[self.IMPACT]
def generate_json_result(self, info, additional_fields={}):
d = OrderedDict()
def generate_json_result(self, info, additional_fields=None):
d = json_utils.generate_json_result(info, additional_fields)
d['check'] = self.ARGUMENT
d['impact'] = classification_txt[self.IMPACT]
d['confidence'] = classification_txt[self.CONFIDENCE]
d['description'] = info
d['elements'] = []
if additional_fields:
d['additional_fields'] = additional_fields
return d
@staticmethod
def _create_base_element(type, name, source_mapping, type_specific_fields={}, additional_fields={}):
element = {'type': type,
'name': name,
'source_mapping': source_mapping}
if type_specific_fields:
element['type_specific_fields'] = type_specific_fields
if additional_fields:
element['additional_fields'] = additional_fields
return element
def _create_parent_element(self, element):
from slither.core.children.child_contract import ChildContract
from slither.core.children.child_function import ChildFunction
from slither.core.children.child_inheritance import ChildInheritance
if isinstance(element, ChildInheritance):
if element.contract_declarer:
contract = {'elements': []}
self.add_contract_to_json(element.contract_declarer, contract)
return contract['elements'][0]
elif isinstance(element, ChildContract):
if element.contract:
contract = {'elements': []}
self.add_contract_to_json(element.contract, contract)
return contract['elements'][0]
elif isinstance(element, ChildFunction):
if element.function:
function = {'elements': []}
self.add_function_to_json(element.function, function)
return function['elements'][0]
return None
def add_variable_to_json(self, variable, d, additional_fields={}):
type_specific_fields = {
'parent': self._create_parent_element(variable)
}
element = self._create_base_element('variable',
variable.name,
variable.source_mapping,
type_specific_fields,
additional_fields)
d['elements'].append(element)
def add_variables_to_json(self, variables, d):
for variable in sorted(variables, key=lambda x:x.name):
self.add_variable_to_json(variable, d)
def add_contract_to_json(self, contract, d, additional_fields={}):
element = self._create_base_element('contract',
contract.name,
contract.source_mapping,
{},
additional_fields)
d['elements'].append(element)
def add_function_to_json(self, function, d, additional_fields={}):
type_specific_fields = {
'parent': self._create_parent_element(function),
'signature': function.full_name
}
element = self._create_base_element('function',
function.name,
function.source_mapping,
type_specific_fields,
additional_fields)
d['elements'].append(element)
def add_functions_to_json(self, functions, d, additional_fields={}):
for function in sorted(functions, key=lambda x: x.name):
self.add_function_to_json(function, d, additional_fields)
def add_enum_to_json(self, enum, d, additional_fields={}):
type_specific_fields = {
'parent': self._create_parent_element(enum)
}
element = self._create_base_element('enum',
enum.name,
enum.source_mapping,
type_specific_fields,
additional_fields)
d['elements'].append(element)
def add_struct_to_json(self, struct, d, additional_fields={}):
type_specific_fields = {
'parent': self._create_parent_element(struct)
}
element = self._create_base_element('struct',
struct.name,
struct.source_mapping,
type_specific_fields,
additional_fields)
d['elements'].append(element)
def add_event_to_json(self, event, d, additional_fields={}):
type_specific_fields = {
'parent': self._create_parent_element(event),
'signature': event.full_name
}
element = self._create_base_element('event',
event.name,
event.source_mapping,
type_specific_fields,
additional_fields)
d['elements'].append(element)
def add_node_to_json(self, node, d, additional_fields={}):
type_specific_fields = {
'parent': self._create_parent_element(node),
}
node_name = str(node.expression) if node.expression else ""
element = self._create_base_element('node',
node_name,
node.source_mapping,
type_specific_fields,
additional_fields)
d['elements'].append(element)
def add_nodes_to_json(self, nodes, d):
for node in sorted(nodes, key=lambda x: x.node_id):
self.add_node_to_json(node, d)
def add_pragma_to_json(self, pragma, d, additional_fields={}):
type_specific_fields = {
'directive': pragma.directive
}
element = self._create_base_element('pragma',
pragma.version,
pragma.source_mapping,
type_specific_fields,
additional_fields)
d['elements'].append(element)
def add_other_to_json(self, name, source_mapping, d, additional_fields={}):
# If this a tuple with (filename, start, end), convert it to a source mapping.
if isinstance(source_mapping, tuple):
# Parse the source id
(filename, start, end) = source_mapping
source_id = next((source_unit_id for (source_unit_id, source_unit_filename) in self.slither.source_units.items() if source_unit_filename == filename), -1)
# Convert to a source mapping string
source_mapping = f"{start}:{end}:{source_id}"
# If this is a source mapping string, parse it.
if isinstance(source_mapping, str):
source_mapping_str = source_mapping
source_mapping = SourceMapping()
source_mapping.set_offset(source_mapping_str, self.slither)
# If this is a source mapping object, get the underlying source mapping dictionary
if isinstance(source_mapping, SourceMapping):
source_mapping = source_mapping.source_mapping
# Create the underlying element and add it to our resulting json
element = self._create_base_element('other',
name,
source_mapping,
{},
additional_fields)
d['elements'].append(element)
def add_variable_to_json(e, d, additional_fields=None):
json_utils.add_variable_to_json(e, d, additional_fields=additional_fields)
@staticmethod
def add_variables_to_json(e, d):
json_utils.add_variables_to_json(e, d)
@staticmethod
def add_contract_to_json(e, d, additional_fields=None):
json_utils.add_contract_to_json(e, d, additional_fields=additional_fields)
@staticmethod
def add_function_to_json(e, d, additional_fields=None):
json_utils.add_function_to_json(e, d, additional_fields=additional_fields)
@staticmethod
def add_functions_to_json(e, d, additional_fields=None):
json_utils.add_functions_to_json(e, d, additional_fields=additional_fields)
@staticmethod
def add_enum_to_json(e, d, additional_fields=None):
json_utils.add_enum_to_json(e, d, additional_fields=additional_fields)
@staticmethod
def add_struct_to_json(e, d, additional_fields=None):
json_utils.add_struct_to_json(e, d, additional_fields=additional_fields)
@staticmethod
def add_event_to_json(e, d, additional_fields=None):
json_utils.add_event_to_json(e, d, additional_fields=additional_fields)
@staticmethod
def add_pragma_to_json(e, d, additional_fields=None):
json_utils.add_pragma_to_json(e, d, additional_fields=additional_fields)
@staticmethod
def add_node_to_json(e, d, additional_fields=None):
json_utils.add_node_to_json(e, d, additional_fields=additional_fields)
@staticmethod
def add_nodes_to_json(e, d):
json_utils.add_nodes_to_json(e, d)
@staticmethod
def add_other_to_json(name, source_mapping, d, slither, additional_fields=None):
json_utils.add_other_to_json(name, source_mapping, d, slither, additional_fields=additional_fields)
@staticmethod
def _format(slither, result):

@ -5,6 +5,7 @@ Recursively check the called functions
from slither.detectors.abstract_detector import AbstractDetector, DetectorClassification
from slither.formatters.attributes.const_functions import format
class ConstantFunctions(AbstractDetector):
"""
Constant function detector

@ -12,6 +12,7 @@ from slither.formatters.attributes.incorrect_solc import format
# 2: version number
# 3: version number
# 4: version number
PATTERN = re.compile('(\^|>|>=|<|<=)?([ ]+)?(\d+)\.(\d+)\.(\d+)')
class IncorrectSolc(AbstractDetector):

@ -6,6 +6,7 @@ A suicidal contract is an unprotected function that calls selfdestruct
from slither.detectors.abstract_detector import AbstractDetector, DetectorClassification
class Suicidal(AbstractDetector):
"""
Unprotected function detector

@ -3,7 +3,6 @@ from slither.detectors.abstract_detector import AbstractDetector, DetectorClassi
from slither.formatters.naming_convention.naming_convention import format
class NamingConvention(AbstractDetector):
"""
Check if naming conventions are followed

@ -6,6 +6,7 @@ from slither.detectors.abstract_detector import AbstractDetector, DetectorClassi
from slither.slithir.operations import HighLevelCall, InternalCall, InternalDynamicCall
from slither.core.variables.state_variable import StateVariable
class UnusedReturnValues(AbstractDetector):
"""
If the return value of a function is never used, it's likely to be bug

@ -2,6 +2,7 @@
from slither.detectors.abstract_detector import AbstractDetector, DetectorClassification
from slither.slithir.operations import Nop
class VoidConstructor(AbstractDetector):
ARGUMENT = 'void-cst'

@ -6,13 +6,11 @@
"""
from slither.core.cfg.node import NodeType
from slither.core.declarations import Function, SolidityFunction, SolidityVariable
from slither.core.declarations import Function
from slither.core.expressions import UnaryOperation, UnaryOperationType
from slither.detectors.abstract_detector import (AbstractDetector,
DetectorClassification)
from slither.slithir.operations import (HighLevelCall, LowLevelCall,
Call,
Send, Transfer)
from slither.detectors.abstract_detector import AbstractDetector
from slither.slithir.operations import Call
def union_dict(d1, d2):
d3 = {k: d1.get(k, set()) | d2.get(k, set()) for k in set(list(d1.keys()) + list(d2.keys()))}

@ -5,14 +5,8 @@
Iterate over all the nodes of the graph until reaching a fixpoint
"""
from slither.core.cfg.node import NodeType
from slither.core.declarations import Function, SolidityFunction
from slither.core.expressions import UnaryOperation, UnaryOperationType
from slither.detectors.abstract_detector import DetectorClassification
from slither.visitors.expression.export_values import ExportValues
from slither.slithir.operations import (HighLevelCall, LowLevelCall,
LibraryCall,
Send, Transfer)
from .reentrancy import Reentrancy

@ -4,13 +4,7 @@
Based on heuristics, it may lead to FP and FN
Iterate over all the nodes of the graph until reaching a fixpoint
"""
from slither.core.cfg.node import NodeType
from slither.core.declarations import Function, SolidityFunction
from slither.core.expressions import UnaryOperation, UnaryOperationType
from slither.detectors.abstract_detector import DetectorClassification
from slither.slithir.operations import (HighLevelCall, LowLevelCall,
LibraryCall,
Send, Transfer)
from .reentrancy import Reentrancy

@ -5,14 +5,7 @@
Iterate over all the nodes of the graph until reaching a fixpoint
"""
from slither.core.cfg.node import NodeType
from slither.core.declarations import Function, SolidityFunction
from slither.core.expressions import UnaryOperation, UnaryOperationType
from slither.detectors.abstract_detector import DetectorClassification
from slither.visitors.expression.export_values import ExportValues
from slither.slithir.operations import (HighLevelCall, LowLevelCall,
LibraryCall,
Send, Transfer)
from .reentrancy import Reentrancy

@ -1,6 +1,7 @@
from slither.detectors.abstract_detector import AbstractDetector, DetectorClassification
import re
class RightToLeftOverride(AbstractDetector):
"""
Detect the usage of a Right-To-Left-Override (U+202E) character
@ -73,7 +74,9 @@ contract Token
info += f"\t- {pattern.findall(source_encoded)[0]}\n"
json = self.generate_json_result(info)
self.add_other_to_json("rtlo-character",
(filename, idx, len(self.RTLO_CHARACTER_ENCODED)), json)
(filename, idx, len(self.RTLO_CHARACTER_ENCODED)),
json,
self.slither)
results.append(json)
# Advance the start index for the next iteration

@ -2,6 +2,7 @@ from slither.detectors.abstract_detector import AbstractDetector, DetectorClassi
from slither.slithir.operations import LowLevelCall
from slither.analyses.data_dependency.data_dependency import is_tainted
class ControlledDelegateCall(AbstractDetector):
"""
"""

@ -3,7 +3,6 @@
"""
import itertools
from slither.analyses.data_dependency.data_dependency import is_dependent_ssa
from slither.core.declarations import Function
from slither.detectors.abstract_detector import (AbstractDetector,
@ -15,7 +14,7 @@ from slither.core.solidity_types import MappingType, ElementaryType
from slither.core.variables.state_variable import StateVariable
from slither.core.declarations.solidity_variables import SolidityVariable, SolidityVariableComposed
from slither.slithir.variables import ReferenceVariable
class IncorrectStrictEquality(AbstractDetector):
ARGUMENT = 'incorrect-equality'

@ -5,6 +5,7 @@ Module detecting numbers with too many digits.
from slither.detectors.abstract_detector import AbstractDetector, DetectorClassification
from slither.slithir.variables import Constant
class TooManyDigits(AbstractDetector):
"""
Detect numbers with too many digits

@ -4,6 +4,7 @@ Module detecting usage of `tx.origin` in a conditional node
from slither.detectors.abstract_detector import AbstractDetector, DetectorClassification
class TxOrigin(AbstractDetector):
"""
Detect usage of tx.origin in a conditional node

@ -6,8 +6,6 @@
"""
from slither.detectors.abstract_detector import AbstractDetector, DetectorClassification
from slither.core.cfg.node import NodeType
from slither.visitors.expression.find_push import FindPush
class UninitializedLocalVars(AbstractDetector):

@ -10,12 +10,7 @@
"""
from slither.detectors.abstract_detector import AbstractDetector, DetectorClassification
from slither.core.variables.state_variable import StateVariable
from slither.slithir.variables import ReferenceVariable
from slither.slithir.operations.assignment import Assignment
from slither.slithir.operations import (OperationWithLValue, Index, Member,
InternalCall, InternalDynamicCall, LibraryCall)
from slither.slithir.operations import InternalCall, LibraryCall
class UninitializedStateVarsDetection(AbstractDetector):

@ -7,8 +7,6 @@
from slither.detectors.abstract_detector import AbstractDetector, DetectorClassification
from slither.visitors.expression.find_push import FindPush
class UninitializedStorageVars(AbstractDetector):
"""
@ -108,7 +106,6 @@ Bob calls `func`. As a result, `owner` is override to 0.
info = "{} in {} ({}) is a storage variable never initialiazed\n"
info = info.format(var_name, function.canonical_name, uninitialized_storage_variable.source_mapping_str)
json = self.generate_json_result(info)
self.add_variable_to_json(uninitialized_storage_variable, json)
self.add_function_to_json(function, json)

@ -64,7 +64,6 @@ class UnusedStateVars(AbstractDetector):
var.source_mapping_str,
c.name)
json = self.generate_json_result(info)
self.add_variable_to_json(var, json)
self.add_contract_to_json(c, json)

@ -1,9 +1,20 @@
import os
import json
import logging
from collections import OrderedDict
from slither.core.source_mapping.source_mapping import SourceMapping
from slither.utils.colors import yellow
logger = logging.getLogger("Slither")
###################################################################################
###################################################################################
# region Output
###################################################################################
###################################################################################
def output_json(filename, error, results):
"""
@ -34,3 +45,293 @@ def output_json(filename, error, results):
else:
with open(filename, 'w', encoding='utf8') as f:
json.dump(json_result, f, indent=2)
# endregion
###################################################################################
###################################################################################
# region Json generation
###################################################################################
###################################################################################
def generate_json_result(info, additional_fields=None):
if additional_fields is None:
additional_fields = {}
d = OrderedDict()
d['elements'] = []
d['description'] = info
if additional_fields:
d['additional_fields'] = additional_fields
return d
# endregion
###################################################################################
###################################################################################
# region Internal functions
###################################################################################
###################################################################################
def _create_base_element(type, name, source_mapping, type_specific_fields=None, additional_fields=None):
if additional_fields is None:
additional_fields = {}
if type_specific_fields is None:
type_specific_fields = {}
element = {'type': type,
'name': name,
'source_mapping': source_mapping}
if type_specific_fields:
element['type_specific_fields'] = type_specific_fields
if additional_fields:
element['additional_fields'] = additional_fields
return element
def _create_parent_element(element):
from slither.core.children.child_contract import ChildContract
from slither.core.children.child_function import ChildFunction
from slither.core.children.child_inheritance import ChildInheritance
if isinstance(element, ChildInheritance):
if element.contract_declarer:
contract = {'elements': []}
add_contract_to_json(element.contract_declarer, contract)
return contract['elements'][0]
elif isinstance(element, ChildContract):
if element.contract:
contract = {'elements': []}
add_contract_to_json(element.contract, contract)
return contract['elements'][0]
elif isinstance(element, ChildFunction):
if element.function:
function = {'elements': []}
add_function_to_json(element.function, function)
return function['elements'][0]
return None
# endregion
###################################################################################
###################################################################################
# region Variables
###################################################################################
###################################################################################
def add_variable_to_json(variable, d, additional_fields=None):
if additional_fields is None:
additional_fields = {}
type_specific_fields = {
'parent': _create_parent_element(variable)
}
element = _create_base_element('variable',
variable.name,
variable.source_mapping,
type_specific_fields,
additional_fields)
d['elements'].append(element)
def add_variables_to_json(variables, d):
for variable in sorted(variables, key=lambda x: x.name):
add_variable_to_json(variable, d)
# endregion
###################################################################################
###################################################################################
# region Contract
###################################################################################
###################################################################################
def add_contract_to_json(contract, d, additional_fields=None):
if additional_fields is None:
additional_fields = {}
element = _create_base_element('contract',
contract.name,
contract.source_mapping,
{},
additional_fields)
d['elements'].append(element)
# endregion
###################################################################################
###################################################################################
# region Functions
###################################################################################
###################################################################################
def add_function_to_json(function, d, additional_fields=None):
if additional_fields is None:
additional_fields = {}
type_specific_fields = {
'parent': _create_parent_element(function),
'signature': function.full_name
}
element = _create_base_element('function',
function.name,
function.source_mapping,
type_specific_fields,
additional_fields)
d['elements'].append(element)
def add_functions_to_json(functions, d, additional_fields=None):
if additional_fields is None:
additional_fields = {}
for function in sorted(functions, key=lambda x: x.name):
add_function_to_json(function, d, additional_fields)
# endregion
###################################################################################
###################################################################################
# region Enum
###################################################################################
###################################################################################
def add_enum_to_json(enum, d, additional_fields=None):
if additional_fields is None:
additional_fields = {}
type_specific_fields = {
'parent': _create_parent_element(enum)
}
element = _create_base_element('enum',
enum.name,
enum.source_mapping,
type_specific_fields,
additional_fields)
d['elements'].append(element)
# endregion
###################################################################################
###################################################################################
# region Structures
###################################################################################
###################################################################################
def add_struct_to_json(struct, d, additional_fields=None):
if additional_fields is None:
additional_fields = {}
type_specific_fields = {
'parent': _create_parent_element(struct)
}
element = _create_base_element('struct',
struct.name,
struct.source_mapping,
type_specific_fields,
additional_fields)
d['elements'].append(element)
# endregion
###################################################################################
###################################################################################
# region Events
###################################################################################
###################################################################################
def add_event_to_json(event, d, additional_fields=None):
if additional_fields is None:
additional_fields = {}
type_specific_fields = {
'parent': _create_parent_element(event),
'signature': event.full_name
}
element = _create_base_element('event',
event.name,
event.source_mapping,
type_specific_fields,
additional_fields)
d['elements'].append(element)
# endregion
###################################################################################
###################################################################################
# region Nodes
###################################################################################
###################################################################################
def add_node_to_json(node, d, additional_fields=None):
if additional_fields is None:
additional_fields = {}
type_specific_fields = {
'parent': _create_parent_element(node),
}
node_name = str(node.expression) if node.expression else ""
element = _create_base_element('node',
node_name,
node.source_mapping,
type_specific_fields,
additional_fields)
d['elements'].append(element)
def add_nodes_to_json(nodes, d):
for node in sorted(nodes, key=lambda x: x.node_id):
add_node_to_json(node, d)
# endregion
###################################################################################
###################################################################################
# region Pragma
###################################################################################
###################################################################################
def add_pragma_to_json(pragma, d, additional_fields=None):
if additional_fields is None:
additional_fields = {}
type_specific_fields = {
'directive': pragma.directive
}
element = _create_base_element('pragma',
pragma.version,
pragma.source_mapping,
type_specific_fields,
additional_fields)
d['elements'].append(element)
# endregion
###################################################################################
###################################################################################
# region Others
###################################################################################
###################################################################################
def add_other_to_json(name, source_mapping, d, slither, additional_fields=None):
# If this a tuple with (filename, start, end), convert it to a source mapping.
if additional_fields is None:
additional_fields = {}
if isinstance(source_mapping, tuple):
# Parse the source id
(filename, start, end) = source_mapping
source_id = next(
(source_unit_id for (source_unit_id, source_unit_filename) in slither.source_units.items() if
source_unit_filename == filename), -1)
# Convert to a source mapping string
source_mapping = f"{start}:{end}:{source_id}"
# If this is a source mapping string, parse it.
if isinstance(source_mapping, str):
source_mapping_str = source_mapping
source_mapping = SourceMapping()
source_mapping.set_offset(source_mapping_str, slither)
# If this is a source mapping object, get the underlying source mapping dictionary
if isinstance(source_mapping, SourceMapping):
source_mapping = source_mapping.source_mapping
# Create the underlying element and add it to our resulting json
element = _create_base_element('other',
name,
source_mapping,
{},
additional_fields)
d['elements'].append(element)

Loading…
Cancel
Save