Skip to content

Commit 77229ac

Browse files
authored
Merge pull request #353 from crytic/update-slither
Update slither tutorials
2 parents 270728e + 85fb52c commit 77229ac

File tree

13 files changed

+260
-36
lines changed

13 files changed

+260
-36
lines changed

.github/workflows/slither.yml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,6 @@ jobs:
2929
- name: Install dependencies
3030
run: |
3131
pip install solc-select
32-
solc-select install 0.5.11
33-
solc-select use 0.5.11
3432
- name: Run Tests
3533
run: |
3634
bash program-analysis/slither/scripts/gh_action_test.sh

SUMMARY.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,4 +119,5 @@
119119
- [API](./program-analysis/slither/api.md)
120120
- [Exercise 1](./program-analysis/slither/exercise1.md)
121121
- [Exercise 2](./program-analysis/slither/exercise2.md)
122+
- [Exercise 3](./program-analysis/slither/exercise3.md)
122123
- [Resources](./resources/tob_blogposts.md)

program-analysis/slither/README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,9 @@ Once you feel confident with the material in this README, proceed to the exercis
1111

1212
- [Exercise 1](./exercise1.md): Function override protection
1313
- [Exercise 2](./exercise2.md): Check for access controls
14+
- [Exercise 3](./exercise3.md): Find variable used in conditional statements
1415

15-
Watch Slither's [code walkthrough](https://www.youtube.com/watch?v=EUl3UlYSluU) to learn about its code structure.
16+
Watch Slither's [code walkthrough](https://www.youtube.com/watch?v=EUl3UlYSluU), or [API walkthrough](https://www.youtube.com/watch?v=Ijf0pellvgw) to learn about its code structure.
1617

1718
## Installation
1819

program-analysis/slither/api.md

Lines changed: 188 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,207 @@
1-
## API Basics
1+
# API Basics
22

33
Slither has an API that allows you to explore basic attributes of contracts and their functions.
44

