# Code Review Rust

> Security review of Rust code — where a memory-safe language still has real bugs: `unsafe`, FFI, panics, and the usual injection/logic sinks. Load when reviewing Rust source/PR, on .rs / Cargo.toml in scope, or "review this Rust". Signals: Cargo.toml, unsafe blocks, extern "C", unwrap/expect, actix/axum/rocket handlers.

- Skill: `noorqureshi/code-review-rust` (Agent Skill)
- Install (CLI): `npx skillmds@latest add noorqureshi/code-review-rust`
- Raw SKILL.md: https://api.skillmd.com/api/skills/noorqureshi/code-review-rust/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Security
- Author: NoorQureshi (https://skillmd.com/u/noorqureshi)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/noorqureshi/code-review-rust

---


# Rust source review

## When it applies
Reviewing Rust (a service, a CLI, a library). Safe Rust removes most memory bugs, so the review
focuses on the places safety is opted out of, the ways Rust code still panics or mis-handles input,
and ordinary injection/logic in web handlers.

## Why it works
Rust's guarantees hold only outside `unsafe` and only for memory safety — they don't stop SQL built
by string, a command run via a shell, a `.unwrap()` that panics on attacker input (DoS), or a logic
error. Concentrating on those boundaries finds the real bugs efficiently.

## Sinks & patterns (grep, then reason about the boundary)
- **`unsafe` blocks**: every one is a manual proof obligation — raw pointer deref, `get_unchecked`,
  `slice::from_raw_parts`, `mem::transmute`, uninitialised memory. Check the invariant it assumes.
- **FFI**: `extern "C"`/`bindgen` boundaries — lengths, lifetimes, and NUL handling across the C edge
  (the C side has none of Rust's guarantees; pair with `code-review-cpp`).
- **Panics as DoS**: `unwrap`/`expect`/`panic!`/indexing `v[i]`/`unreachable!` on
  attacker-controlled input; integer `as` casts that truncate; arithmetic overflow (panics in debug,
  wraps in release — both can be bugs). Prefer `?`/checked ops.
- **Injection**: SQL via `format!` into a query instead of parameter binding (sqlx/diesel);
  `std::process::Command` with `sh -c` and concatenated input; `Command` arg vs shell form.
- **Path / SSRF**: user paths joined without canonicalisation + prefix check; HTTP clients (reqwest)
  fetching user URLs.
- **Deserialization / web**: `serde` into types from untrusted data (resource exhaustion, unexpected
  variants); actix/axum/rocket extractors bound to over-broad structs (mass assignment); missing
  auth middleware on state-changing routes.

## Method
1. Run `cargo audit` (known-vuln deps), `cargo clippy`, and `semgrep`; `miri` for `unsafe` UB where feasible.
2. `rg 'unsafe|unwrap\(\)|expect\(|transmute|Command::new|format!\(.*(SELECT|INSERT|UPDATE)'` → review each.
3. Justify every `unsafe` block's invariant; if you can't, that's a finding.
4. Confirm exploitable injection/logic with the matching runtime skill.

## Gotchas
- Idiomatic Rust is genuinely safe — don't invent memory bugs in safe code; spend effort on `unsafe`,
  FFI, panics, deps, and logic.
- Release-mode integer overflow wraps silently — a `checked_*`/`saturating_*` audit matters for
  size/index math.
- `cargo audit` flags vulnerable crates you'd never see by reading — always run it.

## References
Rustonomicon (unsafe); RustSec advisory DB / cargo-audit; Clippy lint set; Secure Rust Guidelines (ANSSI).

