Merge branch 'master' into printer-human-readable

pull/13/head
Josselin 6 years ago
commit ac787dcc55
  1. 3
      .gitignore
  2. 13
      README.md
  3. 2
      examples/scripts/functions_called.py
  4. 2
      examples/scripts/functions_writing.py
  5. 2
      examples/scripts/variable_in_condition.py
  6. 1
      requirements.txt
  7. 19
      setup.py
  8. 1
      slither/__init__.py
  9. 19
      slither/__main__.py
  10. 2
      slither/core/cfg/nodeType.py
  11. 2
      slither/core/children/childContract.py
  12. 2
      slither/core/children/childEvent.py
  13. 2
      slither/core/children/childFunction.py
  14. 2
      slither/core/children/childSlither.py
  15. 2
      slither/core/children/childStructure.py
  16. 2
      slither/core/context/context.py
  17. 21
      slither/core/declarations/contract.py
  18. 6
      slither/core/declarations/function.py
  19. 2
      slither/core/declarations/modifier.py
  20. 4
      slither/core/declarations/solidityVariables.py
  21. 2
      slither/core/expressions/assignmentOperation.py
  22. 2
      slither/core/expressions/binaryOperation.py
  23. 2
      slither/core/expressions/conditionalExpression.py
  24. 2
      slither/core/expressions/expressionTyped.py
  25. 3
      slither/core/expressions/literal.py
  26. 2
      slither/core/expressions/newArray.py
  27. 2
      slither/core/expressions/newContract.py
  28. 2
      slither/core/expressions/unaryOperation.py
  29. 8
      slither/core/slitherCore.py
  30. 4
      slither/core/solidityTypes/elementaryType.py
  31. 2
      slither/core/variables/eventVariable.py
  32. 2
      slither/core/variables/functionTypeVariable.py
  33. 2
      slither/core/variables/localVariable.py
  34. 2
      slither/core/variables/stateVariable.py
  35. 2
      slither/core/variables/structureVariable.py
  36. 4
      slither/detectors/abstractDetector.py
  37. 2
      slither/detectors/detectorClassification.py
  38. 2
      slither/detectors/detectors.py
  39. 2
      slither/detectors/shadowing/shadowingFunctionsDetection.py
  40. 4
      slither/printers/abstractPrinter.py
  41. 2
      slither/printers/printers.py
  42. 2
      slither/printers/summary/printerSummary.py
  43. 29
      slither/slither.py
  44. 5
      slither/solcParsing/declarations/contractSolc04.py
  45. 2
      slither/solcParsing/solidityTypes/typeParsing.py
  46. 2
      slither/solcParsing/variables/eventVariableSolc.py
  47. 2
      slither/solcParsing/variables/localVariableInitFromTupleSolc.py
  48. 2
      slither/solcParsing/variables/localVariableSolc.py
  49. 2
      slither/solcParsing/variables/stateVariableSolc.py
  50. 2
      slither/solcParsing/variables/structureVariableSolc.py
  51. 2
      slither/visitors/expression/expression.py

3
.gitignore vendored

@ -101,3 +101,6 @@ ENV/
.mypy_cache
*.sw*
# PyCharm project dir
.idea/

