Files
Andrea Cappa (zi0Black) cfe5d7b161 Rust review plugin (#178)
* rust-review: add Rust security review plugin

Add the rust-review plugin: a comprehensive Rust security review skill
with clustered finders covering memory safety, concurrency/data races,
panic-induced DoS, FFI/cross-language boundaries, error handling,
resource handling, async runtime, and static hygiene.

Includes worker, dedup-judge, fp-judge, and planner agents, SARIF
generation with rule descriptions and regression tests, deterministic
cluster chunking, and Codex skills mapping. Versioned at 1.0.0 and
registered in the marketplace, CODEOWNERS, and root README.

* c-review: backport rust-review protocol fixes and planner chunking

Port the language-agnostic fixes made while building rust-review (which
was ported from c-review) back into c-review:

- worker/fp-judge: force findings, coverage gate, and REPORT.md to disk
  via Write instead of returning content in the reply (orchestrator
  context-bloat hardening); add a pre-complete file-existence check.
- worker: move the cache-primer block below the normal self-check and
  pre-work budget so a non-primer worker does not start under a global
  "no tool calls" rule.
- planner: add --max-passes-per-worker (default 4) with deterministic
  split_oversized_clusters chunking; skill passes the flag and documents
  the chunked-subset worker rule.
- scripts: add test_split.py and test_generate_sarif.py regression
  tests. The SARIF test caught a missing RULE_DESCRIPTIONS entry for
  uninitialized-data, now added.

Bump c-review to 1.2.0.

* c-review/rust-review: validate artifacts, index-aware SARIF, protocol cleanups

- Add validate_artifacts.py (+ tests) to both plugins to check worker
  shard, coverage, and finding files before accepting completions.
- generate_sarif.py now reads the canonical findings-index.txt when
  present, falling back to findings/*.md only if the index is absent.
- Merge the worker step-6 verification paragraphs and drop orchestrator
  -internal Phase 7 / plan.json jargon in favor of worker-facing stakes.
- Tighten uninitialized-read-finder guidance: primitive integers still
  require initialization.

* rust-review/c-review: per-cluster max_passes_per_worker override

Lets output-heavy clusters declare a smaller manifest-level
max_passes_per_worker so each expensive pass group gets its own worker,
validated by a single shared cluster_max_passes_per_worker helper and
honored by split_oversized_clusters via an explicit override (0 is
rejected rather than silently falling back to the global cap). rust-review
opts in concurrency-locking and recursion-dos; c-review ports the
capability for parity. validate_artifacts now accepts grouped or repeated
--claimed-count values.

* rust-review: broaden bug-class coverage with capability-gated clusters

Add layout-safety, input-os-safety, and info-disclosure clusters behind new
has_packed_repr / has_fs_io capability gates so packed-repr, path, and
pointer-exposure passes only run where they apply, and gate unsafe-only passes
behind has_unsafe to cut noise on safe crates.

Extend existing clusters with new bug classes: RefCell double-borrow panics,
unflushed BufWriter, string-comparison bypasses, serialize_struct mismatches,
nondeterminism, in-collection key mutation, and destructor-skip cleanup leaks.

Fix detector regexes that missed or over-matched real Rust (packed-field
borrows, RefCell try_borrow_mut, HashMap substrings, path push, packed inner
attrs, fs/path probes) and add a regression test pinning them to snippets.

* fix dedup

* safety-net check for REPORT.md

* on-disk data -> shards reconciliation

* on-disk data -> shards reconciliation - v2

* ls -> glob

* memory-safety gate

* path validation

* fix numbers/counting

* rm PACKEDREF from FFI cluster prompt, it is in layout-safety

* fix unsafe-boundary count

* minor fixes for prompts

* do not filter unknown-severity findings, just mark them as such

* fix minor behavior changes in worker

* Correctness:
- generate_sarif: clamp startLine >=1 (`:0` produced schema-invalid SARIF)
- generate_sarif: don't drop a judged survivor with blank severity
- dedup-judge: Tier-2 carry-forward so a primary can't be demoted/orphaned
- dedup-judge: crash-recovery unions shards with findings/*.md (empty-shard trap)

Robustness:
- generate_sarif: skip frontmatter-less files; add originalUriBaseIds

Contracts:
- SKILL: gate dedup-judge before fp-judge (prevent concurrent-spawn race)
- worker: verbatim coverage cells; sub_prompt_paths omitted-not-empty;
  skip_subclasses reserved; Codebase comma format

* improve prompts regexes, add missing deconflictions

* prompt factual fixes

* fix dozen of small prompt inconsistencies and add missing sections

* more prompt fixes, fix retry guard in SKILL, small fixes in agents

* dozen more small fixes

* final regex fixes

* fixes from rust to c-review

* agents cannot use write tool for reports (strange cc limitation) - bypass via bash

* spawnings agents is capped to 20 - explicit handling for that

* fix glob -> read (glob is blocked for agents that has also bash)

* fix regex patterns to work with grep

* soften output requirements - they were violated anyway

* consolidated clusters are no longer chunked — one worker owns the whole cluster, builds its shared Phase-A inventory once, and runs every phase

* fix judge finding counting and low-severity guidance

* fix metadata

* small fix for skipped findings

* Carry forward guard for `also_known_as` bucket

* Gracefully handle parse_frontmatter error

* Extend has_ffi coverage

* Broader gate for has_concurrency

* Update FFI-safe layout regex to support C, C+packed, and C+u32 in unsafe-boundary and dyn-trait-ffi-finder prompts

* Small refine of regex patterns

* Improve regex patterns for recursive type detection to include Mutex and RwLock

* rm global .codex/rust-review

* backport fixes to c-review

* merge changes

* Backport SARIF merge-survivor + malformed-frontmatter guards to c-review, mark missing locations, fix prompt-regex test extractor, and harden planner/validator scripts across both review plugins

* fix pytest

* fix global gitignore, adds / and ruff_cache

* small fixes from pr-review

* small fixes from pr-review - 2

* fix copilot finding

---------

Co-authored-by: GrosQuildu <e2.8a.95@gmail.com>
2026-06-30 11:14:46 -04:00
..
2026-06-30 11:14:46 -04:00
2026-06-30 11:14:46 -04:00
2026-06-30 11:14:46 -04:00
2026-06-30 11:14:46 -04:00
2026-06-30 11:14:46 -04:00

rust-review

Rust security code review plugin. Bug-class coverage comes from empirical bug-shape research across 245 memory-corruption, 177 unsound safe-API, 150 denial-of-service, and 60 thread-safety advisories in the RustSec Advisory Database (1,078 entries) and audits. Orchestration matches c-review.

Usage

Invoke with /rust-review:rust-review. The skill will prompt for:

  • Threat model (REMOTE / LOCAL_UNPRIVILEGED / BOTH)
  • Worker model (haiku / sonnet / opus)
  • Severity filter (all / medium / high)
  • Scope subpath (optional — defaults to whole repo)

Findings + SARIF are written to $(pwd)/.rust-review-results/<iso-timestamp>/.

Overview

Inputs (AskUserQuestion): threat model, scope subpath (optional), worker model, severity filter.

From these inputs the orchestrator detects Rust capability flags (has_unsafe, has_ffi, has_concurrency, has_async, has_packed_repr, has_fs_io) over the scope and selects clusters from prompts/clusters/manifest.json. Each cluster groups related bug classes anchored on a shared mental model and runs as one parallel worker.

The planner caps each non-consolidated worker at four passes, splitting larger clusters into -1/-2/… chunks; output-heavy clusters can declare a smaller max_passes_per_worker in the manifest (today recursion-dos runs one pass per worker). Consolidated clusters (unsafe-boundary, concurrency-locking) are never chunked — one worker owns the whole cluster so its shared Phase-A inventory is built once and grounds every phase.

Always-on clusters:

  • unsafe-boundary (consolidated) — Unsafe Reachability Analysis (URAPI), transmute misuse, pointer-cast hazards via as (PTRCAST), raw-pointer arithmetic, #[repr(C)] layout, enum discriminant and niche validity (ENUMUB), // SAFETY: documentation rules, debug_assert!-guarded safety invariants.
  • panic-dos — resource exhaustion DoS (RESEXHAUST, P0), unwrap/expect on untrusted input, arithmetic overflow, reachable unreachable!/assert!, vector OOB indexing, non-char-boundary str slicing panics (STRSLICE), reachable RefCell double-borrow panics (REFCELLPANIC).
  • recursion-dos — stack-overflow aborts (uncatchable, distinct from panics) on recursive types: unbounded deserialization depth (serde_yaml/toml/ron/custom Deserialize), recursive Display/Debug/Serialize on attacker-shaped values, implicit Drop of Box<Self>-style chains.
  • error-handling — discarded Results, panics inside Drop, lossy From/Into and as casts, lossy UTF-8 / OS-string / path conversions (LOSSYSTR), unflushed BufWriter swallowing write errors (BUFFLUSH).
  • logic-correctnessOrd/Eq/Hash invariant violations, hostile generic trait impls, closure-panic across unsafe scaffolding, NaN/Inf edge cases, partial-match/case string comparisons (STRCMP), serialize_struct field-count mismatches (SERFIELDS), nondeterminism in replicated state (NONDET), in-collection key mutation (KEYMUT). The hostile-trait (TRAITADV) and closure-panic (CLOSUREPANIC) passes require has_unsafe.
  • static-hygiene — Cargo lint config, MSRV, deprecated APIs (mem::uninitialized).
  • resource-handling — raw file-descriptor double-close and leak (RAWFD), Drop-skipping cleanup via process::exit/mem::forget (DROPSKIP).
  • info-disclosure — pointer/address exposure that defeats ASLR (PTREXPOSE).

Conditional clusters:

  • memory-safety (has_unsafe) — UAF via dangled raw pointer, double-free via ptr::read, invalid-free via assignment-to-uninit, uninitialized-read via premature assume_init, Vec::set_len without slot init (SETLEN), buffer overflow via safe→unsafe index propagation, union variant misread, panic-unsafe custom container drop (PANICUNWIND). The whole cluster is gated on has_unsafe (every memory-safety bug class requires unsafe), so it is omitted entirely for pure safe-Rust crates.
  • concurrency-locking (has_concurrency, consolidated) — MutexGuard double-lock from lexical scope, ABBA ordering, Condvar wait without notifier, channel starvation, Once::call_once reentrancy, signal-handler / callback reentrancy.
  • concurrency-data-race (has_concurrency) — non-atomic atomic sequences, unsafe impl Sync over interior mutability, missing Send/Sync bounds, cross-process shared-memory races, unsynchronized static mut (STATICMUT). The unsafe-sync-impl (UNSAFESYNC) and static-mut (STATICMUT) passes require has_unsafe.
  • ffi-cross-language (has_ffi) — CString::as_ptr dangling, ABI mismatch, #[repr(C)] padding leak, opaque-pointer ownership confusion, FFI-owned-memory drop mismatches, Rust closures across extern "C" without catch_unwind, dyn Trait fat pointers crossing FFI.
  • layout-safety (has_packed_repr) — unaligned references to #[repr(packed)] / wire-format struct fields (PACKEDREF).
  • input-os-safety (has_fs_io) — PathBuf::join path traversal (PATHJOIN), filesystem TOCTOU (TOCTOU).
  • async-runtime (has_async) — blocking calls in async, cancellation-unsafe .await sequences, tokio::select! branch bias.

Same orchestration as c-review: workers spawn foreground (one message per wave of ≤16 workers, after an optional cache primer), write markdown-with-YAML-frontmatter finding files, then a dedup-judge merges duplicates, then an fp-judge assigns fp_verdict / severity / attack_vector / exploitability. A report safety net then runs: SARIF is regenerated unconditionally, and the orchestrator writes REPORT.md itself if the fp-judge failed to.

Architecture

coordinator: write context.md → build_run_plan.py → TaskCreate × M
          → spawn primer (foreground) → spawn M workers (parallel)
          → classify Phase-7 outcomes + write findings-index.txt
          → dedup-judge → fp-judge → report safety net (SARIF + REPORT.md) → return REPORT.md
Subagent type Purpose Tool set
rust-review:rust-review-worker Run assigned cluster, write findings Read, Write, Edit, Bash
rust-review:rust-review-dedup-judge Merge duplicates (runs first) Read, Write, Edit, Glob
rust-review:rust-review-fp-judge FP + severity + final reports (runs second) Read, Write, Edit, Bash

In current Claude Code an agent granted Bash is not also granted the dedicated Glob/Grep tools (the harness expects find/grep/rg via Bash). So the worker and fp-judge search and resolve paths with Read/Bash, running the ripgrep-syntax prompt seeds through rg; only the dedup-judge — which holds no Bash — uses Glob.

Output directory layout

Default: $(pwd)/.rust-review-results/<iso-timestamp>/. Contains:

  • context.md — resolved threat model, severity filter, scope, capability flags, Cargo manifest status
  • plan.json — selected clusters + rendered worker spawn prompts (one per parallel worker)
  • worker-prompts/ — verbatim spawn prompts, one per worker (+ optional cache-primer.txt)
  • findings/ — one markdown file per finding (<PREFIX>-NNN.md with YAML frontmatter)
  • findings-index.d/ — per-worker shards listing finding paths (survive an orchestrator crash)
  • findings-index.txt — canonical sorted list of every finding file on disk (reconciled against the shards)
  • run-summary.md — worker outcome table, retry/abort state, judge status
  • dedup-summary.md — Tier 13 merge + Tier 4 related summary
  • fp-summary.md — verdict counts and per-primary verdict table
  • REPORT.md — human-readable final report grouped by severity, filtered per severity_filter
  • REPORT.sarif — SARIF 2.1.0 export, idempotent (full overwrite), always written

Not for

  • Pure C / C++ codebases — use c-review instead.
  • Smart contracts (Solana, NEAR, Ink!) — use solana-vulnerability-scanner or the contract-specific skill.
  • Kernel-mode Rust without userspace allocator — coverage is incomplete; flag as advisory only.

References

Authors