Move json functions to slither.utils.json_utils

pull/355/head
Josselin 5 years ago
parent 7e58e92a58
commit 8d32de0472
  1. 176
      slither/detectors/abstract_detector.py
  2. 8
      slither/detectors/attributes/const_functions.py
  3. 3
      slither/detectors/attributes/constant_pragma.py
  4. 4
      slither/detectors/attributes/incorrect_solc.py
  5. 5
      slither/detectors/attributes/locked_ether.py
  6. 3
      slither/detectors/erc/incorrect_erc20_interface.py
  7. 3
      slither/detectors/erc/incorrect_erc721_interface.py
  8. 3
      slither/detectors/erc/unindexed_event_parameters.py
  9. 3
      slither/detectors/examples/backdoor.py
  10. 5
      slither/detectors/functions/arbitrary_send.py
  11. 3
      slither/detectors/functions/complex_function.py
  12. 5
      slither/detectors/functions/external_function.py
  13. 4
      slither/detectors/functions/suicidal.py
  14. 22
      slither/detectors/naming_convention/naming_convention.py
  15. 5
      slither/detectors/operations/block_timestamp.py
  16. 5
      slither/detectors/operations/low_level_calls.py
  17. 6
      slither/detectors/operations/unused_return_values.py
  18. 6
      slither/detectors/operations/void_constructor.py
  19. 9
      slither/detectors/reentrancy/reentrancy_benign.py
  20. 10
      slither/detectors/reentrancy/reentrancy_eth.py
  21. 7
      slither/detectors/reentrancy/reentrancy_read_before_write.py
  22. 3
      slither/detectors/shadowing/abstract.py
  23. 7
      slither/detectors/shadowing/builtin_symbols.py
  24. 9
      slither/detectors/shadowing/local.py
  25. 3
      slither/detectors/shadowing/state.py
  26. 9
      slither/detectors/source/rtlo.py
  27. 5
      slither/detectors/statements/assembly.py
  28. 3
      slither/detectors/statements/calls_in_loop.py
  29. 6
      slither/detectors/statements/controlled_delegatecall.py
  30. 5
      slither/detectors/statements/deprecated_calls.py
  31. 6
      slither/detectors/statements/incorrect_strict_equality.py
  32. 4
      slither/detectors/statements/too_many_digits.py
  33. 4
      slither/detectors/statements/tx_origin.py
  34. 3
      slither/detectors/variables/possible_const_state_variables.py
  35. 5
      slither/detectors/variables/uninitialized_local_variables.py
  36. 5
      slither/detectors/variables/uninitialized_state_variables.py
  37. 5
      slither/detectors/variables/uninitialized_storage_variables.py
  38. 5
      slither/detectors/variables/unused_state_variables.py
  39. 301
      slither/utils/json_utils.py

@ -1,8 +1,9 @@
import abc
import re
from collections import OrderedDict, defaultdict
from collections import OrderedDict
from slither.utils import json_utils
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
@ -167,177 +168,14 @@ 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)
return d
@staticmethod
def _format(slither, result):

@ -4,6 +4,8 @@ Recursively check the called functions
"""
from slither.detectors.abstract_detector import AbstractDetector, DetectorClassification
from slither.formatters.attributes.const_functions import format
from slither.utils import json_utils
class ConstantFunctions(AbstractDetector):
"""
@ -59,7 +61,7 @@ All the calls to `get` revert, breaking Bob's smart contract execution.'''
info = '{} ({}) is declared {} but contains assembly code\n'
info = info.format(f.canonical_name, f.source_mapping_str, attr)
json = self.generate_json_result(info, {'contains_assembly': True})
self.add_function_to_json(f, json)
json_utils.add_function_to_json(f, json)
results.append(json)
variables_written = f.all_state_variables_written()
@ -71,8 +73,8 @@ All the calls to `get` revert, breaking Bob's smart contract execution.'''
info += '\t- {}\n'.format(variable_written.canonical_name)
json = self.generate_json_result(info, {'contains_assembly': False})
self.add_function_to_json(f, json)
self.add_variables_to_json(variables_written, json)
json_utils.add_function_to_json(f, json)
json_utils.add_variables_to_json(variables_written, json)
results.append(json)
return results

