Rustle

June 1, 2023 ยท View on GitHub

Rustle

CI Status Build-Image Status License: AGPL v3 AwesomeNEAR Devpost

Rustle is an automatic static analyzer for NEAR smart contracts in Rust. It can help to locate tens of different vulnerabilities in NEAR smart contracts. According to DefiLlama, among the top 10 DApps in NEAR, 8 are audited by BlockSec. With rich audit experience and a deep understanding of NEAR protocol, we build this tool and share it with the community.

Get started

Prerequisite

Linux setup

Install the required toolkits with the following commands for Rustle in Linux. Commands are tested in Ubuntu 20.04 LTS.

# install Rust Toolchain
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

# install LLVM 15
sudo bash -c "$(wget -O - https://apt.llvm.org/llvm.sh)" 15

# install Python toolchain
sudo apt install python3 python3-pip    # requires python >= 3.8
pip3 install -r utils/requirements.txt  # you need to clone this repo first

# add WASM target
rustup target add wasm32-unknown-unknown

# install other components
sudo apt install figlet
cargo install rustfilt

# [optional] useful tools for developing
LLVM_VERSION=
sudo apt install clangd-$LLVM_VERSION clang-format-$LLVM_VERSION clang-tidy-$LLVM_VERSION

macOS setup

The following commands are for users using macOS, they are tested only on Apple Silicon Mac, so use them with caution.

# install Rust Toolchain
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

# install LLVM 15
brew install llvm@15

# install Python packages
pip3 install -r utils/requirements.txt  # you need to clone this repo first
                                        # using macOS default python3

# add WASM target
rustup target add wasm32-unknown-unknown

# install other components
brew install figlet coreutils gsed
cargo install rustfilt

Docker

We provide a docker solution.

# build the image
docker build --build-arg UID=`id -u` --build-arg GID=`id -g` -t rustle .

# run a container from the image
docker run --name rustle -it -v `pwd`:/rustle -w /rustle rustle bash

# exec the container
docker start rustle
docker exec -it -w /rustle rustle bash

Usage

./rustle [-t|--tg_dir <tg_dir>] [-d|--detector <detector_list>] [-o|--output <output_dir>] [-h|--help] <src_dir>
  • src_dir: Path to the contract source.
  • tg_dir: Path to the contract build target. Defaults to be same as src_dir.
  • detector: The detector list. It can be used to pass multiple detectors or groups separated by ,. Defaults to all.
    • pass all group to enable all detectors.
    • pass high, medium, low and info groups to enable detector groups with different severity (refer to Detectors)
    • pass nep-ft, nep-storage and nep-nft groups to enable detectors implemented for specified NEP (refer to NEP detector groups)
    • pass detector ids in the table to enable those detectors
  • output: Path where audit reports will be generated in. Defaults to ./audit-result.

Note: if the target bit code (.bc binary) built by cargo is not in the $src_dir, use -t|--tg_dir to set the target's directory, or it will be set to $src_dir by default.

The command below shows an example of analyzing the LiNEAR.

# clone LiNEAR
git clone https://github.com/linear-protocol/LiNEAR.git ~/near-repo/LiNEAR

# run Rustle
./rustle -t ~/near-repo/LiNEAR ~/near-repo/LiNEAR/contracts/linear

# [optional] run Rustle on specified detectors or severity groups and save audit reports in `~/linear-report`
./rustle -t ~/near-repo/LiNEAR ~/near-repo/LiNEAR/contracts/linear -d high,medium,complex-loop -o ~/linear-report

A CSV-format report will be generated in the directory "./audit-result".

Detectors

All vulnerabilities Rustle can find.

Detector IDDescriptionSeverity
unhandled-promisefind Promises that are not handledHigh
non-private-callbackmissing macro #[private] for callback functionsHigh
reentrancyfind functions that are vulnerable to reentrancy attackHigh
unsafe-mathlack of overflow check for arithmetic operationHigh
self-transfermissing check of sender != receiverHigh
incorrect-json-typeincorrect type used in parameters or return valuesHigh
unsaved-changeschanges to collections are not savedHigh
nft-approval-checkfind nft_transfer without check of approval idHigh
nft-owner-checkfind approve or revoke functions without owner checkHigh
div-before-mulprecision loss due to incorrect operation orderMedium
roundrounding without specifying ceil or floorMedium
lock-callbackpanic in callback function may lock contractMedium
yocto-attachno assert_one_yocto in privileged functionMedium
dup-collection-idduplicate id uses in collectionsMedium
unregistered-receiverno panic on unregistered transfer receiversMedium
nep${id}-interfacefind all unimplemented NEP interfaceMedium
prepaid-gasmissing check of prepaid gas in ft_transfer_callLow
non-callback-privatemacro #[private] used in non-callback functionLow
unused-retfunction result not used or checkedLow
upgrade-funcno upgrade function in contractLow
tautologytautology used in conditional branchLow
storage-gasmissing balance check for storage expansionLow
unclaimed-storage-feemissing balance check before storage unregisterLow
inconsistencyuse of similar but slightly different symbolInfo
timestampfind all uses of timestampInfo
complex-loopfind all loops with complex logic which may lead to DoSInfo
ext-callfind all cross-contract invocationsInfo
promise-resultfind all uses of promise resultInfo
transferfind all transfer actionsInfo
public-interfacefind all public interfacesInfo

NEP detector groups

Apart from the groups by severity level, Rustle provides some detector groups by corresponding NEP. Currently, Rustle supports the following groups.

NEPDetector Group IDDetector IDs
NEP-141nep-ftnep141-interface, self-transfer, unregistered-receiver
NEP-145nep-storagenep145-interface, unclaimed-storage-fee
NEP-171, NEP-178nep-nftnep171-interface, nft-approval-check, nft-owner-check

Add new detectors

  1. Fork this repo to your account.
  2. Put the new detector under /detectors.
  3. Add a detection target in /Makefile with commands to run your detector.
  4. Add the target to the dependency of audit target and its name to detector list and severity groups in ./rustle script.
  5. Add processing code in utils/audit.py (refer to other detectors' code in audit.py).
  6. Submit a pull request from your branch to the main.

Note

Rustle can be used in the development process to scan the NEAR smart contracts iteratively. This can save a lot of manual effort and mitigate part of potential issues. However, vulnerabilities in complex logic or related to semantics are still the limitation of Rustle. Locating complicated semantic issues requires the experts in BlockSec to conduct exhaustive and thorough reviews. Contact us for audit service.

License

This project is under the AGPLv3 License. See the LICENSE file for the full license text.