<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: mehvetero</title>
    <description>The latest articles on DEV Community by mehvetero (@mehvetero).</description>
    <link>https://dev.to/mehvetero</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4045014%2Fe14594a2-4649-4055-9343-47295d21cbc7.jpg</url>
      <title>DEV Community: mehvetero</title>
      <link>https://dev.to/mehvetero</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/mehvetero"/>
    <language>en</language>
    <item>
      <title>I Built an Open-Source Security Linter for Sui Move — Here's What It Found on Real Protocols</title>
      <dc:creator>mehvetero</dc:creator>
      <pubDate>Fri, 24 Jul 2026 14:30:38 +0000</pubDate>
      <link>https://dev.to/mehvetero/i-built-an-open-source-security-linter-for-sui-move-heres-what-it-found-on-real-protocols-230o</link>
      <guid>https://dev.to/mehvetero/i-built-an-open-source-security-linter-for-sui-move-heres-what-it-found-on-real-protocols-230o</guid>
      <description>&lt;p&gt;I've been building &lt;a href="https://github.com/mehvetero/move-test-gen" rel="noopener noreferrer"&gt;move-test-gen&lt;/a&gt;, 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.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem
&lt;/h2&gt;

&lt;p&gt;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. &lt;a href="https://movebit.xyz/" rel="noopener noreferrer"&gt;MoveScanner&lt;/a&gt; exists but is closed-source and commercial.&lt;/p&gt;

&lt;p&gt;If you're writing Move and want a &lt;code&gt;slither&lt;/code&gt;-equivalent that checks security patterns on every PR — there isn't one. Or there wasn't.&lt;/p&gt;

&lt;h2&gt;
  
  
  What move-test-gen Does
&lt;/h2&gt;

&lt;p&gt;Three layers, each independent:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Layer 1 — Assert pairing.&lt;/strong&gt; Scans every &lt;code&gt;assert!&lt;/code&gt; and &lt;code&gt;abort&lt;/code&gt; in your source modules, every &lt;code&gt;#[expected_failure]&lt;/code&gt; in your tests, and tells you which abort paths have no test coverage. Runs in seconds, needs only Node.js.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Layer 2 — Mutation testing.&lt;/strong&gt; Injects deterministic bugs (flip a &lt;code&gt;&amp;lt;&lt;/code&gt; to &lt;code&gt;&amp;gt;=&lt;/code&gt;, comment out an &lt;code&gt;assert!&lt;/code&gt;) 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 &lt;code&gt;sui&lt;/code&gt; CLI.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Layer 3 — Security lint.&lt;/strong&gt; 4 rules that scan for common vulnerability patterns:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Rule&lt;/th&gt;
&lt;th&gt;Severity&lt;/th&gt;
&lt;th&gt;What it catches&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;MOV-001&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;HIGH&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;public fun&lt;/code&gt; with &lt;code&gt;&amp;amp;mut&lt;/code&gt; but no capability, key, or witness — missing access control&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;MOV-002&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;HIGH&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;u64 * u64&lt;/code&gt; without &lt;code&gt;u128&lt;/code&gt; promotion — arithmetic overflow risk&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;MOV-003&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;MEDIUM&lt;/td&gt;
&lt;td&gt;Division by a variable without prior zero-check&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;MOV-004&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;MEDIUM&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;(expr as u64)&lt;/code&gt; downcast from u128/u256 without overflow assert&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

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

&lt;h2&gt;
  
  
  What It Found on Real Protocols
&lt;/h2&gt;

&lt;p&gt;I ran the linter against four production Sui DeFi protocols:&lt;/p&gt;

&lt;h3&gt;
  
  
  Kriya DEX — 1 finding
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;🔴 HIGH  spot_dex.move:187  [MOV-001]
  public function modifies state without capability check:
  `update_pool` takes &amp;amp;mut but no capability parameter
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the same access control gap I &lt;a href="https://github.com/efficacy-finance/kriya-dex-interface/issues/2" rel="noopener noreferrer"&gt;reported manually&lt;/a&gt; in a 6-finding security review. The rule catches it in under a second on 3 source files.&lt;/p&gt;

&lt;h3&gt;
  
  
  Scallop Lending (172 files) — 2 findings
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;🔴 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
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;h3&gt;
  
  
  Bucket Protocol — Fix PR Submitted
&lt;/h3&gt;

&lt;p&gt;MOV-002 flagged three sites in &lt;code&gt;compute_collateral_value_to_buck&lt;/code&gt; and &lt;code&gt;compute_buck_value_to_collateral&lt;/code&gt; where &lt;code&gt;mul_factor&lt;/code&gt; (which safely promotes to u128 internally) is followed by a raw u64 multiplication that undoes the protection. I &lt;a href="https://github.com/Bucket-Protocol/v1-core/pull/12" rel="noopener noreferrer"&gt;submitted a fix PR&lt;/a&gt; — three lines changed, &lt;code&gt;u128&lt;/code&gt; intermediate cast.&lt;/p&gt;

&lt;p&gt;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 &lt;a href="https://github.com/Bucket-Protocol/Audit" rel="noopener noreferrer"&gt;OtterSec V1 audit&lt;/a&gt; caught a related conversion issue but not the overflow in the fix itself.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to Use It
&lt;/h2&gt;