@ -1,7 +1,7 @@
# Slither, the Solidity source analyzer
[![Build Status](https://travis-ci.com/trailofbits/slither.svg?token=JEF97dFy1QsDCfQ2Wusd&branch=master)](https://travis-ci.com/trailofbits/slither)
Slither is a Solidity static analysis framework. It provides an API to easily manipulate Solidity code. In addition to exposing a Solidity contracts AST, Slither provides many APIs to quickly check local and state variable usage.
Slither is a Solidity static analysis framework written in Python 3. It provides an API to easily manipulate Solidity code. In addition to exposing a Solidity contracts AST, Slither provides many APIs to quickly check local and state variable usage.
With Slither you can:
- Detect vulnerabilities
@ -11,15 +11,14 @@ With Slither you can:
## How to install
Slither uses Python 2.
Slither uses Python 3.6.
Use pip to install the dependencies:
```bash
$ pip install -U -r requirements.txt
$ python setup.py install
```
You may also want solc, which can be installed using homebrew:
You may also want solc, the Solidity compiler, which can be installed using homebrew:
```bash
$ brew update
@ -40,11 +39,11 @@ $ sudo apt-get install solc
## How to use
```
$ slither.py file.sol
$ slither file.sol
```
```
$ slither.py examples/uninitialized.sol
$ slither examples/uninitialized.sol
[..]
INFO:Detectors:Uninitialized state variables in examples/uninitialized.sol, Contract: Uninitialized, Vars: destination, Used in ['transfer']
[..]

@ -14,4 +14,4 @@ all_calls = entry_point.all_calls()
all_calls_formated = [f.contract.name + '.' + f.name for f in all_calls]
# Print the result
print 'From entry_point the functions reached are {}'.format(all_calls_formated)
print('From entry_point the functions reached are {}'.format(all_calls_formated))

@ -13,4 +13,4 @@ var_a = contract.get_state_variable_from_name('a')
functions_writing_a = contract.get_functions_writing_variable(var_a)
# Print the result
print 'The function writing "a" are {}'.format([f.name for f in functions_writing_a])
print('The function writing "a" are {}'.format([f.name for f in functions_writing_a]))

@ -17,4 +17,4 @@ function_using_a_as_condition = [f for f in functions_reading_a if\
f.is_reading_in_require_or_assert(var_a)]
# Print the result
print 'The function using "a" in condition are {}'.format([f.name for f in function_using_a_as_condition])
print('The function using "a" in condition are {}'.format([f.name for f in function_using_a_as_condition]))

@ -1 +0,0 @@
prettytable

@ -0,0 +1,19 @@
from setuptools import setup, find_packages
setup(
name='Slither',
description='Slither is a Solidity static analysis framework written in Python 3.',
url='https://github.com/trailofbits/slither',
author='Trail of Bits',
version='0.1',
packages=find_packages(),
python_requires='>=3.6',
install_requires=['prettytable>=0.7.2'],
license='AGPL-3.0',
long_description=open('README.md').read(),
entry_points={
'console_scripts': [
'slither = slither.__main__:main'
]
}
)

@ -0,0 +1 @@
from .slither import Slither

@ -1,22 +1,21 @@
#!/usr/bin/env python2
#!/usr/bin/env python3
import sys
import argparse
import logging
import subprocess
import traceback
import os
import glob
import json
from slither.slither import Slither
from slither.utils.colors import red
from slither.detectors.detectors import Detectors
from slither.printers.printers import Printers
logging.basicConfig()
logger = logging.getLogger("Slither")
def determineChecks(detectors, args):
if args.low:
return detectors.low
@ -42,17 +41,19 @@ def process(filename, args, detectors, printers):
results = [item for sublist in results for item in sublist] #flatten
return results
def output_json(results, filename):
with open(filename, 'w') as f:
json.dump(results, f)
def exit(results):
if not results:
sys.exit(0)
sys.exit(len(results))
if __name__ == '__main__':
def main():
detectors = Detectors()
printers = Printers()
@ -102,8 +103,7 @@ if __name__ == '__main__':
action='store',
default=None)
for detector_name, Detector in detectors.detectors.iteritems():
for detector_name, Detector in detectors.detectors.items():
detector_arg = '--{}'.format(Detector.ARGUMENT)
detector_help = 'Detection of ' + Detector.HELP
parser.add_argument(detector_arg,
@ -112,7 +112,7 @@ if __name__ == '__main__':
dest="detectors_to_run",
const=detector_name)
for printer_name, Printer in printers.printers.iteritems():
for printer_name, Printer in printers.printers.items():
printer_arg = '--{}'.format(Printer.ARGUMENT)
printer_help = Printer.HELP
parser.add_argument(printer_arg,
@ -166,8 +166,11 @@ if __name__ == '__main__':
logger.info('%s analyzed, %d result(s) found', filename, len(results))
exit(results)
except Exception as e:
logging.error('Error in %s'%sys.argv[1])
logging.error(traceback.format_exc())
sys.exit(-1)
if __name__ == '__main__':
main()

@ -1,4 +1,4 @@
class NodeType(object):
class NodeType:
ENTRYPOINT = 0x0 # no expression

@ -1,5 +1,5 @@
class ChildContract(object):
class ChildContract:
def __init__(self):
super(ChildContract, self).__init__()

@ -1,5 +1,5 @@
class ChildEvent(object):
class ChildEvent:
def __init__(self):
super(ChildEvent, self).__init__()
self._event = None

@ -1,5 +1,5 @@
class ChildFunction(object):
class ChildFunction:
def __init__(self):
super(ChildFunction, self).__init__()
self._function = None

@ -1,5 +1,5 @@
class ChildSlither(object):
class ChildSlither:
def __init__(self):
super(ChildSlither, self).__init__()

@ -1,5 +1,5 @@
class ChildStructure(object):
class ChildStructure:
def __init__(self):
super(ChildStructure, self).__init__()

@ -1,4 +1,4 @@
class Context(object):
class Context:
def __init__(self):
super(Context, self).__init__()

@ -32,12 +32,12 @@ class Contract(ChildSlither, SourceMapping):
def __eq__(self, other):
if isinstance(other, unicode):
if isinstance(other, str):
return other == self.name
return NotImplemented
def __neq__(self, other):
if isinstance(other, unicode):
if isinstance(other, str):
return other != self.name
return NotImplemented
@ -73,14 +73,14 @@ class Contract(ChildSlither, SourceMapping):
'''
list(Structure): List of the structures
'''
return self._structures.values()
return list(self._structures.values())
def structures_as_dict(self):
return self._structures
@property
def enums(self):
return self._enums.values()
return list(self._enums.values())
def enums_as_dict(self):
return self._enums
@ -90,7 +90,7 @@ class Contract(ChildSlither, SourceMapping):
'''
list(Modifier): List of the modifiers
'''
return self._modifiers.values()
return list(self._modifiers.values())
def modifiers_as_dict(self):
return self._modifiers
@ -100,7 +100,7 @@ class Contract(ChildSlither, SourceMapping):
'''
list(Function): List of the functions
'''
return self._functions.values()
return list(self._functions.values())
@property
def functions_inherited(self):
@ -114,12 +114,11 @@ class Contract(ChildSlither, SourceMapping):
'''
list(Function): List of functions reachable from the contract (include super)
'''
all_calls = [f.all_calls() for f in self.functions]
all_calls = (f.all_calls() for f in self.functions)
all_calls = [item for sublist in all_calls for item in sublist] + self.functions
all_calls = list(set(all_calls))
all_calls = set(all_calls)
return [c for c in all_calls if isinstance(c, Function)]
def functions_as_dict(self):
return self._functions
@ -128,7 +127,7 @@ class Contract(ChildSlither, SourceMapping):
'''
list(Event): List of the events
'''
return self._events.values()
return list(self._events.values())
def events_as_dict(self):
return self._events
@ -138,7 +137,7 @@ class Contract(ChildSlither, SourceMapping):
'''
list(StateVariable): List of the state variables.
'''
return self._variables.values()
return list(self._variables.values())
@property
def variables(self):

@ -155,7 +155,7 @@ class Function(ChildContract, SourceMapping):
Return all local variables
Include paramters and return values
"""
return self._variables.values()
return list(self._variables.values())
def variables_as_dict(self):
return self._variables
@ -275,7 +275,7 @@ class Function(ChildContract, SourceMapping):
"""
write_var = [x.variables_written_as_expression for x in self.nodes]
write_var = [x for x in write_var if x]
write_var = [item for sublist in write_var for item in sublist ]
write_var = [item for sublist in write_var for item in sublist]
write_var = list(set(write_var))
# Remove dupplicate if they share the same string representation
write_var = [next(obj) for i, obj in groupby(sorted(write_var, key=lambda x: str(x)), lambda x: str(x))]
@ -283,7 +283,7 @@ class Function(ChildContract, SourceMapping):
write_var = [x.variables_written for x in self.nodes]
write_var = [x for x in write_var if x]
write_var = [item for sublist in write_var for item in sublist ]
write_var = [item for sublist in write_var for item in sublist]
write_var = list(set(write_var))
# Remove dupplicate if they share the same string representation
write_var = [next(obj) for i, obj in\

@ -1,7 +1,7 @@
"""
Modifier module
"""
from function import Function
from .function import Function
class Modifier(Function): pass

@ -7,7 +7,7 @@ SOLIDITY_VARIABLES_COMPOSED = ["block.coinbase", "block.difficulty", "block.gasl
SOLIDITY_FUNCTIONS = ["gasleft()", "assert(bool)", "require(bool)", "require(bool,string)", "revert()", "revert(string)", "addmod(uint256,uint256,uint256)", "mulmod(uint256,uint256,uint256)", "keccak256()", "sha256()", "sha3()", "ripemd160()", "ecrecover(bytes32,uint8,bytes32,bytes32)", "selfdestruct(address)", "suicide(address)", "log0(bytes32)", "log1(bytes32,bytes32)", "log2(bytes32,bytes32,bytes32)", "log3(bytes32,bytes32,bytes32,bytes32)", "blockhash(uint256)"]
class SolidityVariable(object):
class SolidityVariable:
def __init__(self, name):
assert name in SOLIDITY_VARIABLES
@ -34,7 +34,7 @@ class SolidityVariableComposed(SolidityVariable):
class SolidityFunction(object):
class SolidityFunction:
def __init__(self, name):
assert name in SOLIDITY_FUNCTIONS

@ -5,7 +5,7 @@ from slither.core.expressions.expression import Expression
logger = logging.getLogger("AssignmentOperation")
class AssignmentOperationType(object):
class AssignmentOperationType:
ASSIGN = 0 # =
ASSIGN_OR = 1 # |=
ASSIGN_CARET = 2 # ^=

@ -5,7 +5,7 @@ from slither.core.expressions.expression import Expression
logger = logging.getLogger("BinaryOperation")
class BinaryOperationType(object):
class BinaryOperationType:
POWER = 0 # **
MULTIPLICATION = 1 # *
DIVISION = 2 # /

@ -1,4 +1,4 @@
from expression import Expression
from .expression import Expression
class ConditionalExpression(Expression):

@ -1,5 +1,5 @@
from expression import Expression
from .expression import Expression
class ExpressionTyped(Expression):

@ -12,7 +12,4 @@ class Literal(Expression):
def __str__(self):
# be sure to handle any character
if isinstance(self._value, unicode):
return self._value.encode('utf-8')
return str(self._value)

@ -1,5 +1,5 @@
import logging
from expression import Expression
from .expression import Expression
from slither.core.solidityTypes.type import Type
logger = logging.getLogger("NewArray")

@ -1,4 +1,4 @@
from expression import Expression
from .expression import Expression
class NewContract(Expression):

@ -5,7 +5,7 @@ from slither.core.solidityTypes.type import Type
logger = logging.getLogger("UnaryOperation")
class UnaryOperationType(object):
class UnaryOperationType:
BANG = 0 # !
TILD = 1 # ~
DELETE = 2 # delete

@ -3,7 +3,7 @@
"""
import os
class Slither(object):
class Slither:
"""
Slither static analyzer
"""
@ -18,13 +18,13 @@ class Slither(object):
@property
def contracts(self):
"""list(Contract): List of contracts."""
return self._contracts.values()
return list(self._contracts.values())
@property
def contracts_derived(self):
"""list(Contract): List of contracts that are derived and not inherited."""
inheritances = [x.inheritances for x in self.contracts]
inheritances = [item for sublist in inheritances for item in sublist]
inheritances = (x.inheritances for x in self.contracts)
inheritances = (item for sublist in inheritances for item in sublist)
return [c for c in self._contracts.values() if c not in inheritances]
def contracts_as_dict(self):

@ -12,8 +12,8 @@ Uint = ['uint', 'uint8', 'uint16', 'uint24', 'uint32', 'uint40', 'uint48', 'uint
Byte = ['byte', 'bytes', 'bytes1', 'bytes2', 'bytes3', 'bytes4', 'bytes5', 'bytes6', 'bytes7', 'bytes8', 'bytes9', 'bytes10', 'bytes11', 'bytes12', 'bytes13', 'bytes14', 'bytes15', 'bytes16', 'bytes17', 'bytes18', 'bytes19', 'bytes20', 'bytes21', 'bytes22', 'bytes23', 'bytes24', 'bytes25', 'bytes26', 'bytes27', 'bytes28', 'bytes29', 'bytes30', 'bytes31', 'bytes32']
# https://solidity.readthedocs.io/en/v0.4.24/types.html#fixed-point-numbers
M = range(8, 257, 8)
N = range(0, 81)
M = list(range(8, 257, 8))
N = list(range(0, 81))
MN = list(itertools.product(M,N))
Fixed = ['fixed{}x{}'.format(m,n) for (m,n) in MN] + ['fixed']

@ -1,4 +1,4 @@
from variable import Variable
from .variable import Variable
from slither.core.children.childEvent import ChildEvent
class EventVariable(ChildEvent, Variable): pass

@ -6,7 +6,7 @@
}
"""
from variable import Variable
from .variable import Variable
class FunctionTypeVariable(Variable): pass

@ -1,4 +1,4 @@
from variable import Variable
from .variable import Variable
from slither.core.children.childFunction import ChildFunction
class LocalVariable(ChildFunction, Variable): pass

@ -1,4 +1,4 @@
from variable import Variable
from .variable import Variable
from slither.core.children.childContract import ChildContract
class StateVariable(ChildContract, Variable): pass

@ -1,4 +1,4 @@
from variable import Variable
from .variable import Variable
from slither.core.children.childStructure import ChildStructure
class StructureVariable(ChildStructure, Variable): pass

@ -6,9 +6,7 @@ from slither.utils.colors import green, yellow, red
class IncorrectDetectorInitialization(Exception):
pass
class AbstractDetector(object):
__metaclass__ = abc.ABCMeta
class AbstractDetector(object, metaclass=abc.ABCMeta):
ARGUMENT = '' # run the detector with slither.py --ARGUMENT
HELP = '' # help information
CLASSIFICATION = None

@ -1,4 +1,4 @@
class DetectorClassification(object):
class DetectorClassification:
LOW = 0
MEDIUM = 1
HIGH = 2

@ -13,7 +13,7 @@ from slither.detectors.variables.uninitializedStateVarsDetection import Uninitia
logger_detector = logging.getLogger("Detectors")
class Detectors(object):
class Detectors:
def __init__(self):
self.detectors = {}

@ -41,7 +41,7 @@ class ShadowingFunctionsDetection(AbstractDetector):
for c in self.contracts:
shadowing = self.detect_shadowing(c)
if shadowing:
for contract, funcs in shadowing.iteritems():
for contract, funcs in shadowing.items():
results.append({'vuln':self.vuln_name,
'filename': self.filename,
'contractShadower': c.name,

@ -3,9 +3,7 @@ import abc
class IncorrectPrinterInitialization(Exception):
pass
class AbstractPrinter(object):
__metaclass__ = abc.ABCMeta
class AbstractPrinter(object, metaclass=abc.ABCMeta):
ARGUMENT = '' # run the printer with slither.py --ARGUMENT
HELP = '' # help information

@ -12,7 +12,7 @@ from slither.printers.functions.authorization import PrinterWrittenVariablesAndA
logger_printer = logging.getLogger("Printers")
class Printers(object):
class Printers:
def __init__(self):
self.printers = {}

@ -14,7 +14,7 @@ class PrinterSummary(AbstractPrinter):
def _convert(l):
if l:
n = 2
l = [l[i:i + n] for i in xrange(0, len(l), n)]
l = [l[i:i + n] for i in range(0, len(l), n)]
l = [str(x) for x in l]
return "\n".join(l)
return str(l)

@ -1,14 +1,15 @@
import os
import sys
import logging
import subprocess
import os.path
from solcParsing.slitherSolc import SlitherSolc
from utils.colors import red
from .solcParsing.slitherSolc import SlitherSolc
from .utils.colors import red
logger = logging.getLogger("Slither")
logging.basicConfig()
class Slither(SlitherSolc):
def __init__(self, filename, solc='solc', disable_solc_warnings=False ,solc_arguments=''):
@ -24,8 +25,8 @@ class Slither(SlitherSolc):
if is_ast_file:
with open(filename) as astFile:
data = astFile.read()
if not data:
stdout = astFile.read()
if not stdout:
logger.info('Empty AST file: %s', filename)
sys.exit(-1)
else:
@ -44,20 +45,22 @@ class Slither(SlitherSolc):
# Add . as default allowed path
if '--allow-paths' not in cmd:
cmd += ['--allow-paths', '.']
process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
data, err = process.communicate()
if err and (not disable_solc_warnings):
err = err.split('\n')
err = [x if 'Error' not in x else red(x) for x in err]
err = '\n'.join(err)
logger.info('Compilation warnings/errors on %s:\n%s', filename, err)
stdout, stderr = process.communicate()
stdout, stderr = stdout.decode(), stderr.decode() # convert bytestrings to unicode strings
if stderr and (not disable_solc_warnings):
stderr = stderr.split('\n')
stderr = [x if 'Error' not in x else red(x) for x in stderr]
stderr = '\n'.join(stderr)
logger.info('Compilation warnings/errors on %s:\n%s', filename, stderr)
data = data.split('\n=')
stdout = stdout.split('\n=')
super(Slither, self).__init__(filename)
for d in data:
for d in stdout:
self.parse_contracts_from_json(d)
self.analyze_contracts()

@ -233,7 +233,7 @@ class ContractSolc04(Contract):
def analyze_params_functions(self):
for father in self.inheritances_reverse:
functions = {k:v for (k,v) in father.functions_as_dict().iteritems()} #if not v.is_constructor}
functions = {k:v for (k,v) in father.functions_as_dict().items()} #if not v.is_constructor}
self._functions.update(functions)
for function in self._functions_no_params:
@ -251,3 +251,6 @@ class ContractSolc04(Contract):
for function in self.functions:
function.analyze_content()
return
def __hash__(self):
return self._id

@ -17,7 +17,7 @@ import re
logger = logging.getLogger('TypeParsing')
class UnknownType(object):
class UnknownType:
def __init__(self, name):
self._name = name

@ -1,5 +1,5 @@
from variableDeclarationSolc import VariableDeclarationSolc
from .variableDeclarationSolc import VariableDeclarationSolc
from slither.core.variables.eventVariable import EventVariable
class EventVariableSolc(VariableDeclarationSolc, EventVariable): pass

@ -1,5 +1,5 @@
from variableDeclarationSolc import VariableDeclarationSolc
from .variableDeclarationSolc import VariableDeclarationSolc
from slither.core.variables.localVariableInitFromTuple import LocalVariableInitFromTuple
class LocalVariableInitFromTupleSolc(VariableDeclarationSolc, LocalVariableInitFromTuple):

@ -1,5 +1,5 @@
from variableDeclarationSolc import VariableDeclarationSolc
from .variableDeclarationSolc import VariableDeclarationSolc
from slither.core.variables.localVariable import LocalVariable
class LocalVariableSolc(VariableDeclarationSolc, LocalVariable): pass

@ -1,5 +1,5 @@
from variableDeclarationSolc import VariableDeclarationSolc
from .variableDeclarationSolc import VariableDeclarationSolc
from slither.core.variables.stateVariable import StateVariable
class StateVariableSolc(VariableDeclarationSolc, StateVariable): pass

@ -1,5 +1,5 @@
from variableDeclarationSolc import VariableDeclarationSolc
from .variableDeclarationSolc import VariableDeclarationSolc
from slither.core.variables.structureVariable import StructureVariable
class StructureVariableSolc(VariableDeclarationSolc, StructureVariable): pass

@ -18,7 +18,7 @@ from slither.core.expressions.unaryOperation import UnaryOperation
logger = logging.getLogger("ExpressionVisitor")
class ExpressionVisitor(object):
class ExpressionVisitor:
def __init__(self, expression):
self._expression = expression

Loading…
Cancel
Save