@ -4,6 +4,7 @@
from slither.detectors.abstract_detector import AbstractDetector, DetectorClassification
from slither.formatters.attributes.constant_pragma import format
from slither.utils import json_utils
class ConstantPragma(AbstractDetector):
@ -39,7 +40,7 @@ class ConstantPragma(AbstractDetector):
# Add each pragma to our elements
for p in pragma:
self.add_pragma_to_json(p, json)
json_utils.add_pragma_to_json(p, json)
results.append(json)
return results

@ -12,6 +12,8 @@ from slither.formatters.attributes.incorrect_solc import format
# 2: version number
# 3: version number
# 4: version number
from slither.utils import json_utils
PATTERN = re.compile('(\^|>|>=|<|<=)?([ ]+)?(\d+)\.(\d+)\.(\d+)')
class IncorrectSolc(AbstractDetector):
@ -102,7 +104,7 @@ Use Solidity 0.4.25 or 0.5.3. Consider using the latest version of Solidity for
info = f"Pragma version \"{p.version}\" {reason} ({p.source_mapping_str})\n"
json = self.generate_json_result(info)
self.add_pragma_to_json(p, json)
json_utils.add_pragma_to_json(p, json)
results.append(json)
return results

@ -6,6 +6,7 @@ from slither.detectors.abstract_detector import (AbstractDetector,
DetectorClassification)
from slither.slithir.operations import (HighLevelCall, LowLevelCall, Send,
Transfer, NewContract, LibraryCall, InternalCall)
from slither.utils import json_utils
class LockedEther(AbstractDetector):
@ -84,8 +85,8 @@ Every ether sent to `Locked` will be lost.'''
[f.name for f in funcs_payable])
json = self.generate_json_result(info)
self.add_contract_to_json(contract, json)
self.add_functions_to_json(funcs_payable, json)
json_utils.add_contract_to_json(contract, json)
json_utils.add_functions_to_json(funcs_payable, json)
results.append(json)
return results

@ -3,6 +3,7 @@ Detect incorrect erc20 interface.
Some contracts do not return a bool on transfer/transferFrom/approve, which may lead to preventing the contract to be used with contracts compiled with recent solc (>0.4.22)
"""
from slither.detectors.abstract_detector import AbstractDetector, DetectorClassification
from slither.utils import json_utils
class IncorrectERC20InterfaceDetection(AbstractDetector):
@ -92,7 +93,7 @@ contract Token{
function.full_name,
function.source_mapping_str)
json = self.generate_json_result(info)
self.add_function_to_json(function, json)
json_utils.add_function_to_json(function, json)
results.append(json)
return results

@ -2,6 +2,7 @@
Detect incorrect erc721 interface.
"""
from slither.detectors.abstract_detector import AbstractDetector, DetectorClassification
from slither.utils import json_utils
class IncorrectERC721InterfaceDetection(AbstractDetector):
@ -91,7 +92,7 @@ contract Token{
function.full_name,
function.source_mapping_str)
json = self.generate_json_result(info)
self.add_function_to_json(function, json)
json_utils.add_function_to_json(function, json)
results.append(json)
return results

@ -2,6 +2,7 @@
Detect mistakenly un-indexed ERC20 event parameters
"""
from slither.detectors.abstract_detector import AbstractDetector, DetectorClassification
from slither.utils import json_utils
class UnindexedERC20EventParameters(AbstractDetector):
@ -75,7 +76,7 @@ In this case, Transfer and Approval events should have the 'indexed' keyword on
# Add the events to the JSON (note: we do not add the params/vars as they have no source mapping).
json = self.generate_json_result(info)
self.add_event_to_json(event, json, {
json_utils.add_event_to_json(event, json, {
"parameter_name": parameter.name
})
results.append(json)

@ -1,4 +1,5 @@
from slither.detectors.abstract_detector import AbstractDetector, DetectorClassification
from slither.utils import json_utils
class Backdoor(AbstractDetector):
@ -30,7 +31,7 @@ class Backdoor(AbstractDetector):
info = info.format(contract.name, f.name, f.source_mapping_str)
# Add the result in result
json = self.generate_json_result(info)
self.add_function_to_json(f, json)
json_utils.add_function_to_json(f, json)
results.append(json)
return results

@ -17,6 +17,7 @@ from slither.detectors.abstract_detector import (AbstractDetector,
DetectorClassification)
from slither.slithir.operations import (HighLevelCall, Index, LowLevelCall,
Send, SolidityCall, Transfer)
from slither.utils import json_utils
class ArbitrarySend(AbstractDetector):
@ -117,8 +118,8 @@ Bob calls `setDestination` and `withdraw`. As a result he withdraws the contract
info += '\t- {} ({})\n'.format(node.expression, node.source_mapping_str)
json = self.generate_json_result(info)
self.add_function_to_json(func, json)
self.add_nodes_to_json(nodes, json)
json_utils.add_function_to_json(func, json)
json_utils.add_nodes_to_json(nodes, json)
results.append(json)
return results

@ -5,6 +5,7 @@ from slither.detectors.abstract_detector import (AbstractDetector,
from slither.slithir.operations import (HighLevelCall,
LowLevelCall,
LibraryCall)
from slither.utils import json_utils
from slither.utils.code_complexity import compute_cyclomatic_complexity
@ -105,7 +106,7 @@ class ComplexFunction(AbstractDetector):
self.log(info)
json = self.generate_json_result(info)
self.add_function_to_json(func, json, {
json_utils.add_function_to_json(func, json, {
'high_number_of_external_calls': cause == self.CAUSE_EXTERNAL_CALL,
'high_number_of_branches': cause == self.CAUSE_CYCLOMATIC,
'high_number_of_state_variables': cause == self.CAUSE_STATE_VARS

@ -3,6 +3,7 @@ from slither.detectors.abstract_detector import (AbstractDetector,
from slither.slithir.operations import SolidityCall
from slither.slithir.operations import (InternalCall, InternalDynamicCall)
from slither.formatters.functions.external_function import format
from slither.utils import json_utils
class ExternalFunction(AbstractDetector):
@ -189,9 +190,9 @@ class ExternalFunction(AbstractDetector):
txt += f" ({other_function_definition.source_mapping_str})\n"
json = self.generate_json_result(txt)
self.add_function_to_json(function_definition, json)
json_utils.add_function_to_json(function_definition, json)
for other_function_definition in all_function_definitions:
self.add_function_to_json(other_function_definition, json)
json_utils.add_function_to_json(other_function_definition, json)
results.append(json)
return results

@ -5,6 +5,8 @@ A suicidal contract is an unprotected function that calls selfdestruct
"""
from slither.detectors.abstract_detector import AbstractDetector, DetectorClassification
from slither.utils import json_utils
class Suicidal(AbstractDetector):
"""
@ -77,7 +79,7 @@ Bob calls `kill` and destructs the contract.'''
func.source_mapping_str)
json = self.generate_json_result(info)
self.add_function_to_json(func, json)
json_utils.add_function_to_json(func, json)
results.append(json)
return results

