mirror of
https://github.com/affaan-m/everything-claude-code.git
synced 2026-03-31 06:03:29 +08:00
* feat(rules): add Rust coding style, hooks, and patterns rules Add language-specific rules for Rust extending the common rule set: - coding-style.md: rustfmt, clippy, ownership idioms, error handling, iterator patterns, module organization, visibility - hooks.md: PostToolUse hooks for rustfmt, clippy, cargo check - patterns.md: trait-based repository, newtype, enum state machines, builder, sealed traits, API response envelope Rules reference existing rust-patterns skill for deep content. Generated with [Claude Code](https://claude.ai/code) via [Happy](https://happy.engineering) Co-Authored-By: Claude <noreply@anthropic.com> Co-Authored-By: Happy <yesreply@happy.engineering> * feat(rules): add Rust testing and security rules Add remaining Rust language-specific rules: - testing.md: cargo test, rstest parameterized tests, mockall mocking with mock! macro, tokio async tests, cargo-llvm-cov coverage - security.md: secrets via env vars, parameterized SQL with sqlx, parse-don't-validate input validation, unsafe code audit requirements, cargo-audit dependency scanning, proper HTTP error status codes Generated with [Claude Code](https://claude.ai/code) via [Happy](https://happy.engineering) Co-Authored-By: Claude <noreply@anthropic.com> Co-Authored-By: Happy <yesreply@happy.engineering> * fix(rules): address review feedback on Rust rules Fixes from Copilot, Greptile, Cubic, and CodeRabbit reviews: - Add missing imports: use std::borrow::Cow, use anyhow::Context - Use anyhow::Result<T> consistently (patterns.md, security.md) - Change sqlx placeholder from ? to $1 (Postgres is most common) - Remove Cargo.lock from hooks.md paths (auto-generated file) - Fix tokio::test to show attribute form #[tokio::test] - Fix mockall mock! name collision, wrap in #[cfg(test)] mod tests - Fix --test target to match file layout (api_test, not integration) Generated with [Claude Code](https://claude.ai/code) via [Happy](https://happy.engineering) Co-Authored-By: Claude <noreply@anthropic.com> Co-Authored-By: Happy <yesreply@happy.engineering> * fix: update catalog counts in README.md and AGENTS.md Update documented counts to match actual repository state after rebase: - Skills: 109 → 113 (new skills merged to main) - Commands: 57 → 58 (new command merged to main) Generated with [Claude Code](https://claude.ai/code) via [Happy](https://happy.engineering) Co-Authored-By: Claude <noreply@anthropic.com> Co-Authored-By: Happy <yesreply@happy.engineering> --------- Co-authored-by: Chris Yau <chris@diveanddev.com> Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: Happy <yesreply@happy.engineering>
142 lines
4.3 KiB
Markdown
142 lines
4.3 KiB
Markdown
---
|
|
paths:
|
|
- "**/*.rs"
|
|
---
|
|
# Rust Security
|
|
|
|
> This file extends [common/security.md](../common/security.md) with Rust-specific content.
|
|
|
|
## Secrets Management
|
|
|
|
- Never hardcode API keys, tokens, or credentials in source code
|
|
- Use environment variables: `std::env::var("API_KEY")`
|
|
- Fail fast if required secrets are missing at startup
|
|
- Keep `.env` files in `.gitignore`
|
|
|
|
```rust
|
|
// BAD
|
|
const API_KEY: &str = "sk-abc123...";
|
|
|
|
// GOOD — environment variable with early validation
|
|
fn load_api_key() -> anyhow::Result<String> {
|
|
std::env::var("PAYMENT_API_KEY")
|
|
.context("PAYMENT_API_KEY must be set")
|
|
}
|
|
```
|
|
|
|
## SQL Injection Prevention
|
|
|
|
- Always use parameterized queries — never format user input into SQL strings
|
|
- Use query builder or ORM (sqlx, diesel, sea-orm) with bind parameters
|
|
|
|
```rust
|
|
// BAD — SQL injection via format string
|
|
let query = format!("SELECT * FROM users WHERE name = '{name}'");
|
|
sqlx::query(&query).fetch_one(&pool).await?;
|
|
|
|
// GOOD — parameterized query with sqlx
|
|
// Placeholder syntax varies by backend: Postgres: $1 | MySQL: ? | SQLite: $1
|
|
sqlx::query("SELECT * FROM users WHERE name = $1")
|
|
.bind(&name)
|
|
.fetch_one(&pool)
|
|
.await?;
|
|
```
|
|
|
|
## Input Validation
|
|
|
|
- Validate all user input at system boundaries before processing
|
|
- Use the type system to enforce invariants (newtype pattern)
|
|
- Parse, don't validate — convert unstructured data to typed structs at the boundary
|
|
- Reject invalid input with clear error messages
|
|
|
|
```rust
|
|
// Parse, don't validate — invalid states are unrepresentable
|
|
pub struct Email(String);
|
|
|
|
impl Email {
|
|
pub fn parse(input: &str) -> Result<Self, ValidationError> {
|
|
let trimmed = input.trim();
|
|
let at_pos = trimmed.find('@')
|
|
.filter(|&p| p > 0 && p < trimmed.len() - 1)
|
|
.ok_or_else(|| ValidationError::InvalidEmail(input.to_string()))?;
|
|
let domain = &trimmed[at_pos + 1..];
|
|
if trimmed.len() > 254 || !domain.contains('.') {
|
|
return Err(ValidationError::InvalidEmail(input.to_string()));
|
|
}
|
|
// For production use, prefer a validated email crate (e.g., `email_address`)
|
|
Ok(Self(trimmed.to_string()))
|
|
}
|
|
|
|
pub fn as_str(&self) -> &str {
|
|
&self.0
|
|
}
|
|
}
|
|
```
|
|
|
|
## Unsafe Code
|
|
|
|
- Minimize `unsafe` blocks — prefer safe abstractions
|
|
- Every `unsafe` block must have a `// SAFETY:` comment explaining the invariant
|
|
- Never use `unsafe` to bypass the borrow checker for convenience
|
|
- Audit all `unsafe` code during review — it is a red flag without justification
|
|
- Prefer `safe` FFI wrappers around C libraries
|
|
|
|
```rust
|
|
// GOOD — safety comment documents ALL required invariants
|
|
let widget: &Widget = {
|
|
// SAFETY: `ptr` is non-null, aligned, points to an initialized Widget,
|
|
// and no mutable references or mutations exist for its lifetime.
|
|
unsafe { &*ptr }
|
|
};
|
|
|
|
// BAD — no safety justification
|
|
unsafe { &*ptr }
|
|
```
|
|
|
|
## Dependency Security
|
|
|
|
- Run `cargo audit` to scan for known CVEs in dependencies
|
|
- Run `cargo deny check` for license and advisory compliance
|
|
- Use `cargo tree` to audit transitive dependencies
|
|
- Keep dependencies updated — set up Dependabot or Renovate
|
|
- Minimize dependency count — evaluate before adding new crates
|
|
|
|
```bash
|
|
# Security audit
|
|
cargo audit
|
|
|
|
# Deny advisories, duplicate versions, and restricted licenses
|
|
cargo deny check
|
|
|
|
# Inspect dependency tree
|
|
cargo tree
|
|
cargo tree -d # Show duplicates only
|
|
```
|
|
|
|
## Error Messages
|
|
|
|
- Never expose internal paths, stack traces, or database errors in API responses
|
|
- Log detailed errors server-side; return generic messages to clients
|
|
- Use `tracing` or `log` for structured server-side logging
|
|
|
|
```rust
|
|
// Map errors to appropriate status codes and generic messages
|
|
// (Example uses axum; adapt the response type to your framework)
|
|
match order_service.find_by_id(id) {
|
|
Ok(order) => Ok((StatusCode::OK, Json(order))),
|
|
Err(ServiceError::NotFound(_)) => {
|
|
tracing::info!(order_id = id, "order not found");
|
|
Err((StatusCode::NOT_FOUND, "Resource not found"))
|
|
}
|
|
Err(e) => {
|
|
tracing::error!(order_id = id, error = %e, "unexpected error");
|
|
Err((StatusCode::INTERNAL_SERVER_ERROR, "Internal server error"))
|
|
}
|
|
}
|
|
```
|
|
|
|
## References
|
|
|
|
See skill: `rust-patterns` for unsafe code guidelines and ownership patterns.
|
|
See skill: `security-review` for general security checklists.
|