Merge branch 'dev' into dev-printer-human-readable

pull/81/head
Josselin 6 years ago
commit 1048c61185
  1. 2
      .gitignore
  2. 3
      .travis.yml
  3. 21
      Dockerfile
  4. 137
      README.md
  5. 96
      docs/PRINTERS.md
  6. 221
      docs/imgs/DAO.svg
  7. BIN
      docs/imgs/quick-summary.png
  8. 11
      examples/bugs/uninitialized.sol
  9. 25
      examples/printers/authorization.sol
  10. 34
      examples/printers/call_graph.sol
  11. 28
      examples/printers/call_graph.sol.dot
  12. BIN
      examples/printers/call_graph.sol.dot.png
  13. 21
      examples/printers/inheritances.sol
  14. 21
      examples/printers/inheritances_graph.sol
  15. 4
      examples/printers/inheritances_graph.sol.dot
  16. BIN
      examples/printers/inheritances_graph.sol.png
  17. 13
      examples/printers/quick_summary.sol
  18. BIN
      examples/printers/quick_summary.sol.png
  19. 27
      examples/printers/slihtir.sol
  20. 30
      examples/scripts/convert_to_ir.py
  21. 18
      examples/scripts/export_to_dot.py
  22. 9
      examples/scripts/functions_called.py
  23. 9
      examples/scripts/functions_writing.py
  24. 32
      examples/scripts/slithIR.py
  25. 84
      examples/scripts/taint_mapping.py
  26. 9
      examples/scripts/variable_in_condition.py
  27. 19
      plugin_example/README.md
  28. 17
      plugin_example/setup.py
  29. 8
      plugin_example/slither_my_plugin/__init__.py
  30. 19
      plugin_example/slither_my_plugin/detectors/example.py
  31. 101
      scripts/pretty_print_and_sort_json.py
  32. 39
      scripts/tests_generate_expected_json.sh
  33. 2
      scripts/travis_install.sh
  34. 91
      scripts/travis_test.sh
  35. 4
      setup.py
  36. 476
      slither/__main__.py
  37. 0
      slither/analyses/__init__.py
  38. 0
      slither/analyses/taint/__init__.py
  39. 58
      slither/analyses/taint/calls.py
  40. 18
      slither/analyses/taint/common.py
  41. 115
      slither/analyses/taint/specific_variable.py
  42. 82
      slither/analyses/taint/state_variables.py
  43. 0
      slither/analyses/write/__init__.py
  44. 57
      slither/analyses/write/are_variables_written.py
  45. 257
      slither/core/cfg/node.py
  46. 63
      slither/core/cfg/nodeType.py
  47. 0
      slither/core/children/child_contract.py
  48. 0
      slither/core/children/child_event.py
  49. 0
      slither/core/children/child_function.py
  50. 20
      slither/core/children/child_node.py
  51. 0
      slither/core/children/child_slither.py
  52. 0
      slither/core/children/child_structure.py
  53. 9
      slither/core/declarations/__init__.py
  54. 53
      slither/core/declarations/contract.py
  55. 4
      slither/core/declarations/enum.py
  56. 4
      slither/core/declarations/event.py
  57. 337
      slither/core/declarations/function.py
  58. 14
      slither/core/declarations/import_directive.py
  59. 21
      slither/core/declarations/pragma_directive.py
  60. 48
      slither/core/declarations/solidityVariables.py
  61. 147
      slither/core/declarations/solidity_variables.py
  62. 4
      slither/core/declarations/structure.py
  63. 16
      slither/core/expressions/__init__.py
  64. 8
      slither/core/expressions/assignment_operation.py
  65. 10
      slither/core/expressions/binary_operation.py
  66. 0
      slither/core/expressions/call_expression.py
  67. 0
      slither/core/expressions/conditional_expression.py
  68. 2
      slither/core/expressions/elementary_type_name_expression.py
  69. 2
      slither/core/expressions/expression.py
  70. 0
      slither/core/expressions/expression_typed.py
  71. 2
      slither/core/expressions/identifier.py
  72. 4
      slither/core/expressions/index_access.py
  73. 4
      slither/core/expressions/member_access.py
  74. 7
      slither/core/expressions/new_array.py
  75. 0
      slither/core/expressions/new_contract.py
  76. 2
      slither/core/expressions/new_elementary_type.py
  77. 2
      slither/core/expressions/super_call_expression.py
  78. 2
      slither/core/expressions/super_identifier.py
  79. 0
      slither/core/expressions/tuple_expression.py
  80. 4
      slither/core/expressions/type_conversion.py
  81. 8
      slither/core/expressions/unary_operation.py
  82. 33
      slither/core/slither_core.py
  83. 26
      slither/core/solidityTypes/functionType.py
  84. 3
      slither/core/solidityTypes/type.py
  85. 23
      slither/core/solidityTypes/userDefinedType.py
  86. 5
      slither/core/solidity_types/__init__.py
  87. 13
      slither/core/solidity_types/array_type.py
  88. 10
      slither/core/solidity_types/elementary_type.py
  89. 66
      slither/core/solidity_types/function_type.py
  90. 11
      slither/core/solidity_types/mapping_type.py
  91. 3
      slither/core/solidity_types/type.py
  92. 35
      slither/core/solidity_types/user_defined_type.py
  93. 18
      slither/core/sourceMapping/sourceMapping.py
  94. 0
      slither/core/source_mapping/__init__.py
  95. 89
      slither/core/source_mapping/source_mapping.py
  96. 2
      slither/core/variables/event_variable.py
  97. 0
      slither/core/variables/function_type_variable.py
  98. 5
      slither/core/variables/localVariable.py
  99. 48
      slither/core/variables/local_variable.py
  100. 2
      slither/core/variables/local_variable_init_from_tuple.py
  101. Some files were not shown because too many files have changed in this diff Show More

2
.gitignore vendored

@ -46,7 +46,7 @@ nosetests.xml
coverage.xml coverage.xml
*.cover *.cover
.hypothesis/ .hypothesis/
.vscode/
# Translations # Translations
*.mo *.mo
*.pot *.pot

@ -3,11 +3,12 @@ os:
- linux - linux
language: python language: python
python: python:
- 2.7.12 - 3.6
branches: branches:
only: only:
- master - master
- dev
install: install:
- scripts/travis_install.sh - scripts/travis_install.sh

