Skip to content
Nanle-codePublic

About

Catches reentrancy bugs, access control issues, and integer underflows in your Stacks smart contracts before attackers do.

Topics

Resources

Stars

3 stars

Watchers

0 watching

Forks

Latest commit

Β 

History

9 Commits

Folders and files

NameName
Last commit message
Last commit date
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Clarus

Static security analyzer for Clarity smart contracts on the Stacks blockchain.

Clarus detects common vulnerability patterns in .clar files before you deploy β€” giving developers fast, actionable feedback without needing a full audit.


Why Clarus?

Clarity was designed to be safe β€” but safe-by-design does not mean bug-free. As the Stacks ecosystem grows and sBTC brings real Bitcoin liquidity into DeFi, the stakes of a single vulnerability rise dramatically.

Clarus fills a gap: there is currently no dedicated static analysis tool for Clarity. Clarus aims to be the Slither of the Stacks ecosystem.


Detectors

ID Severity Description
reentrancy πŸ”΄ Critical State mutation after stx-transfer?, contract-call?, or other external interactions
cross-contract-reentrancy πŸ”΄ Critical Reentrancy traced across contract boundaries, naming the exact callee
trait-dispatch πŸ”΄ Critical State mutation around calls through <trait> parameters β€” any conforming contract could be passed in
access-control πŸ”΄ High Public functions that modify state without tx-sender or contract-caller checks
integer-underflow 🟠 High Subtraction on uint without a preceding bounds check
unchecked-returns 🟑 Medium External calls whose return values are not wrapped in try! or unwrap!
integer-overflow πŸ”΅ Low Addition without upper bound validation

Installation

From source

git clone https://github.com/Nanle-code/clarus.git
cd clarus
cargo build --release

The binary will be at ./target/release/clarus.

Optionally install globally so you can run clarus from anywhere on your machine:

cargo install --path .

Usage

Single file analysis

# analyze a single contract
clarus check mycontract.clar

# analyze a contract anywhere on your system
clarus check /path/to/contract.clar

# output as JSON
clarus check mycontract.clar --json

# exit with code 1 if issues found (for CI/CD)
clarus check mycontract.clar --strict

Multi-contract project analysis

# analyze all .clar files in a directory
clarus scan ./contracts

# with JSON output
clarus scan ./contracts --json

# strict mode for CI/CD gates
clarus scan ./contracts --strict

Call graph visualization

# show all contract interactions
clarus graph ./contracts

# show only cross-contract calls
clarus graph ./contracts --cross-only

Testing External Contracts

Clarus works on any .clar file or directory anywhere on your system.

During development (before installing globally):

cargo run -- check ~/Desktop/mycontract.clar
cargo run -- scan ~/projects/my-stacks-app/contracts
cargo run -- graph ~/projects/my-stacks-app/contracts

Using the release binary directly:

cargo build --release
./target/release/clarus check ~/Desktop/mycontract.clar
./target/release/clarus scan ~/projects/stacks-defi/contracts

After installing globally:

clarus check ~/Desktop/mycontract.clar
clarus scan ~/projects/stacks-defi/contracts
clarus graph ~/projects/stacks-defi/contracts --cross-only

Example Output

Single contract

Clarus β€” Clarity Smart Contract Analyzer
Analyzing: vault.clar
────────────────────────────────────────────────────────────

  [1] [CRITICAL] Reentrancy

    Function :  withdraw
    Location :  Line 8
    Issue    :  'map-set' called after external interaction on line 6
    Fix      :  Move 'map-set' to before the external call on line 6

────────────────────────────────────────────────────────────

  [2] [HIGH] Missing Access Control

    Function :  deposit
    Location :  Line 3
    Issue    :  Public function 'deposit' modifies state without access control check
    Fix      :  Add (asserts! (is-eq tx-sender contract-owner) (err u403)) at the top

────────────────────────────────────────────────────────────

  Result : 2 total findings
           1 critical
           1 high

Multi-contract scan

Clarus β€” Cross-Contract Analysis
Directory: ./contracts
────────────────────────────────────────────────────────────

  [1] [CRITICAL] Cross-Contract Reentrancy

    Contract :  vault
    Function :  withdraw
    Location :  Line 10
    Issue    :  'map-set' mutated after calling token.transfer on line 9
                re-entry possible before state is updated
    Fix      :  Move 'map-set' to before the contract-call? to token.transfer

────────────────────────────────────────────────────────────

  Result : 12 total findings across 3 contracts
           4 critical
           5 high
           2 medium
           1 low

Call graph

Clarus β€” Call Graph
Directory: ./contracts
────────────────────────────────────────────────────────────

  Contracts :  4
  Functions :  11
  Edges     :  9

────────────────────────────────────────────────────────────
  Call Edges:

  governance.execute-proposal  ──▢  token.transfer  (line 22)
  oracle.update-and-notify     ──▢  governance.notify-price-update  (line 33)
  pool.remove-liquidity        ──▢  token.transfer  (line 17)
  staking.unstake              ──▢  token.transfer  (line 21)
  staking.claim-rewards        ──▢  token.transfer  (line 34)

CI/CD Integration