@ -1,7 +1,7 @@
import re
from slither.detectors.abstract_detector import AbstractDetector, DetectorClassification
from slither.formatters.naming_convention.naming_convention import format
from slither.utils import json_utils
class NamingConvention(AbstractDetector):
@ -64,7 +64,7 @@ Solidity defines a [naming convention](https://solidity.readthedocs.io/en/v0.4.2
contract.source_mapping_str)
json = self.generate_json_result(info)
self.add_contract_to_json(contract, json, {
json_utils.add_contract_to_json(contract, json, {
"target": "contract",
"convention": "CapWords"
})
@ -76,7 +76,7 @@ Solidity defines a [naming convention](https://solidity.readthedocs.io/en/v0.4.2
info = info.format(struct.canonical_name, struct.source_mapping_str)
json = self.generate_json_result(info)
self.add_struct_to_json(struct, json, {
json_utils.add_struct_to_json(struct, json, {
"target": "structure",
"convention": "CapWords"
})
@ -88,7 +88,7 @@ Solidity defines a [naming convention](https://solidity.readthedocs.io/en/v0.4.2
info = info.format(event.canonical_name, event.source_mapping_str)
json = self.generate_json_result(info)
self.add_event_to_json(event, json, {
json_utils.add_event_to_json(event, json, {
"target": "event",
"convention": "CapWords"
})
@ -106,7 +106,7 @@ Solidity defines a [naming convention](https://solidity.readthedocs.io/en/v0.4.2
info = info.format(func.canonical_name, func.source_mapping_str)
json = self.generate_json_result(info)
self.add_function_to_json(func, json, {
json_utils.add_function_to_json(func, json, {
"target": "function",
"convention": "mixedCase"
})
@ -127,7 +127,7 @@ Solidity defines a [naming convention](https://solidity.readthedocs.io/en/v0.4.2
argument.source_mapping_str)
json = self.generate_json_result(info)
self.add_variable_to_json(argument, json, {
json_utils.add_variable_to_json(argument, json, {
"target": "parameter",
"convention": "mixedCase"
})
@ -140,7 +140,7 @@ Solidity defines a [naming convention](https://solidity.readthedocs.io/en/v0.4.2
info = info.format(var.canonical_name, var.source_mapping_str)
json = self.generate_json_result(info)
self.add_variable_to_json(var, json, {
json_utils.add_variable_to_json(var, json, {
"target": "variable",
"convention": "l_O_I_should_not_be_used"
})
@ -156,7 +156,7 @@ Solidity defines a [naming convention](https://solidity.readthedocs.io/en/v0.4.2
info = info.format(var.canonical_name, var.source_mapping_str)
json = self.generate_json_result(info)
self.add_variable_to_json(var, json, {
json_utils.add_variable_to_json(var, json, {
"target": "variable_constant",
"convention": "UPPER_CASE_WITH_UNDERSCORES"
})
@ -172,7 +172,7 @@ Solidity defines a [naming convention](https://solidity.readthedocs.io/en/v0.4.2
info = info.format(var.canonical_name, var.source_mapping_str)
json = self.generate_json_result(info)
self.add_variable_to_json(var, json, {
json_utils.add_variable_to_json(var, json, {
"target": "variable",
"convention": "mixedCase"
})
@ -184,7 +184,7 @@ Solidity defines a [naming convention](https://solidity.readthedocs.io/en/v0.4.2
info = info.format(enum.canonical_name, enum.source_mapping_str)
json = self.generate_json_result(info)
self.add_enum_to_json(enum, json, {
json_utils.add_enum_to_json(enum, json, {
"target": "enum",
"convention": "CapWords"
})
@ -197,7 +197,7 @@ Solidity defines a [naming convention](https://solidity.readthedocs.io/en/v0.4.2
modifier.source_mapping_str)
json = self.generate_json_result(info)
self.add_function_to_json(modifier, json, {
json_utils.add_function_to_json(modifier, json, {
"target": "modifier",
"convention": "mixedCase"
})

@ -9,6 +9,7 @@ from slither.core.declarations.solidity_variables import (SolidityFunction,
from slither.detectors.abstract_detector import (AbstractDetector,
DetectorClassification)
from slither.slithir.operations import Binary, BinaryType
from slither.utils import json_utils
class Timestamp(AbstractDetector):
@ -77,8 +78,8 @@ class Timestamp(AbstractDetector):
info += '\t- {} ({})\n'.format(node.expression, node.source_mapping_str)
json = self.generate_json_result(info)
self.add_function_to_json(func, json)
self.add_nodes_to_json(nodes, json)
json_utils.add_function_to_json(func, json)
json_utils.add_nodes_to_json(nodes, json)
results.append(json)
return results

@ -4,6 +4,7 @@ Module detecting usage of low level calls
from slither.detectors.abstract_detector import AbstractDetector, DetectorClassification
from slither.slithir.operations import LowLevelCall
from slither.utils import json_utils
class LowLevelCalls(AbstractDetector):
@ -54,8 +55,8 @@ class LowLevelCalls(AbstractDetector):
info += "\t-{} {}\n".format(str(node.expression), node.source_mapping_str)
json = self.generate_json_result(info)
self.add_function_to_json(func, json)
self.add_nodes_to_json(nodes, json)
json_utils.add_function_to_json(func, json)
json_utils.add_nodes_to_json(nodes, json)
results.append(json)
return results

@ -5,6 +5,8 @@ Module detecting unused return values from external calls
from slither.detectors.abstract_detector import AbstractDetector, DetectorClassification
from slither.slithir.operations import HighLevelCall, InternalCall, InternalDynamicCall
from slither.core.variables.state_variable import StateVariable
from slither.utils import json_utils
class UnusedReturnValues(AbstractDetector):
"""
@ -81,8 +83,8 @@ contract MyConc{
node.source_mapping_str)
json = self.generate_json_result(info)
self.add_node_to_json(node, json)
self.add_function_to_json(f, json)
json_utils.add_node_to_json(node, json)
json_utils.add_function_to_json(f, json)
results.append(json)
return results

@ -1,6 +1,8 @@
from slither.detectors.abstract_detector import AbstractDetector, DetectorClassification
from slither.slithir.operations import Nop
from slither.utils import json_utils
class VoidConstructor(AbstractDetector):
@ -40,7 +42,7 @@ By reading B's constructor definition, the reader might assume that `A()` initia
info += "\t-{} {}\n".format(str(node.expression), node.source_mapping_str)
json = self.generate_json_result(info)
self.add_function_to_json(cst, json)
self.add_nodes_to_json([node], json)
json_utils.add_function_to_json(cst, json)
json_utils.add_nodes_to_json([node], json)
results.append(json)
return results

@ -9,6 +9,7 @@ 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.utils import json_utils
from slither.visitors.expression.export_values import ExportValues
from slither.slithir.operations import (HighLevelCall, LowLevelCall,
LibraryCall,
@ -101,11 +102,11 @@ Only report reentrancy that acts as a double call (see `reentrancy-eth`, `reentr
json = self.generate_json_result(info)
# Add the function with the re-entrancy first
self.add_function_to_json(func, json)
json_utils.add_function_to_json(func, json)
# Add all underlying calls in the function which are potentially problematic.
for call_info in calls:
self.add_node_to_json(call_info, json, {
json_utils.add_node_to_json(call_info, json, {
"underlying_type": "external_calls"
})
@ -114,13 +115,13 @@ Only report reentrancy that acts as a double call (see `reentrancy-eth`, `reentr
# If the calls are not the same ones that send eth, add the eth sending nodes.
if calls != send_eth:
for call_info in send_eth:
self.add_node_to_json(call_info, json, {
json_utils.add_node_to_json(call_info, json, {
"underlying_type": "external_calls_sending_eth"
})
# Add all variables written via nodes which write them.
for (v, node) in varsWritten:
self.add_node_to_json(node, json, {
json_utils.add_node_to_json(node, json, {
"underlying_type": "variables_written",
"variable_name": v.name
})

@ -11,7 +11,7 @@ from slither.detectors.abstract_detector import DetectorClassification
from slither.slithir.operations import (HighLevelCall, LowLevelCall,
LibraryCall,
Send, Transfer)
from slither.utils import json_utils
from .reentrancy import Reentrancy
class ReentrancyEth(Reentrancy):
@ -104,11 +104,11 @@ Bob uses the re-entrancy bug to call `withdrawBalance` two times, and withdraw m
json = self.generate_json_result(info)
# Add the function with the re-entrancy first
self.add_function_to_json(func, json)
json_utils.add_function_to_json(func, json)
# Add all underlying calls in the function which are potentially problematic.
for call_info in calls:
self.add_node_to_json(call_info, json, {
json_utils.add_node_to_json(call_info, json, {
"underlying_type": "external_calls"
})
@ -117,13 +117,13 @@ Bob uses the re-entrancy bug to call `withdrawBalance` two times, and withdraw m
# If the calls are not the same ones that send eth, add the eth sending nodes.
if calls != send_eth:
for call_info in send_eth:
self.add_node_to_json(call_info, json, {
json_utils.add_node_to_json(call_info, json, {
"underlying_type": "external_calls_sending_eth"
})
# Add all variables written via nodes which write them.
for (v, node) in varsWritten:
self.add_node_to_json(node, json, {
json_utils.add_node_to_json(node, json, {
"underlying_type": "variables_written",
"variable_name": v.name
})

@ -9,6 +9,7 @@ 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.utils import json_utils
from slither.visitors.expression.export_values import ExportValues
from slither.slithir.operations import (HighLevelCall, LowLevelCall,
LibraryCall,
@ -95,17 +96,17 @@ Do not report reentrancies that involve ethers (see `reentrancy-eth`)'''
json = self.generate_json_result(info)
# Add the function with the re-entrancy first
self.add_function_to_json(func, json)
json_utils.add_function_to_json(func, json)
# Add all underlying calls in the function which are potentially problematic.
for call_info in calls:
self.add_node_to_json(call_info, json, {
json_utils.add_node_to_json(call_info, json, {
"underlying_type": "external_calls"
})
# Add all variables written via nodes which write them.
for (v, node) in varsWritten:
self.add_node_to_json(node, json, {
json_utils.add_node_to_json(node, json, {
"underlying_type": "variables_written",
"variable_name": v.name
})

@ -4,6 +4,7 @@ Recursively check the called functions
"""
from slither.detectors.abstract_detector import AbstractDetector, DetectorClassification
from slither.utils import json_utils
class ShadowingAbstractDetection(AbstractDetector):
@ -72,7 +73,7 @@ contract DerivedContract is BaseContract{
var.source_mapping_str)
json = self.generate_json_result(info)
self.add_variables_to_json(all_variables, json)
json_utils.add_variables_to_json(all_variables, json)
results.append(json)
return results

@ -3,6 +3,7 @@ Module detecting reserved keyword shadowing
"""
from slither.detectors.abstract_detector import AbstractDetector, DetectorClassification
from slither.utils import json_utils
class BuiltinSymbolShadowing(AbstractDetector):
@ -140,11 +141,11 @@ contract Bug {
# Generate relevant JSON data for this shadowing definition.
json = self.generate_json_result(info)
if shadow_type in [self.SHADOWING_FUNCTION, self.SHADOWING_MODIFIER]:
self.add_function_to_json(shadow_object, json)
json_utils.add_function_to_json(shadow_object, json)
elif shadow_type == self.SHADOWING_EVENT:
self.add_event_to_json(shadow_object, json)
json_utils.add_event_to_json(shadow_object, json)
elif shadow_type in [self.SHADOWING_STATE_VARIABLE, self.SHADOWING_LOCAL_VARIABLE]:
self.add_variable_to_json(shadow_object, json)
json_utils.add_variable_to_json(shadow_object, json)
results.append(json)
return results

@ -3,6 +3,7 @@ Module detecting local variable shadowing
"""
from slither.detectors.abstract_detector import AbstractDetector, DetectorClassification
from slither.utils import json_utils
class LocalShadowing(AbstractDetector):
@ -117,14 +118,14 @@ contract Bug {
# Generate relevant JSON data for this shadowing definition.
json = self.generate_json_result(info)
self.add_variable_to_json(local_variable, json)
json_utils.add_variable_to_json(local_variable, json)
for overshadowed_entry in overshadowed:
if overshadowed_entry[0] in [self.OVERSHADOWED_FUNCTION, self.OVERSHADOWED_MODIFIER]:
self.add_function_to_json(overshadowed_entry[2], json)
json_utils.add_function_to_json(overshadowed_entry[2], json)
elif overshadowed_entry[0] == self.OVERSHADOWED_EVENT:
self.add_event_to_json(overshadowed_entry[2], json)
json_utils.add_event_to_json(overshadowed_entry[2], json)
elif overshadowed_entry[0] == self.OVERSHADOWED_STATE_VARIABLE:
self.add_variable_to_json(overshadowed_entry[2], json)
json_utils.add_variable_to_json(overshadowed_entry[2], json)
results.append(json)
return results

@ -3,6 +3,7 @@ Module detecting shadowing of state variables
"""
from slither.detectors.abstract_detector import AbstractDetector, DetectorClassification
from slither.utils import json_utils
class StateShadowing(AbstractDetector):
@ -83,7 +84,7 @@ contract DerivedContract is BaseContract{
var.source_mapping_str)
json = self.generate_json_result(info)
self.add_variables_to_json(all_variables, json)
json_utils.add_variables_to_json(all_variables, json)
results.append(json)

@ -1,6 +1,9 @@
from slither.detectors.abstract_detector import AbstractDetector, DetectorClassification
import re
from slither.utils import json_utils
class RightToLeftOverride(AbstractDetector):
"""
Detect the usage of a Right-To-Left-Override (U+202E) character
@ -72,8 +75,10 @@ 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)
json_utils.add_other_to_json("rtlo-character",
(filename, idx, len(self.RTLO_CHARACTER_ENCODED)),
json,
self.slither)
results.append(json)
# Advance the start index for the next iteration

@ -4,6 +4,7 @@ Module detecting usage of inline assembly
from slither.detectors.abstract_detector import AbstractDetector, DetectorClassification
from slither.core.cfg.node import NodeType
from slither.utils import json_utils
class Assembly(AbstractDetector):
@ -58,8 +59,8 @@ class Assembly(AbstractDetector):
info += "\t- {}\n".format(node.source_mapping_str)
json = self.generate_json_result(info)
self.add_function_to_json(func, json)
self.add_nodes_to_json(nodes, json)
json_utils.add_function_to_json(func, json)
json_utils.add_nodes_to_json(nodes, json)
results.append(json)
return results

@ -5,6 +5,7 @@ from slither.detectors.abstract_detector import (AbstractDetector,
DetectorClassification)
from slither.slithir.operations import (HighLevelCall, LibraryCall,
LowLevelCall, Send, Transfer)
from slither.utils import json_utils
class MultipleCallsInLoop(AbstractDetector):
@ -91,7 +92,7 @@ If one of the destinations has a fallback function which reverts, `bad` will alw
info = info.format(func.canonical_name, node.expression, node.source_mapping_str)
json = self.generate_json_result(info)
self.add_node_to_json(node, json)
json_utils.add_node_to_json(node, json)
results.append(json)
return results

@ -1,6 +1,8 @@
from slither.detectors.abstract_detector import AbstractDetector, DetectorClassification
from slither.slithir.operations import LowLevelCall
from slither.analyses.data_dependency.data_dependency import is_tainted
from slither.utils import json_utils
class ControlledDelegateCall(AbstractDetector):
"""
@ -52,8 +54,8 @@ Bob calls `delegate` and delegates the execution to its malicious contract. As a
node_info = func_info + '\t- {} ({})\n'.format(node.expression, node.source_mapping_str)
json = self.generate_json_result(node_info)
self.add_node_to_json(node, json)
self.add_function_to_json(f, json)
json_utils.add_node_to_json(node, json)
json_utils.add_function_to_json(f, json)
results.append(json)
return results

@ -3,6 +3,7 @@ Module detecting deprecated standards.
"""
from slither.detectors.abstract_detector import AbstractDetector, DetectorClassification
from slither.utils import json_utils
from slither.visitors.expression.export_values import ExportValues
from slither.core.declarations.solidity_variables import SolidityVariableComposed, SolidityFunction
from slither.core.cfg.node import NodeType
@ -162,9 +163,9 @@ contract ContractWithDeprecatedReferences {
# Generate relevant JSON data for this deprecated standard.
json = self.generate_json_result(info)
if isinstance(source_object, StateVariableSolc) or isinstance(source_object, StateVariable):
self.add_variable_to_json(source_object, json)
json_utils.add_variable_to_json(source_object, json)
else:
self.add_nodes_to_json([source_object], json)
json_utils.add_nodes_to_json([source_object], json)
results.append(json)

@ -16,6 +16,8 @@ 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
from slither.utils import json_utils
class IncorrectStrictEquality(AbstractDetector):
ARGUMENT = 'incorrect-equality'
@ -123,8 +125,8 @@ contract Crowdsale{
node_info = func_info + f"\t- {str(node.expression)}\n"
json = self.generate_json_result(node_info)
self.add_node_to_json(node, json)
self.add_function_to_json(f, json)
json_utils.add_node_to_json(node, json)
json_utils.add_function_to_json(f, json)
results.append(json)
return results

@ -4,6 +4,8 @@ Module detecting numbers with too many digits.
from slither.detectors.abstract_detector import AbstractDetector, DetectorClassification
from slither.slithir.variables import Constant
from slither.utils import json_utils
class TooManyDigits(AbstractDetector):
"""
@ -71,7 +73,7 @@ Use:
# Add the result in result
json = self.generate_json_result(node_info)
self.add_node_to_json(node, json)
json_utils.add_node_to_json(node, json)
results.append(json)
return results

@ -3,6 +3,8 @@ Module detecting usage of `tx.origin` in a conditional node
"""
from slither.detectors.abstract_detector import AbstractDetector, DetectorClassification
from slither.utils import json_utils
class TxOrigin(AbstractDetector):
"""
@ -72,7 +74,7 @@ Bob is the owner of `TxOrigin`. Bob calls Eve's contract. Eve's contract calls `
node.source_mapping_str)
json = self.generate_json_result(info)
self.add_node_to_json(node, json)
json_utils.add_node_to_json(node, json)
results.append(json)
return results

@ -4,6 +4,7 @@ Module detecting state variables that could be declared as constant
from slither.core.solidity_types.elementary_type import ElementaryType
from slither.detectors.abstract_detector import AbstractDetector, DetectorClassification
from slither.utils import json_utils
from slither.visitors.expression.export_values import ExportValues
from slither.core.declarations.solidity_variables import SolidityFunction
from slither.core.variables.state_variable import StateVariable
@ -91,7 +92,7 @@ class ConstCandidateStateVars(AbstractDetector):
info = "{} should be constant ({})\n".format(v.canonical_name,
v.source_mapping_str)
json = self.generate_json_result(info)
self.add_variable_to_json(v, json)
json_utils.add_variable_to_json(v, json)
results.append(json)

@ -7,6 +7,7 @@
from slither.detectors.abstract_detector import AbstractDetector, DetectorClassification
from slither.core.cfg.node import NodeType
from slither.utils import json_utils
from slither.visitors.expression.find_push import FindPush
@ -108,8 +109,8 @@ Bob calls `transfer`. As a result, the ethers are sent to the address 0x0 and ar
json = self.generate_json_result(info)
self.add_variable_to_json(uninitialized_local_variable, json)
self.add_function_to_json(function, json)
json_utils.add_variable_to_json(uninitialized_local_variable, json)
json_utils.add_function_to_json(function, json)
results.append(json)
return results

@ -16,6 +16,7 @@ from slither.slithir.operations.assignment import Assignment
from slither.slithir.operations import (OperationWithLValue, Index, Member,
InternalCall, InternalDynamicCall, LibraryCall)
from slither.utils import json_utils
class UninitializedStateVarsDetection(AbstractDetector):
@ -102,8 +103,8 @@ Initialize all the variables. If a variable is meant to be initialized to zero,
source += [f.source_mapping for f in functions]
json = self.generate_json_result(info)
self.add_variable_to_json(variable, json)
self.add_functions_to_json(functions, json)
json_utils.add_variable_to_json(variable, json)
json_utils.add_functions_to_json(functions, json)
results.append(json)
return results

@ -6,6 +6,7 @@
"""
from slither.detectors.abstract_detector import AbstractDetector, DetectorClassification
from slither.utils import json_utils
from slither.visitors.expression.find_push import FindPush
@ -110,8 +111,8 @@ Bob calls `func`. As a result, `owner` is override to 0.
json = self.generate_json_result(info)
self.add_variable_to_json(uninitialized_storage_variable, json)
self.add_function_to_json(function, json)
json_utils.add_variable_to_json(uninitialized_storage_variable, json)
json_utils.add_function_to_json(function, json)
results.append(json)
return results

@ -4,6 +4,7 @@ Module detecting unused state variables
from slither.detectors.abstract_detector import AbstractDetector, DetectorClassification
from slither.core.solidity_types import ArrayType
from slither.utils import json_utils
from slither.visitors.expression.export_values import ExportValues
from slither.core.variables.state_variable import StateVariable
from slither.formatters.variables.unused_state_variables import format
@ -66,8 +67,8 @@ class UnusedStateVars(AbstractDetector):
json = self.generate_json_result(info)
self.add_variable_to_json(var, json)
self.add_contract_to_json(c, json)
json_utils.add_variable_to_json(var, json)
json_utils.add_contract_to_json(c, json)
results.append(json)
return results

@ -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