@ -0,0 +1,21 @@
FROM alpine:3.6
LABEL name slither
LABEL src "https://github.com/trailofbits/slither"
LABEL creator trailofbits
LABEL dockerfile_maintenance trailofbits
LABEL desc "Static Analyzer for Solidity"
# Mostly stolen from ethereum/solc.
RUN apk add --no-cache git python3 build-base cmake boost-dev \
&& sed -i -E -e 's/include <sys\/poll.h>/include <poll.h>/' /usr/include/boost/asio/detail/socket_types.hpp \
&& git clone --depth 1 --recursive -b release https://github.com/ethereum/solidity \
&& cd /solidity && cmake -DCMAKE_BUILD_TYPE=Release -DTESTS=0 -DSTATIC_LINKING=1 \
&& cd /solidity && make solc && install -s solc/solc /usr/bin \
&& cd / && rm -rf solidity \
&& rm -rf /var/cache/apk/* \
&& git clone https://github.com/trailofbits/slither.git
WORKDIR slither
RUN python3 setup.py install
ENTRYPOINT ["slither"]
CMD ["tests/uninitialized.sol"]

@ -1,84 +1,115 @@
# Slither, the Solidity source analyzer # Slither, the Solidity source analyzer
[![Build Status](https://travis-ci.com/trailofbits/slither.svg?token=JEF97dFy1QsDCfQ2Wusd&branch=master)](https://travis-ci.com/trailofbits/slither) [![Build Status](https://travis-ci.com/trailofbits/slither.svg?token=JEF97dFy1QsDCfQ2Wusd&branch=master)](https://travis-ci.com/trailofbits/slither)
[![Slack Status](https://empireslacking.herokuapp.com/badge.svg)](https://empireslacking.herokuapp.com)
[![PyPI version](https://badge.fury.io/py/slither-analyzer.svg)](https://badge.fury.io/py/slither-analyzer)
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. Slither is a Solidity static analysis framework written in Python 3. It runs a suite of vulnerability detectors, prints visual information about contract details, and provides an API to easily write custom analyses. Slither enables developers to find vulnerabilities, enhance their code comphrehension, and quickly prototype custom analyses.
With Slither you can: ## Features
- Detect vulnerabilities
- Speed up your understanding of code
- Build custom analyses to answer specific questions
- Quickly prototype a new static analysis techniques
## How to install * Detects vulnerable Solidity code with low false positives
* Identifies where the error condition occurs in the source code
Slither uses Python 3.6. * Easy integration into continuous integration and Truffle builds
* Built-in 'printers' quickly report crucial contract information
* Detector API to write custom analyses in Python
* Ability to analyze contracts written with Solidity >= 0.4
* Intermediate representation ([SlithIR](https://github.com/trailofbits/slither/wiki/SlithIR)) enables simple, high-precision analyses
## Usage
```bash Run Slither on a Truffle application:
$ python setup.py install
``` ```
truffle compile
You may also want solc, the Solidity compiler, which can be installed using homebrew: slither .
```bash
$ brew update
$ brew upgrade
$ brew tap ethereum/ethereum
$ brew install solidity
$ brew linkapps solidity
``` ```
or with aptitude: Run Slither on a single file:
```bash
$ sudo add-apt-repository ppa:ethereum/ethereum
$ sudo apt-get update
$ sudo apt-get install solc
``` ```
$ slither tests/uninitialized.sol # argument can be file, folder or glob, be sure to quote the argument when using a glob
## How to use
```
$ slither file.sol
```
```
$ slither examples/uninitialized.sol
[..] [..]
INFO:Detectors:Uninitialized state variables in examples/uninitialized.sol, Contract: Uninitialized, Vars: destination, Used in ['transfer'] INFO:Detectors:Uninitialized state variables in tests/uninitialized.sol, Contract: Uninitialized, Vars: destination, Used in ['transfer']
[..] [..]
``` ```
If Slither is applied on a directory, it will run on every `.sol` file of the directory. If Slither is run on a directory, it will run on every `.sol` file in the directory.
## Options
### Configuration ### Configuration
* `--solc SOLC`: Path to `solc` (default 'solc') * `--solc SOLC`: Path to `solc` (default 'solc')
* `--solc-args SOLC_ARGS`: Add custom solc arguments. `SOLC_ARGS` can contain multiple arguments
* `--disable-solc-warnings`: Do not print solc warnings * `--disable-solc-warnings`: Do not print solc warnings
* `--solc-ast`: Use the solc AST file as input (`solc file.sol --ast-json > file.ast.json`) * `--solc-ast`: Use the solc AST file as input (`solc file.sol --ast-json > file.ast.json`)
* `--json FILE`: Export results as JSON * `--json FILE`: Export results as JSON
* `--solc-args SOLC_ARGS`: Add custom solc arguments. `SOLC_ARGS` can contain multiple arguments.
### Analyses ## Detectors
* `--high`: Run only medium/high severity checks with high confidence
* `--medium`: Run only medium/high severity checks with medium confidence By default, all the detectors are run.
* `--low`: Run only low severity checks
Num | Detector | What it Detects | Impact | Confidence
--- | --- | --- | --- | ---
1 | `suicidal` | [Functions allowing anyone to destruct the contract](https://github.com/trailofbits/slither/wiki/Vulnerabilities-Description#suicidal) | High | High
2 | `uninitialized-local` | [Uninitialized local variables](https://github.com/trailofbits/slither/wiki/Vulnerabilities-Description#uninitialized-local-variables) | High | High
3 | `uninitialized-state` | [Uninitialized state variables](https://github.com/trailofbits/slither/wiki/Vulnerabilities-Description#uninitialized-state-variables) | High | High
4 | `uninitialized-storage` | [Uninitialized storage variables](https://github.com/trailofbits/slither/wiki/Vulnerabilities-Description#uninitialized-storage-variables) | High | High
5 | `arbitrary-send` | [Functions that send ether to arbitrary destinations](https://github.com/trailofbits/slither/wiki/Vulnerabilities-Description#functions-that-send-ether-to-arbitrary-destinations) | High | Medium
6 | `reentrancy` | [Reentrancy vulnerabilities](https://github.com/trailofbits/slither/wiki/Vulnerabilities-Description#reentrancy-vulnerabilities) | High | Medium
7 | `locked-ether` | [Contracts that lock ether](https://github.com/trailofbits/slither/wiki/Vulnerabilities-Description#contracts-that-lock-ether) | Medium | High
8 | `tx-origin` | [Dangerous usage of `tx.origin`](https://github.com/trailofbits/slither/wiki/Vulnerabilities-Description#dangerous-usage-of-txorigin) | Medium | Medium
9 | `assembly` | [Assembly usage](https://github.com/trailofbits/slither/wiki/Vulnerabilities-Description#assembly-usage) | Informational | High
10 | `constable-states` | [State variables that could be declared constant](https://github.com/trailofbits/slither/wiki/Vulnerabilities-Description#state-variables-that-could-be-declared-constant) | Informational | High
11 | `external-function` | [Public function that could be declared as external](https://github.com/trailofbits/slither/wiki/Vulnerabilities-Description#public-function-that-could-be-declared-as-external) | Informational | High
12 | `low-level-calls` | [Low level calls](https://github.com/trailofbits/slither/wiki/Vulnerabilities-Description#low-level-calls) | Informational | High
13 | `naming-convention` | [Conformance to Solidity naming conventions](https://github.com/trailofbits/slither/wiki/Vulnerabilities-Description#conformance-to-solidity-naming-conventions) | Informational | High
14 | `pragma` | [If different pragma directives are used](https://github.com/trailofbits/slither/wiki/Vulnerabilities-Description#state-variables-that-could-be-declared-constant) | Informational | High
15 | `solc-version` | [Old versions of Solidity (< 0.4.23)](https://github.com/trailofbits/slither/wiki/Vulnerabilities-Description#old-versions-of-solidity) | Informational | High
16 | `unused-state` | [Unused state variables](https://github.com/trailofbits/slither/wiki/Vulnerabilities-Description#unused-state-variables) | Informational | High
[Contact us](https://www.trailofbits.com/contact/) to get access to additional detectors.
### Printers ### Printers
* `--print-summary`: Print a summary of the contracts
* `--print-quick-summary`: Print a quick summary of the contracts
* `--print-inheritance`: Print the inheritance graph
For more information about printers, see the [Printers documentation](docs/PRINTERS.md) To run a printer, use `--print` and a comma-separated list of printers.
Num | Printer | Description
--- | --- | ---
1 | `call-graph` | Export the call-graph of the contracts to a dot file
2 | `contract-summary` | Print a summary of the contracts
3 | `function-summary` | Print a summary of the functions
4 | `inheritance` | Print the inheritance relations between contracts
5 | `inheritance-graph` | Export the inheritance graph of each contract to a dot file
6 | `slithir` | Print the slithIR representation of the functions
7 | `vars-and-auth` | Print the state variables written and the authorization of the functions
## How to install
Slither requires Python 3.6+ and [solc](https://github.com/ethereum/solidity/), the Solidity compiler.
### Using Pip
```
$ pip install slither-analyzer
```
### Using Git
```bash
$ git clone https://github.com/trailofbits/slither.git && cd slither
$ python setup.py install
```
## Getting Help
Feel free to stop by our [Slack channel](https://empireslacking.herokuapp.com) (#ethereum) for help using or extending Slither.
* The [Printer documentation](https://github.com/trailofbits/slither/wiki/Printer-documentation) describes the information Slither is capable of visualizing for each contract.
## Checks available * The [Detector documentation](https://github.com/trailofbits/slither/wiki/Adding-a-new-detector) describes how to write a new vulnerability analyses.
Check | Purpose | Severity | Confidence * The [API documentation](https://github.com/trailofbits/slither/wiki/API-examples) describes the methods and objects available for custom analyses.
--- | --- | --- | ---
`--uninitialized`| Detect uninitialized variables | High | High
* The [SlithIR documentation](https://github.com/trailofbits/slither/wiki/SlithIR) describes the SlithIR intermediate representation.
## License ## License
Slither is licensed and distributed under AGPLv3. [Contact us](mailto:opensource@trailofbits.com) if you're looking for an exception to the terms. Slither is licensed and distributed under the AGPLv3 license. [Contact us](mailto:opensource@trailofbits.com) if you're looking for an exception to the terms.

@ -1,96 +0,0 @@
# Slither Printers
Slither allows printing contracts information through its printers.
## Quick Summary
`slither.py file.sol --print-quick-summary`
Output a quick summary of the contract.
Example:
```
$ slither.py vulns/0x01293cd77f68341635814c35299ed30ae212789e.sol --print-quick-summary
```
<img src="imgs/quick-summary.png" width="300">
## Summary
`slither.py file.sol --print-summary`
Output a summary of the contract showing for each function:
- What are the visibility and the modifiers
- What are the state variables read or written
- What are the calls
Example:
```
$ slither.py vulns/0x01293cd77f68341635814c35299ed30ae212789e.sol --print-summary
```
```
[...]
INFO:Slither:Contract NBACrypto
Contract vars: [u'ceoAddress', u'cfoAddress', u'teams', u'players', u'teamsAreInitiated', u'playersAreInitiated', u'isPaused']
+--------------------+------------+--------------+------------------------+---------------+----------------------------------------------+
| Function | Visibility | Modifiers | Read | Write | Calls |
+--------------------+------------+--------------+------------------------+---------------+----------------------------------------------+
| pauseGame | public | [u'onlyCeo'] | [] | [u'isPaused'] | [] |
| unPauseGame | public | [u'onlyCeo'] | [] | [u'isPaused'] | [] |
| GetIsPauded | public | [] | [u'isPaused'] | [] | [] |
| purchaseCountry | public | [] | [u'isPaused'] | [u'teams'] | [u'cfoAddress.transfer', u'mul'] |
| | | | | | [u'require', u'teams.ownerAddress.transfer'] |
| purchasePlayer | public | [] | [u'isPaused'] | [u'players'] | [u'cfoAddress.transfer', u'mul'] |
| | | | | | [u'require', u'teams.ownerAddress.transfer'] |
| | | | | | [u'players.ownerAddress.transfer'] |
| modifyPriceCountry | public | [] | [] | [u'teams'] | [u'require'] |
| getTeam | public | [] | [u'teams'] | [] | [] |
| getPlayer | public | [] | [u'players'] | [] | [] |
| getTeamPrice | public | [] | [] | [] | [] |
| getPlayerPrice | public | [] | [] | [] | [] |
| getTeamOwner | public | [] | [] | [] | [] |
| getPlayerOwner | public | [] | [] | [] | [] |
| mul | internal | [] | [] | [] | [u'assert'] |
| div | internal | [] | [] | [] | [] |
| InitiateTeams | public | [u'onlyCeo'] | [u'teamsAreInitiated'] | [] | [u'require', u'teams.push'] |
| addPlayer | public | [u'onlyCeo'] | [] | [] | [u'players.push'] |
+--------------------+------------+--------------+------------------------+---------------+----------------------------------------------+
```
## Inheritance Graph
`slither.py file.sol --print-inheritance`
Output a graph showing the inheritance interaction between the contracts.
Example:
```
$ slither examples/DAO.sol --print-inheritance
[...]
INFO:PrinterInheritance:Inheritance Graph: examples/DAO.sol.dot
```
The output format is [dot](https://www.graphviz.org/) and can be converted to svg using:
```
dot examples/DAO.sol.dot -Tsvg -o examples/DAO.svg
```
Functions in orange override a parent's functions. If a variable points to another contract, the contract type is written in blue.
<img src="imgs/DAO.svg" width="700">
## Variables written and authorization
`slither.py file.sol --print-variables-written-and-authorization`
Print the variables written and the check on `msg.sender` of each function.
```
...
INFO:Printers:
Contract MyNewBank
+----------+------------------------+-------------------------+
| Function | State variable written | Condition on msg.sender |
+----------+------------------------+-------------------------+
| kill | [] | ['msg.sender != owner'] |
| withdraw | [] | ['msg.sender != owner'] |
| init | [u'owner'] | [] |
| owned | [u'owner'] | [] |
| fallback | [u'deposits'] | [] |
+----------+------------------------+-------------------------+
```

@ -1,221 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<!-- Generated by graphviz version 2.38.0 (20140413.2041)
-->
<!-- Title: %3 Pages: 1 -->
<svg width="789pt" height="2361pt"
viewBox="0.00 0.00 788.50 2361.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 2357)">
<title>%3</title>
<polygon fill="white" stroke="none" points="-4,4 -4,-2357 784.5,-2357 784.5,4 -4,4"/>
<!-- TokenInterface -->
<g id="node1" class="node"><title>TokenInterface</title>
<polygon fill="none" stroke="black" points="125,-262 7.10543e-15,-262 7.10543e-15,-0 125,-0 125,-262"/>
<text text-anchor="start" x="18" y="-243.8" font-family="Times,serif" font-weight="bold" font-size="14.00">TokenInterface</text>
<text text-anchor="start" x="12.5" y="-222.8" font-family="Times,serif" font-style="italic" font-size="14.00">Functions:</text>
<text text-anchor="start" x="12.5" y="-200.8" font-family="Times,serif" font-size="14.00"> &#160;&#160;&#160;balanceOf</text>
<text text-anchor="start" x="12.5" y="-179.8" font-family="Times,serif" font-size="14.00"> &#160;&#160;&#160;transfer</text>
<text text-anchor="start" x="12.5" y="-158.8" font-family="Times,serif" font-size="14.00"> &#160;&#160;&#160;transferFrom</text>
<text text-anchor="start" x="12.5" y="-137.8" font-family="Times,serif" font-size="14.00"> &#160;&#160;&#160;approve</text>
<text text-anchor="start" x="12.5" y="-116.8" font-family="Times,serif" font-size="14.00"> &#160;&#160;&#160;allowance</text>
<text text-anchor="start" x="12.5" y="-96.8" font-family="Times,serif" font-style="italic" font-size="14.00">Public Variables:</text>
<text text-anchor="start" x="12.5" y="-74.8" font-family="Times,serif" font-size="14.00"> &#160;&#160;&#160;totalSupply</text>
<text text-anchor="start" x="12.5" y="-54.8" font-family="Times,serif" font-style="italic" font-size="14.00">Private Variables:</text>
<text text-anchor="start" x="12.5" y="-32.8" font-family="Times,serif" font-size="14.00"> &#160;&#160;&#160;balances</text>
<text text-anchor="start" x="12.5" y="-11.8" font-family="Times,serif" font-size="14.00"> &#160;&#160;&#160;allowed</text>
</g>
<!-- Token -->
<g id="node2" class="node"><title>Token</title>
<polygon fill="none" stroke="black" points="118,-539 7,-539 7,-340 118,-340 118,-539"/>
<text text-anchor="start" x="44.5" y="-520.3" font-family="Times,serif" font-weight="bold" font-size="14.00">Token</text>
<text text-anchor="start" x="19.5" y="-499.3" font-family="Times,serif" font-style="italic" font-size="14.00">Functions:</text>
<text text-anchor="start" x="19.5" y="-477.3" font-family="Times,serif" font-size="14.00" fill="#ffa500"> &#160;&#160;&#160;balanceOf</text>
<text text-anchor="start" x="19.5" y="-456.3" font-family="Times,serif" font-size="14.00" fill="#ffa500"> &#160;&#160;&#160;transfer</text>
<text text-anchor="start" x="19.5" y="-435.3" font-family="Times,serif" font-size="14.00" fill="#ffa500"> &#160;&#160;&#160;transferFrom</text>
<text text-anchor="start" x="19.5" y="-414.3" font-family="Times,serif" font-size="14.00" fill="#ffa500"> &#160;&#160;&#160;approve</text>
<text text-anchor="start" x="19.5" y="-393.3" font-family="Times,serif" font-size="14.00" fill="#ffa500"> &#160;&#160;&#160;allowance</text>
<text text-anchor="start" x="19.5" y="-373.3" font-family="Times,serif" font-style="italic" font-size="14.00">Modifiers:</text>
<text text-anchor="start" x="19.5" y="-351.3" font-family="Times,serif" font-size="14.00"> &#160;&#160;&#160;noEther</text>
</g>
<!-- Token&#45;&gt;TokenInterface -->
<g id="edge1" class="edge"><title>Token&#45;&gt;TokenInterface</title>
<path fill="none" stroke="black" d="M62.5,-339.834C62.5,-318.264 62.5,-295.034 62.5,-272.194"/>
<polygon fill="black" stroke="black" points="66.0001,-272.086 62.5,-262.086 59.0001,-272.086 66.0001,-272.086"/>
</g>
<!-- ManagedAccountInterface -->
<g id="node3" class="node"><title>ManagedAccountInterface</title>
<polygon fill="none" stroke="black" points="680.5,-1204.5 500.5,-1204.5 500.5,-1047.5 680.5,-1047.5 680.5,-1204.5"/>
<text text-anchor="start" x="512.5" y="-1185.8" font-family="Times,serif" font-weight="bold" font-size="14.00">ManagedAccountInterface</text>
<text text-anchor="start" x="512.5" y="-1164.8" font-family="Times,serif" font-style="italic" font-size="14.00">Functions:</text>
<text text-anchor="start" x="512.5" y="-1142.8" font-family="Times,serif" font-size="14.00"> &#160;&#160;&#160;payOut</text>
<text text-anchor="start" x="512.5" y="-1122.8" font-family="Times,serif" font-style="italic" font-size="14.00">Public Variables:</text>
<text text-anchor="start" x="512.5" y="-1100.8" font-family="Times,serif" font-size="14.00"> &#160;&#160;&#160;owner</text>
<text text-anchor="start" x="512.5" y="-1079.8" font-family="Times,serif" font-size="14.00"> &#160;&#160;&#160;payOwnerOnly</text>
<text text-anchor="start" x="512.5" y="-1058.8" font-family="Times,serif" font-size="14.00"> &#160;&#160;&#160;accumulatedInput</text>
</g>
<!-- ManagedAccount -->
<g id="node4" class="node"><title>ManagedAccount</title>
<polygon fill="none" stroke="black" points="654,-2059 527,-2059 527,-1965 654,-1965 654,-2059"/>
<text text-anchor="start" x="539.5" y="-2040.8" font-family="Times,serif" font-weight="bold" font-size="14.00">ManagedAccount</text>
<text text-anchor="start" x="539.5" y="-2019.8" font-family="Times,serif" font-style="italic" font-size="14.00">Functions:</text>
<text text-anchor="start" x="539.5" y="-1997.8" font-family="Times,serif" font-size="14.00"> &#160;&#160;&#160;fallback</text>
<text text-anchor="start" x="539.5" y="-1976.8" font-family="Times,serif" font-size="14.00" fill="#ffa500"> &#160;&#160;&#160;payOut</text>
</g>
<!-- ManagedAccount&#45;&gt;ManagedAccountInterface -->
<g id="edge2" class="edge"><title>ManagedAccount&#45;&gt;ManagedAccountInterface</title>
<path fill="none" stroke="black" d="M590.5,-1964.72C590.5,-1824.03 590.5,-1399.82 590.5,-1214.83"/>
<polygon fill="black" stroke="black" points="594,-1214.6 590.5,-1204.6 587,-1214.6 594,-1214.6"/>
</g>
<!-- TokenCreationInterface -->
<g id="node5" class="node"><title>TokenCreationInterface</title>
<polygon fill="none" stroke="black" points="328.5,-581 136.5,-581 136.5,-298 328.5,-298 328.5,-581"/>
<text text-anchor="start" x="162" y="-562.3" font-family="Times,serif" font-weight="bold" font-size="14.00">TokenCreationInterface</text>
<text text-anchor="start" x="148.5" y="-541.3" font-family="Times,serif" font-style="italic" font-size="14.00">Functions:</text>
<text text-anchor="start" x="148.5" y="-519.3" font-family="Times,serif" font-size="14.00"> &#160;&#160;&#160;createTokenProxy</text>
<text text-anchor="start" x="148.5" y="-498.3" font-family="Times,serif" font-size="14.00"> &#160;&#160;&#160;refund</text>
<text text-anchor="start" x="148.5" y="-477.3" font-family="Times,serif" font-size="14.00"> &#160;&#160;&#160;divisor</text>
<text text-anchor="start" x="148.5" y="-457.3" font-family="Times,serif" font-style="italic" font-size="14.00">Public Variables:</text>
<text text-anchor="start" x="148.5" y="-435.3" font-family="Times,serif" font-size="14.00"> &#160;&#160;&#160;closingTime</text>
<text text-anchor="start" x="148.5" y="-414.3" font-family="Times,serif" font-size="14.00"> &#160;&#160;&#160;minTokensToCreate</text>
<text text-anchor="start" x="148.5" y="-393.3" font-family="Times,serif" font-size="14.00"> &#160;&#160;&#160;isFueled</text>
<text text-anchor="start" x="148.5" y="-372.3" font-family="Times,serif" font-size="14.00"> &#160;&#160;&#160;privateCreation</text>
<text text-anchor="start" x="148.5" y="-352.3" font-family="Times,serif" font-size="14.00"> &#160;&#160;&#160;extraBalance</text>
<text text-anchor="start" x="234.5" y="-352.3" font-family="Times,serif" font-size="10.00" fill="blue"> (ManagedAccount)</text>
<text text-anchor="start" x="148.5" y="-331.3" font-family="Times,serif" font-style="italic" font-size="14.00">Private Variables:</text>
<text text-anchor="start" x="148.5" y="-309.3" font-family="Times,serif" font-size="14.00"> &#160;&#160;&#160;weiGiven</text>
</g>
<!-- TokenCreation -->
<g id="node6" class="node"><title>TokenCreation</title>
<polygon fill="none" stroke="black" points="230,-1183.5 91,-1183.5 91,-1068.5 230,-1068.5 230,-1183.5"/>
<text text-anchor="start" x="117" y="-1164.8" font-family="Times,serif" font-weight="bold" font-size="14.00">TokenCreation</text>
<text text-anchor="start" x="103.5" y="-1143.8" font-family="Times,serif" font-style="italic" font-size="14.00">Functions:</text>
<text text-anchor="start" x="103.5" y="-1121.8" font-family="Times,serif" font-size="14.00" fill="#ffa500"> &#160;&#160;&#160;createTokenProxy</text>
<text text-anchor="start" x="103.5" y="-1100.8" font-family="Times,serif" font-size="14.00" fill="#ffa500"> &#160;&#160;&#160;refund</text>
<text text-anchor="start" x="103.5" y="-1079.8" font-family="Times,serif" font-size="14.00" fill="#ffa500"> &#160;&#160;&#160;divisor</text>
</g>
<!-- TokenCreation&#45;&gt;Token -->
<g id="edge4" class="edge"><title>TokenCreation&#45;&gt;Token</title>
<path fill="none" stroke="black" d="M152.397,-1068.4C136.113,-954.664 99.1762,-696.672 78.0661,-549.224"/>
<polygon fill="black" stroke="black" points="81.5185,-548.642 76.6365,-539.239 74.5891,-549.634 81.5185,-548.642"/>
</g>
<!-- TokenCreation&#45;&gt;TokenCreationInterface -->
<g id="edge3" class="edge"><title>TokenCreation&#45;&gt;TokenCreationInterface</title>
<path fill="none" stroke="black" d="M166.453,-1068.4C177.347,-964.84 200.82,-741.682 216.641,-591.273"/>
<polygon fill="black" stroke="black" points="220.154,-591.334 217.719,-581.023 213.192,-590.602 220.154,-591.334"/>
</g>
<!-- DAOInterface -->
<g id="node7" class="node"><title>DAOInterface</title>
<polygon fill="none" stroke="black" points="482.5,-1635 248.5,-1635 248.5,-617 482.5,-617 482.5,-1635"/>
<text text-anchor="start" x="323.5" y="-1616.8" font-family="Times,serif" font-weight="bold" font-size="14.00">DAOInterface</text>
<text text-anchor="start" x="260.5" y="-1595.8" font-family="Times,serif" font-style="italic" font-size="14.00">Functions:</text>
<text text-anchor="start" x="260.5" y="-1573.8" font-family="Times,serif" font-size="14.00"> &#160;&#160;&#160;fallback</text>
<text text-anchor="start" x="260.5" y="-1552.8" font-family="Times,serif" font-size="14.00"> &#160;&#160;&#160;receiveEther</text>
<text text-anchor="start" x="260.5" y="-1531.8" font-family="Times,serif" font-size="14.00"> &#160;&#160;&#160;newProposal</text>
<text text-anchor="start" x="260.5" y="-1510.8" font-family="Times,serif" font-size="14.00"> &#160;&#160;&#160;checkProposalCode</text>
<text text-anchor="start" x="260.5" y="-1489.8" font-family="Times,serif" font-size="14.00"> &#160;&#160;&#160;vote</text>
<text text-anchor="start" x="260.5" y="-1468.8" font-family="Times,serif" font-size="14.00"> &#160;&#160;&#160;executeProposal</text>
<text text-anchor="start" x="260.5" y="-1447.8" font-family="Times,serif" font-size="14.00"> &#160;&#160;&#160;splitDAO</text>
<text text-anchor="start" x="260.5" y="-1426.8" font-family="Times,serif" font-size="14.00"> &#160;&#160;&#160;newContract</text>
<text text-anchor="start" x="260.5" y="-1405.8" font-family="Times,serif" font-size="14.00"> &#160;&#160;&#160;changeAllowedRecipients</text>
<text text-anchor="start" x="260.5" y="-1384.8" font-family="Times,serif" font-size="14.00"> &#160;&#160;&#160;changeProposalDeposit</text>
<text text-anchor="start" x="260.5" y="-1363.8" font-family="Times,serif" font-size="14.00"> &#160;&#160;&#160;retrieveDAOReward</text>
<text text-anchor="start" x="260.5" y="-1342.8" font-family="Times,serif" font-size="14.00"> &#160;&#160;&#160;getMyReward</text>
<text text-anchor="start" x="260.5" y="-1321.8" font-family="Times,serif" font-size="14.00"> &#160;&#160;&#160;withdrawRewardFor</text>
<text text-anchor="start" x="260.5" y="-1300.8" font-family="Times,serif" font-size="14.00"> &#160;&#160;&#160;transferWithoutReward</text>
<text text-anchor="start" x="260.5" y="-1279.8" font-family="Times,serif" font-size="14.00"> &#160;&#160;&#160;transferFromWithoutReward</text>
<text text-anchor="start" x="260.5" y="-1258.8" font-family="Times,serif" font-size="14.00"> &#160;&#160;&#160;halveMinQuorum</text>
<text text-anchor="start" x="260.5" y="-1237.8" font-family="Times,serif" font-size="14.00"> &#160;&#160;&#160;numberOfProposals</text>
<text text-anchor="start" x="260.5" y="-1216.8" font-family="Times,serif" font-size="14.00"> &#160;&#160;&#160;getNewDAOAddress</text>
<text text-anchor="start" x="260.5" y="-1195.8" font-family="Times,serif" font-size="14.00"> &#160;&#160;&#160;isBlocked</text>
<text text-anchor="start" x="260.5" y="-1174.8" font-family="Times,serif" font-size="14.00"> &#160;&#160;&#160;unblockMe</text>
<text text-anchor="start" x="260.5" y="-1154.8" font-family="Times,serif" font-style="italic" font-size="14.00">Modifiers:</text>
<text text-anchor="start" x="260.5" y="-1132.8" font-family="Times,serif" font-size="14.00"> &#160;&#160;&#160;onlyTokenholders</text>
<text text-anchor="start" x="260.5" y="-1112.8" font-family="Times,serif" font-style="italic" font-size="14.00">Public Variables:</text>
<text text-anchor="start" x="260.5" y="-1090.8" font-family="Times,serif" font-size="14.00"> &#160;&#160;&#160;proposals</text>
<text text-anchor="start" x="260.5" y="-1069.8" font-family="Times,serif" font-size="14.00"> &#160;&#160;&#160;minQuorumDivisor</text>
<text text-anchor="start" x="260.5" y="-1048.8" font-family="Times,serif" font-size="14.00"> &#160;&#160;&#160;lastTimeMinQuorumMet</text>
<text text-anchor="start" x="260.5" y="-1027.8" font-family="Times,serif" font-size="14.00"> &#160;&#160;&#160;curator</text>
<text text-anchor="start" x="260.5" y="-1006.8" font-family="Times,serif" font-size="14.00"> &#160;&#160;&#160;allowedRecipients</text>
<text text-anchor="start" x="260.5" y="-985.8" font-family="Times,serif" font-size="14.00"> &#160;&#160;&#160;rewardToken</text>
<text text-anchor="start" x="260.5" y="-964.8" font-family="Times,serif" font-size="14.00"> &#160;&#160;&#160;totalRewardToken</text>
<text text-anchor="start" x="260.5" y="-944.8" font-family="Times,serif" font-size="14.00"> &#160;&#160;&#160;rewardAccount</text>
<text text-anchor="start" x="359.5" y="-944.8" font-family="Times,serif" font-size="10.00" fill="blue"> (ManagedAccount)</text>
<text text-anchor="start" x="260.5" y="-923.8" font-family="Times,serif" font-size="14.00"> &#160;&#160;&#160;DAOrewardAccount</text>
<text text-anchor="start" x="388.5" y="-923.8" font-family="Times,serif" font-size="10.00" fill="blue"> (ManagedAccount)</text>
<text text-anchor="start" x="260.5" y="-901.8" font-family="Times,serif" font-size="14.00"> &#160;&#160;&#160;DAOpaidOut</text>
<text text-anchor="start" x="260.5" y="-880.8" font-family="Times,serif" font-size="14.00"> &#160;&#160;&#160;paidOut</text>
<text text-anchor="start" x="260.5" y="-859.8" font-family="Times,serif" font-size="14.00"> &#160;&#160;&#160;blocked</text>
<text text-anchor="start" x="260.5" y="-838.8" font-family="Times,serif" font-size="14.00"> &#160;&#160;&#160;proposalDeposit</text>
<text text-anchor="start" x="260.5" y="-818.8" font-family="Times,serif" font-size="14.00"> &#160;&#160;&#160;daoCreator</text>
<text text-anchor="start" x="336.5" y="-818.8" font-family="Times,serif" font-size="10.00" fill="blue"> (DAO_Creator)</text>
<text text-anchor="start" x="260.5" y="-797.8" font-family="Times,serif" font-style="italic" font-size="14.00">Private Variables:</text>
<text text-anchor="start" x="260.5" y="-775.8" font-family="Times,serif" font-size="14.00"> &#160;&#160;&#160;creationGracePeriod</text>
<text text-anchor="start" x="260.5" y="-754.8" font-family="Times,serif" font-size="14.00"> &#160;&#160;&#160;minProposalDebatePeriod</text>
<text text-anchor="start" x="260.5" y="-733.8" font-family="Times,serif" font-size="14.00"> &#160;&#160;&#160;minSplitDebatePeriod</text>
<text text-anchor="start" x="260.5" y="-712.8" font-family="Times,serif" font-size="14.00"> &#160;&#160;&#160;splitExecutionPeriod</text>
<text text-anchor="start" x="260.5" y="-691.8" font-family="Times,serif" font-size="14.00"> &#160;&#160;&#160;quorumHalvingPeriod</text>
<text text-anchor="start" x="260.5" y="-670.8" font-family="Times,serif" font-size="14.00"> &#160;&#160;&#160;executeProposalPeriod</text>
<text text-anchor="start" x="260.5" y="-649.8" font-family="Times,serif" font-size="14.00"> &#160;&#160;&#160;maxDepositDivisor</text>
<text text-anchor="start" x="260.5" y="-628.8" font-family="Times,serif" font-size="14.00"> &#160;&#160;&#160;sumOfProposalDeposits</text>
</g>
<!-- DAO -->
<g id="node8" class="node"><title>DAO</title>
<polygon fill="none" stroke="black" points="259.5,-2353 61.5,-2353 61.5,-1671 259.5,-1671 259.5,-2353"/>
<text text-anchor="start" x="145" y="-2334.8" font-family="Times,serif" font-weight="bold" font-size="14.00">DAO</text>
<text text-anchor="start" x="73.5" y="-2313.8" font-family="Times,serif" font-style="italic" font-size="14.00">Functions:</text>
<text text-anchor="start" x="73.5" y="-2291.8" font-family="Times,serif" font-size="14.00" fill="#ffa500"> &#160;&#160;&#160;fallback</text>
<text text-anchor="start" x="73.5" y="-2270.8" font-family="Times,serif" font-size="14.00" fill="#ffa500"> &#160;&#160;&#160;receiveEther</text>
<text text-anchor="start" x="73.5" y="-2249.8" font-family="Times,serif" font-size="14.00" fill="#ffa500"> &#160;&#160;&#160;newProposal</text>
<text text-anchor="start" x="73.5" y="-2228.8" font-family="Times,serif" font-size="14.00" fill="#ffa500"> &#160;&#160;&#160;checkProposalCode</text>
<text text-anchor="start" x="73.5" y="-2207.8" font-family="Times,serif" font-size="14.00" fill="#ffa500"> &#160;&#160;&#160;vote</text>
<text text-anchor="start" x="73.5" y="-2186.8" font-family="Times,serif" font-size="14.00" fill="#ffa500"> &#160;&#160;&#160;executeProposal</text>
<text text-anchor="start" x="73.5" y="-2165.8" font-family="Times,serif" font-size="14.00"> &#160;&#160;&#160;closeProposal</text>
<text text-anchor="start" x="73.5" y="-2144.8" font-family="Times,serif" font-size="14.00" fill="#ffa500"> &#160;&#160;&#160;splitDAO</text>
<text text-anchor="start" x="73.5" y="-2123.8" font-family="Times,serif" font-size="14.00" fill="#ffa500"> &#160;&#160;&#160;newContract</text>
<text text-anchor="start" x="73.5" y="-2102.8" font-family="Times,serif" font-size="14.00" fill="#ffa500"> &#160;&#160;&#160;retrieveDAOReward</text>
<text text-anchor="start" x="73.5" y="-2081.8" font-family="Times,serif" font-size="14.00" fill="#ffa500"> &#160;&#160;&#160;getMyReward</text>
<text text-anchor="start" x="73.5" y="-2060.8" font-family="Times,serif" font-size="14.00" fill="#ffa500"> &#160;&#160;&#160;withdrawRewardFor</text>
<text text-anchor="start" x="73.5" y="-2039.8" font-family="Times,serif" font-size="14.00" fill="#ffa500"> &#160;&#160;&#160;transfer</text>
<text text-anchor="start" x="73.5" y="-2018.8" font-family="Times,serif" font-size="14.00" fill="#ffa500"> &#160;&#160;&#160;transferWithoutReward</text>
<text text-anchor="start" x="73.5" y="-1997.8" font-family="Times,serif" font-size="14.00" fill="#ffa500"> &#160;&#160;&#160;transferFrom</text>
<text text-anchor="start" x="73.5" y="-1976.8" font-family="Times,serif" font-size="14.00" fill="#ffa500"> &#160;&#160;&#160;transferFromWithoutReward</text>
<text text-anchor="start" x="73.5" y="-1955.8" font-family="Times,serif" font-size="14.00"> &#160;&#160;&#160;transferPaidOut</text>
<text text-anchor="start" x="73.5" y="-1934.8" font-family="Times,serif" font-size="14.00" fill="#ffa500"> &#160;&#160;&#160;changeProposalDeposit</text>
<text text-anchor="start" x="73.5" y="-1913.8" font-family="Times,serif" font-size="14.00" fill="#ffa500"> &#160;&#160;&#160;changeAllowedRecipients</text>
<text text-anchor="start" x="73.5" y="-1892.8" font-family="Times,serif" font-size="14.00"> &#160;&#160;&#160;isRecipientAllowed</text>
<text text-anchor="start" x="73.5" y="-1871.8" font-family="Times,serif" font-size="14.00"> &#160;&#160;&#160;actualBalance</text>
<text text-anchor="start" x="73.5" y="-1850.8" font-family="Times,serif" font-size="14.00"> &#160;&#160;&#160;minQuorum</text>
<text text-anchor="start" x="73.5" y="-1829.8" font-family="Times,serif" font-size="14.00" fill="#ffa500"> &#160;&#160;&#160;halveMinQuorum</text>
<text text-anchor="start" x="73.5" y="-1808.8" font-family="Times,serif" font-size="14.00"> &#160;&#160;&#160;createNewDAO</text>
<text text-anchor="start" x="73.5" y="-1787.8" font-family="Times,serif" font-size="14.00" fill="#ffa500"> &#160;&#160;&#160;numberOfProposals</text>
<text text-anchor="start" x="73.5" y="-1766.8" font-family="Times,serif" font-size="14.00" fill="#ffa500"> &#160;&#160;&#160;getNewDAOAddress</text>
<text text-anchor="start" x="73.5" y="-1745.8" font-family="Times,serif" font-size="14.00" fill="#ffa500"> &#160;&#160;&#160;isBlocked</text>
<text text-anchor="start" x="73.5" y="-1724.8" font-family="Times,serif" font-size="14.00" fill="#ffa500"> &#160;&#160;&#160;unblockMe</text>
<text text-anchor="start" x="73.5" y="-1704.8" font-family="Times,serif" font-style="italic" font-size="14.00">Modifiers:</text>
<text text-anchor="start" x="73.5" y="-1682.8" font-family="Times,serif" font-size="14.00"> &#160;&#160;&#160;onlyTokenholders</text>
</g>
<!-- DAO&#45;&gt;Token -->
<g id="edge6" class="edge"><title>DAO&#45;&gt;Token</title>
<path fill="none" stroke="black" d="M86.0098,-1670.75C84.3319,-1658.71 82.8174,-1646.77 81.5,-1635 36.9399,-1237.01 48.7897,-759.848 57.305,-549.356"/>
<polygon fill="black" stroke="black" points="60.8075,-549.365 57.7204,-539.23 53.8134,-549.078 60.8075,-549.365"/>
</g>
<!-- DAO&#45;&gt;TokenCreation -->
<g id="edge7" class="edge"><title>DAO&#45;&gt;TokenCreation</title>
<path fill="none" stroke="black" d="M160.5,-1670.8C160.5,-1494.42 160.5,-1295.16 160.5,-1193.87"/>
<polygon fill="black" stroke="black" points="164,-1193.78 160.5,-1183.78 157,-1193.78 164,-1193.78"/>
</g>
<!-- DAO&#45;&gt;DAOInterface -->
<g id="edge5" class="edge"><title>DAO&#45;&gt;DAOInterface</title>
<path fill="none" stroke="black" d="M239.392,-1670.8C241.634,-1661.13 243.892,-1651.4 246.162,-1641.61"/>
<polygon fill="black" stroke="black" points="249.586,-1642.34 248.435,-1631.81 242.767,-1640.76 249.586,-1642.34"/>
</g>
<!-- DAO_Creator -->
<g id="node9" class="node"><title>DAO_Creator</title>
<polygon fill="none" stroke="black" points="780.5,-2048.5 672.5,-2048.5 672.5,-1975.5 780.5,-1975.5 780.5,-2048.5"/>
<text text-anchor="start" x="684.5" y="-2029.8" font-family="Times,serif" font-weight="bold" font-size="14.00">DAO_Creator</text>
<text text-anchor="start" x="684.5" y="-2008.8" font-family="Times,serif" font-style="italic" font-size="14.00">Functions:</text>
<text text-anchor="start" x="684.5" y="-1986.8" font-family="Times,serif" font-size="14.00"> &#160;&#160;&#160;createDAO</text>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 81 KiB

@ -1,11 +0,0 @@
contract Uninitialized{
address destination;
function transfer() payable{
destination.transfer(msg.value);
}
}

@ -0,0 +1,25 @@
pragma solidity ^0.4.24;
contract Owner{
address owner;
modifier onlyOwner(){
require(msg.sender == owner);
_;
}
}
contract MyContract is Owner{
mapping(address => uint) balances;
constructor() public{
owner = msg.sender;
}
function mint(uint value) onlyOwner public{
balances[msg.sender] += value;
}
}

@ -0,0 +1,34 @@
library Library {
function library_func() {
}
}
contract ContractA {
uint256 public val = 0;
function my_func_a() {
keccak256(0);
Library.library_func();
}
}
contract ContractB {
ContractA a;
constructor() {
a = new ContractA();
}
function my_func_b() {
a.my_func_a();
my_second_func_b();
}
function my_func_a() {
my_second_func_b();
}
function my_second_func_b(){
a.val();
}
}

@ -0,0 +1,28 @@
strict digraph {
subgraph cluster_5_Library {
label = "Library"
"5_library_func" [label="library_func"]
}
subgraph cluster_22_ContractA {
label = "ContractA"
"22_my_func_a" [label="my_func_a"]
"22_val" [label="val"]
}
subgraph cluster_63_ContractB {
label = "ContractB"
"63_my_second_func_b" [label="my_second_func_b"]
"63_my_func_a" [label="my_func_a"]
"63_constructor" [label="constructor"]
"63_my_func_b" [label="my_func_b"]
"63_my_func_b" -> "63_my_second_func_b"
"63_my_func_a" -> "63_my_second_func_b"
}
subgraph cluster_solidity {
label = "[Solidity]"
"keccak256()"
"22_my_func_a" -> "keccak256()"
}
"22_my_func_a" -> "5_library_func"
"63_my_func_b" -> "22_my_func_a"
"63_my_second_func_b" -> "22_val"
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

@ -1,21 +1,16 @@
contract Contract1{ pragma solidity ^0.4.24;
uint myvar; contract BaseContract1{
function myfunc() public{}
} }
contract Contract2{ contract BaseContract2{
uint public myvar2;
function myfunc2() public{}
function privatefunc() private{}
} }
contract Contract3 is Contract1, Contract2{ contract ChildContract1 is BaseContract1{
}
function myfunc() public{} // override myfunc contract ChildContract2 is BaseContract1, BaseContract2{
}
contract GrandchildContract1 is ChildContract1{
} }

@ -0,0 +1,21 @@
contract Contract1{
uint myvar;
function myfunc() public{}
}
contract Contract2{
uint public myvar2;
function myfunc2() public{}
function privatefunc() private{}
}
contract Contract3 is Contract1, Contract2{
function myfunc() public{} // override myfunc
}

@ -1,7 +1,7 @@
digraph{ digraph{
Contract1[shape="box"label=< <TABLE border="0"><TR><TD align="center"><B>Contract1</B></TD></TR><TR><TD align="left"><I>Public Functions:</I></TD></TR><TR><TD align="left"> myfunc()</TD></TR><TR><TD align="left"><I>Private Variables:</I></TD></TR><TR><TD align="left"> myvar</TD></TR></TABLE> >];
Contract2[shape="box"label=< <TABLE border="0"><TR><TD align="center"><B>Contract2</B></TD></TR><TR><TD align="left"><I>Public Functions:</I></TD></TR><TR><TD align="left"> myfunc2()</TD></TR><TR><TD align="left"><I>Private Functions:</I></TD></TR><TR><TD align="left"> privatefunc()</TD></TR><TR><TD align="left"><I>Public Variables:</I></TD></TR><TR><TD align="left"> myvar2</TD></TR></TABLE> >];
Contract3 -> Contract2; Contract3 -> Contract2;
Contract3 -> Contract1; Contract3 -> Contract1;
Contract3[shape="box"label=< <TABLE border="0"><TR><TD align="center"><B>Contract3</B></TD></TR><TR><TD align="left"><I>Public Functions:</I></TD></TR><TR><TD align="left"><font color="#FFA500"> myfunc()</font></TD></TR><TR><TD align="left"><I>Public Variables:</I></TD></TR><TR><TD align="left"> myvar2</TD></TR><TR><TD align="left"><I>Private Variables:</I></TD></TR><TR><TD align="left"> myvar</TD></TR></TABLE> >]; Contract3[shape="box"label=< <TABLE border="0"><TR><TD align="center"><B>Contract3</B></TD></TR><TR><TD align="left"><I>Public Functions:</I></TD></TR><TR><TD align="left"><font color="#FFA500"> myfunc()</font></TD></TR><TR><TD align="left"><I>Public Variables:</I></TD></TR><TR><TD align="left"> myvar2</TD></TR><TR><TD align="left"><I>Private Variables:</I></TD></TR><TR><TD align="left"> myvar</TD></TR></TABLE> >];
Contract2[shape="box"label=< <TABLE border="0"><TR><TD align="center"><B>Contract2</B></TD></TR><TR><TD align="left"><I>Public Functions:</I></TD></TR><TR><TD align="left"> myfunc2()</TD></TR><TR><TD align="left"><I>Private Functions:</I></TD></TR><TR><TD align="left"> privatefunc()</TD></TR><TR><TD align="left"><I>Public Variables:</I></TD></TR><TR><TD align="left"> myvar2</TD></TR></TABLE> >];
Contract1[shape="box"label=< <TABLE border="0"><TR><TD align="center"><B>Contract1</B></TD></TR><TR><TD align="left"><I>Public Functions:</I></TD></TR><TR><TD align="left"> myfunc()</TD></TR><TR><TD align="left"><I>Private Variables:</I></TD></TR><TR><TD align="left"> myvar</TD></TR></TABLE> >];
} }

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

@ -0,0 +1,13 @@
pragma solidity 0.4.24;
contract MyContract{
function myfunc() public{
}
function myPrivateFunc() private{
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

@ -0,0 +1,27 @@
pragma solidity ^0.4.24;
library UnsafeMath{
function add(uint a, uint b) public pure returns(uint){
return a + b;
}
function min(uint a, uint b) public pure returns(uint){
return a - b;
}
}
contract MyContract{
using UnsafeMath for uint;
mapping(address => uint) balances;
function transfer(address to, uint val) public{
balances[msg.sender] = balances[msg.sender].min(val);
balances[to] = balances[to].add(val);
}
}

@ -0,0 +1,30 @@
import sys
from slither.slither import Slither
from slither.slithir.convert import convert_expression
if len(sys.argv) != 2:
print('python function_called.py functions_called.sol')
exit(-1)
# Init slither
slither = Slither(sys.argv[1])
# Get the contract
contract = slither.get_contract_from_name('Test')
# Get the variable
test = contract.get_function_from_signature('one()')
nodes = test.nodes
for node in nodes:
if node.expression:
print('Expression:\n\t{}'.format(node.expression))
irs = convert_expression(node.expression)
print('IR expressions:')
for ir in irs:
print('\t{}'.format(ir))
print()

@ -0,0 +1,18 @@
import sys
from slither.slither import Slither
if len(sys.argv) != 2:
print('python function_called.py contract.sol')
exit(-1)
# Init slither
slither = Slither(sys.argv[1])
for contract in slither.contracts:
for function in contract.functions + contract.modifiers:
filename = "{}-{}-{}.dot".format(sys.argv[1], contract.name, function.full_name)
print('Export {}'.format(filename))
function.slithir_cfg_to_dot(filename)

@ -1,7 +1,12 @@
import sys
from slither.slither import Slither from slither.slither import Slither
if len(sys.argv) != 2:
print('python functions_called.py functions_called.sol')
exit(-1)
# Init slither # Init slither
slither = Slither('functions_called.sol') slither = Slither(sys.argv[1])
# Get the contract # Get the contract
contract = slither.get_contract_from_name('Contract') contract = slither.get_contract_from_name('Contract')
@ -9,7 +14,7 @@ contract = slither.get_contract_from_name('Contract')
# Get the variable # Get the variable
entry_point = contract.get_function_from_signature('entry_point()') entry_point = contract.get_function_from_signature('entry_point()')
all_calls = entry_point.all_calls() all_calls = entry_point.all_internal_calls()
all_calls_formated = [f.contract.name + '.' + f.name for f in all_calls] all_calls_formated = [f.contract.name + '.' + f.name for f in all_calls]

@ -1,7 +1,12 @@
import sys
from slither.slither import Slither from slither.slither import Slither
if len(sys.argv) != 2:
print('python function_writing.py functions_writing.sol')
exit(-1)
# Init slither # Init slither
slither = Slither('functions_writing.sol') slither = Slither(sys.argv[1])
# Get the contract # Get the contract
contract = slither.get_contract_from_name('Contract') contract = slither.get_contract_from_name('Contract')
@ -10,7 +15,7 @@ contract = slither.get_contract_from_name('Contract')
var_a = contract.get_state_variable_from_name('a') var_a = contract.get_state_variable_from_name('a')
# Get the functions writing the variable # Get the functions writing the variable
functions_writing_a = contract.get_functions_writing_variable(var_a) functions_writing_a = contract.get_functions_writing_to_variable(var_a)
# Print the result # 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]))

@ -0,0 +1,32 @@
import sys
from slither import Slither
if len(sys.argv) != 2:
print('python slithIR.py contract.sol')
exit(-1)
# Init slither
slither = Slither(sys.argv[1])
# Iterate over all the contracts
for contract in slither.contracts:
# Iterate over all the functions
for function in contract.functions:
# Dont explore inherited functions
if function.contract == contract:
print('Function: {}'.format(function.name))
# Iterate over the nodes of the function
for node in function.nodes:
# Print the Solidity expression of the nodes
# And the SlithIR operations
if node.expression:
print('\tSolidity expression: {}'.format(node.expression))
print('\tSlithIR:')
for ir in node.irs:
print('\t\t\t{}'.format(ir))

@ -0,0 +1,84 @@
import sys
from slither.core.declarations.solidity_variables import \
SolidityVariableComposed
from slither.core.variables.state_variable import StateVariable
from slither.slither import Slither
from slither.slithir.operations.high_level_call import HighLevelCall
from slither.slithir.operations.index import Index
from slither.slithir.variables.reference import ReferenceVariable
from slither.slithir.variables.temporary import TemporaryVariable
def visit_node(node, visited):
if node in visited:
return
visited += [node]
taints = node.function.slither.context[KEY]
refs = {}
for ir in node.irs:
if isinstance(ir, Index):
refs[ir.lvalue] = ir.variable_left
if isinstance(ir, Index):
read = [ir.variable_left]
else:
read = ir.read
print(ir)
print('Refs {}'.format(refs))
print('Read {}'.format([str(x) for x in ir.read]))
print('Before {}'.format([str(x) for x in taints]))
if any(var_read in taints for var_read in read):
taints += [ir.lvalue]
lvalue = ir.lvalue
while isinstance(lvalue, ReferenceVariable):
taints += [refs[lvalue]]
lvalue = refs[lvalue]
print('After {}'.format([str(x) for x in taints]))
print()
taints = [v for v in taints if not isinstance(v, (TemporaryVariable, ReferenceVariable))]
node.function.slither.context[KEY] = list(set(taints))
for son in node.sons:
visit_node(son, visited)
def check_call(func, taints):
for node in func.nodes:
for ir in node.irs:
if isinstance(ir, HighLevelCall):
if ir.destination in taints:
print('Call to tainted address found in {}'.format(function.name))
if __name__ == "__main__":
if len(sys.argv) != 2:
print('python taint_mapping.py taint.sol')
exit(-1)
# Init slither
slither = Slither(sys.argv[1])
initial_taint = [SolidityVariableComposed('msg.sender')]
initial_taint += [SolidityVariableComposed('msg.value')]
KEY = 'TAINT'
prev_taints = []
slither.context[KEY] = initial_taint
while(set(prev_taints) != set(slither.context[KEY])):
prev_taints = slither.context[KEY]
for contract in slither.contracts:
for function in contract.functions:
print('Function {}'.format(function.name))
slither.context[KEY] = list(set(slither.context[KEY] + function.parameters))
visit_node(function.entry_point, [])
print('All variables tainted : {}'.format([str(v) for v in slither.context[KEY]]))
print('All state variables tainted : {}'.format([str(v) for v in prev_taints if isinstance(v, StateVariable)]))
for function in contract.functions:
check_call(function, slither.context[KEY])

@ -1,7 +1,12 @@
import sys
from slither.slither import Slither from slither.slither import Slither
if len(sys.argv) != 2:
print('python variable_in_condition.py variable_in_condition.sol')
exit(-1)
# Init slither # Init slither
slither = Slither('variable_in_condition.sol') slither = Slither(sys.argv[1])
# Get the contract # Get the contract
contract = slither.get_contract_from_name('Contract') contract = slither.get_contract_from_name('Contract')
@ -10,7 +15,7 @@ contract = slither.get_contract_from_name('Contract')
var_a = contract.get_state_variable_from_name('a') var_a = contract.get_state_variable_from_name('a')
# Get the functions reading the variable # Get the functions reading the variable
functions_reading_a = contract.get_functions_reading_variable(var_a) functions_reading_a = contract.get_functions_reading_from_variable(var_a)
function_using_a_as_condition = [f for f in functions_reading_a if\ function_using_a_as_condition = [f for f in functions_reading_a if\
f.is_reading_in_conditional_node(var_a) or\ f.is_reading_in_conditional_node(var_a) or\

@ -0,0 +1,19 @@
# Slither, Plugin Example
This repo contains an example of plugin for Slither.
See the [detector documentation](https://github.com/trailofbits/slither/wiki/Adding-a-new-detector).
## Architecture
- `setup.py`: Contain the plugin information
- `slither_my_plugin/__init__.py`: Contain `make_plugin()`. The function must return the list of new detectors and printers
- `slither_my_plugin/detectors/example.py`: Detector plugin skeleton.
Once these files are updated with your plugin, you can install it:
```
python setup.py develop
```
We recommend to use a Python virtual environment (for example: [virtualenvwrapper](https://virtualenvwrapper.readthedocs.io/en/latest/)).

@ -0,0 +1,17 @@
from setuptools import setup, find_packages
setup(
name='slither-my-plugins',
description='This is an example of detectors and printers to Slither.',
url='https://github.com/trailofbits/slither-plugins',
author='Trail of Bits',
version='0.0',
packages=find_packages(),
python_requires='>=3.6',
install_requires=[
'slither-analyzer==0.1'
],
entry_points={
'slither_analyzer.plugin': 'slither my-plugin=slither_my_plugin:make_plugin',
}
)

@ -0,0 +1,8 @@
from slither_my_plugin.detectors.example import Example
def make_plugin():
plugin_detectors = [Example]
plugin_printers = []
return plugin_detectors, plugin_printers

@ -0,0 +1,19 @@
from slither.detectors.abstract_detector import AbstractDetector, DetectorClassification
class Example(AbstractDetector):
"""
Documentation
"""
ARGUMENT = 'mydetector' # slither will launch the detector with slither.py --mydetector
HELP = 'Help printed by slither'
IMPACT = DetectorClassification.HIGH
CONFIDENCE = DetectorClassification.HIGH
def detect(self):
self.logger('Nothing to detect!')
return []

@ -0,0 +1,101 @@
#!/usr/bin/python3
'''
the purpose of this file is to sort the json output from the detectors such that
the order is deterministic
- the keys of a json object are sorted
- json objects in a list will be sorted based on the values of their keys
- lists of strings/numbers are sorted
'''
import sys
import json
raw_json_file = sys.argv[1]
pretty_json_file = sys.argv[2]
from collections import OrderedDict
def create_property_val_tuple(d, props_info):
p_names = props_info[0]
p_types = props_info[1]
result = []
for p in p_names:
if not p in d: # not all objects have the same keys
if p_types[p] is 'number':
result.append(0) # to make sorting work
if p_types[p] is 'string':
result.append("") # to make sorting work
else:
result.append(d[p])
return tuple(result)
def get_props_info(list_of_dicts):
found_props = set()
prop_types = dict()
# gather all prop names
for d in list_of_dicts:
for p in d:
found_props.add(p)
# create a copy, since we are gonna possibly remove props
props_whose_value_we_can_sort_on = set(found_props)
# for each object, loop through list of all found property names,
# if the object contains that property, check that it's of type string or number
# if it is, save it's type (used later on for sorting with objects that don't have that property)
# if it's not of type string/number remove it from list of properties to check
# since we cannot sort on non-string/number values
for p in list(found_props):
if p in props_whose_value_we_can_sort_on: # short circuit
for d in list_of_dicts:
if p in props_whose_value_we_can_sort_on: # less shorter short circuit
if p in d:
# we ae only gonna sort key values if they are of type string or number
if not isinstance(d[p], str) and not isinstance(d[p], int):
props_whose_value_we_can_sort_on.remove(p)
# we need to store the type of the value because not each object
# in a list of output objects for 1 detector will have the same
# keys, so if we want to sort based on the values then if a certain object
# does not have a key which another object does have we are gonna
# put in 0 for number and "" for string for that key such that sorting on values
# still works
elif isinstance(d[p], str):
prop_types[p] = 'string'
elif isinstance(d[p], int):
prop_types[p] = 'number'
return (sorted(list(props_whose_value_we_can_sort_on)), prop_types)
def order_by_prop_value(list_of_dicts):
props_info = get_props_info(list_of_dicts)
return sorted(list_of_dicts, key=lambda d: create_property_val_tuple(d, props_info))
def order_dict(d):
result = OrderedDict() # such that we keep the order
for k, v in sorted(d.items()):
if isinstance(v, dict):
result[k] = order_dict(v)
elif type(v) is list:
result[k] = order_list(v)
else: # string/number
result[k] = v
return result
def order_list(l):
if not l:
return []
if isinstance(l[0], str): # it's a list of string
return sorted(l)
elif isinstance(l[0], int): # it's a list of numbers
return sorted(l)
elif isinstance(l[0], dict): # it's a list of objects
ordered_by_key = [order_dict(v) for v in l]
ordered_by_val = order_by_prop_value(ordered_by_key)
return ordered_by_val
with open(raw_json_file, 'r') as json_data:
with open(pretty_json_file, 'w') as out_file:
out_file.write(json.dumps(order_list(json.load(json_data)), sort_keys=False, indent=4, separators=(',',': ')))

@ -0,0 +1,39 @@
#!/usr/bin/env bash
DIR="$(cd "$(dirname "$0")" && pwd)"
# generate_expected_json file.sol detectors
generate_expected_json(){
# generate output filename
# e.g. file: uninitialized.sol detector: uninitialized-state
# ---> uninitialized.uninitialized-state.json
output_filename="$(basename $1 .sol).$2.json"
# run slither detector on input file and save output as json
slither "$1" --disable-solc-warnings --detect "$2" --json "$DIR/tmp-gen.json"
# convert json file to pretty print and write to destination folder
python "$DIR/pretty_print_and_sort_json.py" "$DIR/tmp-gen.json" "$DIR/../tests/expected_json/$output_filename"
# remove the raw un-prettified json file
rm "$DIR/tmp-gen.json"
}
generate_expected_json tests/uninitialized.sol "uninitialized-state"
generate_expected_json tests/backdoor.sol "backdoor"
generate_expected_json tests/backdoor.sol "suicidal"
generate_expected_json tests/pragma.0.4.24.sol "pragma"
generate_expected_json tests/old_solc.sol.json "solc-version"
generate_expected_json tests/reentrancy.sol "reentrancy"
generate_expected_json tests/uninitialized_storage_pointer.sol "uninitialized-storage"
generate_expected_json tests/tx_origin.sol "tx-origin"
generate_expected_json tests/unused_state.sol "unused-state"
generate_expected_json tests/locked_ether.sol "locked-ether"
generate_expected_json tests/arbitrary_send.sol "arbitrary-send"
generate_expected_json tests/inline_assembly_contract.sol "assembly"
generate_expected_json tests/inline_assembly_library.sol "assembly"
generate_expected_json tests/low_level_calls.sol "low-level-calls"
generate_expected_json tests/const_state_variables.sol "constable-states"
generate_expected_json tests/external_function.sol "external-function"
generate_expected_json tests/naming_convention.sol "naming-convention"
generate_expected_json tests/uninitialized_local_variable.sol "uninitialized-local"

@ -1,5 +1,5 @@
#!/usr/bin/env bash #!/usr/bin/env bash
pip install -r requirements.txt python setup.py install
function install_solc { function install_solc {
sudo wget -O /usr/bin/solc https://github.com/ethereum/solidity/releases/download/v0.4.24/solc-static-linux sudo wget -O /usr/bin/solc https://github.com/ethereum/solidity/releases/download/v0.4.24/solc-static-linux

@ -1,13 +1,96 @@
#!/usr/bin/env bash #!/usr/bin/env bash
./slither.py examples/bugs/uninitialized.sol --disable-solc-warnings ### Test Detectors
if [ $? -ne 1 ]; then
DIR="$(cd "$(dirname "$0")" && pwd)"
# test_slither file.sol detectors
test_slither(){
expected="$DIR/../tests/expected_json/$(basename $1 .sol).$2.json"
actual="$DIR/$(basename $1 .sol).$2.json"
# run slither detector on input file and save output as json
slither "$1" --disable-solc-warnings --detect "$2" --json "$DIR/tmp-test.json"
# convert json file to pretty print and write to destination folder
python "$DIR/pretty_print_and_sort_json.py" "$DIR/tmp-test.json" "$actual"
# remove the raw un-prettified json file
rm "$DIR/tmp-test.json"
result=$(diff "$expected" "$actual")
if [ "$result" != "" ]; then
rm "$actual"
echo ""
echo "failed test of file: $1, detector: $2"
echo ""
echo "$result"
echo ""
exit 1 exit 1
else
rm "$actual"
fi fi
./slither.py examples/bugs/backdoor.sol --disable-solc-warnings # run slither detector on input file and save output as json
if [ $? -ne 1 ]; then slither "$1" --disable-solc-warnings --detect "$2" --compact-ast --json "$DIR/tmp-test.json"
# convert json file to pretty print and write to destination folder
python "$DIR/pretty_print_and_sort_json.py" "$DIR/tmp-test.json" "$actual"
# remove the raw un-prettified json file
rm "$DIR/tmp-test.json"
result=$(diff "$expected" "$actual")
if [ "$result" != "" ]; then
rm "$actual"
echo ""
echo "failed test of file: $1, detector: $2"
echo ""
echo "$result"
echo ""
exit 1 exit 1
else
rm "$actual"
fi fi
}
test_slither tests/uninitialized.sol "uninitialized-state"
test_slither tests/backdoor.sol "backdoor"
test_slither tests/backdoor.sol "suicidal"
test_slither tests/pragma.0.4.24.sol "pragma"
test_slither tests/old_solc.sol.json "solc-version"
test_slither tests/reentrancy.sol "reentrancy"
test_slither tests/uninitialized_storage_pointer.sol "uninitialized-storage"
test_slither tests/tx_origin.sol "tx-origin"
test_slither tests/unused_state.sol "unused-state"
test_slither tests/locked_ether.sol "locked-ether"
test_slither tests/arbitrary_send.sol "arbitrary-send"
test_slither tests/inline_assembly_contract.sol "assembly"
test_slither tests/inline_assembly_library.sol "assembly"
test_slither tests/low_level_calls.sol "low-level-calls"
test_slither tests/const_state_variables.sol "constable-states"
test_slither tests/external_function.sol "external-function"
test_slither tests/naming_convention.sol "naming-convention"
#test_slither tests/complex_func.sol "complex-function"
### Test scripts
python examples/scripts/functions_called.py examples/scripts/functions_called.sol
if [ $? -ne 0 ]; then
exit 1
fi
python examples/scripts/functions_writing.py examples/scripts/functions_writing.sol
if [ $? -ne 0 ]; then
exit 1
fi
python examples/scripts/variable_in_condition.py examples/scripts/variable_in_condition.sol
if [ $? -ne 0 ]; then
exit 1
fi
exit 0 exit 0

@ -1,11 +1,11 @@
from setuptools import setup, find_packages from setuptools import setup, find_packages
setup( setup(
name='Slither', name='slither-analyzer',
description='Slither is a Solidity static analysis framework written in Python 3.', description='Slither is a Solidity static analysis framework written in Python 3.',
url='https://github.com/trailofbits/slither', url='https://github.com/trailofbits/slither',
author='Trail of Bits', author='Trail of Bits',
version='0.1', version='0.2.0',
packages=find_packages(), packages=find_packages(),
python_requires='>=3.6', python_requires='>=3.6',
install_requires=['prettytable>=0.7.2'], install_requires=['prettytable>=0.7.2'],

@ -1,45 +1,81 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import sys
import argparse import argparse
import logging
import traceback
import os
import glob import glob
import json import json
import logging
import os
import sys
import traceback
from pkg_resources import iter_entry_points, require
from slither.detectors.abstract_detector import (AbstractDetector,
DetectorClassification)
from slither.printers.abstract_printer import AbstractPrinter
from slither.slither import Slither from slither.slither import Slither
from slither.detectors.detectors import Detectors from slither.utils.colors import red
from slither.printers.printers import Printers from slither.utils.command_line import output_to_markdown, output_detectors, output_printers
logging.basicConfig() logging.basicConfig()
logger = logging.getLogger("Slither") logger = logging.getLogger("Slither")
def determineChecks(detectors, args): def process(filename, args, detector_classes, printer_classes):
if args.low: """
return detectors.low The core high-level code for running Slither static analysis.
elif args.medium:
return detectors.medium + detectors.high
elif args.high:
return detectors.high
elif args.detectors_to_run:
return args.detectors_to_run
else:
return detectors.high + detectors.medium + detectors.low
Returns:
list(result), int: Result list and number of contracts analyzed
"""
ast = '--ast-json'
if args.compact_ast:
ast = '--ast-compact-json'
slither = Slither(filename, args.solc, args.disable_solc_warnings, args.solc_args, ast)
def process(filename, args, detectors, printers): return _process(slither, detector_classes, printer_classes)
slither = Slither(filename, args.solc, args.disable_solc_warnings, args.solc_args)
if args.printers_to_run: def _process(slither, detector_classes, printer_classes):
[printers.run_printer(slither, p) for p in args.printers_to_run] for detector_cls in detector_classes:
return [] slither.register_detector(detector_cls)
else:
checks = determineChecks(detectors, args) for printer_cls in printer_classes:
results = [detectors.run_detector(slither, c) for c in checks] slither.register_printer(printer_cls)
results = [x for x in results if x] # remove empty results
results = [item for sublist in results for item in sublist] #flatten analyzed_contracts_count = len(slither.contracts)
return results
results = []
if not printer_classes:
detector_results = slither.run_detectors()
detector_results = [x for x in detector_results if x] # remove empty results
detector_results = [item for sublist in detector_results for item in sublist] # flatten
results.extend(detector_results)
slither.run_printers() # Currently printers does not return results
return results, analyzed_contracts_count
def process_truffle(dirname, args, detector_classes, printer_classes):
if not os.path.isdir(os.path.join(dirname, 'build'))\
or not os.path.isdir(os.path.join(dirname, 'build', 'contracts')):
logger.info(red('No truffle build directory found, did you run `truffle compile`?'))
return (0,0)
filenames = glob.glob(os.path.join(dirname,'build','contracts', '*.json'))
all_contracts = []
all_filenames = []
for filename in filenames:
with open(filename) as f:
contract_loaded = json.load(f)
all_contracts.append(contract_loaded['ast'])
all_filenames.append(contract_loaded['sourcePath'])
slither = Slither(all_contracts, args.solc, args.disable_solc_warnings, args.solc_args)
return _process(slither, detector_classes, printer_classes)
def output_json(results, filename): def output_json(results, filename):
@ -53,123 +89,353 @@ def exit(results):
sys.exit(len(results)) sys.exit(len(results))
def get_detectors_and_printers():
"""
NOTE: This contains just a few detectors and printers that we made public.
"""
from slither.detectors.examples.backdoor import Backdoor
from slither.detectors.variables.uninitialized_state_variables import UninitializedStateVarsDetection
from slither.detectors.variables.uninitialized_storage_variables import UninitializedStorageVars
from slither.detectors.variables.uninitialized_local_variables import UninitializedLocalVars
from slither.detectors.attributes.constant_pragma import ConstantPragma
from slither.detectors.attributes.old_solc import OldSolc
from slither.detectors.attributes.locked_ether import LockedEther
from slither.detectors.functions.arbitrary_send import ArbitrarySend
from slither.detectors.functions.suicidal import Suicidal
from slither.detectors.functions.complex_function import ComplexFunction
from slither.detectors.reentrancy.reentrancy import Reentrancy
from slither.detectors.variables.unused_state_variables import UnusedStateVars
from slither.detectors.variables.possible_const_state_variables import ConstCandidateStateVars
from slither.detectors.statements.tx_origin import TxOrigin
from slither.detectors.statements.assembly import Assembly
from slither.detectors.operations.low_level_calls import LowLevelCalls
from slither.detectors.naming_convention.naming_convention import NamingConvention
from slither.detectors.functions.external_function import ExternalFunction
detectors = [Backdoor,
UninitializedStateVarsDetection,
UninitializedStorageVars,
UninitializedLocalVars,
ConstantPragma,
OldSolc,
Reentrancy,
LockedEther,
ArbitrarySend,
Suicidal,
UnusedStateVars,
TxOrigin,
Assembly,
LowLevelCalls,
NamingConvention,
ConstCandidateStateVars,
#ComplexFunction,
ExternalFunction]
from slither.printers.summary.function import FunctionSummary
from slither.printers.summary.contract import ContractSummary
from slither.printers.inheritance.inheritance import PrinterInheritance
from slither.printers.inheritance.inheritance_graph import PrinterInheritanceGraph
from slither.printers.call.call_graph import PrinterCallGraph
from slither.printers.functions.authorization import PrinterWrittenVariablesAndAuthorization
from slither.printers.summary.slithir import PrinterSlithIR
from slither.printers.summary.human_summary import PrinterHumanSummary
printers = [FunctionSummary,
ContractSummary,
PrinterInheritance,
PrinterInheritanceGraph,
PrinterCallGraph,
PrinterWrittenVariablesAndAuthorization,
PrinterSlithIR,
PrinterHumanSummary]
# Handle plugins!
for entry_point in iter_entry_points(group='slither_analyzer.plugin', name=None):
make_plugin = entry_point.load()
plugin_detectors, plugin_printers = make_plugin()
if not all(issubclass(d, AbstractDetector) for d in plugin_detectors):
raise Exception('Error when loading plugin %s, %r is not a detector' % (entry_point, d))
if not all(issubclass(p, AbstractPrinter) for p in plugin_printers):
raise Exception('Error when loading plugin %s, %r is not a printer' % (entry_point, p))
# We convert those to lists in case someone returns a tuple
detectors += list(plugin_detectors)
printers += list(plugin_printers)
return detectors, printers
def main(): def main():
detectors = Detectors() detectors, printers = get_detectors_and_printers()
printers = Printers()
main_impl(all_detector_classes=detectors, all_printer_classes=printers)
def main_impl(all_detector_classes, all_printer_classes):
"""
:param all_detector_classes: A list of all detectors that can be included/excluded.
:param all_printer_classes: A list of all printers that can be included.
"""
args = parse_args(all_detector_classes, all_printer_classes)
printer_classes = choose_printers(args, all_printer_classes)
detector_classes = choose_detectors(args, all_detector_classes)
default_log = logging.INFO if not args.debug else logging.DEBUG
for (l_name, l_level) in [('Slither', default_log),
('Contract', default_log),
('Function', default_log),
('Node', default_log),
('Parsing', default_log),
('Detectors', default_log),
('FunctionSolc', default_log),
('ExpressionParsing', default_log),
('TypeParsing', default_log),
('Printers', default_log)]:
l = logging.getLogger(l_name)
l.setLevel(l_level)
try:
filename = args.filename
globbed_filenames = glob.glob(filename, recursive=True)
if os.path.isfile(filename):
(results, number_contracts) = process(filename, args, detector_classes, printer_classes)
elif os.path.isfile(os.path.join(filename, 'truffle.js')):
(results, number_contracts) = process_truffle(filename, args, detector_classes, printer_classes)
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 len(filenames) == 0:
filenames = globbed_filenames
number_contracts = 0
results = []
for filename in filenames:
(results_tmp, number_contracts_tmp) = process(filename, args, detector_classes, printer_classes)
number_contracts += number_contracts_tmp
results += results_tmp
else:
raise Exception("Unrecognised file/dir path: '#{filename}'".format(filename=filename))
if args.json:
output_json(results, args.json)
# Dont print the number of result for printers
if printer_classes:
logger.info('%s analyzed (%d contracts)', filename, number_contracts)
else:
logger.info('%s analyzed (%d contracts), %d result(s) found', filename, number_contracts, len(results))
exit(results)
except Exception:
logging.error('Error in %s' % args.filename)
logging.error(traceback.format_exc())
sys.exit(-1)
def parse_args(detector_classes, printer_classes):
parser = argparse.ArgumentParser(description='Slither', parser = argparse.ArgumentParser(description='Slither',
usage="slither.py contract.sol [flag]") usage="slither.py contract.sol [flag]")
parser.add_argument('filename', parser.add_argument('filename',
help='contract.sol file') help='contract.sol')
parser.add_argument('--version',
help='displays the current version',
version=require('slither-analyzer')[0].version,
action='version')
group_detector = parser.add_argument_group('Detectors')
group_printer = parser.add_argument_group('Printers')
group_solc = parser.add_argument_group('Solc options')
group_misc = parser.add_argument_group('Additional option')
group_detector.add_argument('--detect',
help='Comma-separated list of detectors, defaults to all, '
'available detectors: {}'.format(
', '.join(d.ARGUMENT for d in detector_classes)),
action='store',
dest='detectors_to_run',
default='all')
parser.add_argument('--solc', group_printer.add_argument('--print',
help='solc path', help='Comma-separated list fo contract information printers, '
'available printers: {}'.format(
', '.join(d.ARGUMENT for d in printer_classes)),
action='store', action='store',
default='solc') dest='printers_to_run',
default='')
parser.add_argument('--solc-args', group_detector.add_argument('--list-detectors',
help='Add custom solc arguments. Example: --solc-args "--allow-path /tmp --evm-version byzantium".', help='List available detectors',
action=ListDetectors,
nargs=0,
default=False)
group_printer.add_argument('--list-printers',
help='List available printers',
action=ListPrinters,
nargs=0,
default=False)
group_detector.add_argument('--exclude-detectors',
help='Comma-separated list of detectors that should be excluded',
action='store', action='store',
default=None) dest='detectors_to_exclude',
default='')
parser.add_argument('--disable-solc-warnings', group_detector.add_argument('--exclude-informational',
help='Disable solc warnings', help='Exclude informational impact analyses',
action='store_true', action='store_true',
default=False) default=False)
parser.add_argument('--solc-ast', group_detector.add_argument('--exclude-low',
help='Provide the ast solc file', help='Exclude low impact analyses',
action='store_true', action='store_true',
default=False) default=False)
parser.add_argument('--low', group_detector.add_argument('--exclude-medium',
help='Only low analyses', help='Exclude medium impact analyses',
action='store_true', action='store_true',
default=False) default=False)
parser.add_argument('--medium', group_detector.add_argument('--exclude-high',
help='Only medium and high analyses', help='Exclude high impact analyses',
action='store_true', action='store_true',
default=False) default=False)
parser.add_argument('--high',
help='Only high analyses', group_solc.add_argument('--solc',
help='solc path',
action='store',
default='solc')
group_solc.add_argument('--solc-args',
help='Add custom solc arguments. Example: --solc-args "--allow-path /tmp --evm-version byzantium".',
action='store',
default=None)
group_solc.add_argument('--disable-solc-warnings',
help='Disable solc warnings',
action='store_true',
default=False)
group_solc.add_argument('--solc-ast',
help='Provide the ast solc file',
action='store_true', action='store_true',
default=False) default=False)
parser.add_argument('--json', group_misc.add_argument('--json',
help='Export results as JSON', help='Export results as JSON',
action='store', action='store',
default=None) default=None)
for detector_name, Detector in detectors.detectors.items():
detector_arg = '--{}'.format(Detector.ARGUMENT)
detector_help = 'Detection of ' + Detector.HELP # debugger command
parser.add_argument(detector_arg,
help=detector_help,
action="append_const",
dest="detectors_to_run",
const=detector_name)
for printer_name, Printer in printers.printers.items():
printer_arg = '--{}'.format(Printer.ARGUMENT)
printer_help = Printer.HELP
parser.add_argument(printer_arg,
help=printer_help,
action="append_const",
dest="printers_to_run",
const=printer_name)
# Debug
parser.add_argument('--debug', parser.add_argument('--debug',
help='Debug mode', help=argparse.SUPPRESS,
action="store_true", action="store_true",
default=False) default=False)
parser.add_argument('--markdown',
help=argparse.SUPPRESS,
action=OutputMarkdown,
nargs=0,
default=False)
parser.add_argument('--compact-ast',
help=argparse.SUPPRESS,
action='store_true',
default=False)
if len(sys.argv) == 1:
parser.print_help(sys.stderr)
sys.exit(1)
args = parser.parse_args() args = parser.parse_args()
default_log = logging.INFO
if args.debug:
default_log = logging.DEBUG
for (l_name, l_level) in [('Slither', default_log), return args
('Contract', default_log),
('Function', default_log),
('Node', default_log),
('Parsing', default_log),
('Detectors', default_log),
('FunctionSolc', default_log),
('ExpressionParsing', default_log),
('TypeParsing', default_log),
('Printers', default_log)]:
l = logging.getLogger(l_name)
l.setLevel(l_level)
try: class ListDetectors(argparse.Action):
filename = sys.argv[1] def __call__(self, parser, *args, **kwargs):
detectors, _ = get_detectors_and_printers()
output_detectors(detectors)
parser.exit()
if os.path.isfile(filename): class ListPrinters(argparse.Action):
results = process(filename, args, detectors, printers) def __call__(self, parser, *args, **kwargs):
elif os.path.isdir(filename): _, printers = get_detectors_and_printers()
extension = "*.sol" if not args.solc_ast else "*.json" output_printers(printers)
filenames = glob.glob(os.path.join(filename, extension)) parser.exit()
results = [process(filename, args, detectors, printers) for filename in filenames]
results = [item for sublist in results for item in sublist] #flatten
#if args.json:
# output_json(results, args.json)
#exit(results)
else:
raise Exception("Unrecognised file/dir path: '#{filename}'".format(filename=filename))
if args.json: class OutputMarkdown(argparse.Action):
output_json(results, args.json) def __call__(self, parser, *args, **kwargs):
logger.info('%s analyzed, %d result(s) found', filename, len(results)) detectors, printers = get_detectors_and_printers()
exit(results) output_to_markdown(detectors, printers)
parser.exit()
except Exception as e:
logging.error('Error in %s'%sys.argv[1]) def choose_detectors(args, all_detector_classes):
logging.error(traceback.format_exc()) # If detectors are specified, run only these ones
sys.exit(-1)
detectors_to_run = []
detectors = {d.ARGUMENT: d for d in all_detector_classes}
if args.detectors_to_run == 'all':
detectors_to_run = all_detector_classes
detectors_excluded = args.detectors_to_exclude.split(',')
for d in detectors:
if d in detectors_excluded:
detectors_to_run.remove(detectors[d])
else:
for d in args.detectors_to_run.split(','):
if d in detectors:
detectors_to_run.append(detectors[d])
else:
raise Exception('Error: {} is not a detector'.format(d))
return detectors_to_run
if args.exclude_informational:
detectors_to_run = [d for d in detectors_to_run if
d.IMPACT != DetectorClassification.INFORMATIONAL]
if args.exclude_low:
detectors_to_run = [d for d in detectors_to_run if
d.IMPACT != DetectorClassification.LOW]
if args.exclude_medium:
detectors_to_run = [d for d in detectors_to_run if
d.IMPACT != DetectorClassification.MEDIUM]
if args.exclude_high:
detectors_to_run = [d for d in detectors_to_run if
d.IMPACT != DetectorClassification.HIGH]
if args.detectors_to_exclude:
detectors_to_run = [d for d in detectors_to_run if
d.ARGUMENT not in args.detectors_to_exclude]
return detectors_to_run
def choose_printers(args, all_printer_classes):
printers_to_run = []
# disable default printer
if args.printers_to_run == '':
return []
printers = {p.ARGUMENT: p for p in all_printer_classes}
for p in args.printers_to_run.split(','):
if p in printers:
printers_to_run.append(printers[p])
else:
raise Exception('Error: {} is not a printer'.format(p))
return printers_to_run
if __name__ == '__main__': if __name__ == '__main__':

@ -0,0 +1,58 @@
"""
Compute taint on call
use taint from state_variable
call from slithIR with a taint set to yes means its destination is tainted
"""
from slither.analyses.taint.state_variables import get_taint as get_taint_state
from slither.core.declarations import SolidityVariableComposed
from slither.slithir.operations import (HighLevelCall, Index, LowLevelCall,
Member, OperationWithLValue, Send,
Transfer)
from slither.slithir.variables import ReferenceVariable
from .common import iterate_over_irs
KEY = 'TAINT_CALL_DESTINATION'
def _transfer_func(ir, read, refs, taints):
if isinstance(ir, OperationWithLValue) and any(var_read in taints for var_read in read):
taints += [ir.lvalue]
lvalue = ir.lvalue
while isinstance(lvalue, ReferenceVariable):
taints += [refs[lvalue]]
lvalue = refs[lvalue]
if isinstance(ir, (HighLevelCall, LowLevelCall, Transfer, Send)):
if ir.destination in taints:
ir.context[KEY] = True
return taints
def _visit_node(node, visited, taints):
if node in visited:
return
visited += [node]
taints = iterate_over_irs(node.irs, _transfer_func, taints)
for son in node.sons:
_visit_node(son, visited, taints)
def _run_taint(slither, initial_taint):
if KEY in slither.context:
return
for contract in slither.contracts:
for function in contract.functions:
if not function.is_implemented:
continue
_visit_node(function.entry_point, [], initial_taint + function.parameters)
def run_taint(slither):
initial_taint = get_taint_state(slither)
initial_taint += [SolidityVariableComposed('msg.sender')]
if KEY not in slither.context:
_run_taint(slither, initial_taint)

@ -0,0 +1,18 @@
from slither.slithir.operations import (Index, Member, Length, Balance)
def iterate_over_irs(irs, transfer_func, taints):
refs = {}
for ir in irs:
if isinstance(ir, (Index, Member)):
refs[ir.lvalue] = ir.variable_left
if isinstance(ir, (Length, Balance)):
refs[ir.lvalue] = ir.value
if isinstance(ir, Index):
read = [ir.variable_left]
else:
read = ir.read
taints = transfer_func(ir, read, refs, taints)
return taints

@ -0,0 +1,115 @@
"""
Compute taint from a specific variable
Do not propagate taint on protected function or constructor
Propage to state variables
Iterate until it finding a fixpoint
"""
from slither.core.declarations.solidity_variables import SolidityVariable
from slither.core.variables.state_variable import StateVariable
from slither.core.variables.variable import Variable
from slither.slithir.operations import Index, Member, OperationWithLValue
from slither.slithir.variables import ReferenceVariable, TemporaryVariable
from .common import iterate_over_irs
def make_key(variable):
if isinstance(variable, Variable):
key = 'TAINT_{}'.format(id(variable))
else:
assert isinstance(variable, SolidityVariable)
key = 'TAINT_{}{}'.format(variable.name,
str(type(variable)))
return key
def _transfer_func_with_key(ir, read, refs, taints, key):
if isinstance(ir, OperationWithLValue) and ir.lvalue:
if any(is_tainted_from_key(var_read, key) or var_read in taints for var_read in read):
taints += [ir.lvalue]
ir.lvalue.context[key] = True
lvalue = ir.lvalue
while isinstance(lvalue, ReferenceVariable):
taints += [refs[lvalue]]
lvalue = refs[lvalue]
lvalue.context[key] = True
return taints
def _visit_node(node, visited, key):
if node in visited:
return
visited = visited + [node]
taints = node.function.slither.context[key]
# taints only increase
# if we already see this node with the last taint set
# we dont need to explore itœ
if node in node.slither.context['visited_all_paths']:
if node.slither.context['visited_all_paths'][node] == taints:
return
node.slither.context['visited_all_paths'][node] = taints
# use of lambda function, as the key is required for this transfer_func
_transfer_func_ = lambda _ir, _read, _refs, _taints: _transfer_func_with_key(_ir,
_read,
_refs,
_taints,
key)
taints = iterate_over_irs(node.irs, _transfer_func_, taints)
node.function.slither.context[key] = list(set(taints))
for son in node.sons:
_visit_node(son, visited, key)
def run_taint(slither, taint):
key = make_key(taint)
# if a node was already visited by another path
# we will only explore it if the traversal brings
# new variables written
# This speedup the exploration through a light fixpoint
# Its particular useful on 'complex' functions with several loops and conditions
slither.context['visited_all_paths'] = {}
prev_taints = []
slither.context[key] = [taint]
# Loop until reaching a fixpoint
while(set(prev_taints) != set(slither.context[key])):
prev_taints = slither.context[key]
for contract in slither.contracts:
for function in contract.functions:
# Dont propagated taint on protected functions
if function.is_implemented and not function.is_protected():
slither.context[key] = list(set(slither.context[key]))
_visit_node(function.entry_point, [], key)
slither.context[key] = [v for v in prev_taints if isinstance(v, (StateVariable, SolidityVariable))]
def is_tainted(variable, taint):
"""
Args:
variable (Variable)
taint (Variable): Root of the taint
"""
if not isinstance(variable, (Variable, SolidityVariable)):
return False
key = make_key(taint)
return (key in variable.context and variable.context[key]) or variable == taint
def is_tainted_from_key(variable, key):
"""
Args:
variable (Variable)
key (str): key
"""
if not isinstance(variable, (Variable, SolidityVariable)):
return False
return key in variable.context and variable.context[key]
def get_state_variable_tainted(slither, taint):
key = make_key(taint)
return slither.context[key]

@ -0,0 +1,82 @@
"""
Compute taint on state variables
Do not propagate taint on protected function
Compute taint from function parameters, msg.sender and msg.value
Iterate until it finding a fixpoint
"""
from slither.core.declarations.solidity_variables import \
SolidityVariableComposed
from slither.core.variables.state_variable import StateVariable
from slither.slithir.operations import Index, Member, OperationWithLValue
from slither.slithir.variables import ReferenceVariable, TemporaryVariable
from .common import iterate_over_irs
KEY = 'TAINT_STATE_VARIABLES'
def _transfer_func(ir, read, refs, taints):
if isinstance(ir, OperationWithLValue) and any(var_read in taints for var_read in read):
taints += [ir.lvalue]
lvalue = ir.lvalue
while isinstance(lvalue, ReferenceVariable):
taints += [refs[lvalue]]
lvalue = refs[lvalue]
return taints
def _visit_node(node, visited):
if node in visited:
return
visited += [node]
taints = node.function.slither.context[KEY]
taints = iterate_over_irs(node.irs, _transfer_func, taints)
taints = [v for v in taints if not isinstance(v, (TemporaryVariable, ReferenceVariable))]
node.function.slither.context[KEY] = list(set(taints))
for son in node.sons:
_visit_node(son, visited)
def _run_taint(slither, initial_taint):
if KEY in slither.context:
return
prev_taints = []
slither.context[KEY] = initial_taint
# Loop until reaching a fixpoint
while(set(prev_taints) != set(slither.context[KEY])):
prev_taints = slither.context[KEY]
for contract in slither.contracts:
for function in contract.functions:
if not function.is_implemented:
continue
# Dont propagated taint on protected functions
if not function.is_protected():
slither.context[KEY] = list(set(slither.context[KEY] + function.parameters))
_visit_node(function.entry_point, [])
slither.context[KEY] = [v for v in prev_taints if isinstance(v, StateVariable)]
def run_taint(slither, initial_taint=None):
if initial_taint is None:
initial_taint = [SolidityVariableComposed('msg.sender')]
initial_taint += [SolidityVariableComposed('msg.value')]
if KEY not in slither.context:
_run_taint(slither, initial_taint)
def get_taint(slither, initial_taint=None):
"""
Return the state variables tainted
Args:
slither:
initial_taint (List Variable)
Returns:
List(StateVariable)
"""
run_taint(slither, initial_taint)
return slither.context[KEY]

@ -0,0 +1,57 @@
"""
Detect if all the given variables are written in all the paths of the function
"""
from slither.core.cfg.node import NodeType
from slither.core.declarations import SolidityFunction
from slither.slithir.operations import (Index, Member, OperationWithLValue,
SolidityCall, Length, Balance)
from slither.slithir.variables import ReferenceVariable
def _visit(node, visited, variables_written, variables_to_write):
if node in visited:
return []
visited = visited + [node]
refs = {}
for ir in node.irs:
if isinstance(ir, SolidityCall):
# TODO convert the revert to a THROW node
if ir.function in [SolidityFunction('revert(string)'),
SolidityFunction('revert()')]:
return []
if not isinstance(ir, OperationWithLValue):
continue
if isinstance(ir, (Index, Member)):
refs[ir.lvalue] = ir.variable_left
if isinstance(ir, (Length, Balance)):
refs[ir.lvalue] = ir.value
variables_written = variables_written + [ir.lvalue]
lvalue = ir.lvalue
while isinstance(lvalue, ReferenceVariable):
variables_written = variables_written + [refs[lvalue]]
lvalue = refs[lvalue]
ret = []
if not node.sons and not node.type in [NodeType.THROW, NodeType.RETURN]:
ret += [v for v in variables_to_write if not v in variables_written]
for son in node.sons:
ret += _visit(son, visited, variables_written, variables_to_write)
return ret
def are_variables_written(function, variables_to_write):
"""
Return the list of variable that are not written at the end of the function
Args:
function (Function)
variables_to_write (list Variable): variable that must be written
Returns:
list(Variable): List of variable that are not written (sublist of variables_to_write)
"""
return list(set(_visit(function.entry_point, [], [], variables_to_write)))

@ -3,19 +3,90 @@
""" """
import logging import logging
from slither.core.sourceMapping.sourceMapping import SourceMapping from slither.core.children.child_function import ChildFunction
from slither.core.cfg.nodeType import NodeType from slither.core.declarations import Contract
from slither.core.declarations.solidity_variables import (SolidityFunction,
SolidityVariable)
from slither.core.source_mapping.source_mapping import SourceMapping
from slither.core.variables.state_variable import StateVariable
from slither.core.variables.variable import Variable from slither.core.variables.variable import Variable
from slither.slithir.convert import convert_expression
from slither.slithir.operations import (Balance, HighLevelCall, Index,
InternalCall, Length, LibraryCall,
LowLevelCall, Member,
OperationWithLValue, SolidityCall)
from slither.slithir.variables import (Constant, ReferenceVariable,
TemporaryVariable, TupleVariable)
from slither.visitors.expression.expression_printer import ExpressionPrinter
from slither.visitors.expression.read_var import ReadVar
from slither.visitors.expression.write_var import WriteVar
from slither.visitors.expression.expressionPrinter import ExpressionPrinter
from slither.visitors.expression.readVar import ReadVar
from slither.visitors.expression.writeVar import WriteVar
from slither.core.children.childFunction import ChildFunction
from slither.core.declarations.solidityVariables import SolidityFunction
logger = logging.getLogger("Node") logger = logging.getLogger("Node")
class NodeType:
ENTRYPOINT = 0x0 # no expression
# Node with expression
EXPRESSION = 0x10 # normal case
RETURN = 0x11 # RETURN may contain an expression
IF = 0x12
VARIABLE = 0x13 # Declaration of variable
ASSEMBLY = 0x14
IFLOOP = 0x15
# Below the nodes have no expression
# But are used to expression CFG structure
# Absorbing node
THROW = 0x20
# Loop related nodes
BREAK = 0x31
CONTINUE = 0x32
# Only modifier node
PLACEHOLDER = 0x40
# Merging nodes
# Unclear if they will be necessary
ENDIF = 0x50
STARTLOOP = 0x51
ENDLOOP = 0x52
# @staticmethod
def str(t):
if t == 0x0:
return 'ENTRY_POINT'
if t == 0x10:
return 'EXPRESSION'
if t == 0x11:
return 'RETURN'
if t == 0x12:
return 'IF'
if t == 0x13:
return 'NEW VARIABLE'
if t == 0x14:
return 'INLINE ASM'
if t == 0x15:
return 'IF_LOOP'
if t == 0x20:
return 'THROW'
if t == 0x31:
return 'BREAK'
if t == 0x32:
return 'CONTINUE'
if t == 0x40:
return '_'
if t == 0x50:
return 'END_IF'
if t == 0x51:
return 'BEGIN_LOOP'
if t == 0x52:
return 'END_LOOP'
return 'Unknown type {}'.format(hex(t))
def link_nodes(n1, n2): def link_nodes(n1, n2):
n1.add_son(n2) n1.add_son(n2)
n2.add_father(n1) n2.add_father(n1)
@ -36,7 +107,12 @@ class Node(SourceMapping, ChildFunction):
self._node_id = node_id self._node_id = node_id
self._vars_written = [] self._vars_written = []
self._vars_read = [] self._vars_read = []
self._calls = [] self._internal_calls = []
self._solidity_calls = []
self._high_level_calls = []
self._low_level_calls = []
self._external_calls_as_expressions = []
self._irs = []
self._state_vars_written = [] self._state_vars_written = []
self._state_vars_read = [] self._state_vars_read = []
@ -46,6 +122,10 @@ class Node(SourceMapping, ChildFunction):
self._expression_vars_read = [] self._expression_vars_read = []
self._expression_calls = [] self._expression_calls = []
@property
def slither(self):
return self.function.slither
@property @property
def node_id(self): def node_id(self):
"""Unique node id.""" """Unique node id."""
@ -63,21 +143,21 @@ class Node(SourceMapping, ChildFunction):
""" """
list(Variable): Variables read (local/state/solidity) list(Variable): Variables read (local/state/solidity)
""" """
return self._vars_read return list(self._vars_read)
@property @property
def state_variables_read(self): def state_variables_read(self):
""" """
list(StateVariable): State variables read list(StateVariable): State variables read
""" """
return self._state_vars_read return list(self._state_vars_read)
@property @property
def solidity_variables_read(self): def solidity_variables_read(self):
""" """
list(SolidityVariable): State variables read list(SolidityVariable): State variables read
""" """
return self._solidity_vars_read return list(self._solidity_vars_read)
@property @property
def variables_read_as_expression(self): def variables_read_as_expression(self):
@ -88,29 +168,63 @@ class Node(SourceMapping, ChildFunction):
""" """
list(Variable): Variables written (local/state/solidity) list(Variable): Variables written (local/state/solidity)
""" """
return self._vars_written return list(self._vars_written)
@property @property
def state_variables_written(self): def state_variables_written(self):
""" """
list(StateVariable): State variables written list(StateVariable): State variables written
""" """
return self._state_vars_written return list(self._state_vars_written)
@property @property
def variables_written_as_expression(self): def variables_written_as_expression(self):
return self._expression_vars_written return self._expression_vars_written
@property @property
def calls(self): def internal_calls(self):
"""
list(Function or SolidityFunction): List of internal/soldiity function calls
"""
return list(self._internal_calls)
@property
def solidity_calls(self):
"""
list(SolidityFunction): List of Soldity calls
"""
return list(self._internal_calls)
@property
def high_level_calls(self):
"""
list((Contract, Function|Variable)):
List of high level calls (external calls).
A variable is called in case of call to a public state variable
Include library calls
"""
return list(self._high_level_calls)
@property
def low_level_calls(self):
"""
list((Variable|SolidityVariable, str)): List of low_level call
A low level call is defined by
- the variable called
- the name of the function (call/delegatecall/codecall)
"""
return list(self._low_level_calls)
@property
def external_calls_as_expressions(self):
""" """
list(Function or SolidityFunction): List of calls list(CallExpression): List of message calls (that creates a transaction)
""" """
return self._calls return self._external_calls_as_expressions
@property @property
def calls_as_expression(self): def calls_as_expression(self):
return self._expression_calls return list(self._expression_calls)
@property @property
def expression(self): def expression(self):
@ -126,7 +240,16 @@ class Node(SourceMapping, ChildFunction):
def add_variable_declaration(self, var): def add_variable_declaration(self, var):
assert self._variable_declaration is None assert self._variable_declaration is None
self._variable_declaration = var self._variable_declaration = var
self._expression = var.expression if var.expression:
self._vars_written += [var]
@property
def variable_declaration(self):
"""
Returns:
LocalVariable
"""
return self._variable_declaration
def __str__(self): def __str__(self):
txt = NodeType.str(self._node_type) + ' '+ str(self.expression) txt = NodeType.str(self._node_type) + ' '+ str(self.expression)
@ -138,19 +261,25 @@ class Node(SourceMapping, ChildFunction):
Returns: Returns:
bool: True if the node has a require or assert call bool: True if the node has a require or assert call
""" """
return self.calls and\ return any(c.name in ['require(bool)', 'require(bool,string)', 'assert(bool)'] for c in self.internal_calls)
any(isinstance(c, SolidityFunction) and\
(c.name in ['require(bool)', 'require(bool,string)', 'assert(bool)'])\
for c in self.calls)
def contains_if(self): def contains_if(self):
""" """
Check if the node is a conditional node Check if the node is a IF node
Returns: Returns:
bool: True if the node is a conditional node (IF or IFLOOP) bool: True if the node is a conditional node (IF or IFLOOP)
""" """
return self.type in [NodeType.IF, NodeType.IFLOOP] return self.type in [NodeType.IF, NodeType.IFLOOP]
def is_conditional(self):
"""
Check if the node is a conditional node
A conditional node is either a IF or a require/assert
Returns:
bool: True if the node is a conditional node
"""
return self.contains_if() or self.contains_require_or_assert()
def add_father(self, father): def add_father(self, father):
""" Add a father node """ Add a father node
@ -172,9 +301,10 @@ class Node(SourceMapping, ChildFunction):
""" Returns the father nodes """ Returns the father nodes
Returns: Returns:
fathers: list of fathers list(Node): list of fathers
""" """
return self._fathers return list(self._fathers)
def remove_father(self, father): def remove_father(self, father):
""" Remove the father node. Do nothing if the node is not a father """ Remove the father node. Do nothing if the node is not a father
@ -184,6 +314,13 @@ class Node(SourceMapping, ChildFunction):
""" """
self._fathers = [x for x in self._fathers if x.node_id != father.node_id] self._fathers = [x for x in self._fathers if x.node_id != father.node_id]
def remove_son(self, son):
""" Remove the son node. Do nothing if the node is not a son
Args:
fathers: list of fathers to add
"""
self._sons = [x for x in self._sons if x.node_id != son.node_id]
def add_son(self, son): def add_son(self, son):
""" Add a son node """ Add a son node
@ -206,7 +343,69 @@ class Node(SourceMapping, ChildFunction):
""" Returns the son nodes """ Returns the son nodes
Returns: Returns:
sons: list of sons list(Node): list of sons
"""
return list(self._sons)
@property
def irs(self):
""" Returns the slithIR representation
return
list(slithIR.Operation)
""" """
return self._sons return self._irs
def slithir_generation(self):
if self.expression:
expression = self.expression
self._irs = convert_expression(expression, self)
self._find_read_write_call()
def _find_read_write_call(self):
def is_slithir_var(var):
return isinstance(var, (Constant, ReferenceVariable, TemporaryVariable, TupleVariable))
for ir in self.irs:
self._vars_read += [v for v in ir.read if not is_slithir_var(v)]
if isinstance(ir, OperationWithLValue):
if isinstance(ir, (Index, Member, Length, Balance)):
continue # Don't consider Member and Index operations -> ReferenceVariable
var = ir.lvalue
# If its a reference, we loop until finding the origin
if isinstance(var, (ReferenceVariable)):
while isinstance(var, ReferenceVariable):
var = var.points_to
# Only store non-slithIR variables
if not is_slithir_var(var):
self._vars_written.append(var)
if isinstance(ir, InternalCall):
self._internal_calls.append(ir.function)
if isinstance(ir, SolidityCall):
# TODO: consider removing dependancy of solidity_call to internal_call
self._solidity_calls.append(ir.function)
self._internal_calls.append(ir.function)
if isinstance(ir, LowLevelCall):
assert isinstance(ir.destination, (Variable, SolidityVariable))
self._low_level_calls.append((ir.destination, ir.function_name.value))
elif isinstance(ir, (HighLevelCall)) and not isinstance(ir, LibraryCall):
if isinstance(ir.destination.type, Contract):
self._high_level_calls.append((ir.destination.type, ir.function))
else:
self._high_level_calls.append((ir.destination.type.type, ir.function))
elif isinstance(ir, LibraryCall):
assert isinstance(ir.destination, Contract)
self._high_level_calls.append((ir.destination, ir.function))
self._vars_read = list(set(self._vars_read))
self._state_vars_read = [v for v in self._vars_read if isinstance(v, StateVariable)]
self._solidity_vars_read = [v for v in self._vars_read if isinstance(v, SolidityVariable)]
self._vars_written = list(set(self._vars_written))
self._state_vars_written = [v for v in self._vars_written if isinstance(v, StateVariable)]
self._internal_calls = list(set(self._internal_calls))
self._solidity_calls = list(set(self._solidity_calls))
self._high_level_calls = list(set(self._high_level_calls))
self._low_level_calls = list(set(self._low_level_calls))

@ -1,63 +0,0 @@
class NodeType:
ENTRYPOINT = 0x0 # no expression
# Node with expression
EXPRESSION = 0x10 # normal case
RETURN = 0x11 # RETURN may contain an expression
IF = 0x12
VARIABLE = 0x13 # Declaration of variable
ASSEMBLY = 0x14
IFLOOP = 0x15
# Below the nodes have no expression
# But are used to expression CFG structure
# Absorbing node
THROW = 0x20
# Loop related nodes
BREAK = 0x31
CONTINUE = 0x32
# Only modifier node
PLACEHOLDER = 0x40
# Merging nodes
# Unclear if they will be necessary
ENDIF = 0x50
STARTLOOP = 0x51
ENDLOOP = 0x52
@staticmethod
def str(t):
if t == 0x0:
return 'EntryPoint'
if t == 0x10:
return 'Expressions'
if t == 0x11:
return 'Return'
if t == 0x12:
return 'If'
if t == 0x13:
return 'New variable'
if t == 0x14:
return 'Inline Assembly'
if t == 0x15:
return 'IfLoop'
if t == 0x20:
return 'Throw'
if t == 0x31:
return 'Break'
if t == 0x32:
return 'Continue'
if t == 0x40:
return '_'
if t == 0x50:
return 'EndIf'
if t == 0x51:
return 'BeginLoop'
if t == 0x52:
return 'EndLoop'
return 'Unknown type {}'.format(hex(t))

@ -0,0 +1,20 @@
class ChildNode(object):
def __init__(self):
super(ChildNode, self).__init__()
self._node = None
def set_node(self, node):
self._node = node
@property
def node(self):
return self._node
@property
def function(self):
return self.node.function
@property
def contract(self):
return self.node.function.contract

@ -0,0 +1,9 @@
from .contract import Contract
from .enum import Enum
from .event import Event
from .function import Function
from .import_directive import Import
from .modifier import Modifier
from .pragma_directive import Pragma
from .solidity_variables import SolidityVariable, SolidityVariableComposed, SolidityFunction
from .structure import Structure

@ -2,8 +2,8 @@
Contract module Contract module
""" """
import logging import logging
from slither.core.children.childSlither import ChildSlither from slither.core.children.child_slither import ChildSlither
from slither.core.sourceMapping.sourceMapping import SourceMapping from slither.core.source_mapping.source_mapping import SourceMapping
from slither.core.declarations.function import Function from slither.core.declarations.function import Function
logger = logging.getLogger("Contract") logger = logging.getLogger("Contract")
@ -19,7 +19,7 @@ class Contract(ChildSlither, SourceMapping):
self._name = None self._name = None
self._id = None self._id = None
self._inheritances = [] self._inheritance = []
self._enums = {} self._enums = {}
self._structures = {} self._structures = {}
@ -52,21 +52,21 @@ class Contract(ChildSlither, SourceMapping):
return self._id return self._id
@property @property
def inheritances(self): def inheritance(self):
''' '''
list(Contract): Inheritances list. Order: the first elem is the first father to be executed list(Contract): Inheritance list. Order: the first elem is the first father to be executed
''' '''
return self._inheritances return list(self._inheritance)
@property @property
def inheritances_reverse(self): def inheritance_reverse(self):
''' '''
list(Contract): Inheritances list. Order: the last elem is the first father to be executed list(Contract): Inheritance list. Order: the last elem is the first father to be executed
''' '''
return reversed(self._inheritances) return reversed(self._inheritance)
def setInheritances(self, inheritances): def setInheritance(self, inheritance):
self._inheritances = inheritances self._inheritance = inheritance
@property @property
def structures(self): def structures(self):
@ -95,6 +95,10 @@ class Contract(ChildSlither, SourceMapping):
def modifiers_as_dict(self): def modifiers_as_dict(self):
return self._modifiers return self._modifiers
@property
def constructor(self):
return next((func for func in self.functions if func.is_constructor), None)
@property @property
def functions(self): def functions(self):
''' '''
@ -110,13 +114,19 @@ class Contract(ChildSlither, SourceMapping):
return [f for f in self.functions if f.contract != self] return [f for f in self.functions if f.contract != self]
@property @property
def functions_all_called(self): def all_functions_called(self):
''' '''
list(Function): List of functions reachable from the contract (include super) 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_internal_calls() for f in self.functions)
all_calls = [item for sublist in all_calls for item in sublist] + self.functions all_calls = [item for sublist in all_calls for item in sublist] + self.functions
all_calls = set(all_calls) all_calls = list(set(all_calls))
all_constructors = [c.constructor for c in self.inheritance]
all_constructors = list(set([c for c in all_constructors if c]))
all_calls = set(all_calls+all_constructors)
return [c for c in all_calls if isinstance(c, Function)] return [c for c in all_calls if isinstance(c, Function)]
def functions_as_dict(self): def functions_as_dict(self):
@ -144,7 +154,7 @@ class Contract(ChildSlither, SourceMapping):
''' '''
list(StateVariable): List of the state variables. Alias to self.state_variables list(StateVariable): List of the state variables. Alias to self.state_variables
''' '''
return self.state_variables return list(self.state_variables)
def variables_as_dict(self): def variables_as_dict(self):
return self._variables return self._variables
@ -154,6 +164,10 @@ class Contract(ChildSlither, SourceMapping):
return self._using_for return self._using_for
def reverse_using_for(self, name): def reverse_using_for(self, name):
'''
Returns:
(list)
'''
return self._using_for[name] return self._using_for[name]
@property @property
@ -163,13 +177,13 @@ class Contract(ChildSlither, SourceMapping):
def __str__(self): def __str__(self):
return self.name return self.name
def get_functions_reading_variable(self, variable): def get_functions_reading_from_variable(self, variable):
''' '''
Return the functions reading the variable Return the functions reading the variable
''' '''
return [f for f in self.functions if f.is_reading(variable)] return [f for f in self.functions if f.is_reading(variable)]
def get_functions_writing_variable(self, variable): def get_functions_writing_to_variable(self, variable):
''' '''
Return the functions writting the variable Return the functions writting the variable
''' '''
@ -289,6 +303,7 @@ class Contract(ChildSlither, SourceMapping):
def is_erc20(self): def is_erc20(self):
""" """
Check if the contract is an erc20 token Check if the contract is an erc20 token
Note: it does not check for correct return values Note: it does not check for correct return values
Returns: Returns:
bool bool
@ -302,8 +317,8 @@ class Contract(ChildSlither, SourceMapping):
""" Return the function summary """ Return the function summary
Returns: Returns:
(str, list, list, list): (name, variables, fuction summaries, modifier summaries) (str, list, list, list, list): (name, inheritance, variables, fuction summaries, modifier summaries)
""" """
func_summaries = [f.get_summary() for f in self.functions] func_summaries = [f.get_summary() for f in self.functions]
modif_summaries = [f.get_summary() for f in self.modifiers] modif_summaries = [f.get_summary() for f in self.modifiers]
return (self.name, [str(x) for x in self.inheritances], [str(x) for x in self.variables], func_summaries, modif_summaries) return (self.name, [str(x) for x in self.inheritance], [str(x) for x in self.variables], func_summaries, modif_summaries)

@ -1,5 +1,5 @@
from slither.core.sourceMapping.sourceMapping import SourceMapping from slither.core.source_mapping.source_mapping import SourceMapping
from slither.core.children.childContract import ChildContract from slither.core.children.child_contract import ChildContract
class Enum(ChildContract, SourceMapping): class Enum(ChildContract, SourceMapping):
def __init__(self, name, canonical_name, values): def __init__(self, name, canonical_name, values):

@ -1,5 +1,5 @@
from slither.core.children.childContract import ChildContract from slither.core.children.child_contract import ChildContract
from slither.core.sourceMapping.sourceMapping import SourceMapping from slither.core.source_mapping.source_mapping import SourceMapping
class Event(ChildContract, SourceMapping): class Event(ChildContract, SourceMapping):

@ -3,16 +3,17 @@
""" """
import logging import logging
from itertools import groupby from itertools import groupby
from slither.core.sourceMapping.sourceMapping import SourceMapping
from slither.core.children.childContract import ChildContract
from slither.core.variables.stateVariable import StateVariable from slither.core.children.child_contract import ChildContract
from slither.core.declarations.solidity_variables import (SolidityFunction,
SolidityVariable,
SolidityVariableComposed)
from slither.core.expressions.identifier import Identifier from slither.core.expressions.identifier import Identifier
from slither.core.expressions.unaryOperation import UnaryOperation from slither.core.expressions.index_access import IndexAccess
from slither.core.expressions.memberAccess import MemberAccess from slither.core.expressions.member_access import MemberAccess
from slither.core.expressions.indexAccess import IndexAccess from slither.core.expressions.unary_operation import UnaryOperation
from slither.core.source_mapping.source_mapping import SourceMapping
from slither.core.declarations.solidityVariables import SolidityVariable, SolidityFunction from slither.core.variables.state_variable import StateVariable
logger = logging.getLogger("Function") logger = logging.getLogger("Function")
@ -42,7 +43,11 @@ class Function(ChildContract, SourceMapping):
self._vars_read_or_written = [] self._vars_read_or_written = []
self._solidity_vars_read = [] self._solidity_vars_read = []
self._state_vars_written = [] self._state_vars_written = []
self._calls = [] self._internal_calls = []
self._solidity_calls = []
self._low_level_calls = []
self._high_level_calls = []
self._external_calls_as_expressions = []
self._expression_vars_read = [] self._expression_vars_read = []
self._expression_vars_written = [] self._expression_vars_written = []
self._expression_calls = [] self._expression_calls = []
@ -50,6 +55,18 @@ class Function(ChildContract, SourceMapping):
self._modifiers = [] self._modifiers = []
self._payable = False self._payable = False
@property
def return_type(self):
"""
Return the list of return type
If no return, return None
"""
returns = self.returns
if returns:
return [r.type for r in returns]
return None
@property @property
def name(self): def name(self):
""" """
@ -67,7 +84,7 @@ class Function(ChildContract, SourceMapping):
""" """
list(Node): List of the nodes list(Node): List of the nodes
""" """
return self._nodes return list(self._nodes)
@property @property
def entry_point(self): def entry_point(self):
@ -130,21 +147,21 @@ class Function(ChildContract, SourceMapping):
""" """
list(LocalVariable): List of the parameters list(LocalVariable): List of the parameters
""" """
return self._parameters return list(self._parameters)
@property @property
def returns(self): def returns(self):
""" """
list(LocalVariable): List of the return variables list(LocalVariable): List of the return variables
""" """
return self._returns return list(self._returns)
@property @property
def modifiers(self): def modifiers(self):
""" """
list(Modifier): List of the modifiers list(Modifier): List of the modifiers
""" """
return self._modifiers return list(self._modifiers)
def __str__(self): def __str__(self):
return self._name return self._name
@ -157,6 +174,13 @@ class Function(ChildContract, SourceMapping):
""" """
return list(self._variables.values()) return list(self._variables.values())
@property
def local_variables(self):
"""
Return all local variables (dont include paramters and return values)
"""
return list(set(self.variables) - set(self.returns) - set(self.parameters))
def variables_as_dict(self): def variables_as_dict(self):
return self._variables return self._variables
@ -165,42 +189,42 @@ class Function(ChildContract, SourceMapping):
""" """
list(Variable): Variables read (local/state/solidity) list(Variable): Variables read (local/state/solidity)
""" """
return self._vars_read return list(self._vars_read)
@property @property
def variables_written(self): def variables_written(self):
""" """
list(Variable): Variables written (local/state/solidity) list(Variable): Variables written (local/state/solidity)
""" """
return self._vars_written return list(self._vars_written)
@property @property
def state_variables_read(self): def state_variables_read(self):
""" """
list(StateVariable): State variables read list(StateVariable): State variables read
""" """
return self._state_vars_read return list(self._state_vars_read)
@property @property
def solidity_variables_read(self): def solidity_variables_read(self):
""" """
list(SolidityVariable): Solidity variables read list(SolidityVariable): Solidity variables read
""" """
return self._solidity_vars_read return list(self._solidity_vars_read)
@property @property
def state_variables_written(self): def state_variables_written(self):
""" """
list(StateVariable): State variables written list(StateVariable): State variables written
""" """
return self._state_vars_written return list(self._state_vars_written)
@property @property
def variables_read_or_written(self): def variables_read_or_written(self):
""" """
list(Variable): Variables read or written (local/state/solidity) list(Variable): Variables read or written (local/state/solidity)
""" """
return self._vars_read_or_written return list(self._vars_read_or_written)
@property @property
def variables_read_as_expression(self): def variables_read_as_expression(self):
@ -211,14 +235,49 @@ class Function(ChildContract, SourceMapping):
return self._expression_vars_written return self._expression_vars_written
@property @property
def calls(self): def internal_calls(self):
"""
list(Function or SolidityFunction): List of function calls (that does not create a transaction)
"""
return list(self._internal_calls)
@property
def solidity_calls(self):
""" """
list(Function or SolidityFunction): List of calls list(SolidityFunction): List of Soldity calls
""" """
return self._calls return list(self._internal_calls)
@property @property
def calls_as_expression(self): def high_level_calls(self):
"""
list((Contract, Function|Variable)):
List of high level calls (external calls).
A variable is called in case of call to a public state variable
Include library calls
"""
return list(self._high_level_calls)
@property
def low_level_calls(self):
"""
list((Variable|SolidityVariable, str)): List of low_level call
A low level call is defined by
- the variable called
- the name of the function (call/delegatecall/codecall)
"""
return list(self._low_level_calls)
@property
def external_calls_as_expressions(self):
"""
list(ExpressionCall): List of message calls (that creates a transaction)
"""
return list(self._external_calls_as_expressions)
@property
def calls_as_expressions(self):
return self._expression_calls return self._expression_calls
@property @property
@ -233,7 +292,8 @@ class Function(ChildContract, SourceMapping):
@property @property
def signature(self): def signature(self):
""" """
(str, list(str), list(str)): Function signature as (name, list parameters type, list return values type) (str, list(str), list(str)): Function signature as
(name, list parameters type, list return values type)
""" """
return self.name, [str(x.type) for x in self.parameters], [str(x.type) for x in self.returns] return self.name, [str(x.type) for x in self.parameters], [str(x.type) for x in self.returns]
@ -256,6 +316,10 @@ class Function(ChildContract, SourceMapping):
return name+'('+','.join(parameters)+')' return name+'('+','.join(parameters)+')'
@property
def slither(self):
return self.contract.slither
def _filter_state_variables_written(self, expressions): def _filter_state_variables_written(self, expressions):
ret = [] ret = []
for expression in expressions: for expression in expressions:
@ -320,44 +384,49 @@ class Function(ChildContract, SourceMapping):
calls = [x for x in calls if x] calls = [x for x in calls if x]
calls = [item for sublist in calls for item in sublist] calls = [item for sublist in calls for item in sublist]
# Remove dupplicate if they share the same string representation # Remove dupplicate if they share the same string representation
# TODO: check if groupby is still necessary here
calls = [next(obj) for i, obj in\ calls = [next(obj) for i, obj in\
groupby(sorted(calls, key=lambda x: str(x)), lambda x: str(x))] groupby(sorted(calls, key=lambda x: str(x)), lambda x: str(x))]
self._expression_calls = calls self._expression_calls = calls
calls = [x.calls for x in self.nodes] internal_calls = [x.internal_calls for x in self.nodes]
calls = [x for x in calls if x] internal_calls = [x for x in internal_calls if x]
calls = [item for sublist in calls for item in sublist] internal_calls = [item for sublist in internal_calls for item in sublist]
calls = [next(obj) for i, obj in\ internal_calls = [next(obj) for i, obj in
groupby(sorted(calls, key=lambda x: str(x)), lambda x: str(x))] groupby(sorted(internal_calls, key=lambda x: str(x)), lambda x: str(x))]
self._calls = [c for c in calls if isinstance(c, (Function, SolidityFunction))] self._internal_calls = internal_calls
def all_state_variables_read(self): self._solidity_calls = [c for c in internal_calls if isinstance(c, SolidityFunction)]
""" recursive version of variables_read
"""
variables = self.state_variables_read
explored = [self]
to_explore = [c for c in self.calls if isinstance(c, Function) and c not in explored]
to_explore += [m for m in self.modifiers if m not in explored]
while to_explore: low_level_calls = [x.low_level_calls for x in self.nodes]
f = to_explore[0] low_level_calls = [x for x in low_level_calls if x]
to_explore = to_explore[1:] low_level_calls = [item for sublist in low_level_calls for item in sublist]
if f in explored: low_level_calls = [next(obj) for i, obj in
continue groupby(sorted(low_level_calls, key=lambda x: str(x)), lambda x: str(x))]
explored.append(f)
variables += f.state_variables_read
to_explore += [c for c in f.calls if\
isinstance(c, Function) and c not in explored and c not in to_explore]
to_explore += [m for m in f.modifiers if m not in explored and m not in to_explore]
return list(set(variables)) self._low_level_calls = low_level_calls
def all_solidity_variables_read(self): high_level_calls = [x.high_level_calls for x in self.nodes]
""" recursive version of solidity_read high_level_calls = [x for x in high_level_calls if x]
""" high_level_calls = [item for sublist in high_level_calls for item in sublist]
variables = self.solidity_variables_read high_level_calls = [next(obj) for i, obj in
groupby(sorted(high_level_calls, key=lambda x: str(x)), lambda x: str(x))]
self._high_level_calls = high_level_calls
external_calls_as_expressions = [x.external_calls_as_expressions for x in self.nodes]
external_calls_as_expressions = [x for x in external_calls_as_expressions if x]
external_calls_as_expressions = [item for sublist in external_calls_as_expressions for item in sublist]
external_calls_as_expressions = [next(obj) for i, obj in
groupby(sorted(external_calls_as_expressions, key=lambda x: str(x)), lambda x: str(x))]
self._external_calls_as_expressions = external_calls_as_expressions
def _explore_functions(self, f_new_values):
values = f_new_values(self)
explored = [self] explored = [self]
to_explore = [c for c in self.calls if isinstance(c, Function) and c not in explored] to_explore = [c for c in self.internal_calls if
isinstance(c, Function) and c not in explored]
to_explore += [m for m in self.modifiers if m not in explored] to_explore += [m for m in self.modifiers if m not in explored]
while to_explore: while to_explore:
@ -366,75 +435,90 @@ class Function(ChildContract, SourceMapping):
if f in explored: if f in explored:
continue continue
explored.append(f) explored.append(f)
variables += f.solidity_variables_read
to_explore += [c for c in f.calls if\ values += f_new_values(f)
to_explore += [c for c in f.internal_calls if\
isinstance(c, Function) and c not in explored and c not in to_explore] isinstance(c, Function) and c not in explored and c not in to_explore]
to_explore += [m for m in f.modifiers if m not in explored and m not in to_explore] to_explore += [m for m in f.modifiers if m not in explored and m not in to_explore]
return list(set(variables)) return list(set(values))
def all_expressions(self): def all_state_variables_read(self):
""" recursive version of variables_read """ recursive version of variables_read
""" """
variables = self.expressions return self._explore_functions(lambda x: x.state_variables_read)
explored = [self]
to_explore = [c for c in self.calls if isinstance(c, Function) and c not in explored]
to_explore += [m for m in self.modifiers if m not in explored]
while to_explore: def all_solidity_variables_read(self):
f = to_explore[0] """ recursive version of solidity_read
to_explore = to_explore[1:] """
if f in explored: return self._explore_functions(lambda x: x.solidity_variables_read)
continue
explored.append(f)
variables += f.expressions
to_explore += [c for c in f.calls if\
isinstance(c, Function) and c not in explored and c not in to_explore]
to_explore += [m for m in f.modifiers if m not in explored and m not in to_explore]
return list(set(variables)) def all_expressions(self):
""" recursive version of variables_read
"""
return self._explore_functions(lambda x: x.expressions)
def all_state_variables_written(self): def all_state_variables_written(self):
""" recursive version of variables_written """ recursive version of variables_written
""" """
variables = self.state_variables_written return self._explore_functions(lambda x: x.state_variables_written)
explored = [self]
to_explore = [c for c in self.calls if isinstance(c, Function) and c not in explored]
to_explore += [m for m in self.modifiers if m not in explored]
while to_explore: def all_internal_calls(self):
f = to_explore[0] """ recursive version of internal_calls
to_explore = to_explore[1:] """
if f in explored: return self._explore_functions(lambda x: x.internal_calls)
continue
explored.append(f)
variables += f.state_variables_written
to_explore += [c for c in f.calls if\
isinstance(c, Function) and c not in explored and c not in to_explore]
to_explore += [m for m in f.modifiers if m not in explored and m not in to_explore]
return list(set(variables)) def all_conditional_state_variables_read(self):
"""
Return the state variable used in a condition
def all_calls(self): Over approximate and also return index access
""" recursive version of calls It won't work if the variable is assigned to a temp variable
""" """
calls = self.calls def _explore_func(func):
explored = [self] ret = [n.state_variables_read for n in func.nodes if n.is_conditional()]
to_explore = [c for c in self.calls if isinstance(c, Function) and c not in explored] return [item for sublist in ret for item in sublist]
to_explore += [m for m in self.modifiers if m not in explored] return self._explore_functions(lambda x: _explore_func(x))
while to_explore: def all_conditional_solidity_variables_read(self):
f = to_explore[0] """
to_explore = to_explore[1:] Return the Soldiity variables directly used in a condtion
if f in explored:
continue Use of the IR to filter index access
explored.append(f) Assumption: the solidity vars are used directly in the conditional node
calls += f.calls It won't work if the variable is assigned to a temp variable
to_explore += [c for c in f.calls if\ """
isinstance(c, Function) and c not in explored and c not in to_explore] from slither.slithir.operations.binary import Binary
to_explore += [m for m in f.modifiers if m not in explored and m not in to_explore] def _solidity_variable_in_node(node):
ret = []
for ir in node.irs:
if isinstance(ir, Binary):
ret += ir.read
return [var for var in ret if isinstance(var, SolidityVariable)]
def _explore_func(func, f):
ret = [f(n) for n in func.nodes if n.is_conditional()]
return [item for sublist in ret for item in sublist]
return self._explore_functions(lambda x: _explore_func(x, _solidity_variable_in_node))
return list(set(calls)) def all_solidity_variables_used_as_args(self):
"""
Return the Soldiity variables directly used in a call
Use of the IR to filter index access
Used to catch check(msg.sender)
"""
from slither.slithir.operations.internal_call import InternalCall
def _solidity_variable_in_node(node):
ret = []
for ir in node.irs:
if isinstance(ir, InternalCall):
ret += ir.read
return [var for var in ret if isinstance(var, SolidityVariable)]
def _explore_func(func, f):
ret = [f(n) for n in func.nodes]
return [item for sublist in ret for item in sublist]
return self._explore_functions(lambda x: _explore_func(x, _solidity_variable_in_node))
def is_reading(self, variable): def is_reading(self, variable):
""" """
@ -508,15 +592,54 @@ class Function(ChildContract, SourceMapping):
f.write("}\n") f.write("}\n")
def slithir_cfg_to_dot(self, filename):
"""
Export the function to a dot file
Args:
filename (str)
"""
from slither.core.cfg.node import NodeType
with open(filename, 'w') as f:
f.write('digraph{\n')
for node in self.nodes:
label = 'Node Type: {}\n'.format(NodeType.str(node.type))
if node.expression:
label += '\nEXPRESSION:\n{}\n'.format(node.expression)
label += '\nIRs:\n' + '\n'.join([str(ir) for ir in node.irs])
f.write('{}[label="{}"];\n'.format(node.node_id, label))
for son in node.sons:
f.write('{}->{};\n'.format(node.node_id, son.node_id))
f.write("}\n")
def get_summary(self): def get_summary(self):
""" """
Return the function summary Return the function summary
Returns: Returns:
(str, str, list(str), list(str), listr(str), list(str); (str, str, list(str), list(str), listr(str), list(str), list(str);
name, visibility, modifiers, variables read, variables written, calls name, visibility, modifiers, vars read, vars written, internal_calls, external_calls_as_expressions
""" """
return (self.name, self.visibility, return (self.name, self.visibility,
[str(x) for x in self.modifiers], [str(x) for x in self.modifiers],
[str(x) for x in self.state_variables_read + self.solidity_variables_read], [str(x) for x in self.state_variables_read + self.solidity_variables_read],
[str(x) for x in self.state_variables_written], [str(x) for x in self.state_variables_written],
[str(x) for x in self.calls]) [str(x) for x in self.internal_calls],
[str(x) for x in self.external_calls_as_expressions])
def is_protected(self):
"""
Determine if the function is protected using a check on msg.sender
Only detects if msg.sender is directly used in a condition
For example, it wont work for:
address a = msg.sender
require(a == owner)
Returns
(bool)
"""
if self.is_constructor:
return True
conditional_vars = self.all_conditional_solidity_variables_read()
args_vars = self.all_solidity_variables_used_as_args()
return SolidityVariableComposed('msg.sender') in conditional_vars + args_vars

@ -0,0 +1,14 @@
from slither.core.source_mapping.source_mapping import SourceMapping
class Import(SourceMapping):
def __init__(self, filename):
super(Import, self).__init__()
self._filename = filename
@property
def filename(self):
return self._filename
def __str__(self):
return self.filename

@ -0,0 +1,21 @@
from slither.core.source_mapping.source_mapping import SourceMapping
class Pragma(SourceMapping):
def __init__(self, directive):
super(Pragma, self).__init__()
self._directive = directive
@property
def directive(self):
'''
list(str)
'''
return self._directive
@property
def version(self):
return ''.join(self.directive[1:])
def __str__(self):
return 'pragma '+''.join(self.directive)

@ -1,48 +0,0 @@
# https://solidity.readthedocs.io/en/v0.4.24/units-and-global-variables.html
SOLIDITY_VARIABLES = ["block", "msg", "now", "tx", "this", "super", 'abi']
SOLIDITY_VARIABLES_COMPOSED = ["block.coinbase", "block.difficulty", "block.gaslimit", "block.number", "block.timestamp", "msg.data", "msg.gas", "msg.sender", "msg.sig", "msg.value", "tx.gasprice", "tx.origin"]
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:
def __init__(self, name):
assert name in SOLIDITY_VARIABLES
self._name = name
@property
def name(self):
return self._name
def __str__(self):
return self._name
class SolidityVariableComposed(SolidityVariable):
def __init__(self, name):
assert name in SOLIDITY_VARIABLES_COMPOSED
self._name = name
@property
def name(self):
return self._name
def __str__(self):
return self._name
class SolidityFunction:
def __init__(self, name):
assert name in SOLIDITY_FUNCTIONS
self._name = name
@property
def name(self):
return self._name
def __str__(self):
return self._name

@ -0,0 +1,147 @@
# https://solidity.readthedocs.io/en/v0.4.24/units-and-global-variables.html
from slither.core.context.context import Context
from slither.core.solidity_types import ElementaryType
SOLIDITY_VARIABLES = {"now":'uint256',
"this":'address',
'abi':'address', # to simplify the conversion, assume that abi return an address
'msg':'',
'tx':'',
'block':'',
'super':''}
SOLIDITY_VARIABLES_COMPOSED = {"block.coinbase":"address",
"block.difficulty":"uint256",
"block.gaslimit":"uint256",
"block.number":"uint256",
"block.timestamp":"uint256",
"block.blockhash":"uint256", # alias for blockhash. It's a call
"msg.data":"bytes",
"msg.gas":"uint256",
"msg.sender":"address",
"msg.sig":"bytes4",
"msg.value":"uint256",
"tx.gasprice":"uint256",
"tx.origin":"address"}
SOLIDITY_FUNCTIONS = {"gasleft()":['uint256'],
"assert(bool)":[],
"require(bool)":[],
"require(bool,string)":[],
"revert()":[],
"revert(string)":[],
"addmod(uint256,uint256,uint256)":['uint256'],
"mulmod(uint256,uint256,uint256)":['uint256'],
"keccak256()":['bytes32'],
"sha256()":['bytes32'],
"sha3()":['bytes32'],
"ripemd160()":['bytes32'],
"ecrecover(bytes32,uint8,bytes32,bytes32)":['address'],
"selfdestruct(address)":[],
"suicide(address)":[],
"log0(bytes32)":[],
"log1(bytes32,bytes32)":[],
"log2(bytes32,bytes32,bytes32)":[],
"log3(bytes32,bytes32,bytes32,bytes32)":[],
"blockhash(uint256)":['bytes32'],
# the following need a special handling
# as they are recognized as a SolidityVariableComposed
# and converted to a SolidityFunction by SlithIR
"this.balance()":['uint256'],
"abi.encode()":['bytes'],
"abi.encodePacked()":['bytes'],
"abi.encodeWithSelector()":["bytes"],
"abi.encodeWithSignature()":["bytes"]}
def solidity_function_signature(name):
"""
Return the function signature (containing the return value)
It is useful if a solidity function is used as a pointer
(see exoressionParsing.find_variable documentation)
Args:
name(str):
Returns:
str
"""
return name+' returns({})'.format(','.join(SOLIDITY_FUNCTIONS[name]))
class SolidityVariable(Context):
def __init__(self, name):
super(SolidityVariable, self).__init__()
self._check_name(name)
self._name = name
# dev function, will be removed once the code is stable
def _check_name(self, name):
assert name in SOLIDITY_VARIABLES
@property
def name(self):
return self._name
@property
def type(self):
return ElementaryType(SOLIDITY_VARIABLES[self.name])
def __str__(self):
return self._name
def __eq__(self, other):
return self.__class__ == other.__class__ and self.name == other.name
def __hash__(self):
return hash(self.name)
class SolidityVariableComposed(SolidityVariable):
def __init__(self, name):
super(SolidityVariableComposed, self).__init__(name)
def _check_name(self, name):
assert name in SOLIDITY_VARIABLES_COMPOSED
@property
def name(self):
return self._name
@property
def type(self):
return ElementaryType(SOLIDITY_VARIABLES_COMPOSED[self.name])
def __str__(self):
return self._name
def __eq__(self, other):
return self.__class__ == other.__class__ and self.name == other.name
def __hash__(self):
return hash(self.name)
class SolidityFunction:
def __init__(self, name):
assert name in SOLIDITY_FUNCTIONS
self._name = name
@property
def name(self):
return self._name
@property
def full_name(self):
return self.name
@property
def return_type(self):
return [ElementaryType(x) for x in SOLIDITY_FUNCTIONS[self.name]]
def __str__(self):
return self._name
def __eq__(self, other):
return self.__class__ == other.__class__ and self.name == other.name
def __hash__(self):
return hash(self.name)

@ -1,5 +1,5 @@
from slither.core.sourceMapping.sourceMapping import SourceMapping from slither.core.source_mapping.source_mapping import SourceMapping
from slither.core.children.childContract import ChildContract from slither.core.children.child_contract import ChildContract
from slither.core.variables.variable import Variable from slither.core.variables.variable import Variable

@ -0,0 +1,16 @@
from .assignment_operation import AssignmentOperation, AssignmentOperationType
from .binary_operation import BinaryOperation, BinaryOperationType
from .call_expression import CallExpression
from .conditional_expression import ConditionalExpression
from .elementary_type_name_expression import ElementaryTypeNameExpression
from .identifier import Identifier
from .index_access import IndexAccess
from .literal import Literal
from .new_array import NewArray
from .new_contract import NewContract
from .new_elementary_type import NewElementaryType
from .super_call_expression import SuperCallExpression
from .super_identifier import SuperIdentifier
from .tuple_expression import TupleExpression
from .type_conversion import TypeConversion
from .unary_operation import UnaryOperation, UnaryOperationType

@ -1,5 +1,5 @@
import logging import logging
from slither.core.expressions.expressionTyped import ExpressionTyped from slither.core.expressions.expression_typed import ExpressionTyped
from slither.core.expressions.expression import Expression from slither.core.expressions.expression import Expression
@ -13,7 +13,7 @@ class AssignmentOperationType:
ASSIGN_LEFT_SHIFT = 4 # <<= ASSIGN_LEFT_SHIFT = 4 # <<=
ASSIGN_RIGHT_SHIFT = 5 # >>= ASSIGN_RIGHT_SHIFT = 5 # >>=
ASSIGN_ADDITION = 6 # += ASSIGN_ADDITION = 6 # +=
ASSIGN_SUBSTRACTION = 7 # -= ASSIGN_SUBTRACTION = 7 # -=
ASSIGN_MULTIPLICATION = 8 # *= ASSIGN_MULTIPLICATION = 8 # *=
ASSIGN_DIVISION = 9 # /= ASSIGN_DIVISION = 9 # /=
ASSIGN_MODULO = 10 # %= ASSIGN_MODULO = 10 # %=
@ -35,7 +35,7 @@ class AssignmentOperationType:
if operation_type == '+=': if operation_type == '+=':
return AssignmentOperationType.ASSIGN_ADDITION return AssignmentOperationType.ASSIGN_ADDITION
if operation_type == '-=': if operation_type == '-=':
return AssignmentOperationType.ASSIGN_SUBSTRACTION return AssignmentOperationType.ASSIGN_SUBTRACTION
if operation_type == '*=': if operation_type == '*=':
return AssignmentOperationType.ASSIGN_MULTIPLICATION return AssignmentOperationType.ASSIGN_MULTIPLICATION
if operation_type == '/=': if operation_type == '/=':
@ -62,7 +62,7 @@ class AssignmentOperationType:
return '>>=' return '>>='
if operation_type == AssignmentOperationType.ASSIGN_ADDITION: if operation_type == AssignmentOperationType.ASSIGN_ADDITION:
return '+=' return '+='
if operation_type == AssignmentOperationType.ASSIGN_SUBSTRACTION: if operation_type == AssignmentOperationType.ASSIGN_SUBTRACTION:
return '-=' return '-='
if operation_type == AssignmentOperationType.ASSIGN_MULTIPLICATION: if operation_type == AssignmentOperationType.ASSIGN_MULTIPLICATION:
return '*=' return '*='

@ -1,5 +1,5 @@
import logging import logging
from slither.core.expressions.expressionTyped import ExpressionTyped from slither.core.expressions.expression_typed import ExpressionTyped
from slither.core.expressions.expression import Expression from slither.core.expressions.expression import Expression
@ -11,7 +11,7 @@ class BinaryOperationType:
DIVISION = 2 # / DIVISION = 2 # /
MODULO = 3 # % MODULO = 3 # %
ADDITION = 4 # + ADDITION = 4 # +
SUBSTRACTION = 5 # - SUBTRACTION = 5 # -
LEFT_SHIFT = 6 # << LEFT_SHIFT = 6 # <<
RIGHT_SHIT = 7 # >>> RIGHT_SHIT = 7 # >>>
AND = 8 # & AND = 8 # &
@ -39,7 +39,7 @@ class BinaryOperationType:
if operation_type == '+': if operation_type == '+':
return BinaryOperationType.ADDITION return BinaryOperationType.ADDITION
if operation_type == '-': if operation_type == '-':
return BinaryOperationType.SUBSTRACTION return BinaryOperationType.SUBTRACTION
if operation_type == '<<': if operation_type == '<<':
return BinaryOperationType.LEFT_SHIFT return BinaryOperationType.LEFT_SHIFT
if operation_type == '>>': if operation_type == '>>':
@ -82,7 +82,7 @@ class BinaryOperationType:
return '%' return '%'
if operation_type == BinaryOperationType.ADDITION: if operation_type == BinaryOperationType.ADDITION:
return '+' return '+'
if operation_type == BinaryOperationType.SUBSTRACTION: if operation_type == BinaryOperationType.SUBTRACTION:
return '-' return '-'
if operation_type == BinaryOperationType.LEFT_SHIFT: if operation_type == BinaryOperationType.LEFT_SHIFT:
return '<<' return '<<'
@ -123,7 +123,7 @@ class BinaryOperation(ExpressionTyped):
self._type = expression_type self._type = expression_type
@property @property
def get_expression(self): def expressions(self):
return self._expressions return self._expressions
@property @property

@ -2,7 +2,7 @@
This expression does nothing, if a contract used it, its probably a bug This expression does nothing, if a contract used it, its probably a bug
""" """
from slither.core.expressions.expression import Expression from slither.core.expressions.expression import Expression
from slither.core.solidityTypes.type import Type from slither.core.solidity_types.type import Type
class ElementaryTypeNameExpression(Expression): class ElementaryTypeNameExpression(Expression):

@ -1,4 +1,4 @@
from slither.core.sourceMapping.sourceMapping import SourceMapping from slither.core.source_mapping.source_mapping import SourceMapping
class Expression( SourceMapping): class Expression( SourceMapping):

@ -1,4 +1,4 @@
from slither.core.expressions.expressionTyped import ExpressionTyped from slither.core.expressions.expression_typed import ExpressionTyped
class Identifier(ExpressionTyped): class Identifier(ExpressionTyped):

@ -1,5 +1,5 @@
from slither.core.expressions.expressionTyped import ExpressionTyped from slither.core.expressions.expression_typed import ExpressionTyped
from slither.core.solidityTypes.type import Type from slither.core.solidity_types.type import Type
class IndexAccess(ExpressionTyped): class IndexAccess(ExpressionTyped):

@ -1,6 +1,6 @@
from slither.core.expressions.expression import Expression from slither.core.expressions.expression import Expression
from slither.core.expressions.expressionTyped import ExpressionTyped from slither.core.expressions.expression_typed import ExpressionTyped
from slither.core.solidityTypes.type import Type from slither.core.solidity_types.type import Type
class MemberAccess(ExpressionTyped): class MemberAccess(ExpressionTyped):

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

@ -1,5 +1,5 @@
from slither.core.expressions.expression import Expression from slither.core.expressions.expression import Expression
from slither.core.solidityTypes.elementaryType import ElementaryType from slither.core.solidity_types.elementary_type import ElementaryType
class NewElementaryType(Expression): class NewElementaryType(Expression):

@ -1,4 +1,4 @@
from slither.core.expressions.expression import Expression from slither.core.expressions.expression import Expression
from slither.core.expressions.callExpression import CallExpression from slither.core.expressions.call_expression import CallExpression
class SuperCallExpression(CallExpression): pass class SuperCallExpression(CallExpression): pass

@ -1,4 +1,4 @@
from slither.core.expressions.expressionTyped import ExpressionTyped from slither.core.expressions.expression_typed import ExpressionTyped
from slither.core.expressions.identifier import Identifier from slither.core.expressions.identifier import Identifier
class SuperIdentifier(Identifier): class SuperIdentifier(Identifier):

@ -1,6 +1,6 @@
from slither.core.expressions.expressionTyped import ExpressionTyped from slither.core.expressions.expression_typed import ExpressionTyped
from slither.core.expressions.expression import Expression from slither.core.expressions.expression import Expression
from slither.core.solidityTypes.type import Type from slither.core.solidity_types.type import Type
class TypeConversion(ExpressionTyped): class TypeConversion(ExpressionTyped):

@ -1,7 +1,7 @@
import logging import logging
from slither.core.expressions.expressionTyped import ExpressionTyped from slither.core.expressions.expression_typed import ExpressionTyped
from slither.core.expressions.expression import Expression from slither.core.expressions.expression import Expression
from slither.core.solidityTypes.type import Type from slither.core.solidity_types.type import Type
logger = logging.getLogger("UnaryOperation") logger = logging.getLogger("UnaryOperation")
@ -101,6 +101,10 @@ class UnaryOperation(ExpressionTyped):
def type_str(self): def type_str(self):
return UnaryOperationType.str(self._type) return UnaryOperationType.str(self._type)
@property
def type(self):
return self._type
@property @property
def is_prefix(self): def is_prefix(self):
return UnaryOperationType.is_prefix(self._type) return UnaryOperationType.is_prefix(self._type)

@ -2,18 +2,26 @@
Main module Main module
""" """
import os import os
from slither.core.context.context import Context
class Slither: class Slither(Context):
""" """
Slither static analyzer Slither static analyzer
""" """
name_class = 'Slither'
def __init__(self): def __init__(self):
super(Slither, self).__init__()
self._contracts = {} self._contracts = {}
self._filename = None self._filename = None
self._source_units = {} self._source_units = {}
self._solc_version = None # '0.3' or '0.4':! self._solc_version = None # '0.3' or '0.4':!
self._pragma_directives = []
self._import_directives = []
self._raw_source_code = {}
@property
def source_units(self):
return self._source_units
@property @property
def contracts(self): def contracts(self):
@ -23,9 +31,9 @@ class Slither:
@property @property
def contracts_derived(self): def contracts_derived(self):
"""list(Contract): List of contracts that are derived and not inherited.""" """list(Contract): List of contracts that are derived and not inherited."""
inheritances = (x.inheritances for x in self.contracts) inheritance = (x.inheritance for x in self.contracts)
inheritances = (item for sublist in inheritances for item in sublist) inheritance = [item for sublist in inheritance for item in sublist]
return [c for c in self._contracts.values() if c not in inheritances] return [c for c in self._contracts.values() if c not in inheritance]
def contracts_as_dict(self): def contracts_as_dict(self):
"""list(dict(str: Contract): List of contracts as dict: name -> Contract.""" """list(dict(str: Contract): List of contracts as dict: name -> Contract."""
@ -41,6 +49,21 @@ class Slither:
"""str: Solidity version.""" """str: Solidity version."""
return self._solc_version return self._solc_version
@property
def pragma_directives(self):
""" list(list(str)): Pragma directives. Example [['solidity', '^', '0.4', '.24']]"""
return self._pragma_directives
@property
def import_directives(self):
""" list(str): Import directives"""
return self._import_directives
@property
def source_code(self):
""" {filename: source_code}: source code """
return self._raw_source_code
def get_contract_from_name(self, contract_name): def get_contract_from_name(self, contract_name):
""" """
Return a contract from a name Return a contract from a name

@ -1,26 +0,0 @@
from slither.core.solidityTypes.type import Type
from slither.core.variables.functionTypeVariable import FunctionTypeVariable
from slither.core.expressions.expression import Expression
class FunctionType(Type):
def __init__(self, params, return_values):
assert all(isinstance(x, FunctionTypeVariable) for x in params)
assert all(isinstance(x, FunctionTypeVariable) for x in return_values)
super(FunctionType, self).__init__()
self._params = params
self._return_values = return_values
@property
def params(self):
return self._params
@property
def return_values(self):
return self._return_values
def __str__(self):
params = ".".join([str(x) for x in self._params])
return_values = ".".join([str(x) for x in self._return_values])
return 'function({}) returns ({})'.format(params, return_values)

@ -1,3 +0,0 @@
from slither.core.sourceMapping.sourceMapping import SourceMapping
class Type(SourceMapping): pass

@ -1,23 +0,0 @@
from slither.core.solidityTypes.type import Type
from slither.core.declarations.structure import Structure
from slither.core.declarations.enum import Enum
from slither.core.declarations.contract import Contract
class UserDefinedType(Type):
def __init__(self, t):
assert isinstance(t, (Contract, Enum, Structure))
super(UserDefinedType, self).__init__()
self._type = t
@property
def type(self):
return self._type
def __str__(self):
if isinstance(self.type, (Enum, Structure)):
return str(self.type.contract)+'.'+str(self.type.name)
return str(self.type.name)

@ -0,0 +1,5 @@
from .array_type import ArrayType
from .elementary_type import ElementaryType
from .function_type import FunctionType
from .mapping_type import MappingType
from .user_defined_type import UserDefinedType

@ -1,4 +1,5 @@
from slither.core.solidityTypes.type import Type from slither.core.variables.variable import Variable
from slither.core.solidity_types.type import Type
from slither.core.expressions.expression import Expression from slither.core.expressions.expression import Expression
class ArrayType(Type): class ArrayType(Type):
@ -21,6 +22,16 @@ class ArrayType(Type):
def __str__(self): def __str__(self):
if self._length: if self._length:
if isinstance(self._length.value, Variable) and self._length.value.is_constant:
return str(self._type)+'[{}]'.format(str(self._length.value.expression))
return str(self._type)+'[{}]'.format(str(self._length)) return str(self._type)+'[{}]'.format(str(self._length))
return str(self._type)+'[]' return str(self._type)+'[]'
def __eq__(self, other):
if not isinstance(other, ArrayType):
return False
return self._type == other.type and self.length == other.length
def __hash__(self):
return hash(str(self))

@ -1,6 +1,6 @@
import itertools import itertools
from slither.core.solidityTypes.type import Type from slither.core.solidity_types.type import Type
# see https://solidity.readthedocs.io/en/v0.4.24/miscellaneous.html?highlight=grammar # see https://solidity.readthedocs.io/en/v0.4.24/miscellaneous.html?highlight=grammar
@ -48,3 +48,11 @@ class ElementaryType(Type):
def __str__(self): def __str__(self):
return self._type return self._type
def __eq__(self, other):
if not isinstance(other, ElementaryType):
return False
return self.type == other.type
def __hash__(self):
return hash(str(self))

@ -0,0 +1,66 @@
from slither.core.solidity_types.type import Type
from slither.core.variables.function_type_variable import FunctionTypeVariable
from slither.core.expressions.expression import Expression
class FunctionType(Type):
def __init__(self, params, return_values):
assert all(isinstance(x, FunctionTypeVariable) for x in params)
assert all(isinstance(x, FunctionTypeVariable) for x in return_values)
super(FunctionType, self).__init__()
self._params = params
self._return_values = return_values
@property
def params(self):
return self._params
@property
def return_values(self):
return self._return_values
@property
def return_type(self):
return [x.type for x in self.return_values]
def __str__(self):
# Use x.type
# x.name may be empty
params = ",".join([str(x.type) for x in self._params])
return_values = ",".join([str(x.type) for x in self._return_values])
if return_values:
return 'function({}) returns({})'.format(params, return_values)
return 'function({})'.format(params)
@property
def parameters_signature(self):
'''
Return the parameters signature(without the return statetement)
'''
# Use x.type
# x.name may be empty
params = ",".join([str(x.type) for x in self._params])
return '({})'.format(params)
@property
def signature(self):
'''
Return the signature(with the return statetement if it exists)
'''
# Use x.type
# x.name may be empty
params = ",".join([str(x.type) for x in self._params])
return_values = ",".join([str(x.type) for x in self._return_values])
if return_values:
return '({}) returns({})'.format(params, return_values)
return '({})'.format(params)
def __eq__(self, other):
if not isinstance(other, FunctionType):
return False
return self.params == other.params and self.return_values == other.return_values
def __hash__(self):
return hash(str(self))

@ -1,4 +1,4 @@
from slither.core.solidityTypes.type import Type from slither.core.solidity_types.type import Type
class MappingType(Type): class MappingType(Type):
@ -19,3 +19,12 @@ class MappingType(Type):
def __str__(self): def __str__(self):
return 'mapping({} => {}'.format(str(self._from), str(self._to)) return 'mapping({} => {}'.format(str(self._from), str(self._to))
def __eq__(self, other):
if not isinstance(other, MappingType):
return False
return self.type_from == other.type_from and self.type_to == other.type_to
def __hash__(self):
return hash(str(self))

@ -0,0 +1,3 @@
from slither.core.source_mapping.source_mapping import SourceMapping
class Type(SourceMapping): pass

@ -0,0 +1,35 @@
from slither.core.solidity_types.type import Type
class UserDefinedType(Type):
def __init__(self, t):
from slither.core.declarations.structure import Structure
from slither.core.declarations.enum import Enum
from slither.core.declarations.contract import Contract
assert isinstance(t, (Contract, Enum, Structure))
super(UserDefinedType, self).__init__()
self._type = t
@property
def type(self):
return self._type
def __str__(self):
from slither.core.declarations.structure import Structure
from slither.core.declarations.enum import Enum
if isinstance(self.type, (Enum, Structure)):
return str(self.type.contract)+'.'+str(self.type.name)
return str(self.type.name)
def __eq__(self, other):
if not isinstance(other, UserDefinedType):
return False
return self.type == other.type
def __hash__(self):
return hash(str(self))

@ -1,18 +0,0 @@
from slither.core.context.context import Context
class SourceMapping(Context):
def __init__(self):
super(SourceMapping, self).__init__()
self._source_mapping = None
self._offset = None
def set_source_mapping(self, source_mapping):
self._source_mapping = source_mapping
@property
def source_mapping(self):
return self._source_mapping
def set_offset(self, offset):
self._offset = offset

@ -0,0 +1,89 @@
import re
from slither.core.context.context import Context
class SourceMapping(Context):
def __init__(self):
super(SourceMapping, self).__init__()
self._source_mapping = None
@property
def source_mapping(self):
return self._source_mapping
@staticmethod
def _compute_line(source_code, start, length):
"""
Compute line(s) number from a start/end offset
Not done in an efficient way
"""
total_length = len(source_code)
source_code = source_code.split('\n')
counter = 0
i = 0
lines = []
while counter < total_length:
counter += len(source_code[i]) +1
i = i+1
if counter > start:
lines.append(i)
if counter > start+length:
break
return lines
@staticmethod
def _convert_source_mapping(offset, slither):
'''
Convert a text offset to a real offset
see https://solidity.readthedocs.io/en/develop/miscellaneous.html#source-mappings
Returns:
(dict): {'start':0, 'length':0, 'filename': 'file.sol'}
'''
sourceUnits = slither.source_units
position = re.findall('([0-9]*):([0-9]*):([-]?[0-9]*)', offset)
if len(position) != 1:
return {}
s, l, f = position[0]
s = int(s)
l = int(l)
f = int(f)
if f not in sourceUnits:
return {'start':s, 'length':l}
filename = sourceUnits[f]
lines = []
if filename in slither.source_code:
lines = SourceMapping._compute_line(slither.source_code[filename], s, l)
return {'start':s, 'length':l, 'filename': filename, 'lines' : lines }
def set_offset(self, offset, slither):
if isinstance(offset, dict):
self._source_mapping = offset
else:
self._source_mapping = self._convert_source_mapping(offset, slither)
@property
def source_mapping_str(self):
def relative_path(path):
# Remove absolute path for printing
# Truffle returns absolutePath
if '/contracts/' in path:
return path[path.find('/contracts/'):]
return path
lines = self.source_mapping['lines']
if not lines:
lines = ''
elif len(lines) == 1:
lines = '#{}'.format(lines[0])
else:
lines = '#{}-{}'.format(lines[0], lines[-1])
return '{}{}'.format(relative_path(self.source_mapping['filename']), lines)

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

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

@ -0,0 +1,48 @@
from .variable import Variable
from slither.core.children.child_function import ChildFunction
from slither.core.solidity_types.user_defined_type import UserDefinedType
from slither.core.solidity_types.array_type import ArrayType
from slither.core.declarations.structure import Structure
class LocalVariable(ChildFunction, Variable):
def __init__(self):
super(LocalVariable, self).__init__()
self._location = None
def set_location(self, loc):
self._location = loc
@property
def location(self):
'''
Variable Location
Can be storage/memory or default
Returns:
(str)
'''
return self._location
@property
def is_storage(self):
"""
Return true if the variable is located in storage
See https://solidity.readthedocs.io/en/v0.4.24/types.html?highlight=storage%20location#data-location
Returns:
(bool)
"""
if self.location == 'memory':
return False
if self.location == 'storage':
return True
if isinstance(self.type, ArrayType):
return True
if isinstance(self.type, UserDefinedType):
return isinstance(self.type.type, Structure)
return False

@ -1,4 +1,4 @@
from slither.core.variables.localVariable import LocalVariable from slither.core.variables.local_variable import LocalVariable
class LocalVariableInitFromTuple(LocalVariable): class LocalVariableInitFromTuple(LocalVariable):
""" """

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

Loading…
Cancel
Save