agentsclimarketplace

Rust security

Skill Mattakushi432/Claude-Code-Skills-Custom-DevTools-Pack/plugins/devtools-pack/skills/rust-security

When to activate: Rust security, unsafe code, input validation, secret handling, cryptography, supply chain, audit, injection preventionFrom its SKILL.md

Install
npx -y skills add Mattakushi432/Claude-Code-Skills-Custom-DevTools-Pack --skill rust-security

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 0 stars0 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

SKILL.md

4.4 KB, ~1.1k tokens by cl100k_base, as published. Nobody here has run it

Rust Security Patterns

Safe Defaults

Rust's type system prevents many vulnerability classes in safe code:

  • Memory safety: no buffer overflows, use-after-free, or dangling pointers
  • No null pointer dereferences: Option<T> forces explicit handling
  • Thread safety: Send/Sync prevent data races at compile time
  • Integer overflow: caught in debug; use checked_* / saturating_* in release
fn safe_multiply(a: u64, b: u64) -> Option<u64> {
    a.checked_mul(b)
}

Handling Secrets

[dependencies]
secrecy = "0.10"
zeroize = "1"
subtle = "2"
use secrecy::{Secret, ExposeSecret};

struct Credentials {
    username: String,
    password: Secret<String>, // will not appear in Debug output
}

fn authenticate(creds: &Credentials, input: &str) -> bool {
    let pwd = creds.password.expose_secret();
    // Constant-time comparison prevents timing attacks
    use subtle::ConstantTimeEq;
    pwd.as_bytes().ct_eq(input.as_bytes()).into()
}

Input Validation

use validator::Validate;

#[derive(Debug, Validate, serde::Deserialize)]
struct CreateUserRequest {
    #[validate(length(min = 2, max = 64))]
    name: String,

    #[validate(email)]
    email: String,

    #[validate(length(min = 12))]
    password: String,
}

async fn create_user(Json(req): Json<CreateUserRequest>) -> Result<Json<User>, ApiError> {
    req.validate().map_err(|e| ApiError::BadRequest(e.to_string()))?;
    todo!()
}

SQL Injection Prevention

Always use parameterized queries — never string-interpolate user input into SQL.

// NEVER DO THIS
let query = format!("SELECT * FROM users WHERE name = '{}'", user_input);

// ALWAYS DO THIS (sqlx)
let user = sqlx::query_as!(User, "SELECT * FROM users WHERE name = $1", user_input)
    .fetch_optional(pool).await?;

Path Traversal Prevention

fn safe_join(base: &std::path::Path, user_input: &str) -> anyhow::Result<std::path::PathBuf> {
    let clean = user_input.trim_start_matches(['/', '.', '\\']);
    let candidate = base.join(clean);
    let canonical = candidate.canonicalize()?;
    anyhow::ensure!(
        canonical.starts_with(base),
        "path traversal attempt: {:?} outside {:?}", canonical, base
    );
    Ok(canonical)
}

Password Hashing

[dependencies]
argon2 = "0.5"
rand = "0.8"
use argon2::{Argon2, PasswordHash, PasswordHasher, PasswordVerifier};
use argon2::password_hash::{rand_core::OsRng, SaltString};

fn hash_password(password: &str) -> anyhow::Result<String> {
    let salt = SaltString::generate(&mut OsRng);
    Ok(Argon2::default().hash_password(password.as_bytes(), &salt)?.to_string())
}

fn verify_password(password: &str, hash: &str) -> anyhow::Result<bool> {
    let parsed = PasswordHash::new(hash)?;
    Ok(Argon2::default().verify_password(password.as_bytes(), &parsed).is_ok())
}

Secure Random Tokens

use rand::Rng;
fn generate_token() -> String {
    let bytes: Vec<u8> = (0..32).map(|_| rand::thread_rng().gen()).collect();
    hex::encode(bytes)
}

Supply Chain Security

cargo install cargo-audit && cargo audit   # known CVEs
cargo install cargo-deny                  # license/ban/source policies
cargo install cargo-outdated && cargo outdated

unsafe Code Guidelines

unsafe fn dangerous(ptr: *const u8, len: usize) -> &'static [u8] {
    // SAFETY:
    // - `ptr` is valid for `len` bytes (caller's responsibility)
    // - the lifetime is correct (caller ensures data outlives 'static)
    // - no mutable aliasing exists (caller's guarantee)
    std::slice::from_raw_parts(ptr, len)
}

Common Anti-Patterns

  • Logging secrets or PII — redact before logging; use secrecy::Secret
  • unwrap() on user-controlled deserialization — handle errors explicitly
  • md5/sha1 for passwords — use argon2, bcrypt, or scrypt
  • Trusting Content-Type headers — validate the actual content, not the declared type
  • Not committing Cargo.lock for services — commit it for binaries/services; omit for libraries

What ships with it

Read from the repository

Just SKILL.md. No reference files, no scripts.

Keep looking

Skills are one crate of 326,851. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.