Skip to main content

rust-strict

Rust security, strictness, and vulnerability prevention rules. Use when writing, reviewing, or auditing Rust code. Complements rust-skills (179 general rules) with security-focused rules: unsafe audit, unwrap/expect bans, error handling hierarchy, secret handling, concurrency safety, input validation for Tauri commands, and release profile hardening. Derived from production Rust projects.

Source facts

Repository
0xMassi/claude-skills
Last source activity
April 28, 2026 at 15:15
Detected SKILL.md language
English
Stars
8
Forks
2

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-strict
description
Rust security, strictness, and vulnerability prevention rules. Use when writing, reviewing, or auditing Rust code. Complements rust-skills (179 general rules) with security-focused rules: unsafe audit, unwrap/expect bans, error handling hierarchy, secret handling, concurrency safety, input validation for Tauri commands, and release profile hardening. Derived from production Rust projects.
# Rust Strict Standard Security and strictness rules complementing the existing `rust-skills` (179 rules). These rules are derived from 5 production Rust projects. ## CRITICAL: Workspace Lint Configuration Every Rust project must configure workspace lints. Baseline: ```toml # Cargo.toml (workspace root) [workspace.lints.rust] unsafe_code = "deny" # No unsafe in production code unused_qualifications = "deny" [workspace.lints.clippy] unwrap_used = "deny" # Force proper error handling expect_used = "deny" # Same: use ? or ok_or_else() ``` Per-crate opt-in: ```toml # crates/my-crate/Cargo.toml [lints] workspace = true ``` ### Exceptions (feature-gated, never blanket) ```rust // FFI crate only: deny everywhere else #![cfg_attr(feature = "local-llm", allow(unsafe_code))] // Static initialization (regex, lazy): this is the ONLY acceptable expect() static PATTERN: LazyLock<Regex> = LazyLock::new(|| { Regex::new(r"...").expect("regex must compile") // Bug if this fails }); ``` ## CRITICAL: Error Handling Hierarchy ### Rule: Library crates use `thiserror`, app crates use `anyhow` ```rust // Library crate: structured errors #[derive(Debug, thiserror::Error)] pub enum GatewayError { #[error("authentication required")] AuthRequired, #[error("rate limited: retry after {retry_after_ms}ms")] RateLimited { retry_after_ms: u64 }, #[error("payload too large: {size} exceeds {max} bytes")] PayloadTooLarge { size: usize, max: usize }, } impl GatewayError { pub fn status_code(&self) -> StatusCode { /* match self */ } pub fn is_retryable(&self) -> bool { matches!(self, Self::RateLimited { .. }) } } // App crate: flexible propagation fn main() -> anyhow::Result<()> { let config = load_config().context("failed to load configuration")?; Ok(()) } ``` ### Rule: Tauri commands return `Result<T, String>` ```rust #[tauri::command] pub async fn my_command( state: tauri::State<'_, AppState>, ) -> Result<ResponseData, String> { let data = do_work().map_err(|e| format!("operation failed: {e}"))?; Ok(data) } ``` Tauri serializes errors as strings: this is the framework constraint, not a hack. ### Rule: Never `Box<dyn Error>`: use domain-specific enums ```rust // BAD fn process() -> Result<(), Box<dyn std::error::Error>> { ... } // GOOD fn process() -> Result<(), GatewayError> { ... } ``` ## CRITICAL: Unsafe Audit Rules ### When unsafe is acceptable (with mandatory SAFETY comment) ```rust // 1. SIMD intrinsics with runtime feature detection #[target_feature(enable = "avx2,fma")] unsafe fn cosine_similarity_avx2(a: &[f32], b: &[f32]) -> f64 { // SAFETY: AVX2 feature verified by caller via is_x86_feature_detected! let va = unsafe { _mm256_loadu_ps(a_ptr.add(offset)) }; // offset < chunks*8 <= n: bounds checked by loop structure } // 2. Test-only env manipulation (with guard lock) #[cfg(test)] fn test_env_var() { let _guard = env_lock(); // Prevents test race conditions // SAFETY: env_lock serializes all env var access in tests unsafe { std::env::set_var("KEY", "value") }; } ``` ### When unsafe is NEVER acceptable - String/buffer manipulation (use safe APIs) - Pointer arithmetic without bounds proof - `transmute` (almost always wrong: use `From`/`Into`) - Implementing `Send`/`Sync` manually (unless wrapping C FFI) - `unsafe impl` without audited invariants ## HIGH: Concurrency Safety ### Rule: Never hold RwLock/Mutex across `.await` ```rust // BAD: deadlock risk let mut data = state.data.write().await; data.insert(key, value); let json = serde_json::to_string(&*data)?; tokio::fs::write(path, json).await; // STILL LOCKED // GOOD: clone, drop, then write let json = { let mut data = state.data.write().await; data.insert(key, value); serde_json::to_string(&*data)? }; // Lock dropped tokio::fs::write(path, json).await; ``` ### Rule: Never hold RwLock across disk I/O ```rust // BAD let mut cookies = state.cookies.write().await; cookies.insert(name, result); std::fs::write(&path, serde_json::to_string(&*cookies)?); // SYNC I/O WHILE LOCKED // GOOD let json = { let mut cookies = state.cookies.write().await; cookies.insert(name, result); serde_json::to_string(&*cookies)? }; tokio::fs::write(&path, json).await; ``` ### Rule: Use atomics for flags/counters, RwLock for data ```rust pub struct AppState { // Atomics: lock-free for simple state pub is_running: AtomicBool, pub connection_count: AtomicUsize, // RwLock: for collections and complex data pub sessions: RwLock<HashMap<String, Session>>, // Watch channel: for shutdown signaling pub shutdown: watch::Sender<bool>, } ``` ### Rule: BoundedMap for all caches (prevent memory leaks) ```rust // BAD: unbounded HashMap cache let cache: HashMap<String, CachedValue> = HashMap::new(); // GOOD: bounded with capacity + TTL let cache = BoundedMap::new(1000, Duration::from_secs(300)); ``` ## HIGH: Secret Handling ### Rule: Secrets in Keychain, never plain files ```rust // macOS: system `security` CLI (subprocess, not FFI) #[cfg(target_os = "macos")] fn keychain_get(account: &str) -> Result<Option<String>, String> { let output = Command::new("security") .args(["find-generic-password", "-s", "AppName", "-a", account, "-w"]) .output() .map_err(|e| format!("keychain read failed: {e}"))?; // ... } // Fallback: Tauri plugin-store (encrypted) #[cfg(not(target_os = "macos"))] fn save_fallback(app: &AppHandle, name: &str, value: &str) -> Result<(), String> { ... } ``` ### Rule: Use `obfstr!()` for hardcoded strings in binary ```rust // BAD: visible in binary strings let api_url = "https://api.internal.example.com/v2"; // GOOD: obfuscated at compile time let api_url = obfstr::obfstr!("https://api.internal.example.com/v2"); ``` ### Rule: Validate empty strings on secret storage ```rust pub fn save_secret(name: &str, value: &str) -> Result<(), String> { let trimmed = value.trim(); if trimmed.is_empty() { return Err("Secret value cannot be empty".to_string()); } // Proceed with trimmed value } ``` ## HIGH: Input Validation (Tauri Commands) ### Rule: Enum-constrain all command inputs ```rust // BAD: arbitrary string input #[tauri::command] fn spawn_agent(agent: String) -> Result<(), String> { Command::new(&agent).spawn(); // COMMAND INJECTION } // GOOD: enum-constrained #[derive(Debug, Clone, Deserialize)] pub enum AgentType { Claude, Codex, Opencode } #[tauri::command] fn spawn_agent(agent: AgentType) -> Result<(), String> { let binary = match agent { AgentType::Claude => "claude", AgentType::Codex => "codex", AgentType::Opencode => "opencode", }; // Safe: only known values } ``` ### Rule: Use `tempfile` crate for temp files ```rust // BAD: predictable path, symlink attacks std::fs::write("/tmp/prompt.txt", prompt)?; // GOOD: unpredictable, auto-cleanup let mut tmp = tempfile::NamedTempFile::new() .map_err(|e| format!("temp file failed: {e}"))?; writeln!(tmp, "{}", prompt)?; let path = tmp.path().to_string_lossy().to_string(); ``` ## MEDIUM: Dependency Rules ### Rule: Explicit feature control ```toml # BAD: pulls in everything reqwest = "0.12" # GOOD: only what you need reqwest = { version = "0.12", default-features = false, features = ["json", "stream", "rustls-tls"] } ``` ### Rule: `rustls-tls` over `native-tls` Pure Rust TLS stack: no OpenSSL dependency, auditable, consistent behavior. ### Rule: Workspace dependency inheritance ```toml # Workspace Cargo.toml [workspace.dependencies] tokio = { version = "1", features = ["full"] } serde = { version = "1", features = ["derive"] } # Crate Cargo.toml [dependencies] tokio = { workspace = true } serde = { workspace = true } ``` ## HIGH: Type Safety Patterns ### Rule: Newtype for IDs and units ```rust // BAD: any UUID can be passed where any ID is expected pub fn ban_user(uid: Uuid, by: Uuid) { ... } // GOOD: type system rejects swapped arguments pub struct UserId(Uuid); pub struct AdminId(Uuid); pub fn ban_user(uid: UserId, by: AdminId) { ... } ``` Same pattern for units: `Bytes(u64)`, `Millis(u64)`, `RetryCount(u32)`. ### Rule: `#[must_use]` on results that should not be silently dropped ```rust #[must_use = "builder is incomplete until .build() is called"] pub struct RequestBuilder { ... } #[must_use] pub fn try_acquire(&self) -> Option<Lease> { ... } ``` `Result<T, E>` already has `#[must_use]`. Add it to builders, locks, and validation outcomes. ### Rule: Explicit overflow handling on arithmetic ```rust // BAD: wraps silently in release mode let next = current + delta; // GOOD: pick the semantic you mean let next = current.checked_add(delta).ok_or(Error::Overflow)?; // fail let next = current.saturating_add(delta); // clamp let next = current.wrapping_add(delta); // wrap (rare, document why) ``` Default to `checked_*` for anything user-influenced (sizes, counts, indices). ## HIGH: Edition 2024 Edition 2024 ships with Rust 1.85 (Feb 2025). Current stable is **1.95.0**. New crates start at edition 2024 with current stable. Migration changes that affect strict-mode rules: ### Rule: Wrap external symbols in `unsafe extern "C"` blocks ```rust // BAD: edition 2024 hard error extern "C" { fn external_fn(); } // GOOD unsafe extern "C" { fn external_fn(); } ``` Forces the FFI declaration site to acknowledge unsafety. ### Rule: `unsafe_op_in_unsafe_fn` is warn-by-default Inside `unsafe fn`, every individual unsafe operation now needs its own `unsafe { ... }` block. This makes the audit surface explicit, do not bypass with `#[allow]`. ### Rule: Capture lifetimes explicitly with `use<...>` on RPIT ```rust // Edition 2024: opaque return types capture all generic params by default
View on GitHub
This SKILL.md is very large, so SkillsMP previews the first section here. View on GitHub