&lt;h3&gt;
  
  
  One-liner (no install)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx mehvetero/move-test-gen sources tests &lt;span class="nt"&gt;--lint&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  In CI — GitHub Action
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# .github/workflows/move-security.yml&lt;/span&gt;
&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;move-security&lt;/span&gt;
&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;pull_request&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;lint&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v4&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;mehvetero/move-test-gen@v1.3.0&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;sources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;sources&lt;/span&gt;
          &lt;span class="na"&gt;tests&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;tests&lt;/span&gt;
          &lt;span class="na"&gt;lint&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;true'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Layer 1 + lint runs in seconds with zero dependencies. For mutation testing, add &lt;code&gt;mutate: 'true'&lt;/code&gt; and install &lt;code&gt;sui&lt;/code&gt; — there's a &lt;a href="https://github.com/mehvetero/move-test-gen/blob/main/examples/workflows/nightly-mutation.yml" rel="noopener noreferrer"&gt;nightly schedule example&lt;/a&gt; in the repo.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Marketplace:&lt;/strong&gt; &lt;a href="https://github.com/marketplace/actions/move-test-gen-coverage-check" rel="noopener noreferrer"&gt;move-test-gen coverage check&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  As an Agent Skill
&lt;/h3&gt;

&lt;p&gt;The tool is also an &lt;a href="https://github.com/agentskills/agentskills" rel="noopener noreferrer"&gt;agentskills&lt;/a&gt;-compatible skill. Install:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx skills add mehvetero/move-test-gen
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;&lt;strong&gt;Agent Skill listing:&lt;/strong&gt; &lt;a href="https://github.com/agentskills/agentskills/discussions/451" rel="noopener noreferrer"&gt;agentskills Discussion #451&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  How It Compares
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;move-test-gen&lt;/th&gt;
&lt;th&gt;Sui built-in linter&lt;/th&gt;
&lt;th&gt;MoveScanner&lt;/th&gt;
&lt;th&gt;Aptos move-mutation-tools&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Open source&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Security rules&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;4 (MOV-001~004)&lt;/td&gt;
&lt;td&gt;0 (object-handling only)&lt;/td&gt;
&lt;td&gt;Many (undisclosed)&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Mutation testing&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Yes (7 operators)&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes (Aptos only)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Sui support&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No (Aptos only)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;CI integration&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;GitHub Action&lt;/td&gt;
&lt;td&gt;CLI flag&lt;/td&gt;
&lt;td&gt;Commercial&lt;/td&gt;
&lt;td&gt;CLI&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Parser&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Lightweight (function-level types)&lt;/td&gt;
&lt;td&gt;Full compiler&lt;/td&gt;
&lt;td&gt;Full compiler&lt;/td&gt;
&lt;td&gt;Full compiler&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Price&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Free&lt;/td&gt;
&lt;td&gt;Free&lt;/td&gt;
&lt;td&gt;Paid&lt;/td&gt;
&lt;td&gt;Free&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;h2&gt;
  
  
  Measured, Not Trusted
&lt;/h2&gt;

&lt;p&gt;The tool has an eval lab (&lt;code&gt;eval/&lt;/code&gt;) with 5 campaigns, 13 scenarios, and 47 rounds of testing. Frozen prompt templates, retirement-by-saturation protocol, dated records. The methodology is borrowed from &lt;a href="https://github.com/TheColliery" rel="noopener noreferrer"&gt;TheColliery&lt;/a&gt; — full lineage in the &lt;a href="https://github.com/mehvetero/move-test-gen/blob/main/eval/RESULTS.md" rel="noopener noreferrer"&gt;RESULTS.md&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's Next
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;More rules — clock staleness, upgrade policy, shared object access patterns&lt;/li&gt;
&lt;li&gt;Parser improvements — tracking types through function calls, not just declarations&lt;/li&gt;
&lt;li&gt;More protocol scans — each scan either validates the rules or finds a new pattern to handle&lt;/li&gt;
&lt;li&gt;Contribution: if you have a Move security pattern that should be a rule, &lt;a href="https://github.com/mehvetero/move-test-gen/issues" rel="noopener noreferrer"&gt;open an issue&lt;/a&gt; or PR a &lt;code&gt;rules/mov-NNN-*.mjs&lt;/code&gt; file&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;Repo:&lt;/strong&gt; &lt;a href="https://github.com/mehvetero/move-test-gen" rel="noopener noreferrer"&gt;github.com/mehvetero/move-test-gen&lt;/a&gt;&lt;br&gt;
&lt;strong&gt;Marketplace:&lt;/strong&gt; &lt;a href="https://github.com/marketplace/actions/move-test-gen-coverage-check" rel="noopener noreferrer"&gt;move-test-gen coverage check&lt;/a&gt;&lt;br&gt;
&lt;strong&gt;Blog:&lt;/strong&gt; &lt;a href="https://mehvetero.com" rel="noopener noreferrer"&gt;mehvetero.com&lt;/a&gt;&lt;/p&gt;

</description>
      <category>security</category>
      <category>testing</category>
      <category>tools</category>
      <category>move</category>
    </item>
  </channel>
</rss>
