Skip to main content

rust-panic-audit

Standalone Rust audit for panic and brutal-exit hazards, including panic macros, production `.unwrap()` / `.expect()`, process exits, overflow, and indexing. Use for a dedicated panic check; `/rust-review-all` uses KMS Caveman and does not invoke this skill again.

Source facts

Repository
cosmian/kms
Last source activity
October 20, 2026 at 16:18
Detected SKILL.md language
English
Stars
367
Forks
43

Install options

The review-first prompt is selected by default. You can switch to a direct command or download a local copy.

Review the source files

Read SKILL.md and any companion files shown by SkillsMP before deciding whether to install.

Showing SKILL.md

SKILL.md
Source instructions · Read-only preview
name
rust-panic-audit
description
Standalone Rust audit for panic and brutal-exit hazards, including panic macros, production `.unwrap()` / `.expect()`, process exits, overflow, and indexing. Use for a dedicated panic check; `/rust-review-all` uses KMS Caveman and does not invoke this skill again.
# Rust Panic & Brutal-Exit Audit Detect every construct in Rust code that can cause a panic, abort, or brutal process exit outside of `#[cfg(test)]` blocks. Produces a report in `./review-YYYY-MM-DD/rust-panic-audit.md`. **Usage**: `/rust-panic-audit` (diff scope) or `/rust-panic-audit crate/server/src/` --- ## Step 0 — Create output directory ```bash mkdir -p ./review-$(date +%F) REPORT="./review-$(date +%F)/rust-panic-audit.md" echo "# Rust Panic & Brutal-Exit Audit" > "${REPORT}" echo "Generated: $(date -u +%Y-%m-%dT%H:%M:%SZ)" >> "${REPORT}" echo "" >> "${REPORT}" ``` --- ## Step 1 — Determine scope If a path was provided, scan only that path. Otherwise default to recently changed Rust files: ```bash SCOPE=$(git diff --name-only origin/develop...HEAD 2>/dev/null || git diff --name-only HEAD~1) # Fall back to full codebase if diff is empty [ -z "${SCOPE}" ] && SCOPE="crate/" ``` --- ## Step 2 — Mechanical scan (ripgrep passes) Run each grep pass; collect file + line number + snippet. ### Pass A — Direct panic macros ```bash rg -n '(^|[^/])\bpanic!\s*(' --type rust ${SCOPE} ``` ### Pass B — Placeholder macros (always panic at runtime) ```bash rg -n '\b(todo!|unimplemented!)\s*(' --type rust ${SCOPE} ``` ### Pass C — `.unwrap()` in production code ```bash rg -n '\.unwrap\(\)' --type rust ${SCOPE} ``` Filter out lines inside `#[cfg(test)]` modules (manual review for context). ### Pass D — `.expect(...)` in production code ```bash rg -n '\.expect\(' --type rust ${SCOPE} ``` ### Pass E — `unreachable!` outside debug-only guards ```bash rg -n '\bunreachable!\s*(' --type rust ${SCOPE} ``` Flag each hit unless it is inside `#[cfg(debug_assertions)]` or `#[cfg(test)]`. ### Pass F — Process exit / abort ```bash rg -n '\bstd::process::(exit|abort)\b|\bprocess::(exit|abort)\b' --type rust ${SCOPE} ``` ### Pass G — Integer arithmetic without overflow protection on untrusted data ```bash # Look for direct + / * on integer types in path-handling, length, or index contexts rg -n '\b(len|index|offset|size|count)\s*[+\*]\s' --type rust ${SCOPE} ``` ### Pass H — Direct slice indexing on non-literal indices ```bash rg -n '\[\s*[a-z_][a-zA-Z0-9_]*\s*\]' --type rust ${SCOPE} \ | grep -v 'cfg\|macro\|derive\|//\|test' ``` ### Optional: shrink wide-scope hit lists When `omp-jev-tools` is installed (OMP only — not available in VS Code Copilot Chat, Copilot CLI, or Copilot Cloud Agent) and Pass C, G, or H returns more hits than fit a manual pass, rerank each hit with `jev_rerank`: `query` is the pass's own heading text (e.g. "Direct slice indexing on non-literal indices"), one candidate per hit (`id` = `path:line`, `text` = the matched line plus two lines of context), keep only the top half by relevance before Step 3. Never apply this to Pass A, B, D, E, or F — every hit from those passes must still reach Step 3. Without the tool, review every hit from every pass. --- ## Step 3 — Classify findings For each finding, assign a severity: | Severity | Criteria | |----------|----------| | **CRITICAL** | `panic!`, `todo!`, `unimplemented!`, `process::exit`, or `process::abort` in production code outside the documented `fn main()` exception | | **HIGH** | Any `.unwrap()` or `.expect()` confirmed in production code; this repository forbids both | | **MEDIUM** | `unreachable!` outside debug guards, unchecked slice indexing on untrusted input | | **INFO** | `.expect()` in test code (allowed but must have a meaningful message) | --- ## Step 4 — Filter allowed occurrences The following are **allowed** and should be marked `[OK]`: - Any line inside a `#[cfg(test)]` module. - `unreachable!()` inside a `#[cfg(debug_assertions)]` block with a `// PANIC:` comment. - `process::exit(0)` inside `fn main()` only, with a `// PANIC:` comment. --- ## Step 5b — Scan for all lint suppressions added in this diff Covers `#[allow(...)]` and `#[expect(...)]` for **every lint**, not just Clippy. ```bash # Single pass: catches all new #[allow(...)] and #[expect(...)] on any lint git diff origin/develop...HEAD -- '*.rs' \ | grep '^+' \ | grep -v '^+++' \ | grep -E '#\[(allow|expect)\(' \ | grep -v '//.*tracked\|//.*issue\|//.*#[0-9]\|//.*false.positive' \ | tee /tmp/lint-suppressions.txt cat /tmp/lint-suppressions.txt ``` Classify each hit by severity: | Pattern found | Severity | |---------------|----------| | `#[allow(warnings)]` | **CRITICAL** — unconditionally forbidden, no exception | | `#[allow(unused_imports)]` | **CRITICAL** — remove the import | | `#[allow(dead_code)]` / `#[expect(dead_code)]` | **CRITICAL** — remove the dead item | | `#[allow(unused_*)]` / `#[expect(unused_*)]` | **HIGH** — remove or rename to `_` | | `#[allow(deprecated)]` / `#[expect(deprecated)]` | **HIGH** — migrate off the deprecated API | | `#[allow(clippy::*)]` / `#[expect(clippy::*)]` without justification | **HIGH** — fix the lint | | `#[allow(clippy::*)]` / `#[expect(clippy::*)]` with `// tracked in #N` | **LOW** (pre-existing, logged only) | Any CRITICAL or HIGH match without a `// tracked in #<N>` or `// false positive:` justification comment on the same line is a **blocker** — reject and fix. Append all findings to `./review-YYYY-MM-DD/rust-panic-audit.md` under a `## Lint Suppressions` heading. --- ## Step 6 — Generate patches for CRITICAL and HIGH findings For each CRITICAL/HIGH finding, produce a concrete patch: **`.unwrap()` on `Result`:** ```rust // ❌ Before let key = kms.get_key(id).await.unwrap(); // ✅ After let key = kms.get_key(id).await .map_err(|e| KmsError::InvalidRequest(format!("Failed to get key {id}: {e}")))?; ``` **`.expect()` on `Option`:** ```rust // ❌ Before let val = map.get(&key).expect("key must exist"); // ✅ After let val = map.get(&key) .ok_or_else(|| KmsError::ItemNotFound(key.clone()))?; ``` **`panic!` with static message:** ```rust // ❌ Before panic!("unexpected state"); // ✅ After return Err(KmsError::InvalidRequest("unexpected state".to_owned())); ``` --- ## Step 6 — Write report Append to `./review-YYYY-MM-DD/rust-panic-audit.md`: ```markdown ## Summary | Severity | Count | |----------|-------| | CRITICAL | N | | HIGH | N | | MEDIUM | N | | LOW | N | | INFO | N | | OK | N | ## Findings ### [CRITICAL] panic! in request handler - **File**: `crate/server/src/core/operations/get.rs:42` - **Code**: `panic!("key not found")` - **Impact**: Aborts the Actix-web worker thread; potential DoS via crafted request. - **Fix**: Replace with `return Err(KmsError::ItemNotFound(...))`. ... ``` --- ## Step 7 — Summary verdict Print to chat: ``` Rust Panic Audit complete. CRITICAL: N HIGH: N MEDIUM: N LOW: N Report: ./review-YYYY-MM-DD/rust-panic-audit.md ``` If CRITICAL or HIGH count > 0, recommend running `/rust-review-all` for a full quality gate before the PR is submitted.
View on GitHub