5+
On a high level there are 6 layers:
6+
7+
- `Slither` - main slither object
8+
- `SlitherCompilationUnit` - group of files used by one call to solc
9+
- `Contract` - contract level
10+
- `Function` - function level
11+
- `Node` - control flow graph
12+
- `SlithrIR` - intermediate representation
13+
14+
Watch our [API walkthrough](https://www.youtube.com/watch?v=Ijf0pellvgw) for more details
15+
16+
## Slither object
17+
518
To load a codebase:
619

720
```python
821
from slither import Slither
922
slither = Slither('/path/to/project')
1023
```
1124

12-
### Exploring Contracts and Functions
25+
To load a contract deployed:
1326

14-
A `Slither` object has:
27+
```python
28+
from slither import Slither
29+
slither = Slither('0x..') # assuming the code is verified on etherscan
30+
```
31+
32+
Use `etherscan_api_key` to provide an [Etherscan API KEY](https://docs.etherscan.io/getting-started/viewing-api-usage-statistics)
33+
34+
```python
35+
slither = Slither('0x..', etherscan_api_key='..')
36+
```
37+
38+
You can retrieve the list of compilation units with:
39+
40+
- `sl.compilation_units # array of SlitherCompilationUnit`
41+
42+
## SlitherCompilationUnit object
43+
44+
- ~ group of files used by one call to solc
45+
- Most targets have 1 compilation, but not always true
46+
- Partial compilation for optimization
47+
- Multiple solc version used
48+
- Etc..
49+
- Why compilation unit matters?
50+
- Some APIs might be not intuitive
51+
- Ex: looking for a contract based on the name?
52+
- Can have multiple contracts
53+
- For hacking you can (probably) use the first compilation unit
54+
- `compilation_unit = sl.compilation_units[0]`
55+
56+
A [`SlitherCompilationUnit`](https://github.com/crytic/slither/blob/master/slither/core/compilation_unit.py) has:
1557

1658
- `contracts (list(Contract))`: A list of contracts
1759
- `contracts_derived (list(Contract))`: A list of contracts that are not inherited by another contract (a subset of contracts)
1860
- `get_contract_from_name (str)`: Returns a list of contracts matching the name
61+
- `[structures | enums | events | variables | functions]_top_level`: Top level object
62+
63+
Example
64+
65+
```python
66+
from slither import Slither
67+
sl = Slither("0xdac17f958d2ee523a2206206994597c13d831ec7")
68+
compilation_unit = sl.compilation_units[0]
69+
70+
# Print all the contracts from the USDT address
71+
print([str(c) for c in compilation_unit.contracts])
72+
73+
# Print the most derived contracts from the USDT address
74+
print([str(c) for c in compilation_unit.contracts_derived])
75+
```
76+
77+
```bash
78+
% python test.py
79+
['SafeMath', 'Ownable', 'ERC20Basic', 'ERC20', 'BasicToken', 'StandardToken', 'Pausable', 'BlackList', 'UpgradedStandardToken', 'TetherToken']
80+
81+
['SafeMath', 'UpgradedStandardToken', 'TetherToken']
82+
```
83+
84+
## Contract Object
85+
86+
A [`Contract`](https://github.com/crytic/slither/blob/master/slither/core/declarations/contract.py) object has:
87+
88+
- `name: str`: The name of the contract
89+
- `functions: list[Function]`: A list of functions
90+
- `modifiers: list[Modifier]`: A list of modifiers
91+
- `all_functions_called: list[Function/Modifier]`: A list of all internal functions reachable by the contract
92+
- `inheritance: list[Contract]`: A list of inherited contracts (c3 linearization order)
93+
- `derived_contracts: list[Contract]`: contracts derived from it
94+
- `get_function_from_signature(str): Function`: Returns a Function from its signature
95+
- `get_modifier_from_signature(str): Modifier`: Returns a Modifier from its signature
96+
- `get_state_variable_from_name(str): StateVariable`: Returns a StateVariable from its name
97+
- `state_variables: List[StateVariable]`: list of accessible variables
98+
- `state_variables_ordered: List[StateVariable]`: all variable ordered by declaration
99+
100+
Example
101+
102+
```python
103+
from slither import Slither
104+
sl = Slither("0xdac17f958d2ee523a2206206994597c13d831ec7")
105+
compilation_unit = sl.compilation_units[0]
19106

20-
A `Contract` object has:
21-
22-
- `name (str)`: The name of the contract
23-
- `functions (list(Function))`: A list of functions
24-
- `modifiers (list(Modifier))`: A list of modifiers
25-
- `all_functions_called (list(Function/Modifier))`: A list of all internal functions reachable by the contract
26-
- `inheritance (list(Contract))`: A list of inherited contracts
27-
- `get_function_from_signature (str)`: Returns a Function from its signature
28-
- `get_modifier_from_signature (str)`: Returns a Modifier from its signature
29-
- `get_state_variable_from_name (str)`: Returns a StateVariable from its name
30-
31-
A `Function` or a `Modifier` object has:
32-
33-
- `name (str)`: The name of the function
34-
- `contract (contract)`: The contract where the function is declared
35-
- `nodes (list(Node))`: A list of nodes composing the CFG of the function/modifier
36-
- `entry_point (Node)`: The entry point of the CFG
37-
- `variables_read (list(Variable))`: A list of variables read
38-
- `variables_written (list(Variable))`: A list of variables written
39-
- `state_variables_read (list(StateVariable))`: A list of state variables read (a subset of `variables_read`)
40-
- `state_variables_written (list(StateVariable))`: A list of state variables written (a subset of `variables_written`)
107+
# Print all the state variables of the USDT token
108+
contract = compilation_unit.get_contract_from_name("TetherToken")[0]
109+
print([str(v) for v in contract.state_variables])
110+
```
111+
112+
```bash
113+
% python test.py
114+
['owner', 'paused', '_totalSupply', 'balances', 'basisPointsRate', 'maximumFee', 'allowed', 'MAX_UINT', 'isBlackListed', 'name', 'symbol', 'decimals', 'upgradedAddress', 'deprecated']
115+
```
116+
117+
## Function object
118+
119+
A [`Function`](https://github.com/crytic/slither/blob/master/slither/core/declarations/function.py) or a `Modifier` object has:
120+
121+
- `name: str`: The name of the function
122+
- `contract: Contract`: The contract where the function is declared
123+
- `nodes: list[Node]`: A list of nodes composing the CFG of the function/modifier
124+
- `entry_point: Node`: The entry point of the CFG
125+
- `[state |local]_variable_[read |write]: list[StateVariable]`: A list of local/state variables read/write
126+
- All can be prefixed by “all\_” for recursive lookup
127+
- Ex: `all_state_variable_read`: return all the state variables read in internal calls
128+
- `slithir_operations: List[Operation]`: list of IR operations
129+
130+
```python
131+
from slither import Slither
132+
sl = Slither("0xdac17f958d2ee523a2206206994597c13d831ec7")
133+
compilation_unit = sl.compilation_units[0]
134+
contract = compilation_unit.get_contract_from_name("TetherToken")[0]
135+
136+
transfer = contract.get_function_from_signature("transfer(address,uint256)")
137+
138+
# Print all the state variables read by the transfer function
139+
print([str(v) for v in transfer.state_variables_read])
140+
# Print all the state variables read by the transfer function and its internal calls
141+
print([str(v) for v in transfer.all_state_variables_read])
142+
```
143+
144+
```bash
145+
% python test.py
146+
['deprecated', 'isBlackListed', 'upgradedAddress']
147+
['owner', 'basisPointsRate', 'deprecated', 'paused', 'isBlackListed', 'maximumFee', 'upgradedAddress', 'balances']
148+
```
149+
150+
## Node object
151+
152+
[Node](https://github.com/crytic/slither/blob/master/slither/core/cfg/node.py)
153+
154+
To explore the nodes:
155+
156+
- If order does not matter
157+
- `for node in function.nodes`
158+
- If order matters, walk through the nodes
159+
160+
```python
161+
def visit_node(node: Node, visited: List[Node]):
162+
163+
if node in visited:
164+
return
165+
visited += [node]
166+
167+
# custom action
168+
for son in node.sons:
169+
visit_node(son, visited)
170+
```
171+
172+
- If need to iterate more than once (advanced usages)
173+
- Bound the iteration X times
174+
- Create a fix-point - abstract interpretation style analysis
175+
176+
## SlithIR
177+
178+
- [slither/slithir](https://github.com/crytic/slither/tree/master/slither/slithir)
179+
- Every IR operation has its own methods
180+
- Check if an operation is of a type:
181+
- `isinstance(ir, TYPE)`
182+
- Ex: `isinstance(ir, Call)`
183+
- Check if the operation is an addition
184+
- `isinstance(ir, Binary) & ir.type == BinaryType.ADDITION`
185+
- Check if the operation is a call to MyContract
186+
- `isinstance(ir, HighLevelCall) & ir.destination == MyContract`
187+
188+
```python
189+
from slither import Slither
190+
sl = Slither("0xdac17f958d2ee523a2206206994597c13d831ec7")
191+
compilation_unit = sl.compilation_units[0]
192+
contract = compilation_unit.get_contract_from_name("TetherToken")[0]
193+
totalSupply = contract.get_function_from_signature("totalSupply()")
194+
195+
# Print the external call made in the totalSupply function
196+
for ir in totalSupply.slithir_operations:
197+
if isinstance(ir, HighLevelCall):
198+
print(f"External call found {ir} ({ir.node.source_mapping})")
199+
```
200+
201+
```bash
202+
% python test.py
203+
External call found HIGH_LEVEL_CALL, […] (...TetherToken.sol#339)
204+
```
41205

42206
### Example: Print Basic Information
43207

program-analysis/slither/examples/coin.sol

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
// SPDX-License-Identifier: AGPL-3.0
2-
pragma solidity ^0.5.0;
2+
pragma solidity ^0.8.0;
33

44
contract Coin {
55
address owner = msg.sender;
66

77
mapping(address => uint256) balances;
88

99
// _mint must not be overriden
10-
function _mint(address dst, uint256 val) internal {
10+
function _mint(address dst, uint256 val) internal virtual {
1111
require(msg.sender == owner);
1212
balances[dst] += val;
1313
}
@@ -20,7 +20,7 @@ contract Coin {
2020
contract MyCoin is Coin {
2121
event Mint(address, uint256);
2222

23-
function _mint(address dst, uint256 val) internal {
23+
function _mint(address dst, uint256 val) internal override {
2424
balances[dst] += val;
2525
emit Mint(dst, val);
2626
}

program-analysis/slither/exercise1.md

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,17 @@
11
# Exercise 1: Function Overridden Protection
22

3-
The goal is to create a script that fills in a missing feature of Solidity: function overriding protection.
3+
The goal is to create a script that performs a feature that was not present in previous version of Solidity: function overriding protection.
44

5-
[exercises/exercise1/coin.sol](exercises/exercise1/coin.sol) contains a function that must never be overridden:
5+
[exercises/exercise1/coin.sol](https://github.com/crytic/building-secure-contracts/tree/master/program-analysis/slither/exercises/exercise1/coin.sol) contains a function that must never be overridden:
66

77
```solidity
88
_mint(address dst, uint256 val)
99
```
1010

1111
Use Slither to ensure that no contract inheriting Coin overrides this function.
1212

13+
Use `solc-select install 0.5.0 && solc-select use 0.5.0` to switch to solc 0.5.0
14+
1315
## Proposed Algorithm
1416

1517
```
@@ -28,4 +30,4 @@ Get the Coin contract
2830

2931
## Solution
3032

31-
See [exercises/exercise1/solution.py](exercises/exercise1/solution.py).
33+
See [exercises/exercise1/solution.py](https://github.com/crytic/building-secure-contracts/tree/master/program-analysis/slither/exercises/exercise1/solution.py).

program-analysis/slither/exercise2.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Exercise 2: Access Control
22

3-
The [exercises/exercise2/coin.sol](exercises/exercise2/coin.sol) file contains an access control implementation with the `onlyOwner` modifier. A common mistake is forgetting to add the modifier to a crucial function. In this exercise, we will use Slither to implement a conservative access control approach.
3+
The [exercises/exercise2/coin.sol](https://github.com/crytic/building-secure-contracts/tree/master/program-analysis/slither/exercises/exercise2/coin.sol) file contains an access control implementation with the `onlyOwner` modifier. A common mistake is forgetting to add the modifier to a crucial function. In this exercise, we will use Slither to implement a conservative access control approach.
44

55
Our goal is to create a script that ensures all public and external functions call `onlyOwner`, except for the functions on the whitelist.
66

@@ -18,4 +18,4 @@ Explore all the functions
1818

1919
## Solution
2020

21-
Refer to [exercises/exercise2/solution.py](exercises/exercise2/solution.py) for the solution.
21+
Refer to [exercises/exercise2/solution.py](https://github.com/crytic/building-secure-contracts/tree/master/program-analysis/slither/exercises/exercise2/solution.py) for the solution.
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# Exercise 3: Find function that use a given variable in a condition
2+
3+
The [exercises/exercise3/find.sol](https://github.com/crytic/building-secure-contracts/tree/master/program-analysis/slither/exercises/exercise3/find.sol) file contains a contract that use `my_variable` variable in multiple locations.
4+
5+
Our goal is to create a script that list all the functions that use `my_variable` in a conditional or require statement.
6+
7+
## Proposed Approach
8+
9+
Explore all the helpers provided by [`Function`](https://github.com/crytic/slither/blob/master/slither/core/declarations/function.py) object to find an easy way to reach the goal
10+
11+
## Solution
12+
13+
Refer to [exercises/exercise3/solution.py](https://github.com/crytic/building-secure-contracts/tree/master/program-analysis/slither/exercises/exercise3/solution.py) for the solution.

program-analysis/slither/exercises/exercise2/coin.sol

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
// SPDX-License-Identifier: AGPL-3.0
2-
pragma solidity ^0.5.0;
2+
pragma solidity ^0.8.0;
33

44
contract Owned {
55
address public owner;
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
The function using "a" in condition are ['condition', 'call_require']

0 commit comments

Comments
 (0)