Add Clarus to your GitHub Actions workflow:

- name: Run Clarus
  run: |
    cargo install --path .
    clarus scan contracts/ --strict

The --strict flag causes Clarus to exit with code 1 if any findings are detected, blocking the pull request.

For single file checks:

- name: Run Clarus
  run: |
    cargo install --path .
    clarus check contracts/vault.clar --strict

Vulnerability Patterns

Reentrancy

When a contract performs an external call before updating its own state, a malicious contract can re-enter and exploit the stale state.

Vulnerable:

(define-public (withdraw (amount uint))
  (begin
    (stx-transfer? amount (as-contract tx-sender) tx-sender)
    (map-set balances tx-sender u0)
  ))

Safe (checks-effects-interactions):

(define-public (withdraw (amount uint))
  (begin
    (map-set balances tx-sender u0)
    (try! (stx-transfer? amount (as-contract tx-sender) tx-sender))
    (ok true)
  ))

Cross-Contract Reentrancy

Only detectable with multi-contract analysis. When contract A calls contract B and then updates state, contract B can call back into contract A before the state update completes.

vault.withdraw ──▢ token.transfer ──▢ vault.withdraw (re-entry!)

Clarus traces this path across contracts and names the exact callee involved.

Trait-Based Dispatch

When a function accepts a <trait> parameter, any conforming contract can be passed in at runtime β€” including a malicious one.

Vulnerable:

(define-public (swap (token <ft-trait>) (amount uint))
  (begin
    (contract-call? token transfer amount tx-sender recipient)
    (map-set reserves recipient (+ reserve amount))
  ))

Unchecked Return Values

Calling stx-transfer? or contract-call? without try! means failures are silently ignored.

Vulnerable:

(stx-transfer? amount (as-contract tx-sender) tx-sender)
(map-delete balances tx-sender)

Safe:

(try! (stx-transfer? amount (as-contract tx-sender) tx-sender))
(map-delete balances tx-sender)

Project Structure

clarus/
β”œβ”€β”€ src/
β”‚   β”œβ”€β”€ main.rs                    β€” CLI entry point
β”‚   β”œβ”€β”€ ast.rs                     β€” AST node definitions
β”‚   β”œβ”€β”€ parser.rs                  β€” Clarity S-expression parser
β”‚   β”œβ”€β”€ analyzer.rs                β€” Orchestrates all detectors
β”‚   β”œβ”€β”€ reporter.rs                β€” Terminal and JSON output
β”‚   β”œβ”€β”€ loader.rs                  β€” Multi-file contract loader
β”‚   β”œβ”€β”€ registry.rs                β€” Contract registry for project analysis
β”‚   β”œβ”€β”€ callgraph.rs               β€” Call graph builder and cycle detector
β”‚   └── detector/
β”‚       β”œβ”€β”€ mod.rs
β”‚       β”œβ”€β”€ reentrancy.rs          β€” Single-contract reentrancy
β”‚       β”œβ”€β”€ cross_contract.rs      β€” Cross-contract reentrancy
β”‚       β”œβ”€β”€ trait_dispatch.rs      β€” Trait-based dispatch detection
β”‚       β”œβ”€β”€ integer_overflow.rs    β€” Integer underflow and overflow
β”‚       β”œβ”€β”€ access_control.rs      β€” Missing access control
β”‚       └── unchecked_returns.rs   β€” Unchecked return values
└── tests/
    β”œβ”€β”€ contracts/
    β”‚   β”œβ”€β”€ vulnerable.clar
    β”‚   β”œβ”€β”€ safe.clar
    β”‚   └── bug.clar
    └── multicontract/
    |   β”œβ”€β”€ vault.clar
    |   β”œβ”€β”€ token.clar
    |   β”œβ”€β”€ rewards.clar
    |   β”œβ”€β”€ dex.clar
    |__ multicontract2/
        β”œβ”€β”€ governance.clar
        β”œβ”€β”€ oracle.clar
        β”œβ”€β”€ pool.clar
        β”œβ”€β”€ staking.clar

Roadmap

Phase 1 β€” MVP βœ…

  • Single file analysis
  • Reentrancy detection
  • Integer underflow and overflow
  • Missing access control
  • Unchecked return values
  • CLI with JSON output and strict mode

Phase 2 βœ…

  • Multi-file and multi-contract analysis
  • Cross-contract reentrancy detection
  • Trait-based dynamic dispatch detection
  • Call graph visualization with cycle detection
  • Project-wide severity-sorted reports

Phase 3

  • Clarinet plugin integration
  • GitHub Action
  • VS Code extension
  • Web-based playground
  • Public Clarity vulnerability database

Links to users testing clarus

  1. https://www.loom.com/share/9dae2eb731c7493a97dc5c7c22de0ad4
  2. https://www.loom.com/share/4588dcadfa2e4ce1a662f57863eaf7cb

Contributing

Clarus is open source and welcomes contributions. If you find a Clarity vulnerability pattern that Clarus misses, please open an issue with a minimal reproducing contract.


License

MIT

About

Catches reentrancy bugs, access control issues, and integer underflows in your Stacks smart contracts before attackers do.

Topics

Resources

Stars

3 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages