diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 12301490..807728d9 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -4,3 +4,23 @@ updates: directory: "/" schedule: interval: "daily" + - package-ecosystem: "gitsubmodule" + directory: "/program-analysis/medusa" + schedule: + interval: "daily" # Check for updates daily + commit-message: + prefix: "Update medusa" + assignees: + - ggrieco-tob + reviewers: + - ggrieco-tob + - package-ecosystem: "gitsubmodule" + directory: "/program-analysis/slither" + schedule: + interval: "daily" # Check for updates daily + commit-message: + prefix: "Update slither" + assignees: + - ggrieco-tob + reviewers: + - ggrieco-tob diff --git a/.github/workflows/lint_format.yml b/.github/workflows/lint_format.yml index c3d6605a..faaae2a2 100644 --- a/.github/workflows/lint_format.yml +++ b/.github/workflows/lint_format.yml @@ -32,6 +32,10 @@ jobs: rm -rf program-analysis/medusa/fuzzing rm program-analysis/medusa/docs/theme/highlight.js + - name: Remove slither uncessary files + run: | + rm -rf program-analysis/slither + - name: Run lint run: | npm run lint:format diff --git a/.github/workflows/slither.yml b/.github/workflows/slither.yml deleted file mode 100644 index 6daee37f..00000000 --- a/.github/workflows/slither.yml +++ /dev/null @@ -1,34 +0,0 @@ -name: Run Slither tests - -on: - push: - paths: - - ".github/workflows/slither.yml" - - "program-analysis/slither/**/*.py" - branches: - - master - pull_request: - paths: - - ".github/workflows/slither.yml" - - "program-analysis/slither/**/*.py" - schedule: - # run CI every day even if no PRs/merges occur - - cron: "0 12 * * *" - -jobs: - tests: - runs-on: ubuntu-22.04 - strategy: - fail-fast: false - steps: - - uses: actions/checkout@v4 - - name: Set up Python 3.8 - uses: actions/setup-python@v5 - with: - python-version: 3.8 - - name: Install dependencies - run: | - pip install solc-select - - name: Run Tests - run: | - bash program-analysis/slither/scripts/gh_action_test.sh diff --git a/.gitmodules b/.gitmodules index dc6ee18d..a23130ed 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,7 @@ [submodule "program-analysis/medusa"] path = program-analysis/medusa url = https://github.com/crytic/medusa.git +[submodule "program-analysis/slither"] + path = program-analysis/slither + url = https://github.com/crytic/slither.git + branch = master diff --git a/README.md b/README.md index 0634fad3..d6c8ff6f 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ Brought to you by [Trail of Bits](https://www.trailofbits.com/), this repository - [Program Analysis](./program-analysis): Using automated tools to secure contracts - [Echidna](./program-analysis/echidna): A fuzzer that checks your contract's properties - [Medusa](./program-analysis/medusa/docs/src): A next-gen fuzzer that checks your contract's properties - - [Slither](./program-analysis/slither): A static analyzer with both CLI and scriptable interfaces + - [Slither](./program-analysis/slither/docs/src/): A static analyzer with both CLI and scriptable interfaces - [Manticore](./program-analysis/manticore): A symbolic execution engine that proves the correctness of properties - For each tool, this training material provides: - A theoretical introduction, an API walkthrough, and a set of exercises diff --git a/SUMMARY.md b/SUMMARY.md index a71ba664..f872edd5 100644 --- a/SUMMARY.md +++ b/SUMMARY.md @@ -73,6 +73,7 @@ - [Int as boolean](./not-so-smart-contracts/ton/int_as_boolean/README.md) - [Program Analysis](./program-analysis/README.md) + - [Echidna](./program-analysis/echidna/README.md) - [Introduction](./program-analysis/echidna/introduction/README.md) - [Installation](./program-analysis/echidna/introduction/installation.md) @@ -169,12 +170,38 @@ - [Example](./program-analysis/manticore/exercises/example.md) - [Exercise 1](./program-analysis/manticore/exercises/exercise1.md) - [Exercise 2](./program-analysis/manticore/exercises/exercise2.md) - - [Slither](./program-analysis/slither/README.md) - - [Static Analysis](./program-analysis/slither/static_analysis.md) - - [API](./program-analysis/slither/api.md) - - [Exercise 1](./program-analysis/slither/exercise1.md) - - [Exercise 2](./program-analysis/slither/exercise2.md) - - [Exercise 3](./program-analysis/slither/exercise3.md) + - [Slither](./program-analysis/slither/docs/src/README.md) + - [Usage](./program-analysis/slither/docs/src/Usage.md) + - [API](./program-analysis/slither/docs/src/api/README.md) + - [Static Analysis](./program-analysis/slither/docs/src/api/static_analysis.md) + - [API](./program-analysis/slither/docs/src/api/api.md) + - [SlithIR](./program-analysis/slither/docs/src/api/SlithIR.md) + - [SSA](./program-analysis/slither/docs/src/api/SlithIR-SSA.md) + - [Data dependency](./program-analysis/slither/docs/src/api/Data-dependency.md) + - [JSON output](./program-analysis/slither/docs/src/api/JSON-output.md) + - [Detectors](./program-analysis/slither/docs/src/detectors/Detector-Documentation.md) + - [Detectors](./program-analysis/slither/docs/src/detectors/Detector-Documentation.md) + - [Adding a detector](./program-analysis/slither/docs/src/detectors/Adding-a-new-detector.md) + - [Printers](./program-analysis/slither/docs/src/printers/Printer-documentation.md) + - [Tools](./program-analysis/slither/docs/src/tools/README.md) + - [Adding a new tool](./program-analysis/slither/docs/src/tools/Adding-a-new-utility.md) + - [Code Similarity](./program-analysis/slither/docs/src/tools/Code-Similarity-Detector.md) + - [Contract Flattening](./program-analysis/slither/docs/src/tools/Contract-Flattening.md) + - [Documentation](./program-analysis/slither/docs/src/tools/Documentation.md) + - [Doctor](./program-analysis/slither/docs/src/tools/Doctor.md) + - [ERC Conformance](./program-analysis/slither/docs/src/tools/ERC-Conformance.md) + - [Interface](./program-analysis/slither/docs/src/tools/Interface.md) + - [Mutator](./program-analysis/slither/docs/src/tools/Mutator.md) + - [Path Finding Utility](./program-analysis/slither/docs/src/tools/Path-Finding-Utility.md) + - [Property Generation](./program-analysis/slither/docs/src/tools/Property-generation.md) + - [Read Storage](./program-analysis/slither/docs/src/tools/ReadStorage.md) + - [Format](./program-analysis/slither/docs/src/tools/Slither-format.md) + - [Upgradeability checks](./program-analysis/slither/docs/src/tools/Upgradeability-Checks.md) + - [Tutorials](./program-analysis/slither/docs/src/tutorials/README.md) + - [Exercise 1](./program-analysis/slither/docs/src/tutorials/exercise1.md) + - [Exercise 2](./program-analysis/slither/docs/src/tutorials/exercise2.md) + - [Exercise 3](./program-analysis/slither/docs/src/tutorials/exercise3.md) + - [Resources](./resources/tob_blogposts.md) - [Security contact](./resources/contact.md) - [Blog posts](./resources/tob_blogposts.md) diff --git a/dependabot.yml b/dependabot.yml deleted file mode 100644 index 8717fb64..00000000 --- a/dependabot.yml +++ /dev/null @@ -1,12 +0,0 @@ -version: 2 -updates: - - package-ecosystem: "gitsubmodule" - directory: "/program-analysis/medusa" - schedule: - interval: "daily" # Check for updates daily - commit-message: - prefix: "Update medusa" - assignees: - - montyly - reviewers: - - montyly diff --git a/program-analysis/medusa b/program-analysis/medusa index be3c8a39..4f7a62e1 160000 --- a/program-analysis/medusa +++ b/program-analysis/medusa @@ -1 +1 @@ -Subproject commit be3c8a39564b687e2ef8e604549f824f0542b89d +Subproject commit 4f7a62e1456a2b67ddcf296fd1ab922ad65cc62e diff --git a/program-analysis/slither b/program-analysis/slither new file mode 160000 index 00000000..7f54c8b9 --- /dev/null +++ b/program-analysis/slither @@ -0,0 +1 @@ +Subproject commit 7f54c8b948c34fb35e1d61adaa1bd568ca733253 diff --git a/program-analysis/slither/README.md b/program-analysis/slither/README.md deleted file mode 100644 index f8da1617..00000000 --- a/program-analysis/slither/README.md +++ /dev/null @@ -1,54 +0,0 @@ -# Slither - -The objective of this tutorial is to demonstrate how to use Slither to automatically find bugs in smart contracts. - -- [Installation](#installation) -- [Command line usage](#command-line) -- [Introduction to static analysis](./static_analysis.md): A concise introduction to static analysis -- [API](./api.md): Python API description - -Once you feel confident with the material in this README, proceed to the exercises: - -- [Exercise 1](./exercise1.md): Function override protection -- [Exercise 2](./exercise2.md): Check for access controls -- [Exercise 3](./exercise3.md): Find variable used in conditional statements - -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. - -## Installation - -Slither requires Python >= 3.8. You can install it through pip or by using Docker. - -Installing Slither through pip: - -```bash -pip3 install --user slither-analyzer -``` - -### Docker - -Installing Slither through Docker: - -```bash -docker pull trailofbits/eth-security-toolbox -docker run -it -v "$PWD":/home/trufflecon trailofbits/eth-security-toolbox -``` - -_The last command runs the eth-security-toolbox in a Docker container that has access to your current directory. You can modify the files from your host, and run the tools on the files from the Docker container._ - -Inside the Docker container, run: - -```bash -solc-select 0.5.11 -cd /home/trufflecon/ -``` - -## Command Line - -**Command line vs. user-defined scripts.** Slither comes with a set of pre-defined detectors that can identify many common bugs. Running Slither from the command line will execute all the detectors without requiring detailed knowledge of static analysis: - -```bash -slither project_paths -``` - -Besides detectors, Slither also offers code review capabilities through its [printers](https://github.com/crytic/slither#printers) and [tools](https://github.com/crytic/slither#tools). diff --git a/program-analysis/slither/api.md b/program-analysis/slither/api.md deleted file mode 100644 index d347d790..00000000 --- a/program-analysis/slither/api.md +++ /dev/null @@ -1,208 +0,0 @@ -# API Basics - -Slither has an API that allows you to explore basic attributes of contracts and their functions. - -On a high level there are 6 layers: - -- `Slither` - main slither object -- `SlitherCompilationUnit` - group of files used by one call to solc -- `Contract` - contract level -- `Function` - function level -- `Node` - control flow graph -- `SlithrIR` - intermediate representation - -Watch our [API walkthrough](https://www.youtube.com/watch?v=Ijf0pellvgw) for more details - -## Slither object - -To load a codebase: - -```python -from slither import Slither -slither = Slither('/path/to/project') -``` - -To load a contract deployed: - -```python -from slither import Slither -slither = Slither('0x..') # assuming the code is verified on etherscan -``` - -Use `etherscan_api_key` to provide an [Etherscan API KEY](https://docs.etherscan.io/getting-started/viewing-api-usage-statistics) - -```python -slither = Slither('0x..', etherscan_api_key='..') -``` - -You can retrieve the list of compilation units with: - -- `sl.compilation_units # array of SlitherCompilationUnit` - -## SlitherCompilationUnit object - -- ~ group of files used by one call to solc -- Most targets have 1 compilation, but not always true - - Partial compilation for optimization - - Multiple solc version used - - Etc.. -- Why compilation unit matters? - - Some APIs might be not intuitive - - Ex: looking for a contract based on the name? - - Can have multiple contracts -- For hacking you can (probably) use the first compilation unit - - `compilation_unit = sl.compilation_units[0]` - -A [`SlitherCompilationUnit`](https://github.com/crytic/slither/blob/master/slither/core/compilation_unit.py) has: - -- `contracts (list(Contract))`: A list of contracts -- `contracts_derived (list(Contract))`: A list of contracts that are not inherited by another contract (a subset of contracts) -- `get_contract_from_name (str)`: Returns a list of contracts matching the name -- `[structures | enums | events | variables | functions]_top_level`: Top level object - -Example - -```python -from slither import Slither -sl = Slither("0xdac17f958d2ee523a2206206994597c13d831ec7") -compilation_unit = sl.compilation_units[0] - -# Print all the contracts from the USDT address -print([str(c) for c in compilation_unit.contracts]) - -# Print the most derived contracts from the USDT address -print([str(c) for c in compilation_unit.contracts_derived]) -``` - -```bash -% python test.py -['SafeMath', 'Ownable', 'ERC20Basic', 'ERC20', 'BasicToken', 'StandardToken', 'Pausable', 'BlackList', 'UpgradedStandardToken', 'TetherToken'] - -['SafeMath', 'UpgradedStandardToken', 'TetherToken'] -``` - -## Contract Object - -A [`Contract`](https://github.com/crytic/slither/blob/master/slither/core/declarations/contract.py) object has: - -- `name: str`: The name of the contract -- `functions: list[Function]`: A list of functions -- `modifiers: list[Modifier]`: A list of modifiers -- `all_functions_called: list[Function/Modifier]`: A list of all internal functions reachable by the contract -- `inheritance: list[Contract]`: A list of inherited contracts (c3 linearization order) -- `derived_contracts: list[Contract]`: contracts derived from it -- `get_function_from_signature(str): Function`: Returns a Function from its signature -- `get_modifier_from_signature(str): Modifier`: Returns a Modifier from its signature -- `get_state_variable_from_name(str): StateVariable`: Returns a StateVariable from its name -- `state_variables: List[StateVariable]`: list of accessible variables -- `state_variables_ordered: List[StateVariable]`: all variable ordered by declaration - -Example - -```python -from slither import Slither -sl = Slither("0xdac17f958d2ee523a2206206994597c13d831ec7") -compilation_unit = sl.compilation_units[0] - -# Print all the state variables of the USDT token -contract = compilation_unit.get_contract_from_name("TetherToken")[0] -print([str(v) for v in contract.state_variables]) -``` - -```bash -% python test.py -['owner', 'paused', '_totalSupply', 'balances', 'basisPointsRate', 'maximumFee', 'allowed', 'MAX_UINT', 'isBlackListed', 'name', 'symbol', 'decimals', 'upgradedAddress', 'deprecated'] -``` - -## Function object - -A [`Function`](https://github.com/crytic/slither/blob/master/slither/core/declarations/function.py) or a `Modifier` object has: - -- `name: str`: The name of the function -- `contract: Contract`: The contract where the function is declared -- `nodes: list[Node]`: A list of nodes composing the CFG of the function/modifier -- `entry_point: Node`: The entry point of the CFG -- `[state |local]_variable_[read |write]: list[StateVariable]`: A list of local/state variables read/write - - All can be prefixed by “all\_” for recursive lookup - - Ex: `all_state_variable_read`: return all the state variables read in internal calls -- `slithir_operations: List[Operation]`: list of IR operations - -```python -from slither import Slither -sl = Slither("0xdac17f958d2ee523a2206206994597c13d831ec7") -compilation_unit = sl.compilation_units[0] -contract = compilation_unit.get_contract_from_name("TetherToken")[0] - -transfer = contract.get_function_from_signature("transfer(address,uint256)") - -# Print all the state variables read by the transfer function -print([str(v) for v in transfer.state_variables_read]) -# Print all the state variables read by the transfer function and its internal calls -print([str(v) for v in transfer.all_state_variables_read]) -``` - -```bash -% python test.py -['deprecated', 'isBlackListed', 'upgradedAddress'] -['owner', 'basisPointsRate', 'deprecated', 'paused', 'isBlackListed', 'maximumFee', 'upgradedAddress', 'balances'] -``` - -## Node object - -[Node](https://github.com/crytic/slither/blob/master/slither/core/cfg/node.py) - -To explore the nodes: - -- If order does not matter - - `for node in function.nodes` -- If order matters, walk through the nodes - -```python -def visit_node(node: Node, visited: List[Node]): - - if node in visited: - return - visited += [node] - - # custom action - for son in node.sons: - visit_node(son, visited) -``` - -- If need to iterate more than once (advanced usages) -- Bound the iteration X times -- Create a fix-point - abstract interpretation style analysis - -## SlithIR - -- [slither/slithir](https://github.com/crytic/slither/tree/master/slither/slithir) -- Every IR operation has its own methods -- Check if an operation is of a type: - - `isinstance(ir, TYPE)` - - Ex: `isinstance(ir, Call)` -- Check if the operation is an addition -- `isinstance(ir, Binary) & ir.type == BinaryType.ADDITION` -- Check if the operation is a call to MyContract -- `isinstance(ir, HighLevelCall) & ir.destination == MyContract` - -```python -from slither import Slither -sl = Slither("0xdac17f958d2ee523a2206206994597c13d831ec7") -compilation_unit = sl.compilation_units[0] -contract = compilation_unit.get_contract_from_name("TetherToken")[0] -totalSupply = contract.get_function_from_signature("totalSupply()") - -# Print the external call made in the totalSupply function -for ir in totalSupply.slithir_operations: - if isinstance(ir, HighLevelCall): - print(f"External call found {ir} ({ir.node.source_mapping})") -``` - -```bash -% python test.py -External call found HIGH_LEVEL_CALL, […] (...TetherToken.sol#339) -``` - -### Example: Print Basic Information - -[print_basic_information.py](https://github.com/crytic/building-secure-contracts/tree/master/program-analysis/slither/examples/print_basic_information.py) demonstrates how to print basic information about a project. diff --git a/program-analysis/slither/examples/coin.sol b/program-analysis/slither/examples/coin.sol deleted file mode 100644 index 210753a9..00000000 --- a/program-analysis/slither/examples/coin.sol +++ /dev/null @@ -1,27 +0,0 @@ -// SPDX-License-Identifier: AGPL-3.0 -pragma solidity ^0.8.0; - -contract Coin { - address owner = msg.sender; - - mapping(address => uint256) balances; - - // _mint must not be overridden - function _mint(address dst, uint256 val) internal virtual { - require(msg.sender == owner); - balances[dst] += val; - } - - function mint(address dst, uint256 val) public { - _mint(dst, val); - } -} - -contract MyCoin is Coin { - event Mint(address, uint256); - - function _mint(address dst, uint256 val) internal override { - balances[dst] += val; - emit Mint(dst, val); - } -} diff --git a/program-analysis/slither/examples/expected_results_print_basic_information.txt b/program-analysis/slither/examples/expected_results_print_basic_information.txt deleted file mode 100644 index 19a224f9..00000000 --- a/program-analysis/slither/examples/expected_results_print_basic_information.txt +++ /dev/null @@ -1,39 +0,0 @@ -Contract: Coin - Inherit from[] - _mint(address,uint256): - Visibility: internal - Contract: Coin - Modifier: [] - Is constructor? False - mint(address,uint256): - Visibility: public - Contract: Coin - Modifier: [] - Is constructor? False - slitherConstructorVariables(): - Visibility: internal - Contract: Coin - Modifier: [] - Is constructor? False -Contract: MyCoin - Inherit from['Coin'] - _mint(address,uint256): - Visibility: internal - Contract: MyCoin - Modifier: [] - Is constructor? False - mint(address,uint256): - Visibility: public - Contract: MyCoin - Modifier: [] - Is constructor? False - _mint(address,uint256): - Visibility: internal - Contract: MyCoin - Modifier: [] - Is constructor? False - slitherConstructorVariables(): - Visibility: internal - Contract: MyCoin - Modifier: [] - Is constructor? False diff --git a/program-analysis/slither/examples/print_basic_information.py b/program-analysis/slither/examples/print_basic_information.py deleted file mode 100644 index aee217e0..00000000 --- a/program-analysis/slither/examples/print_basic_information.py +++ /dev/null @@ -1,18 +0,0 @@ -import sys -from slither import Slither - -# Init slither -slither = Slither('coin.sol') - -for contract in slither.contracts: - # Print the contract's name - print(f'Contract: {contract.name}') - # Print the name of the contract inherited - print(f'\tInherit from{[c.name for c in contract.inheritance]}') - for function in contract.functions: - # For each function, print basic information - print(f'\t{function.full_name}:') - print(f'\t\tVisibility: {function.visibility}') - print(f'\t\tContract: {function.contract}') - print(f'\t\tModifier: {[m.name for m in function.modifiers]}') - print(f'\t\tIs constructor? {function.is_constructor}') diff --git a/program-analysis/slither/exercise1.md b/program-analysis/slither/exercise1.md deleted file mode 100644 index 8ce079b8..00000000 --- a/program-analysis/slither/exercise1.md +++ /dev/null @@ -1,33 +0,0 @@ -# Exercise 1: Function Overridden Protection - -The goal is to create a script that performs a feature that was not present in previous version of Solidity: function overriding protection. - -[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: - -```solidity -_mint(address dst, uint256 val) -``` - -Use Slither to ensure that no contract inheriting Coin overrides this function. - -Use `solc-select install 0.5.0 && solc-select use 0.5.0` to switch to solc 0.5.0 - -## Proposed Algorithm - -``` -Get the Coin contract - For each contract in the project: - If Coin is in the list of inherited contracts: - Get the _mint function - If the contract declaring the _mint function is not Coin: - A bug is found. -``` - -## Tips - -- To get a specific contract, use `slither.get_contract_from_name` (note: it returns a list) -- To get a specific function, use `contract.get_function_from_signature` - -## Solution - -See [exercises/exercise1/solution.py](https://github.com/crytic/building-secure-contracts/tree/master/program-analysis/slither/exercises/exercise1/solution.py). diff --git a/program-analysis/slither/exercise2.md b/program-analysis/slither/exercise2.md deleted file mode 100644 index 922a76d4..00000000 --- a/program-analysis/slither/exercise2.md +++ /dev/null @@ -1,21 +0,0 @@ -# Exercise 2: Access Control - -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. - -Our goal is to create a script that ensures all public and external functions call `onlyOwner`, except for the functions on the whitelist. - -## Proposed Algorithm - -``` -Create a whitelist of signatures -Explore all the functions - If the function is in the whitelist of signatures: - Skip - If the function is public or external: - If onlyOwner is not in the modifiers: - A bug is found -``` - -## Solution - -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. diff --git a/program-analysis/slither/exercise3.md b/program-analysis/slither/exercise3.md deleted file mode 100644 index 02333b66..00000000 --- a/program-analysis/slither/exercise3.md +++ /dev/null @@ -1,13 +0,0 @@ -# Exercise 3: Find function that use a given variable in a condition - -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. - -Our goal is to create a script that list all the functions that use `my_variable` in a conditional or require statement. - -## Proposed Approach - -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 - -## Solution - -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. diff --git a/program-analysis/slither/exercises/exercise1/coin.sol b/program-analysis/slither/exercises/exercise1/coin.sol deleted file mode 100644 index 7170e3da..00000000 --- a/program-analysis/slither/exercises/exercise1/coin.sol +++ /dev/null @@ -1,27 +0,0 @@ -// SPDX-License-Identifier: AGPL-3.0 -pragma solidity ^0.5.0; - -contract Coin { - address owner = msg.sender; - - mapping(address => uint256) balances; - - // _mint must not be overridden - function _mint(address dst, uint256 val) internal { - require(msg.sender == owner); - balances[dst] += val; - } - - function mint(address dst, uint256 val) public { - _mint(dst, val); - } -} - -contract MyCoin is Coin { - event Mint(address, uint256); - - function _mint(address dst, uint256 val) internal { - balances[dst] += val; - emit Mint(dst, val); - } -} diff --git a/program-analysis/slither/exercises/exercise1/expected_results.txt b/program-analysis/slither/exercises/exercise1/expected_results.txt deleted file mode 100644 index 8ac9a8c5..00000000 --- a/program-analysis/slither/exercises/exercise1/expected_results.txt +++ /dev/null @@ -1 +0,0 @@ -Error, MyCoin overrides _mint diff --git a/program-analysis/slither/exercises/exercise1/solution.py b/program-analysis/slither/exercises/exercise1/solution.py deleted file mode 100644 index 4ad0ce73..00000000 --- a/program-analysis/slither/exercises/exercise1/solution.py +++ /dev/null @@ -1,15 +0,0 @@ -from slither.slither import Slither - -slither = Slither('coin.sol') -coin = slither.get_contract_from_name('Coin')[0] - -# Iterate over all the contracts -for contract in slither.contracts: - # If the contract is derived from MyContract - if coin in contract.inheritance: - # Get the function definition - mint = contract.get_function_from_signature('_mint(address,uint256)') - # If the function was not declared by coin, there is a bug ! - # Detect error only for contracts overriding the '_mint' function - if mint.contract_declarer == contract: - print(f'Error, {contract} overrides {mint}') diff --git a/program-analysis/slither/exercises/exercise2/coin.sol b/program-analysis/slither/exercises/exercise2/coin.sol deleted file mode 100644 index bec73556..00000000 --- a/program-analysis/slither/exercises/exercise2/coin.sol +++ /dev/null @@ -1,52 +0,0 @@ -// SPDX-License-Identifier: AGPL-3.0 -pragma solidity ^0.8.0; - -contract Owned { - address public owner; - - constructor() public { - owner = msg.sender; - } - - modifier onlyOwner() { - require(msg.sender == owner); - _; - } -} - -contract Coin is Owned { - uint256 decimals = 18; - - mapping(address => uint256) balances; - - event Mint(address indexed destination, uint256 amount); - - /// @notice Mint tokens - /// @param addr The address holding the new token - /// @param value The amount of token to be minted - /// @dev This function performed no check on the caller. Must stay internal - function _mint(address addr, uint256 value) internal { - balances[addr] += value; - require(balances[addr] >= value); - emit Mint(addr, value); - } - - /// @notice Mint tokens. Callable only by the owner - /// @param addr The address holding the new token - /// @param value The amount of token to be minted - function mint(address addr, uint256 value) public onlyOwner { - _mint(addr, value); - } - - /// @notice Mint tokens. Used by the owner to mint directly tokens to himself. Callable only by the owner - /// @param value The amount of token to be minted - function mint(uint256 value) public { - _mint(msg.sender, value); - } - - /// @notice Return the user's balance - /// @param dst User address - function balanceOf(address dst) public view returns (uint256) { - return balances[dst]; - } -} diff --git a/program-analysis/slither/exercises/exercise2/expected_results.txt b/program-analysis/slither/exercises/exercise2/expected_results.txt deleted file mode 100644 index eebacba3..00000000 --- a/program-analysis/slither/exercises/exercise2/expected_results.txt +++ /dev/null @@ -1 +0,0 @@ -mint(uint256) is unprotected! diff --git a/program-analysis/slither/exercises/exercise2/solution.py b/program-analysis/slither/exercises/exercise2/solution.py deleted file mode 100644 index f693a970..00000000 --- a/program-analysis/slither/exercises/exercise2/solution.py +++ /dev/null @@ -1,15 +0,0 @@ -from slither import Slither - -slither = Slither('coin.sol') - -whitelist = ['balanceOf(address)'] - -for contract in slither.contracts: - for function in contract.functions: - if function.full_name in whitelist: - continue - if function.is_constructor: - continue - if function.visibility in ['public', 'external']: - if not 'onlyOwner()' in [m.full_name for m in function.modifiers]: - print(f'{function.full_name} is unprotected!') diff --git a/program-analysis/slither/exercises/exercise3/expected_results.txt b/program-analysis/slither/exercises/exercise3/expected_results.txt deleted file mode 100644 index f0603322..00000000 --- a/program-analysis/slither/exercises/exercise3/expected_results.txt +++ /dev/null @@ -1 +0,0 @@ -The function using "a" in condition are ['condition', 'call_require'] diff --git a/program-analysis/slither/exercises/exercise3/find.sol b/program-analysis/slither/exercises/exercise3/find.sol deleted file mode 100644 index 0e30388e..00000000 --- a/program-analysis/slither/exercises/exercise3/find.sol +++ /dev/null @@ -1,15 +0,0 @@ -contract Find { - uint my_variable; - - function condition() public { - if (my_variable == 0) {} - } - - function call_require() public { - require(my_variable == 0); - } - - function read_and_write() public { - my_variable = my_variable + 1; - } -} diff --git a/program-analysis/slither/exercises/exercise3/solution.py b/program-analysis/slither/exercises/exercise3/solution.py deleted file mode 100644 index 410194ca..00000000 --- a/program-analysis/slither/exercises/exercise3/solution.py +++ /dev/null @@ -1,20 +0,0 @@ -from slither.slither import Slither - -slither = Slither('find.sol') -find = slither.get_contract_from_name('Find')[0] - -assert find - -# Get the variable -my_variable = find.get_state_variable_from_name("my_variable") -assert my_variable - - -function_using_a_as_condition = [ - f - for f in find.functions - if f.is_reading_in_conditional_node(my_variable) or f.is_reading_in_require_or_assert(my_variable) -] - -# Print the result -print(f'The function using "a" in condition are {[f.name for f in function_using_a_as_condition]}') \ No newline at end of file diff --git a/program-analysis/slither/images/ast.png b/program-analysis/slither/images/ast.png deleted file mode 100644 index b19e3277..00000000 Binary files a/program-analysis/slither/images/ast.png and /dev/null differ diff --git a/program-analysis/slither/images/cfg.png b/program-analysis/slither/images/cfg.png deleted file mode 100644 index 22110e50..00000000 Binary files a/program-analysis/slither/images/cfg.png and /dev/null differ diff --git a/program-analysis/slither/scripts/gh_action_test.sh b/program-analysis/slither/scripts/gh_action_test.sh deleted file mode 100644 index d2bc01e4..00000000 --- a/program-analysis/slither/scripts/gh_action_test.sh +++ /dev/null @@ -1,73 +0,0 @@ -#!/usr/bin/env bash - -test_examples(){ - cd examples - - python print_basic_information.py > results.txt - if [ $? -ne 0 ] - then - exit -1 - fi - - DIFF=$(diff results.txt expected_results_print_basic_information.txt) - - if [ "$DIFF" != "" ] - then - echo "print_basic_information.py failed" - cat results.txt - echo "" - cat expected_results_print_basic_information.txt - echo "" - echo "$DIFF" - exit -1 - fi - - echo "print_basic_information.py passed" - cd .. -} - -test_exercise(){ - cd "exercises/exercise$1" - - python solution.py > results.txt - if [ $? -ne 0 ] - then - exit -1 - fi - - DIFF=$(diff results.txt expected_results.txt) - - if [ "$DIFF" != "" ] - then - echo "exercise $1 failed" - cat results.txt - echo "" - cat expected_results.txt - echo "" - echo "$DIFF" - exit -1 - fi - - echo "exercise $1 passed" - cd ../.. -} - - -cd program-analysis/slither -pip install slither-analyzer -solc-select install 0.8.20 -solc-select use 0.8.20 - -test_examples - -solc-select install 0.5.11 -solc-select use 0.5.11 -test_exercise 1 - -solc-select use 0.8.20 -test_exercise 2 - -test_exercise 3 - -echo "Slither tests passed" - diff --git a/program-analysis/slither/static_analysis.md b/program-analysis/slither/static_analysis.md deleted file mode 100644 index 85cf4b28..00000000 --- a/program-analysis/slither/static_analysis.md +++ /dev/null @@ -1,124 +0,0 @@ -## Static analysis - -The capabilities and design of the Slither static analysis framework have been described in blog posts ([1](https://blog.trailofbits.com/2018/10/19/slither-a-solidity-static-analysis-framework/), [2](https://blog.trailofbits.com/2019/05/27/slither-the-leading-static-analyzer-for-smart-contracts/)) and an [academic paper](https://github.com/trailofbits/publications/blob/master/papers/wetseb19.pdf). - -Static analysis comes in different flavors. You may already know that compilers like [clang](https://clang-analyzer.llvm.org/) and [gcc](https://lwn.net/Articles/806099/) rely on these research techniques, as do tools like [Infer](https://fbinfer.com/), [CodeClimate](https://codeclimate.com/), [FindBugs](http://findbugs.sourceforge.net/), and tools based on formal methods like [Frama-C](https://frama-c.com/) and [Polyspace](https://www.mathworks.com/products/polyspace.html). - -In this article, we will not provide an exhaustive review of static analysis techniques and research. Instead, we'll focus on what you need to understand about how Slither works, so you can more effectively use it to find bugs and understand code. - -- [Code representation](#code-representation) -- [Code analysis](#analysis) -- [Intermediate representation](#intermediate-representation) - -### Code representation - -Unlike dynamic analysis, which reasons about a single execution path, static analysis reasons about all paths at once. To do so, it relies on a different code representation. The two most common ones are the abstract syntax tree (AST) and the control flow graph (CFG). - -### Abstract Syntax Trees (AST) - -ASTs are used every time a compiler parses code. They are arguably the most basic structure upon which static analysis can be performed. - -In a nutshell, an AST is a structured tree where, usually, each leaf contains a variable or a constant, and internal nodes are operands or control flow operations. Consider the following code: - -```solidity -function safeAdd(uint256 a, uint256 b) internal pure returns (uint256) { - if (a + b <= a) { - revert(); - } - return a + b; -} -``` - -The corresponding AST is shown in the following illustration: - -![AST](./images/ast.png) - -Slither uses the AST exported by solc. - -While simple to build, the AST is a nested structure that's not always straightforward to analyze. For example, to identify the operations used by the expression `a + b <= a`, you must first analyze `<=` and then `+`. A common approach is to use the so-called visitor pattern, which navigates through the tree recursively. Slither contains a generic visitor in [`ExpressionVisitor`](https://github.com/crytic/slither/blob/master/slither/visitors/expression/expression.py). - -The following code uses `ExpressionVisitor` to detect if an expression contains an addition: - -```python -from slither.visitors.expression.expression import ExpressionVisitor -from slither.core.expressions.binary_operation import BinaryOperationType - -class HasAddition(ExpressionVisitor): - - def result(self): - return self._result - - def _post_binary_operation(self, expression): - if expression.type == BinaryOperationType.ADDITION: - self._result = True - -visitor = HasAddition(expression) # expression is the expression to be tested -print(f'The expression {expression} has an addition: {visitor.result()}') -``` - -### Control Flow Graph (CFG) - -The second most common code representation is the control flow graph (CFG). As its name suggests, it is a graph-based representation that reveals all the execution paths. Each node contains one or multiple instructions, and edges in the graph represent control flow operations (if/then/else, loop, etc). The CFG of our previous example is as follows: - -![CFG](./images/cfg.png) - -Most analyses are built on top of the CFG representation. - -There are many other code representations, each with its advantages and drawbacks depending on the desired analysis. - -### Analysis - -The simplest types of analyses that can be performed with Slither are syntactic analyses. - -### Syntax analysis - -Slither can navigate through the different components of the code and their representation to find inconsistencies and flaws using a pattern matching-like approach. - -For example, the following detectors look for syntax-related issues: - -- [State variable shadowing](https://github.com/crytic/slither/wiki/Detector-Documentation#state-variable-shadowing): iterates over all state variables and checks if any shadow a variable from an inherited contract ([state.py#L51-L62](https://github.com/crytic/slither/blob/0441338e055ab7151b30ca69258561a5a793f8ba/slither/detectors/shadowing/state.py#L51-L62)) - -- [Incorrect ERC20 interface](https://github.com/crytic/slither/wiki/Detector-Documentation#incorrect-erc20-interface): searches for incorrect ERC20 function signatures ([incorrect_erc20_interface.py#L34-L55](https://github.com/crytic/slither/blob/0441338e055ab7151b30ca69258561a5a793f8ba/slither/detectors/erc/incorrect_erc20_interface.py#L34-L55)) - -### Semantic analysis - -In contrast to syntax analysis, semantic analysis delves deeper and analyzes the "meaning" of the code. This category includes a broad range of analyses that yield more powerful and useful results but are more complex to write. - -Semantic analyses are used for advanced vulnerability detection. - -#### Data dependency analysis - -A variable `variable_a` is said to be data-dependent on `variable_b` if there is a path for which the value of `variable_a` is influenced by `variable_b`. - -In the following code, `variable_a` is dependent on `variable_b`: - -```solidity -// ... -variable_a = variable_b + 1; -``` - -Slither comes with built-in [data dependency](https://github.com/crytic/slither/wiki/data-dependency) capabilities, thanks to its intermediate representation (discussed later). - -An example of data dependency usage can be found in the [dangerous strict equality detector](https://github.com/crytic/slither/wiki/Detector-Documentation#dangerous-strict-equalities). Slither looks for strict equality comparisons to dangerous values ([incorrect_strict_equality.py#L86-L87](https://github.com/crytic/slither/blob/6d86220a53603476f9567c3358524ea4db07fb25/slither/detectors/statements/incorrect_strict_equality.py#L86-L87)) and informs the user that they should use `>=` or `<=` instead of `==` to prevent attackers from trapping the contract. Among other things, the detector considers the return value of a call to `balanceOf(address)` to be dangerous ([incorrect_strict_equality.py#L63-L64](https://github.com/crytic/slither/blob/6d86220a53603476f9567c3358524ea4db07fb25/slither/detectors/statements/incorrect_strict_equality.py#L63-L64)) and uses the data dependency engine to track its usage. - -#### Fixed-point computation - -If your analysis navigates through the CFG and follows the edges, you're likely to encounter already visited nodes. For example, if a loop is presented as shown below: - -```solidity -for(uint256 i; i < range; ++) { - variable_a += 1; -} -``` - -Your analysis will need to know when to stop. There are two main strategies: (1) iterate on each node a finite number of times, (2) compute a so-called _fixpoint_. A fixpoint essentially means that analyzing the node doesn't provide any meaningful information. - -An example of a fixpoint used can be found in the reentrancy detectors: Slither explores the nodes and looks for external calls, reads, and writes to storage. Once it has reached a fixpoint ([reentrancy.py#L125-L131](https://github.com/crytic/slither/blob/master/slither/detectors/reentrancy/reentrancy.py#L125-L131)), it stops the exploration and analyzes the results to see if a reentrancy is present, through different reentrancy patterns ([reentrancy_benign.py](https://github.com/crytic/slither/blob/b275bcc824b1b932310cf03b6bfb1a1fef0ebae1/slither/detectors/reentrancy/reentrancy_benign.py), [reentrancy_read_before_write.py](https://github.com/crytic/slither/blob/b275bcc824b1b932310cf03b6bfb1a1fef0ebae1/slither/detectors/reentrancy/reentrancy_read_before_write.py), [reentrancy_eth.py](https://github.com/crytic/slither/blob/b275bcc824b1b932310cf03b6bfb1a1fef0ebae1/slither/detectors/reentrancy/reentrancy_eth.py)). - -Writing analyses using efficient fixed-point computation requires a good understanding of how the analysis propagates its information. - -### Intermediate representation - -An intermediate representation (IR) is a language designed to be more amenable to static analysis than the original one. Slither translates Solidity to its own IR: [SlithIR](https://github.com/crytic/slither/wiki/SlithIR). - -Understanding SlithIR is not necessary if you only want to write basic checks. However, it becomes essential if you plan to write advanced semantic analyses. The [SlithIR](https://github.com/crytic/slither/wiki/Printer-documentation#slithir) and [SSA](https://github.com/crytic/slither/wiki/Printer-documentation#slithir-ssa) printers can help you understand how the code is translated.