Merge branch 'dev' into dev-slither-format-tool-only-new

pull/238/head
Josselin 5 years ago
commit ab40693de7
  1. 24
      CONTRIBUTING.md
  2. 32
      README.md
  3. 26
      examples/printers/constructors.sol
  4. 2
      scripts/tests_generate_expected_json_4.sh
  5. 1
      scripts/tests_generate_expected_json_5.sh
  6. 7
      scripts/travis_install.sh
  7. 1
      scripts/travis_test_5.sh
  8. 8
      setup.py
  9. 189
      slither/__main__.py
  10. 56
      slither/core/cfg/node.py
  11. 4
      slither/core/children/child_node.py
  12. 79
      slither/core/declarations/function.py
  13. 10
      slither/core/expressions/literal.py
  14. 9
      slither/core/slither_core.py
  15. 13
      slither/core/source_mapping/source_mapping.py
  16. 1
      slither/detectors/all_detectors.py
  17. 5
      slither/detectors/naming_convention/naming_convention.py
  18. 46
      slither/detectors/operations/void_constructor.py
  19. 39
      slither/detectors/reentrancy/reentrancy.py
  20. 1
      slither/detectors/statements/too_many_digits.py
  21. 1
      slither/printers/all_printers.py
  22. 47
      slither/printers/summary/constructor_calls.py
  23. 8
      slither/printers/summary/slithir.py
  24. 19
      slither/slither.py
  25. 5
      slither/slithir/convert.py
  26. 1
      slither/slithir/operations/__init__.py
  27. 13
      slither/slithir/operations/call.py
  28. 50
      slither/slithir/operations/high_level_call.py
  29. 12
      slither/slithir/operations/library_call.py
  30. 14
      slither/slithir/operations/low_level_call.py
  31. 40
      slither/slithir/operations/new_contract.py
  32. 14
      slither/slithir/operations/nop.py
  33. 3
      slither/slithir/operations/send.py
  34. 2
      slither/slithir/operations/transfer.py
  35. 8
      slither/slithir/variables/constant.py
  36. 42
      slither/solc_parsing/declarations/contract.py
  37. 42
      slither/solc_parsing/declarations/function.py
  38. 51
      slither/solc_parsing/expressions/expression_parsing.py
  39. 55
      slither/solc_parsing/slitherSolc.py
  40. 0
      slither/tools/__init__.py
  41. 6
      slither/tools/demo/README.md
  42. 0
      slither/tools/demo/__init__.py
  43. 38
      slither/tools/demo/__main__.py
  44. 0
      slither/tools/possible_paths/__init__.py
  45. 0
      slither/tools/possible_paths/__main__.py
  46. 0
      slither/tools/possible_paths/possible_paths.py
  47. 0
      slither/tools/similarity/__init__.py
  48. 0
      slither/tools/similarity/__main__.py
  49. 0
      slither/tools/similarity/cache.py
  50. 0
      slither/tools/similarity/encode.py
  51. 0
      slither/tools/similarity/info.py
  52. 0
      slither/tools/similarity/model.py
  53. 0
      slither/tools/similarity/plot.py
  54. 0
      slither/tools/similarity/similarity.py
  55. 0
      slither/tools/similarity/test.py
  56. 0
      slither/tools/similarity/train.py
  57. 0
      slither/tools/slither_format/.gitignore
  58. 0
      slither/tools/slither_format/README.md
  59. 0
      slither/tools/slither_format/__init__.py
  60. 0
      slither/tools/slither_format/__main__.py
  61. 0
      slither/tools/slither_format/exceptions.py
  62. 0
      slither/tools/slither_format/formatters/__init__.py
  63. 0
      slither/tools/slither_format/formatters/constable_states.py
  64. 0
      slither/tools/slither_format/formatters/constant_function.py
  65. 0
      slither/tools/slither_format/formatters/external_function.py
  66. 0
      slither/tools/slither_format/formatters/naming_convention.py
  67. 0
      slither/tools/slither_format/formatters/pragma.py
  68. 0
      slither/tools/slither_format/formatters/solc_version.py
  69. 0
      slither/tools/slither_format/formatters/unused_state.py
  70. 0
      slither/tools/slither_format/slither_format.py
  71. 0
      slither/tools/slither_format/tests/.gitignore
  72. 0
      slither/tools/slither_format/tests/__init__.py
  73. 0
      slither/tools/slither_format/tests/real_world/0x0000000000b3F879cb30FE243b4Dfee438691c04_GasToken2.sol
  74. 0
      slither/tools/slither_format/tests/real_world/0x006bea43baa3f7a6f765f14f10a1a1b08334ef45_StoxSmartToken.sol
  75. 0
      slither/tools/slither_format/tests/real_world/0x0235fe624e044a05eed7a43e16e3083bc8a4287a_OriginalToken.sol
  76. 0
      slither/tools/slither_format/tests/real_world/0x05cf67329a262818e67c080e9d511a34d36152c0_MultiSigWallet.sol
  77. 0
      slither/tools/slither_format/tests/real_world/0x05f4a42e251f2d52b8ed15e9fedaacfcef1fad27_ZilliqaToken.sol
  78. 0
      slither/tools/slither_format/tests/real_world/0x06012c8cf97bead5deae237070f9587f8e7a266d_KittyCore.sol
  79. 0
      slither/tools/slither_format/tests/real_world/0x5d0d76787d9d564061dd23f8209f804a3b8ad2f2_FoMo3Dlong.sol
  80. 0
      slither/tools/slither_format/tests/real_world/0xbf45f4280cfbe7c2d2515a7d984b8c71c15e82b7_EnclavesDEXProxy.sol
  81. 0
      slither/tools/slither_format/tests/real_world/0xc6725ae749677f21e4d8f85f41cfb6de49b9db29_BancorConverter.sol
  82. 0
      slither/tools/slither_format/tests/real_world/0xf5ed2dc77f0d1ea7f106ecbd1850e406adc41b51_OceanToken.sol
  83. 0
      slither/tools/slither_format/tests/runSlitherFormat.py
  84. 0
      slither/tools/slither_format/tests/run_all_tests.py
  85. 0
      slither/tools/slither_format/tests/test_constable_states.py
  86. 0
      slither/tools/slither_format/tests/test_constant_function.py
  87. 0
      slither/tools/slither_format/tests/test_data/const_state_variables.sol
  88. 0
      slither/tools/slither_format/tests/test_data/constant-0.5.1.sol
  89. 0
      slither/tools/slither_format/tests/test_data/constant.sol
  90. 0
      slither/tools/slither_format/tests/test_data/detector_combinations.sol
  91. 0
      slither/tools/slither_format/tests/test_data/external_function.sol
  92. 0
      slither/tools/slither_format/tests/test_data/external_function_2.sol
  93. 0
      slither/tools/slither_format/tests/test_data/external_function_import.sol
  94. 0
      slither/tools/slither_format/tests/test_data/naming_convention.sol
  95. 0
      slither/tools/slither_format/tests/test_data/naming_convention_contract.sol
  96. 0
      slither/tools/slither_format/tests/test_data/naming_convention_enum.sol
  97. 0
      slither/tools/slither_format/tests/test_data/naming_convention_event.sol
  98. 0
      slither/tools/slither_format/tests/test_data/naming_convention_function.sol
  99. 0
      slither/tools/slither_format/tests/test_data/naming_convention_modifier.sol
  100. 0
      slither/tools/slither_format/tests/test_data/naming_convention_parameter.sol
  101. Some files were not shown because too many files have changed in this diff Show More

