# I Built an Open-Source Security Linter for Sui Move — Here's What It Found on Real Protocols

I've been building [move-test-gen](https://github.com/mehvetero/move-test-gen), a security tool for Sui Move smart contracts. It started as a test generator and grew into a coverage checker with mutation testing and a security linter. This post covers what it does, what it found when I pointed it at production DeFi protocols, and how to use it in your own CI.

## The Problem

Sui's built-in linter has 6 rules — all focused on object handling (self-transfer, share-owned, etc.). Nothing about arithmetic overflow, missing access control, or unsafe type casting. [MoveScanner](https://movebit.xyz/) exists but is closed-source and commercial.

If you're writing Move and want a `slither`\-equivalent that checks security patterns on every PR — there isn't one. Or there wasn't.

## What move-test-gen Does

Three layers, each independent:

**Layer 1 — Assert pairing.** Scans every `assert!` and `abort` in your source modules, every `#[expected_failure]` in your tests, and tells you which abort paths have no test coverage. Runs in seconds, needs only Node.js.

**Layer 2 — Mutation testing.** Injects deterministic bugs (flip a `<` to `>=`, comment out an `assert!`) and checks whether your test suite catches them. If a mutant survives, the test that should have caught it is too weak. 7 operators, exhaustive mode. Needs `sui` CLI.

**Layer 3 — Security lint.** 4 rules that scan for common vulnerability patterns:

| Rule | Severity | What it catches |
| --- | --- | --- |
| **MOV-001** | HIGH | `public fun` with `&mut` but no capability, key, or witness — missing access control |
| **MOV-002** | HIGH | `u64 * u64` without `u128` promotion — arithmetic overflow risk |
| **MOV-003** | MEDIUM | Division by a variable without prior zero-check |
| **MOV-004** | MEDIUM | `(expr as u64)` downcast from u128/u256 without overflow assert |

MOV-002 and MOV-004 use a lightweight Move parser (`scripts/move-parser.mjs`) that tracks variable types through declarations, casts, and naming conventions. So it knows that `numerator1` assigned from `liquidity_u256 << RESOLUTION` is u256 — no false positive on the multiplication.

## What It Found on Real Protocols

I ran the linter against four production Sui DeFi protocols:

### Kriya DEX — 1 finding

```plaintext
🔴 HIGH  spot_dex.move:187  [MOV-001]
  public function modifies state without capability check:
  `update_pool` takes &mut but no capability parameter
```

This is the same access control gap I [reported manually](https://github.com/efficacy-finance/kriya-dex-interface/issues/2) in a 6-finding security review. The rule catches it in under a second on 3 source files.

### Scallop Lending (172 files) — 2 findings

```plaintext
🔴 HIGH  liquidation_evaluator.move:143  [MOV-002]
  `LIQUIDATION_CAP_DIVISOR * debt_price_raw` — overflow risk

🟡 MEDIUM  limiter.move:189  [MOV-004]
  `(i as u64)` — downcast without overflow check
```

Initial run hit 82 false positives. Generic functions `fun name<X, Y>()` were invisible to the regex, test modules were scanned, Sui-specific patterns like `Witness<T>` and `Version` params weren't recognized. 9 commits to fix all of that. After hardening: these 2 legitimate findings, zero false positives on production code.

### Bucket Protocol — Fix PR Submitted

MOV-002 flagged three sites in `compute_collateral_value_to_buck` and `compute_buck_value_to_collateral` where `mul_factor` (which safely promotes to u128 internally) is followed by a raw u64 multiplication that undoes the protection. I [submitted a fix PR](https://github.com/Bucket-Protocol/v1-core/pull/12) — three lines changed, `u128` intermediate cast.

The overflow doesn't affect current deployments (SUI is 9 decimals = BUCK decimals, so the scaling factor is 1), but adding a collateral type with higher decimals would break liquidation, redemption, and health checks. The [OtterSec V1 audit](https://github.com/Bucket-Protocol/Audit) caught a related conversion issue but not the overflow in the fix itself.

## How to Use It

### One-liner (no install)

```bash
npx mehvetero/move-test-gen sources tests --lint
```

### In CI — GitHub Action

```yaml
# .github/workflows/move-security.yml
name: move-security
on: [pull_request]
jobs:
  lint:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: mehvetero/move-test-gen@v1.3.0
        with:
          sources: sources
          tests: tests
          lint: 'true'
```

Layer 1 + lint runs in seconds with zero dependencies. For mutation testing, add `mutate: 'true'` and install `sui` — there's a [nightly schedule example](https://github.com/mehvetero/move-test-gen/blob/main/examples/workflows/nightly-mutation.yml) in the repo.

**Marketplace:** [move-test-gen coverage check](https://github.com/marketplace/actions/move-test-gen-coverage-check)

### As an Agent Skill

The tool is also an [agentskills](https://github.com/agentskills/agentskills)\-compatible skill. Install:

```bash
npx skills add mehvetero/move-test-gen
```

Then ask your coding agent: *"Generate edge-case tests for sources/vault.move"* — it produces `#[test]` and `#[expected_failure]` functions covering boundary values, arithmetic overflows, access control violations, and economic edge cases.

**Agent Skill listing:** [agentskills Discussion #451](https://github.com/agentskills/agentskills/discussions/451)

## How It Compares

|  | move-test-gen | Sui built-in linter | MoveScanner | Aptos move-mutation-tools |
| --- | --- | --- | --- | --- |
| **Open source** | Yes | Yes | No | Yes |
| **Security rules** | 4 (MOV-001~004) | 0 (object-handling only) | Many (undisclosed) | 0 |
| **Mutation testing** | Yes (7 operators) | No | No | Yes (Aptos only) |
| **Sui support** | Yes | Yes | Yes | No (Aptos only) |
| **CI integration** | GitHub Action | CLI flag | Commercial | CLI |
| **Parser** | Lightweight (function-level types) | Full compiler | Full compiler | Full compiler |
| **Price** | Free | Free | Paid | Free |

The gap this fills: there's no open-source tool that does security-focused static analysis on Sui Move. The built-in linter handles object patterns. MoveScanner is commercial. Aptos has mutation tools but they don't work on Sui. This tool covers the middle ground — regex + parser based, not compiler-grade, but catches the patterns that actually show up in exploits.

## Measured, Not Trusted

The tool has an eval lab (`eval/`) with 5 campaigns, 13 scenarios, and 47 rounds of testing. Frozen prompt templates, retirement-by-saturation protocol, dated records. The methodology is borrowed from [TheColliery](https://github.com/TheColliery) — full lineage in the [RESULTS.md](https://github.com/mehvetero/move-test-gen/blob/main/eval/RESULTS.md).

The lint rules were validated against Kriya DEX, Scallop (172 files), Bucket Protocol, and Turbos CLMM. Every false positive fix gets a selftest pin before it ships — 11 regression cases, all enforced in CI.

## What's Next

*   More rules — clock staleness, upgrade policy, shared object access patterns
    
*   Parser improvements — tracking types through function calls, not just declarations
    
*   More protocol scans — each scan either validates the rules or finds a new pattern to handle
    
*   Contribution: if you have a Move security pattern that should be a rule, [open an issue](https://github.com/mehvetero/move-test-gen/issues) or PR a `rules/mov-NNN-*.mjs` file
    

* * *

**Repo:** [github.com/mehvetero/move-test-gen](https://github.com/mehvetero/move-test-gen) **Marketplace:** [move-test-gen coverage check](https://github.com/marketplace/actions/move-test-gen-coverage-check)