@ -0,0 +1,24 @@
# Contributing to Slither
First, thanks for your interest in contributing to Slither! We welcome and appreciate all contributions, including bug reports, feature suggestions, tutorials/blog posts, and code improvements.
If you're unsure where to start, we recommend our [`good first issue`](https://github.com/crytic/slither/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22) and [`help wanted`](https://github.com/crytic/slither/issues?q=is%3Aissue+is%3Aopen+label%3A%22help+wanted%22) issue labels.
# Bug reports and feature suggestions
Bug reports and feature suggestions can be submitted to our issue tracker. For bug reports, attaching the contract that caused the bug will help us in debugging and resolving the issue quickly. If you find a security vulnerability, do not open an issue; email opensource@trailofbits.com instead.
# Questions
Questions can be submitted to the issue tracker, but you may get a faster response if you ask in our [chat room](https://empireslacking.herokuapp.com/) (in the #ethereum channel).
# Code
Slither uses the pull request contribution model. Please make an account on Github, fork this repo, and submit code contributions via pull request. For more documentation, look [here](https://guides.github.com/activities/forking/).
Some pull request guidelines:
- Work from the [`dev`](https://github.com/crytic/slither/tree/dev) branch. We performed extensive tests prior to merging anything to `master`, working from `dev` will allow us to merge your work faster.
- Minimize irrelevant changes (formatting, whitespace, etc) to code that would otherwise not be touched by this patch. Save formatting or style corrections for a separate pull request that does not make any semantic changes.
- When possible, large changes should be split up into smaller focused pull requests.
- Fill out the pull request description with a summary of what your patch does, key changes that have been made, and any further points of discussion, if applicable.
- Title your pull request with a brief description of what it's changing. "Fixes #123" is a good comment to add to the description, but makes for an unclear title on its own.
# Development Environment
Instructions for installing a development version of Slither can be found in our [wiki](https://github.com/crytic/slither/wiki/Developer-installation).

@ -61,20 +61,22 @@ Num | Detector | What it Detects | Impact | Confidence
20 | `unused-return` | [Unused return values](https://github.com/crytic/slither/wiki/Detector-Documentation#unused-return) | Medium | Medium 20 | `unused-return` | [Unused return values](https://github.com/crytic/slither/wiki/Detector-Documentation#unused-return) | Medium | Medium
21 | `shadowing-builtin` | [Built-in symbol shadowing](https://github.com/crytic/slither/wiki/Detector-Documentation#builtin-symbol-shadowing) | Low | High 21 | `shadowing-builtin` | [Built-in symbol shadowing](https://github.com/crytic/slither/wiki/Detector-Documentation#builtin-symbol-shadowing) | Low | High
22 | `shadowing-local` | [Local variables shadowing](https://github.com/crytic/slither/wiki/Detector-Documentation#local-variable-shadowing) | Low | High 22 | `shadowing-local` | [Local variables shadowing](https://github.com/crytic/slither/wiki/Detector-Documentation#local-variable-shadowing) | Low | High
23 | `calls-loop` | [Multiple calls in a loop](https://github.com/crytic/slither/wiki/Detector-Documentation/_edit#calls-inside-a-loop) | Low | Medium 23 | `void-cst` | [Constructor called not implemented](https://github.com/crytic/slither/wiki/Detector-Documentation#void-constructor) | Low | High
24 | `reentrancy-benign` | [Benign reentrancy vulnerabilities](https://github.com/crytic/slither/wiki/Detector-Documentation#reentrancy-vulnerabilities-2) | Low | Medium 24 | `calls-loop` | [Multiple calls in a loop](https://github.com/crytic/slither/wiki/Detector-Documentation/_edit#calls-inside-a-loop) | Low | Medium
25 | `timestamp` | [Dangerous usage of `block.timestamp`](https://github.com/crytic/slither/wiki/Detector-Documentation#block-timestamp) | Low | Medium 25 | `reentrancy-benign` | [Benign reentrancy vulnerabilities](https://github.com/crytic/slither/wiki/Detector-Documentation#reentrancy-vulnerabilities-2) | Low | Medium
26 | `assembly` | [Assembly usage](https://github.com/crytic/slither/wiki/Detector-Documentation#assembly-usage) | Informational | High 26 | `timestamp` | [Dangerous usage of `block.timestamp`](https://github.com/crytic/slither/wiki/Detector-Documentation#block-timestamp) | Low | Medium
27 | `deprecated-standards` | [Deprecated Solidity Standards](https://github.com/crytic/slither/wiki/Detector-Documentation#deprecated-standards) | Informational | High 27 | `assembly` | [Assembly usage](https://github.com/crytic/slither/wiki/Detector-Documentation#assembly-usage) | Informational | High
28 | `erc20-indexed` | [Un-indexed ERC20 event parameters](https://github.com/crytic/slither/wiki/Detector-Documentation#unindexed-erc20-event-parameters) | Informational | High 28 | `deprecated-standards` | [Deprecated Solidity Standards](https://github.com/crytic/slither/wiki/Detector-Documentation#deprecated-standards) | Informational | High
29 | `low-level-calls` | [Low level calls](https://github.com/crytic/slither/wiki/Detector-Documentation#low-level-calls) | Informational | High 29 | `erc20-indexed` | [Un-indexed ERC20 event parameters](https://github.com/crytic/slither/wiki/Detector-Documentation#unindexed-erc20-event-parameters) | Informational | High
30 | `naming-convention` | [Conformance to Solidity naming conventions](https://github.com/crytic/slither/wiki/Detector-Documentation#conformance-to-solidity-naming-conventions) | Informational | High 30 | `low-level-calls` | [Low level calls](https://github.com/crytic/slither/wiki/Detector-Documentation#low-level-calls) | Informational | High
31 | `pragma` | [If different pragma directives are used](https://github.com/crytic/slither/wiki/Detector-Documentation#different-pragma-directives-are-used) | Informational | High 31 | `naming-convention` | [Conformance to Solidity naming conventions](https://github.com/crytic/slither/wiki/Detector-Documentation#conformance-to-solidity-naming-conventions) | Informational | High
32 | `solc-version` | [Incorrect Solidity version (< 0.4.24 or complex pragma)](https://github.com/crytic/slither/wiki/Detector-Documentation#incorrect-versions-of-solidity) | Informational | High 32 | `pragma` | [If different pragma directives are used](https://github.com/crytic/slither/wiki/Detector-Documentation#different-pragma-directives-are-used) | Informational | High
33 | `unused-state` | [Unused state variables](https://github.com/crytic/slither/wiki/Detector-Documentation#unused-state-variables) | Informational | High 33 | `solc-version` | [Incorrect Solidity version (< 0.4.24 or complex pragma)](https://github.com/crytic/slither/wiki/Detector-Documentation#incorrect-versions-of-solidity) | Informational | High
34 | `too-many-digits` | [Conformance to numeric notation best practices](https://github.com/crytic/slither/wiki/Detector-Documentation#too-many-digits) | Informational | Medium 34 | `unused-state` | [Unused state variables](https://github.com/crytic/slither/wiki/Detector-Documentation#unused-state-variables) | Informational | High
35 | `constable-states` | [State variables that could be declared constant](https://github.com/crytic/slither/wiki/Detector-Documentation#state-variables-that-could-be-declared-constant) | Optimization | High 35 | `too-many-digits` | [Conformance to numeric notation best practices](https://github.com/crytic/slither/wiki/Detector-Documentation#too-many-digits) | Informational | Medium
36 | `external-function` | [Public function that could be declared as external](https://github.com/crytic/slither/wiki/Detector-Documentation#public-function-that-could-be-declared-as-external) | Optimization | High 36 | `constable-states` | [State variables that could be declared constant](https://github.com/crytic/slither/wiki/Detector-Documentation#state-variables-that-could-be-declared-constant) | Optimization | High
37 | `external-function` | [Public function that could be declared as external](https://github.com/crytic/slither/wiki/Detector-Documentation#public-function-that-could-be-declared-as-external) | Optimization | High
[Contact us](https://www.trailofbits.com/contact/) to get access to additional detectors. [Contact us](https://www.trailofbits.com/contact/) to get access to additional detectors.
@ -113,7 +115,7 @@ $ pip install slither-analyzer
### Using Git ### Using Git
```bash ```bash
$ git clone https://github.com/trailofbits/slither.git && cd slither $ git clone https://github.com/crytic/slither.git && cd slither
$ python setup.py install $ python setup.py install
``` ```

@ -0,0 +1,26 @@
pragma solidity >=0.4.22 <0.6.0;
contract test{
uint a;
constructor()public{
a =5;
}
}
contract test2 is test{
constructor()public{
a=10;
}
}
contract test3 is test2{
address owner;
bytes32 name;
constructor(bytes32 _name)public{
owner = msg.sender;
name = _name;
a=20;
}
function print() public returns(uint b){
b=a;
}
}

@ -21,7 +21,7 @@ generate_expected_json(){
} }
generate_expected_json tests/deprecated_calls.sol "deprecated-standards" #generate_expected_json tests/deprecated_calls.sol "deprecated-standards"
#generate_expected_json tests/erc20_indexed.sol "erc20-indexed" #generate_expected_json tests/erc20_indexed.sol "erc20-indexed"
#generate_expected_json tests/incorrect_erc20_interface.sol "erc20-interface" #generate_expected_json tests/incorrect_erc20_interface.sol "erc20-interface"
#generate_expected_json tests/incorrect_erc721_interface.sol "erc721-interface" #generate_expected_json tests/incorrect_erc721_interface.sol "erc721-interface"

@ -20,6 +20,7 @@ generate_expected_json(){
sed "s|$CURRENT_PATH|$TRAVIS_PATH|g" "$output_filename_txt" -i sed "s|$CURRENT_PATH|$TRAVIS_PATH|g" "$output_filename_txt" -i
} }
#generate_expected_json tests/void-cst.sol "void-cst"
#generate_expected_json tests/solc_version_incorrect_05.ast.json "solc-version" #generate_expected_json tests/solc_version_incorrect_05.ast.json "solc-version"
#generate_expected_json tests/uninitialized-0.5.1.sol "uninitialized-state" #generate_expected_json tests/uninitialized-0.5.1.sol "uninitialized-state"
#generate_expected_json tests/backdoor.sol "backdoor" #generate_expected_json tests/backdoor.sol "backdoor"

@ -1,12 +1,5 @@
#!/usr/bin/env bash #!/usr/bin/env bash
# TODO: temporary until the next crytic-compile release
git clone https://github.com/crytic/crytic-compile
cd crytic-compile
git checkout dev
python setup.py install
cd ..
python setup.py install python setup.py install
# Used by travis_test.sh # Used by travis_test.sh
pip install deepdiff pip install deepdiff

@ -69,6 +69,7 @@ test_slither(){
} }
test_slither tests/void-cst.sol "void-cst"
test_slither tests/solc_version_incorrect_05.ast.json "solc-version" test_slither tests/solc_version_incorrect_05.ast.json "solc-version"
test_slither tests/unchecked_lowlevel-0.5.1.sol "unchecked-lowlevel" test_slither tests/unchecked_lowlevel-0.5.1.sol "unchecked-lowlevel"
test_slither tests/unchecked_send-0.5.1.sol "unchecked-send" test_slither tests/unchecked_send-0.5.1.sol "unchecked-send"

@ -14,10 +14,10 @@ setup(
entry_points={ entry_points={
'console_scripts': [ 'console_scripts': [
'slither = slither.__main__:main', 'slither = slither.__main__:main',
'slither-check-upgradeability = utils.upgradeability.__main__:main', 'slither-check-upgradeability = slither.tools.upgradeability.__main__:main',
'slither-find-paths = utils.possible_paths.__main__:main', 'slither-find-paths = slither.tools.possible_paths.__main__:main',
'slither-simil = utils.similarity.__main__:main', 'slither-simil = slither.tools.similarity.__main__:main'
'slither-format = utils.slither_format.__main__:main' 'slither-format = slither.tools.slither_format.__main__:main'
] ]
} }
) )

@ -11,6 +11,7 @@ import traceback
from pkg_resources import iter_entry_points, require from pkg_resources import iter_entry_points, require
from crytic_compile import cryticparser from crytic_compile import cryticparser
from crytic_compile.platform.standard import generate_standard_export
from slither.detectors import all_detectors from slither.detectors import all_detectors
from slither.detectors.abstract_detector import (AbstractDetector, from slither.detectors.abstract_detector import (AbstractDetector,
@ -18,12 +19,13 @@ from slither.detectors.abstract_detector import (AbstractDetector,
from slither.printers import all_printers from slither.printers import all_printers
from slither.printers.abstract_printer import AbstractPrinter from slither.printers.abstract_printer import AbstractPrinter
from slither.slither import Slither from slither.slither import Slither
from slither.utils.output_capture import StandardOutputCapture
from slither.utils.colors import red, yellow, set_colorization_enabled from slither.utils.colors import red, yellow, set_colorization_enabled
from slither.utils.command_line import (output_detectors, output_results_to_markdown, from slither.utils.command_line import (output_detectors, output_results_to_markdown,
output_detectors_json, output_printers, output_detectors_json, output_printers, output_printers_json,
output_to_markdown, output_wiki, defaults_flag_in_config, output_to_markdown, output_wiki, defaults_flag_in_config,
read_config_file) read_config_file, JSON_OUTPUT_TYPES)
from crytic_compile import is_supported from crytic_compile import compile_all, is_supported
from slither.exceptions import SlitherException from slither.exceptions import SlitherException
logging.basicConfig() logging.basicConfig()
@ -35,7 +37,8 @@ logger = logging.getLogger("Slither")
################################################################################### ###################################################################################
################################################################################### ###################################################################################
def process(filename, args, detector_classes, printer_classes):
def process_single(target, args, detector_classes, printer_classes):
""" """
The core high-level code for running Slither static analysis. The core high-level code for running Slither static analysis.
@ -45,13 +48,26 @@ def process(filename, args, detector_classes, printer_classes):
ast = '--ast-compact-json' ast = '--ast-compact-json'
if args.legacy_ast: if args.legacy_ast:
ast = '--ast-json' ast = '--ast-json'
args.filter_paths = parse_filter_paths(args) slither = Slither(target,
slither = Slither(filename,
ast_format=ast, ast_format=ast,
**vars(args)) **vars(args))
return _process(slither, detector_classes, printer_classes) return _process(slither, detector_classes, printer_classes)
def process_all(target, args, detector_classes, printer_classes):
compilations = compile_all(target, **vars(args))
slither_instances = []
results = []
analyzed_contracts_count = 0
for compilation in compilations:
(slither, current_results, current_analyzed_count) = process_single(compilation, args, detector_classes, printer_classes)
results.extend(current_results)
slither_instances.append(slither)
analyzed_contracts_count += current_analyzed_count
return slither_instances, results, analyzed_contracts_count
def _process(slither, detector_classes, printer_classes): def _process(slither, detector_classes, printer_classes):
for detector_cls in detector_classes: for detector_cls in detector_classes:
slither.register_detector(detector_cls) slither.register_detector(detector_cls)
@ -72,10 +88,10 @@ def _process(slither, detector_classes, printer_classes):
slither.run_printers() # Currently printers does not return results slither.run_printers() # Currently printers does not return results
return results, analyzed_contracts_count return slither, results, analyzed_contracts_count
def process_files(filenames, args, detector_classes, printer_classes): def process_from_asts(filenames, args, detector_classes, printer_classes):
all_contracts = [] all_contracts = []
for filename in filenames: for filename in filenames:
@ -83,11 +99,9 @@ def process_files(filenames, args, detector_classes, printer_classes):
contract_loaded = json.load(f) contract_loaded = json.load(f)
all_contracts.append(contract_loaded['ast']) all_contracts.append(contract_loaded['ast'])
slither = Slither(all_contracts, return process_single(all_contracts, args, detector_classes, printer_classes)
filter_paths=parse_filter_paths(args),
**vars(args))
return _process(slither, detector_classes, printer_classes)
# endregion # endregion
################################################################################### ###################################################################################
@ -97,26 +111,15 @@ def process_files(filenames, args, detector_classes, printer_classes):
################################################################################### ###################################################################################
def wrap_json_detectors_results(success, error_message, results=None): def output_json(filename, error, results):
""" # Create our encapsulated JSON result.
Wrap the detector results. json_result = {
:param success: "success": error is None,
:param error_message: "error": error,
:param results: "results": results
:return:
"""
results_json = {}
if results:
results_json['detectors'] = results
return {
"success": success,
"error": error_message,
"results": results_json
} }
# Determine if we should output to stdout
def output_json(results, filename):
json_result = wrap_json_detectors_results(True, None, results)
if filename is None: if filename is None:
# Write json to console # Write json to console
print(json.dumps(json_result)) print(json.dumps(json_result))
@ -271,7 +274,7 @@ def parse_args(detector_classes, printer_classes):
group_detector = parser.add_argument_group('Detectors') group_detector = parser.add_argument_group('Detectors')
group_printer = parser.add_argument_group('Printers') group_printer = parser.add_argument_group('Printers')
group_misc = parser.add_argument_group('Additional option') group_misc = parser.add_argument_group('Additional options')
group_detector.add_argument('--detect', group_detector.add_argument('--detect',
help='Comma-separated list of detectors, defaults to all, ' help='Comma-separated list of detectors, defaults to all, '
@ -337,12 +340,17 @@ def parse_args(detector_classes, printer_classes):
action='store_true', action='store_true',
default=defaults_flag_in_config['exclude_high']) default=defaults_flag_in_config['exclude_high'])
group_misc.add_argument('--json', group_misc.add_argument('--json',
help='Export the results as a JSON file ("--json -" to export to stdout)', help='Export the results as a JSON file ("--json -" to export to stdout)',
action='store', action='store',
default=defaults_flag_in_config['json']) default=defaults_flag_in_config['json'])
group_misc.add_argument('--json-types',
help='Comma-separated list of result types to output to JSON, defaults to all, '
'available types: {}'.format(
', '.join(output_type for output_type in JSON_OUTPUT_TYPES)),
action='store',
default=defaults_flag_in_config['json-types'])
group_misc.add_argument('--disable-color', group_misc.add_argument('--disable-color',
help='Disable output colorization', help='Disable output colorization',
@ -383,7 +391,6 @@ def parse_args(detector_classes, printer_classes):
action=OutputMarkdown, action=OutputMarkdown,
default=False) default=False)
group_misc.add_argument('--checklist', group_misc.add_argument('--checklist',
help=argparse.SUPPRESS, help=argparse.SUPPRESS,
action='store_true', action='store_true',
@ -423,6 +430,14 @@ def parse_args(detector_classes, printer_classes):
args = parser.parse_args() args = parser.parse_args()
read_config_file(args) read_config_file(args)
args.filter_paths = parse_filter_paths(args)
# Verify our json-type output is valid
args.json_types = set(args.json_types.split(','))
for json_type in args.json_types:
if json_type not in JSON_OUTPUT_TYPES:
raise Exception(f"Error: \"{json_type}\" is not a valid JSON result output type.")
return args return args
class ListDetectors(argparse.Action): class ListDetectors(argparse.Action):
@ -434,7 +449,8 @@ class ListDetectors(argparse.Action):
class ListDetectorsJson(argparse.Action): class ListDetectorsJson(argparse.Action):
def __call__(self, parser, *args, **kwargs): def __call__(self, parser, *args, **kwargs):
detectors, _ = get_detectors_and_printers() detectors, _ = get_detectors_and_printers()
output_detectors_json(detectors) detector_types_json = output_detectors_json(detectors)
print(json.dumps(detector_types_json))
parser.exit() parser.exit()
class ListPrinters(argparse.Action): class ListPrinters(argparse.Action):
@ -501,10 +517,16 @@ def main_impl(all_detector_classes, all_printer_classes):
# Set colorization option # Set colorization option
set_colorization_enabled(not args.disable_color) set_colorization_enabled(not args.disable_color)
# If we are outputting json to stdout, we'll want to disable any logging. # Define some variables for potential JSON output
stdout_json = args.json == "-" json_results = {}
if stdout_json: output_error = None
logging.disable(logging.CRITICAL) outputting_json = args.json is not None
outputting_json_stdout = args.json == '-'
# If we are outputting JSON, capture all standard output. If we are outputting to stdout, we block typical stdout
# output.
if outputting_json:
StandardOutputCapture.enable(outputting_json_stdout)
printer_classes = choose_printers(args, all_printer_classes) printer_classes = choose_printers(args, all_printer_classes)
detector_classes = choose_detectors(args, all_detector_classes) detector_classes = choose_detectors(args, all_detector_classes)
@ -540,33 +562,56 @@ def main_impl(all_detector_classes, all_printer_classes):
try: try:
filename = args.filename filename = args.filename
globbed_filenames = glob.glob(filename, recursive=True) # Determine if we are handling ast from solc
if args.solc_ast or (filename.endswith('.json') and not is_supported(filename)):
if os.path.isfile(filename) or is_supported(filename): globbed_filenames = glob.glob(filename, recursive=True)
(results, number_contracts) = process(filename, args, detector_classes, printer_classes) filenames = glob.glob(os.path.join(filename, "*.json"))
elif os.path.isdir(filename) or len(globbed_filenames) > 0:
extension = "*.sol" if not args.solc_ast else "*.json"
filenames = glob.glob(os.path.join(filename, extension))
if not filenames: if not filenames:
filenames = globbed_filenames filenames = globbed_filenames
number_contracts = 0 number_contracts = 0
results = [] results = []
if args.splitted and args.solc_ast: slither_instances = []
(results, number_contracts) = process_files(filenames, args, detector_classes, printer_classes) if args.splitted:
(slither_instance, results, number_contracts) = process_from_asts(filenames, args, detector_classes, printer_classes)
slither_instances.append(slither_instance)
else: else:
for filename in filenames: for filename in filenames:
(results_tmp, number_contracts_tmp) = process(filename, args, detector_classes, printer_classes) (slither_instance, results_tmp, number_contracts_tmp) = process_single(filename, args, detector_classes, printer_classes)
number_contracts += number_contracts_tmp number_contracts += number_contracts_tmp
results += results_tmp results += results_tmp
slither_instances.append(slither_instance)
# Rely on CryticCompile to discern the underlying type of compilations.
else: else:
raise Exception("Unrecognised file/dir path: '#{filename}'".format(filename=filename)) (slither_instances, results, number_contracts) = process_all(filename, args, detector_classes, printer_classes)
if args.json: # Determine if we are outputting JSON
output_json(results, None if stdout_json else args.json) if outputting_json:
# Add our compilation information to JSON
if 'compilations' in args.json_types:
compilation_results = []
for slither_instance in slither_instances:
compilation_results.append(generate_standard_export(slither_instance.crytic_compile))
json_results['compilations'] = compilation_results
# Add our detector results to JSON if desired.
if results and 'detectors' in args.json_types:
json_results['detectors'] = results
# Add our detector types to JSON
if 'list-detectors' in args.json_types:
detectors, _ = get_detectors_and_printers()
json_results['list-detectors'] = output_detectors_json(detectors)
# Add our detector types to JSON
if 'list-printers' in args.json_types:
_, printers = get_detectors_and_printers()
json_results['list-printers'] = output_printers_json(printers)
# Output our results to markdown if we wish to compile a checklist.
if args.checklist: if args.checklist:
output_results_to_markdown(results) output_results_to_markdown(results)
# Dont print the number of result for printers # Dont print the number of result for printers
if number_contracts == 0: if number_contracts == 0:
logger.warn(red('No contract was analyzed')) logger.warn(red('No contract was analyzed'))
@ -576,27 +621,33 @@ def main_impl(all_detector_classes, all_printer_classes):
logger.info('%s analyzed (%d contracts), %d result(s) found', filename, number_contracts, len(results)) logger.info('%s analyzed (%d contracts), %d result(s) found', filename, number_contracts, len(results))
if args.ignore_return_value: if args.ignore_return_value:
return return
exit(results)
except SlitherException as se: except SlitherException as se:
# Output our error accordingly, via JSON or logging. output_error = str(se)
if stdout_json: logging.error(red('Error:'))
print(json.dumps(wrap_json_detectors_results(False, str(se), []))) logging.error(red(output_error))
else: logging.error('Please report an issue to https://github.com/crytic/slither/issues')
logging.error(red('Error:'))
logging.error(red(se))
logging.error('Please report an issue to https://github.com/crytic/slither/issues')
sys.exit(-1)
except Exception: except Exception:
# Output our error accordingly, via JSON or logging. output_error = traceback.format_exc()
if stdout_json: logging.error('Error in %s' % args.filename)
print(json.dumps(wrap_json_detectors_results(False, traceback.format_exc(), []))) logging.error(output_error)
else:
logging.error('Error in %s' % args.filename) # If we are outputting JSON, capture the redirected output and disable the redirect to output the final JSON.
logging.error(traceback.format_exc()) if outputting_json:
if 'console' in args.json_types:
json_results['console'] = {
'stdout': StandardOutputCapture.get_stdout_output(),
'stderr': StandardOutputCapture.get_stderr_output()
}
StandardOutputCapture.disable()
output_json(None if outputting_json_stdout else args.json, output_error, json_results)
# Exit with the appropriate status code
if output_error:
sys.exit(-1) sys.exit(-1)
else:
exit(results)
if __name__ == '__main__': if __name__ == '__main__':

@ -64,7 +64,7 @@ class NodeType:
# Node not related to the CFG # Node not related to the CFG
# Use for state variable declaration, or modifier calls # Use for state variable declaration, or modifier calls
STANDALONE = 0x50 OTHER_ENTRYPOINT = 0x50
# @staticmethod # @staticmethod
@ -111,6 +111,23 @@ def link_nodes(n1, n2):
n1.add_son(n2) n1.add_son(n2)
n2.add_father(n1) n2.add_father(n1)
def recheable(node):
'''
Return the set of nodes reacheable from the node
:param node:
:return: set(Node)
'''
nodes = node.sons
visited = set()
while nodes:
next = nodes[0]
nodes = nodes[1:]
if not next in visited:
visited.add(next)
for son in next.sons:
if not son in visited:
nodes.append(son)
return visited
# endregion # endregion
@ -183,6 +200,10 @@ class Node(SourceMapping, ChildFunction):
self._expression_vars_read = [] self._expression_vars_read = []
self._expression_calls = [] self._expression_calls = []
# Computed on the fly, can be True of False
self._can_reenter = None
self._can_send_eth = None
################################################################################### ###################################################################################
################################################################################### ###################################################################################
# region General's properties # region General's properties
@ -385,6 +406,39 @@ class Node(SourceMapping, ChildFunction):
def calls_as_expression(self): def calls_as_expression(self):
return list(self._expression_calls) return list(self._expression_calls)
def can_reenter(self, callstack=None):
'''
Check if the node can re-enter
Do not consider CREATE as potential re-enter, but check if the
destination's constructor can contain a call (recurs. follow nested CREATE)
For Solidity > 0.5, filter access to public variables and constant/pure/view
For call to this. check if the destination can re-enter
Do not consider Send/Transfer as there is not enough gas
:param callstack: used internally to check for recursion
:return bool:
'''
from slither.slithir.operations import Call
if self._can_reenter is None:
self._can_reenter = False
for ir in self.irs:
if isinstance(ir, Call) and ir.can_reenter(callstack):
self._can_reenter = True
return True
return self._can_reenter
def can_send_eth(self):
'''
Check if the node can send eth
:return bool:
'''
from slither.slithir.operations import Call
if self._can_send_eth is None:
for ir in self.all_slithir_operations():
if isinstance(ir, Call) and ir.can_send_eth():
self._can_send_eth = True
return True
return self._can_reenter
# endregion # endregion
################################################################################### ###################################################################################
################################################################################### ###################################################################################

@ -18,3 +18,7 @@ class ChildNode(object):
@property @property
def contract(self): def contract(self):
return self.node.function.contract return self.node.function.contract
@property
def slither(self):
return self.contract.slither

@ -22,7 +22,33 @@ logger = logging.getLogger("Function")
ReacheableNode = namedtuple('ReacheableNode', ['node', 'ir']) ReacheableNode = namedtuple('ReacheableNode', ['node', 'ir'])
ModifierStatements = namedtuple('Modifier', ['modifier', 'node']) class ModifierStatements:
def __init__(self, modifier, entry_point, nodes):
self._modifier = modifier
self._entry_point = entry_point
self._nodes = nodes
@property
def modifier(self):
return self._modifier
@property
def entry_point(self):
return self._entry_point
@entry_point.setter
def entry_point(self, entry_point):
self._entry_point = entry_point
@property
def nodes(self):
return self._nodes
@nodes.setter
def nodes(self, nodes):
self._nodes = nodes
class FunctionType(Enum): class FunctionType(Enum):
NORMAL = 0 NORMAL = 0
@ -106,6 +132,10 @@ class Function(ChildContract, ChildInheritance, SourceMapping):
self._function_type = None self._function_type = None
self._is_constructor = None self._is_constructor = None
# Computed on the fly, can be True of False
self._can_reenter = None
self._can_send_eth = None
################################################################################### ###################################################################################
################################################################################### ###################################################################################
# region General properties # region General properties
@ -117,7 +147,7 @@ class Function(ChildContract, ChildInheritance, SourceMapping):
""" """
str: function name str: function name
""" """
if self._function_type == FunctionType.CONSTRUCTOR: if self._name == '' and self._function_type == FunctionType.CONSTRUCTOR:
return 'constructor' return 'constructor'
elif self._function_type == FunctionType.FALLBACK: elif self._function_type == FunctionType.FALLBACK:
return 'fallback' return 'fallback'
@ -143,11 +173,44 @@ class Function(ChildContract, ChildInheritance, SourceMapping):
name, parameters, _ = self.signature name, parameters, _ = self.signature
return self.contract_declarer.name + '.' + name + '(' + ','.join(parameters) + ')' return self.contract_declarer.name + '.' + name + '(' + ','.join(parameters) + ')'
@property @property
def contains_assembly(self): def contains_assembly(self):
return self._contains_assembly return self._contains_assembly
def can_reenter(self, callstack=None):
'''
Check if the function can re-enter
Follow internal calls.
Do not consider CREATE as potential re-enter, but check if the
destination's constructor can contain a call (recurs. follow nested CREATE)
For Solidity > 0.5, filter access to public variables and constant/pure/view
For call to this. check if the destination can re-enter
Do not consider Send/Transfer as there is not enough gas
:param callstack: used internally to check for recursion
:return bool:
'''
from slither.slithir.operations import Call
if self._can_reenter is None:
self._can_reenter = False
for ir in self.all_slithir_operations():
if isinstance(ir, Call) and ir.can_reenter(callstack):
self._can_reenter = True
return True
return self._can_reenter
def can_send_eth(self):
'''
Check if the function can send eth
:return bool:
'''
from slither.slithir.operations import Call
if self._can_send_eth is None:
for ir in self.all_slithir_operations():
if isinstance(ir, Call) and ir.can_send_eth():
self._can_send_eth = True
return True
return self._can_reenter
@property @property
def slither(self): def slither(self):
return self.contract.slither return self.contract.slither
@ -403,7 +466,7 @@ class Function(ChildContract, ChildInheritance, SourceMapping):
""" """
# This is a list of contracts internally, so we convert it to a list of constructor functions. # This is a list of contracts internally, so we convert it to a list of constructor functions.
return [c for c in self._explicit_base_constructor_calls if c.modifier.constructors_declared] return list(self._explicit_base_constructor_calls)
# endregion # endregion
@ -1158,8 +1221,6 @@ class Function(ChildContract, ChildInheritance, SourceMapping):
external_calls_as_expressions = [item for sublist in external_calls_as_expressions for item in sublist] external_calls_as_expressions = [item for sublist in external_calls_as_expressions for item in sublist]
self._external_calls_as_expressions = list(set(external_calls_as_expressions)) self._external_calls_as_expressions = list(set(external_calls_as_expressions))
# endregion # endregion
################################################################################### ###################################################################################
################################################################################### ###################################################################################
@ -1266,10 +1327,12 @@ class Function(ChildContract, ChildInheritance, SourceMapping):
node.slithir_generation() node.slithir_generation()
for modifier_statement in self.modifiers_statements: for modifier_statement in self.modifiers_statements:
modifier_statement.node.slithir_generation() for node in modifier_statement.nodes:
node.slithir_generation()
for modifier_statement in self.explicit_base_constructor_calls_statements: for modifier_statement in self.explicit_base_constructor_calls_statements:
modifier_statement.node.slithir_generation() for node in modifier_statement.nodes:
node.slithir_generation()
self._analyze_read_write() self._analyze_read_write()
self._analyze_calls() self._analyze_calls()

@ -1,11 +1,13 @@
from slither.core.expressions.expression import Expression from slither.core.expressions.expression import Expression
from slither.utils.arithmetic import convert_subdenomination
class Literal(Expression): class Literal(Expression):
def __init__(self, value, type): def __init__(self, value, type, subdenomination=None):
super(Literal, self).__init__() super(Literal, self).__init__()
self._value = value self._value = value
self._type = type self._type = type
self._subdenomination = subdenomination
@property @property
def value(self): def value(self):
@ -15,6 +17,12 @@ class Literal(Expression):
def type(self): def type(self):
return self._type return self._type
@property
def subdenomination(self):
return self._subdenomination
def __str__(self): def __str__(self):
if self.subdenomination:
return str(convert_subdenomination(self._value, self.subdenomination))
# be sure to handle any character # be sure to handle any character
return str(self._value) return str(self._value)

@ -61,8 +61,11 @@ class Slither(Context):
:param path: :param path:
:return: :return:
""" """
with open(path, encoding='utf8', newline='') as f: if self.crytic_compile and path in self.crytic_compile.src_content:
self.source_code[path] = f.read() self.source_code[path] = self.crytic_compile.src_content[path]
else:
with open(path, encoding='utf8', newline='') as f:
self.source_code[path] = f.read()
# endregion # endregion
################################################################################### ###################################################################################
@ -74,6 +77,8 @@ class Slither(Context):
@property @property
def solc_version(self): def solc_version(self):
"""str: Solidity version.""" """str: Solidity version."""
if self.crytic_compile:
return self.crytic_compile.compiler_version.version
return self._solc_version return self._solc_version
@property @property

@ -90,18 +90,23 @@ class SourceMapping(Context):
is_dependency = slither.crytic_compile.is_dependency(filename_absolute) is_dependency = slither.crytic_compile.is_dependency(filename_absolute)
if filename_absolute in slither.source_code: if filename_absolute in slither.source_code or filename_absolute in slither.crytic_compile.src_content:
filename = filename_absolute filename = filename_absolute
elif filename_relative in slither.source_code: elif filename_relative in slither.source_code:
filename = filename_relative filename = filename_relative
elif filename_short in slither.source_code: elif filename_short in slither.source_code:
filename = filename_short filename = filename_short
else:# else:
filename = filename_used.used filename = filename_used
else: else:
filename = filename_used filename = filename_used
if filename in slither.source_code: if slither.crytic_compile and filename in slither.crytic_compile.src_content:
source_code = slither.crytic_compile.src_content[filename]
(lines, starting_column, ending_column) = SourceMapping._compute_line(source_code,
s,
l)
elif filename in slither.source_code:
source_code = slither.source_code[filename] source_code = slither.source_code[filename]
(lines, starting_column, ending_column) = SourceMapping._compute_line(source_code, (lines, starting_column, ending_column) = SourceMapping._compute_line(source_code,
s, s,

@ -36,5 +36,6 @@ from .source.rtlo import RightToLeftOverride
from .statements.too_many_digits import TooManyDigits from .statements.too_many_digits import TooManyDigits
from .operations.unchecked_low_level_return_values import UncheckedLowLevel from .operations.unchecked_low_level_return_values import UncheckedLowLevel
from .operations.unchecked_send_return_value import UncheckedSend from .operations.unchecked_send_return_value import UncheckedSend
from .operations.void_constructor import VoidConstructor
# #
# #

@ -10,6 +10,7 @@ class NamingConvention(AbstractDetector):
Exceptions: Exceptions:
- Allow constant variables name/symbol/decimals to be lowercase (ERC20) - Allow constant variables name/symbol/decimals to be lowercase (ERC20)
- Allow '_' at the beggining of the mixed_case match for private variables and unused parameters - Allow '_' at the beggining of the mixed_case match for private variables and unused parameters
- Ignore echidna properties (functions with names starting 'echidna_' or 'crytic_'
""" """
ARGUMENT = 'naming-convention' ARGUMENT = 'naming-convention'
@ -92,9 +93,13 @@ Solidity defines a [naming convention](https://solidity.readthedocs.io/en/v0.4.2
results.append(json) results.append(json)
for func in contract.functions_declared: for func in contract.functions_declared:
if func.is_constructor:
continue
if not self.is_mixed_case(func.name): if not self.is_mixed_case(func.name):
if func.visibility in ['internal', 'private'] and self.is_mixed_case_with_underscore(func.name): if func.visibility in ['internal', 'private'] and self.is_mixed_case_with_underscore(func.name):
continue continue
if func.name.startswith("echidna_") or func.name.startswith("crytic_"):
continue
info = "Function '{}' ({}) is not in mixedCase\n" info = "Function '{}' ({}) is not in mixedCase\n"
info = info.format(func.canonical_name, func.source_mapping_str) info = info.format(func.canonical_name, func.source_mapping_str)

@ -0,0 +1,46 @@
from slither.detectors.abstract_detector import AbstractDetector, DetectorClassification
from slither.slithir.operations import Nop
class VoidConstructor(AbstractDetector):
ARGUMENT = 'void-cst'
HELP = 'Constructor called not implemented'
IMPACT = DetectorClassification.LOW
CONFIDENCE = DetectorClassification.HIGH
WIKI = 'https://github.com/crytic/slither/wiki/Detector-Documentation#void-constructor'
WIKI_TITLE = 'Void Constructor'
WIKI_DESCRIPTION = 'Detect the call to a constructor not implemented'
WIKI_RECOMMENDATION = 'Remove the constructor call.'
WIKI_EXPLOIT_SCENARIO = '''
```solidity
contract A{}
contract B is A{
constructor() public A(){}
}
```
By reading B's constructor definition, the reader might assume that `A()` initiate the contract, while no code is executed.'''
def _detect(self):
"""
"""
results = []
for c in self.contracts:
cst = c.constructor
if cst:
for constructor_call in cst.explicit_base_constructor_calls_statements:
for node in constructor_call.nodes:
if any(isinstance(ir, Nop) for ir in node.irs):
info = "Void constructor called in {} ({}):\n"
info = info.format(cst.canonical_name, cst.source_mapping_str)
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)
results.append(json)
return results

@ -11,9 +11,8 @@ from slither.core.expressions import UnaryOperation, UnaryOperationType
from slither.detectors.abstract_detector import (AbstractDetector, from slither.detectors.abstract_detector import (AbstractDetector,
DetectorClassification) DetectorClassification)
from slither.slithir.operations import (HighLevelCall, LowLevelCall, from slither.slithir.operations import (HighLevelCall, LowLevelCall,
LibraryCall, Call,
Send, Transfer) Send, Transfer)
from slither.core.variables.variable import Variable
def union_dict(d1, d2): 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()))} d3 = {k: d1.get(k, set()) | d2.get(k, set()) for k in set(list(d1.keys()) + list(d2.keys()))}
@ -25,12 +24,6 @@ def dict_are_equal(d1, d2):
return all(set(d1[k]) == set(d2[k]) for k in d1.keys()) return all(set(d1[k]) == set(d2[k]) for k in d1.keys())
class Reentrancy(AbstractDetector): class Reentrancy(AbstractDetector):
# This detector is not meant to be registered
# It is inherited by reentrancy variantsœ
# ARGUMENT = 'reentrancy'
# HELP = 'Reentrancy vulnerabilities'
# IMPACT = DetectorClassification.HIGH
# CONFIDENCE = DetectorClassification.HIGH
KEY = 'REENTRANCY' KEY = 'REENTRANCY'
@ -43,27 +36,10 @@ class Reentrancy(AbstractDetector):
- low level call - low level call
- high level call - high level call
Do not consider Send/Transfer as there is not enough gas
""" """
for ir in irs: for ir in irs:
if isinstance(ir, LowLevelCall): if isinstance(ir, Call) and ir.can_reenter():
return True
if isinstance(ir, HighLevelCall) and not isinstance(ir, LibraryCall):
# If solidity >0.5, STATICCALL is used
if self.slither.solc_version and self.slither.solc_version.startswith('0.5.'):
if isinstance(ir.function, Function) and (ir.function.view or ir.function.pure):
continue
if isinstance(ir.function, Variable):
continue
# If there is a call to itself
# We can check that the function called is
# reentrancy-safe
if ir.destination == SolidityVariable('this'):
if isinstance(ir.function, Variable):
continue
if not ir.function.all_high_level_calls():
if not ir.function.all_low_level_calls():
continue
return True return True
return False return False
@ -73,9 +49,11 @@ class Reentrancy(AbstractDetector):
Detect if the node can send eth Detect if the node can send eth
""" """
for ir in irs: for ir in irs:
if isinstance(ir, (HighLevelCall, LowLevelCall, Transfer, Send)): if isinstance(ir, Call) and ir.can_send_eth():
if ir.call_value: return True
return True # if isinstance(ir, (HighLevelCall, LowLevelCall, Transfer, Send)):
# if ir.call_value:
# return True
return False return False
def _filter_if(self, node): def _filter_if(self, node):
@ -174,7 +152,6 @@ class Reentrancy(AbstractDetector):
self._explore(son, visited, node) self._explore(son, visited, node)
sons = [sons[0]] sons = [sons[0]]
for son in sons: for son in sons:
self._explore(son, visited) self._explore(son, visited)

@ -48,7 +48,6 @@ Use:
if isinstance(read, Constant): if isinstance(read, Constant):
# read.value can return an int or a str. Convert it to str # read.value can return an int or a str. Convert it to str
value_as_str = read.original_value value_as_str = read.original_value
line_of_code = str(node.expression)
if '00000' in value_as_str: if '00000' in value_as_str:
# Info to be printed # Info to be printed
ret.append(node) ret.append(node)

@ -13,3 +13,4 @@ from .summary.variable_order import VariableOrder
from .summary.data_depenency import DataDependency from .summary.data_depenency import DataDependency
from .summary.modifier_calls import Modifiers from .summary.modifier_calls import Modifiers
from .summary.require_calls import RequireOrAssert from .summary.require_calls import RequireOrAssert
from .summary.constructor_calls import ConstructorPrinter

@ -0,0 +1,47 @@
"""
Module printing summary of the contract
"""
from slither.printers.abstract_printer import AbstractPrinter
class ConstructorPrinter(AbstractPrinter):
WIKI = 'https://github.com/crytic/slither/wiki/Printer-documentation#constructor-calls'
ARGUMENT = 'constructor-calls'
HELP = 'Print the constructors executed'
def _get_soruce_code(self,cst):
src_mapping = cst.source_mapping
content= self.slither.source_code[src_mapping['filename_absolute']]
start = src_mapping['start']
end = src_mapping['start'] + src_mapping['length']
initial_space = src_mapping['starting_column']
return ' ' * initial_space + content[start:end]
def output(self,_filename):
for contract in self.contracts:
stack_name = []
stack_definition = []
print("\n\nContact Name:",contract.name)
print(" Constructor Call Sequence: ", sep=' ', end='', flush=True)
cst = contract.constructors_declared
if cst:
stack_name.append(contract.name)
stack_definition.append(self._get_soruce_code(cst))
for inherited_contract in contract.inheritance:
cst = inherited_contract.constructors_declared
if cst:
stack_name.append(inherited_contract.name)
stack_definition.append(self._get_soruce_code(cst))
if len(stack_name)>0:
print(" ",stack_name[len(stack_name)-1], sep=' ', end='', flush=True)
count = len(stack_name)-2;
while count>=0:
print("-->",stack_name[count], sep=' ', end='', flush=True)
count= count-1;
print("\n Constructor Definitions:")
count = len(stack_definition)-1
while count>=0:
print("\n Contract name:", stack_name[count])
print ("\n", stack_definition[count])
count = count-1;

@ -35,13 +35,9 @@ class PrinterSlithIR(AbstractPrinter):
for ir in node.irs: for ir in node.irs:
print('\t\t\t{}'.format(ir)) print('\t\t\t{}'.format(ir))
for modifier_statement in function.modifiers_statements: for modifier_statement in function.modifiers_statements:
print(f'\t\tModifier Call {modifier_statement.node.expression}') print(f'\t\tModifier Call {modifier_statement.entry_point.expression}')
for ir in modifier_statement.node.irs:
print('\t\t\t{}'.format(ir))
for modifier_statement in function.explicit_base_constructor_calls_statements: for modifier_statement in function.explicit_base_constructor_calls_statements:
print(f'\t\tConstructor Call {modifier_statement.node.expression}') print(f'\t\tConstructor Call {modifier_statement.entry_point.expression}')
for ir in modifier_statement.node.irs:
print('\t\t\t{}'.format(ir))
for modifier in contract.modifiers: for modifier in contract.modifiers:
print('\tModifier {}'.format(modifier.canonical_name)) print('\tModifier {}'.format(modifier.canonical_name))
for node in modifier.nodes: for node in modifier.nodes:

@ -22,14 +22,14 @@ logger_printer = logging.getLogger("Printers")
class Slither(SlitherSolc): class Slither(SlitherSolc):
def __init__(self, contract, **kwargs): def __init__(self, target, **kwargs):
''' '''
Args: Args:
contract (str| list(json)) target (str | list(json) | CryticCompile)
Keyword Args: Keyword Args:
solc (str): solc binary location (default 'solc') solc (str): solc binary location (default 'solc')
disable_solc_warnings (bool): True to disable solc warnings (default false) disable_solc_warnings (bool): True to disable solc warnings (default false)
solc_argeuments (str): solc arguments (default '') solc_arguments (str): solc arguments (default '')
ast_format (str): ast format (default '--ast-compact-json') ast_format (str): ast format (default '--ast-compact-json')
filter_paths (list(str)): list of path to filter (default []) filter_paths (list(str)): list of path to filter (default [])
triage_mode (bool): if true, switch to triage mode (default false) triage_mode (bool): if true, switch to triage mode (default false)
@ -46,14 +46,17 @@ class Slither(SlitherSolc):
''' '''
# list of files provided (see --splitted option) # list of files provided (see --splitted option)
if isinstance(contract, list): if isinstance(target, list):
self._init_from_list(contract) self._init_from_list(target)
elif contract.endswith('.json'): elif isinstance(target, str) and target.endswith('.json'):
self._init_from_raw_json(contract) self._init_from_raw_json(target)
else: else:
super(Slither, self).__init__('') super(Slither, self).__init__('')
try: try:
crytic_compile = CryticCompile(contract, **kwargs) if isinstance(target, CryticCompile):
crytic_compile = target
else:
crytic_compile = CryticCompile(target, **kwargs)
self._crytic_compile = crytic_compile self._crytic_compile = crytic_compile
except InvalidCompilation as e: except InvalidCompilation as e:
raise SlitherError('Invalid compilation: \n'+str(e)) raise SlitherError('Invalid compilation: \n'+str(e))

@ -21,7 +21,7 @@ from slither.slithir.operations import (Assignment, Balance, Binary,
NewElementaryType, NewStructure, NewElementaryType, NewStructure,
OperationWithLValue, Push, Return, OperationWithLValue, Push, Return,
Send, SolidityCall, Transfer, Send, SolidityCall, Transfer,
TypeConversion, Unary, Unpack) TypeConversion, Unary, Unpack, Nop)
from slither.slithir.tmp_operations.argument import Argument, ArgumentType from slither.slithir.tmp_operations.argument import Argument, ArgumentType
from slither.slithir.tmp_operations.tmp_call import TmpCall from slither.slithir.tmp_operations.tmp_call import TmpCall
from slither.slithir.tmp_operations.tmp_new_array import TmpNewArray from slither.slithir.tmp_operations.tmp_new_array import TmpNewArray
@ -621,6 +621,9 @@ def extract_tmp_call(ins, contract):
return e return e
if isinstance(ins.called, Contract): if isinstance(ins.called, Contract):
# Called a base constructor, where there is no constructor
if ins.called.constructor is None:
return Nop()
internalcall = InternalCall(ins.called.constructor, ins.nbr_arguments, ins.lvalue, internalcall = InternalCall(ins.called.constructor, ins.nbr_arguments, ins.lvalue,
ins.type_call) ins.type_call)
internalcall.call_id = ins.call_id internalcall.call_id = ins.call_id

@ -30,3 +30,4 @@ from .length import Length
from .balance import Balance from .balance import Balance
from .phi import Phi from .phi import Phi
from .phi_callback import PhiCallback from .phi_callback import PhiCallback
from .nop import Nop

@ -15,3 +15,16 @@ class Call(Operation):
def arguments(self, v): def arguments(self, v):
self._arguments = v self._arguments = v
def can_reenter(self, callstack=None):
'''
Must be called after slithIR analysis pass
:return: bool
'''
return False
def can_send_eth(self):
'''
Must be called after slithIR analysis pass
:return: bool
'''
return False

@ -2,6 +2,7 @@ from slither.slithir.operations.call import Call
from slither.slithir.operations.lvalue import OperationWithLValue from slither.slithir.operations.lvalue import OperationWithLValue
from slither.core.variables.variable import Variable from slither.core.variables.variable import Variable
from slither.core.declarations.solidity_variables import SolidityVariable from slither.core.declarations.solidity_variables import SolidityVariable
from slither.core.declarations.function import Function
from slither.slithir.utils.utils import is_valid_lvalue from slither.slithir.utils.utils import is_valid_lvalue
from slither.slithir.variables.constant import Constant from slither.slithir.variables.constant import Constant
@ -86,6 +87,55 @@ class HighLevelCall(Call, OperationWithLValue):
def type_call(self): def type_call(self):
return self._type_call return self._type_call
###################################################################################
###################################################################################
# region Analyses
###################################################################################
###################################################################################
def can_reenter(self, callstack=None):
'''
Must be called after slithIR analysis pass
For Solidity > 0.5, filter access to public variables and constant/pure/view
For call to this. check if the destination can re-enter
:param callstack: check for recursion
:return: bool
'''
# If solidity >0.5, STATICCALL is used
if self.slither.solc_version and self.slither.solc_version.startswith('0.5.'):
if isinstance(self.function, Function) and (self.function.view or self.function.pure):
return False
if isinstance(self.function, Variable):
return False
# If there is a call to itself
# We can check that the function called is
# reentrancy-safe
if self.destination == SolidityVariable('this'):
if isinstance(self.function, Variable):
return False
# In case of recursion, return False
callstack = [] if callstack is None else callstack
if self.function in callstack:
return False
callstack = callstack + [self.function]
if self.function.can_reenter(callstack):
return True
return True
def can_send_eth(self):
'''
Must be called after slithIR analysis pass
:return: bool
'''
return self._call_value is not None
# endregion
###################################################################################
###################################################################################
# region Built in
###################################################################################
###################################################################################
def __str__(self): def __str__(self):
value = '' value = ''
gas = '' gas = ''

@ -9,6 +9,18 @@ class LibraryCall(HighLevelCall):
def _check_destination(self, destination): def _check_destination(self, destination):
assert isinstance(destination, (Contract)) assert isinstance(destination, (Contract))
def can_reenter(self, callstack=None):
'''
Must be called after slithIR analysis pass
:return: bool
'''
# In case of recursion, return False
callstack = [] if callstack is None else callstack
if self.function in callstack:
return False
callstack = callstack + [self.function]
return self.function.can_reenter(callstack)
def __str__(self): def __str__(self):
gas = '' gas = ''
if self.call_gas: if self.call_gas:

@ -54,6 +54,20 @@ class LowLevelCall(Call, OperationWithLValue):
# remove None # remove None
return self._unroll([x for x in all_read if x]) return self._unroll([x for x in all_read if x])
def can_reenter(self, callstack=None):
'''
Must be called after slithIR analysis pass
:return: bool
'''
return True
def can_send_eth(self):
'''
Must be called after slithIR analysis pass
:return: bool
'''
return self._call_value is not None
@property @property
def destination(self): def destination(self):
return self._destination return self._destination

@ -31,16 +31,52 @@ class NewContract(Call, OperationWithLValue):
def call_id(self, c): def call_id(self, c):
self._callid = c self._callid = c
@property @property
def contract_name(self): def contract_name(self):
return self._contract_name return self._contract_name
@property @property
def read(self): def read(self):
return self._unroll(self.arguments) return self._unroll(self.arguments)
@property
def contract_created(self):
contract_name = self.contract_name
contract_instance = self.slither.get_contract_from_name(contract_name)
return contract_instance
###################################################################################
###################################################################################
# region Analyses
###################################################################################
###################################################################################
def can_reenter(self, callstack=None):
'''
Must be called after slithIR analysis pass
For Solidity > 0.5, filter access to public variables and constant/pure/view
For call to this. check if the destination can re-enter
:param callstack: check for recursion
:return: bool
'''
callstack = [] if callstack is None else callstack
constructor = self.contract_created.constructor
if constructor is None:
return False
if constructor in callstack:
return False
callstack = callstack + [constructor]
return constructor.can_reenter(callstack)
def can_send_eth(self):
'''
Must be called after slithIR analysis pass
:return: bool
'''
return self._call_value is not None
# endregion
def __str__(self): def __str__(self):
value = '' value = ''
if self.call_value: if self.call_value:

@ -0,0 +1,14 @@
from .operation import Operation
class Nop(Operation):
@property
def read(self):
return []
@property
def used(self):
return []
def __str__(self):
return "NOP"

@ -17,6 +17,9 @@ class Send(Call, OperationWithLValue):
self._call_value = value self._call_value = value
def can_send_eth(self):
return True
@property @property
def call_value(self): def call_value(self):
return self._call_value return self._call_value

@ -11,6 +11,8 @@ class Transfer(Call):
self._call_value = value self._call_value = value
def can_send_eth(self):
return True
@property @property
def call_value(self): def call_value(self):

@ -1,14 +1,18 @@
from .variable import SlithIRVariable from .variable import SlithIRVariable
from slither.core.solidity_types.elementary_type import ElementaryType, Int, Uint from slither.core.solidity_types.elementary_type import ElementaryType, Int, Uint
from slither.utils.arithmetic import convert_subdenomination
class Constant(SlithIRVariable): class Constant(SlithIRVariable):
def __init__(self, val, type=None): def __init__(self, val, type=None, subdenomination=None):
super(Constant, self).__init__() super(Constant, self).__init__()
assert isinstance(val, str) assert isinstance(val, str)
self._original_value = val self._original_value = val
self._subdenomination = subdenomination
if subdenomination:
val = str(convert_subdenomination(val, subdenomination))
if type: if type:
assert isinstance(type, ElementaryType) assert isinstance(type, ElementaryType)

@ -20,7 +20,7 @@ class ContractSolc04(Contract):
def __init__(self, slitherSolc, data): def __init__(self, slitherSolc, data):
assert slitherSolc.solc_version.startswith('0.4') #assert slitherSolc.solc_version.startswith('0.4')
super(ContractSolc04, self).__init__() super(ContractSolc04, self).__init__()
self.set_slither(slitherSolc) self.set_slither(slitherSolc)
@ -373,7 +373,7 @@ class ContractSolc04(Contract):
def _create_node(self, func, counter, variable): def _create_node(self, func, counter, variable):
# Function uses to create node for state variable declaration statements # Function uses to create node for state variable declaration statements
node = Node(NodeType.STANDALONE, counter) node = Node(NodeType.OTHER_ENTRYPOINT, counter)
node.set_offset(variable.source_mapping, self.slither) node.set_offset(variable.source_mapping, self.slither)
node.set_function(func) node.set_function(func)
func.add_node(node) func.add_node(node)
@ -387,27 +387,29 @@ class ContractSolc04(Contract):
def add_constructor_variables(self): def add_constructor_variables(self):
if self.state_variables: if self.state_variables:
found_candidate = False
for (idx, variable_candidate) in enumerate(self.state_variables): for (idx, variable_candidate) in enumerate(self.state_variables):
if variable_candidate.expression and not variable_candidate.is_constant: if variable_candidate.expression and not variable_candidate.is_constant:
found_candidate = True
constructor_variable = Function()
constructor_variable.set_function_type(FunctionType.CONSTRUCTOR_VARIABLES)
constructor_variable.set_contract(self)
constructor_variable.set_contract_declarer(self)
constructor_variable.set_visibility('internal')
# For now, source mapping of the constructor variable is the whole contract
# Could be improved with a targeted source mapping
constructor_variable.set_offset(self.source_mapping, self.slither)
self._functions[constructor_variable.canonical_name] = constructor_variable
prev_node = self._create_node(constructor_variable, 0, variable_candidate)
counter = 1
for v in self.state_variables[idx+1:]:
if v.expression and not v.is_constant:
next_node = self._create_node(constructor_variable, counter, v)
prev_node.add_son(next_node)
next_node.add_father(prev_node)
counter += 1
break break
if found_candidate:
constructor_variable = Function()
constructor_variable.set_function_type(FunctionType.CONSTRUCTOR_VARIABLES)
constructor_variable.set_contract(self)
constructor_variable.set_contract_declarer(self)
constructor_variable.set_visibility('internal')
self._functions[constructor_variable.canonical_name] = constructor_variable
prev_node = self._create_node(constructor_variable, 0, variable_candidate)
counter = 1
for v in self.state_variables[idx+1:]:
if v.expression and not v.is_constant:
next_node = self._create_node(constructor_variable, counter, v)
prev_node.add_son(next_node)
next_node.add_father(prev_node)
counter += 1

@ -2,7 +2,7 @@
""" """
import logging import logging
from slither.core.cfg.node import NodeType, link_nodes from slither.core.cfg.node import NodeType, link_nodes, recheable
from slither.core.declarations.contract import Contract from slither.core.declarations.contract import Contract
from slither.core.declarations.function import Function, ModifierStatements, FunctionType from slither.core.declarations.function import Function, ModifierStatements, FunctionType
@ -98,9 +98,10 @@ class FunctionSolc(Function):
# This is done to prevent collision during SSA translation # This is done to prevent collision during SSA translation
# Use of while in case of collision # Use of while in case of collision
# In the worst case, the name will be really long # In the worst case, the name will be really long
while local_var.name in self._variables: if local_var.name:
local_var.name += "_scope_{}".format(self._counter_scope_local_variables) while local_var.name in self._variables:
self._counter_scope_local_variables += 1 local_var.name += "_scope_{}".format(self._counter_scope_local_variables)
self._counter_scope_local_variables += 1
if not local_var.reference_id is None: if not local_var.reference_id is None:
self._variables_renamed[local_var.reference_id] = local_var self._variables_renamed[local_var.reference_id] = local_var
self._variables[local_var.name] = local_var self._variables[local_var.name] = local_var
@ -219,13 +220,13 @@ class FunctionSolc(Function):
for node in self.nodes: for node in self.nodes:
node.analyze_expressions(self) node.analyze_expressions(self)
for modifier_statement in self.modifiers_statements: if self._filter_ternary():
modifier_statement.node.analyze_expressions(self) for modifier_statement in self.modifiers_statements:
modifier_statement.nodes = recheable(modifier_statement.entry_point)
for modifier_statement in self.explicit_base_constructor_calls_statements: for modifier_statement in self.explicit_base_constructor_calls_statements:
modifier_statement.node.analyze_expressions(self) modifier_statement.nodes = recheable(modifier_statement.entry_point)
self._filter_ternary()
self._remove_alone_endif() self._remove_alone_endif()
@ -902,15 +903,21 @@ class FunctionSolc(Function):
self._expression_modifiers.append(m) self._expression_modifiers.append(m)
for m in ExportValues(m).result(): for m in ExportValues(m).result():
if isinstance(m, Function): if isinstance(m, Function):
node = self._new_node(NodeType.STANDALONE, modifier['src']) entry_point = self._new_node(NodeType.OTHER_ENTRYPOINT, modifier['src'])
node = self._new_node(NodeType.EXPRESSION, modifier['src'])
node.add_unparsed_expression(modifier) node.add_unparsed_expression(modifier)
link_nodes(entry_point, node)
self._modifiers.append(ModifierStatements(modifier=m, self._modifiers.append(ModifierStatements(modifier=m,
node=node)) entry_point=entry_point,
nodes=[entry_point, node]))
elif isinstance(m, Contract): elif isinstance(m, Contract):
node = self._new_node(NodeType.STANDALONE, modifier['src']) entry_point = self._new_node(NodeType.OTHER_ENTRYPOINT, modifier['src'])
node = self._new_node(NodeType.EXPRESSION, modifier['src'])
node.add_unparsed_expression(modifier) node.add_unparsed_expression(modifier)
link_nodes(entry_point, node)
self._explicit_base_constructor_calls.append(ModifierStatements(modifier=m, self._explicit_base_constructor_calls.append(ModifierStatements(modifier=m,
node=node)) entry_point=entry_point,
nodes=[entry_point, node]))
# endregion # endregion
################################################################################### ###################################################################################
@ -970,9 +977,10 @@ class FunctionSolc(Function):
def _filter_ternary(self): def _filter_ternary(self):
ternary_found = True ternary_found = True
updated = False
while ternary_found: while ternary_found:
ternary_found = False ternary_found = False
for node in self.nodes: for node in self._nodes:
has_cond = HasConditional(node.expression) has_cond = HasConditional(node.expression)
if has_cond.result(): if has_cond.result():
st = SplitTernaryExpression(node.expression) st = SplitTernaryExpression(node.expression)
@ -981,11 +989,13 @@ class FunctionSolc(Function):
raise ParsingError(f'Incorrect ternary conversion {node.expression} {node.source_mapping_str}') raise ParsingError(f'Incorrect ternary conversion {node.expression} {node.source_mapping_str}')
true_expr = st.true_expression true_expr = st.true_expression
false_expr = st.false_expression false_expr = st.false_expression
self.split_ternary_node(node, condition, true_expr, false_expr) self._split_ternary_node(node, condition, true_expr, false_expr)
ternary_found = True ternary_found = True
updated = True
break break
return updated
def split_ternary_node(self, node, condition, true_expr, false_expr): def _split_ternary_node(self, node, condition, true_expr, false_expr):
condition_node = self._new_node(NodeType.IF, node.source_mapping) condition_node = self._new_node(NodeType.IF, node.source_mapping)
condition_node.add_expression(condition) condition_node.add_expression(condition)
condition_node.analyze_expressions(self) condition_node.analyze_expressions(self)

@ -220,45 +220,7 @@ def filter_name(value):
return value return value
# endregion # endregion
###################################################################################
###################################################################################
# region Conversion
###################################################################################
###################################################################################
def convert_subdenomination(value, sub):
if sub is None:
return value
# to allow 0.1 ether conversion
if value[0:2] == "0x":
value = float(int(value, 16))
else:
value = float(value)
if sub == 'wei':
return int(value)
if sub == 'szabo':
return int(value * int(1e12))
if sub == 'finney':
return int(value * int(1e15))
if sub == 'ether':
return int(value * int(1e18))
if sub == 'seconds':
return int(value)
if sub == 'minutes':
return int(value * 60)
if sub == 'hours':
return int(value * 60 * 60)
if sub == 'days':
return int(value * 60 * 60 * 24)
if sub == 'weeks':
return int(value * 60 * 60 * 24 * 7)
if sub == 'years':
return int(value * 60 * 60 * 24 * 7 * 365)
logger.error('Subdemoniation not found {}'.format(sub))
return int(value)
# endregion
################################################################################### ###################################################################################
################################################################################### ###################################################################################
# region Parsing # region Parsing
@ -499,14 +461,19 @@ def parse_expression(expression, caller_context):
assignement.set_offset(expression['src'], caller_context.slither) assignement.set_offset(expression['src'], caller_context.slither)
return assignement return assignement
elif name == 'Literal': elif name == 'Literal':
subdenomination = None
assert 'children' not in expression assert 'children' not in expression
if is_compact_ast: if is_compact_ast:
value = expression['value'] value = expression['value']
if value: if value:
if 'subdenomination' in expression and expression['subdenomination']: if 'subdenomination' in expression and expression['subdenomination']:
value = str(convert_subdenomination(value, expression['subdenomination'])) subdenomination = expression['subdenomination']
elif not value and value != "": elif not value and value != "":
value = '0x'+expression['hexValue'] value = '0x'+expression['hexValue']
type = expression['typeDescriptions']['typeString'] type = expression['typeDescriptions']['typeString']
@ -519,7 +486,7 @@ def parse_expression(expression, caller_context):
value = expression['attributes']['value'] value = expression['attributes']['value']
if value: if value:
if 'subdenomination' in expression['attributes'] and expression['attributes']['subdenomination']: if 'subdenomination' in expression['attributes'] and expression['attributes']['subdenomination']:
value = str(convert_subdenomination(value, expression['attributes']['subdenomination'])) subdenomination = expression['attributes']['subdenomination']
elif value is None: elif value is None:
# for literal declared as hex # for literal declared as hex
# see https://solidity.readthedocs.io/en/v0.4.25/types.html?highlight=hex#hexadecimal-literals # see https://solidity.readthedocs.io/en/v0.4.25/types.html?highlight=hex#hexadecimal-literals
@ -540,7 +507,7 @@ def parse_expression(expression, caller_context):
type = ElementaryType('address') type = ElementaryType('address')
else: else:
type = ElementaryType('string') type = ElementaryType('string')
literal = Literal(value, type) literal = Literal(value, type, subdenomination)
literal.set_offset(expression['src'], caller_context.slither) literal.set_offset(expression['src'], caller_context.slither)
return literal return literal
@ -590,7 +557,7 @@ def parse_expression(expression, caller_context):
# if abi.decode is used # if abi.decode is used
# For example, abi.decode(data, ...(uint[]) ) # For example, abi.decode(data, ...(uint[]) )
if right is None: if right is None:
return _parse_elementary_type_name_expression(left, is_compact_ast, caller_context) return parse_expression(left, caller_context)
left_expression = parse_expression(left, caller_context) left_expression = parse_expression(left, caller_context)
right_expression = parse_expression(right, caller_context) right_expression = parse_expression(right, caller_context)

@ -104,30 +104,27 @@ class SlitherSolc(Slither):
return return
for contract_data in data_loaded[self.get_children()]: for contract_data in data_loaded[self.get_children()]:
# if self.solc_version == '0.3':
# assert contract_data[self.get_key()] == 'Contract' assert contract_data[self.get_key()] in ['ContractDefinition', 'PragmaDirective', 'ImportDirective']
# contract = ContractSolc03(self, contract_data) if contract_data[self.get_key()] == 'ContractDefinition':
if self.solc_version == '0.4': contract = ContractSolc04(self, contract_data)
assert contract_data[self.get_key()] in ['ContractDefinition', 'PragmaDirective', 'ImportDirective'] if 'src' in contract_data:
if contract_data[self.get_key()] == 'ContractDefinition': contract.set_offset(contract_data['src'], self)
contract = ContractSolc04(self, contract_data) self._contractsNotParsed.append(contract)
if 'src' in contract_data: elif contract_data[self.get_key()] == 'PragmaDirective':
contract.set_offset(contract_data['src'], self) if self._is_compact_ast:
self._contractsNotParsed.append(contract) pragma = Pragma(contract_data['literals'])
elif contract_data[self.get_key()] == 'PragmaDirective': else:
if self._is_compact_ast: pragma = Pragma(contract_data['attributes']["literals"])
pragma = Pragma(contract_data['literals']) pragma.set_offset(contract_data['src'], self)
else: self._pragma_directives.append(pragma)
pragma = Pragma(contract_data['attributes']["literals"]) elif contract_data[self.get_key()] == 'ImportDirective':
pragma.set_offset(contract_data['src'], self) if self.is_compact_ast:
self._pragma_directives.append(pragma) import_directive = Import(contract_data["absolutePath"])
elif contract_data[self.get_key()] == 'ImportDirective': else:
if self.is_compact_ast: import_directive = Import(contract_data['attributes']["absolutePath"])
import_directive = Import(contract_data["absolutePath"]) import_directive.set_offset(contract_data['src'], self)
else: self._import_directives.append(import_directive)
import_directive = Import(contract_data['attributes']["absolutePath"])
import_directive.set_offset(contract_data['src'], self)
self._import_directives.append(import_directive)
def _parse_source_unit(self, data, filename): def _parse_source_unit(self, data, filename):
@ -224,11 +221,11 @@ class SlitherSolc(Slither):
else: else:
father_constructors.append(self._contracts_by_id[i]) father_constructors.append(self._contracts_by_id[i])
except KeyError: except KeyError as e:
txt = 'A contract was not found, it is likely that your codebase contains muliple contracts with the same name' txt = f'A contract was not found (id {e}), it is likely that your codebase contains muliple contracts with the same name. '
txt += 'Truffle does not handle this case during compilation' txt += 'Truffle does not handle this case during compilation. '
txt += 'Please read https://github.com/trailofbits/slither/wiki#keyerror-or-nonetype-error' txt += 'Please read https://github.com/trailofbits/slither/wiki#keyerror-or-nonetype-error, '
txt += 'And update your code to remove the duplicate' txt += 'and update your code to remove the duplicate'
raise ParsingContractNotFound(txt) raise ParsingContractNotFound(txt)
contract.setInheritance(ancestors, fathers, father_constructors) contract.setInheritance(ancestors, fathers, father_constructors)

@ -0,0 +1,6 @@
## Demo
This directory contains an example of Slither utility.
See the [utility documentation](https://github.com/crytic/slither/wiki/Adding-a-new-utility)

@ -0,0 +1,38 @@
import os
import argparse
import logging
from slither import Slither
from crytic_compile import cryticparser
logging.basicConfig()
logging.getLogger("Slither").setLevel(logging.INFO)
logger = logging.getLogger("Slither-demo")
def parse_args():
"""
Parse the underlying arguments for the program.
:return: Returns the arguments for the program.
"""
parser = argparse.ArgumentParser(description='Demo',
usage='slither-demo filename')
parser.add_argument('filename',
help='The filename of the contract or truffle directory to analyze.')
# Add default arguments from crytic-compile
cryticparser.init(parser)
return parser.parse_args()
def main():
args = parse_args()
# Perform slither analysis on the given filename
slither = Slither(args.filename, **vars(args))
logger.info('Analysis done!')
if __name__ == '__main__':
main()

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save