rust_checker 1.0.0

A modular Rust code validation tool with HTML, JSON, SVG badge, and JUnit XML report export. Includes optional web dashboard and PQC guardrails via plugins.
Documentation
use std::{fs, path::Path};

fn main() {
    let mut any_secret_context = false;
    let mut uses_thread_rng_for_secret = false;
    let mut saw_osrng = false;
    let mut saw_const_time_eq = false;
    let mut saw_zeroize = false;
    let mut saw_pqc = false; // pqcrypto-* or oqs usage

    let files = collect_rs_files("src");
    for f in files {
        let Ok(src) = fs::read_to_string(&f) else { continue; };

        // Fast check for PQC crates mentioned in code (imports/paths)
        if src.contains("pqcrypto") || src.contains("oqs") {
            saw_pqc = true;
        }

        // line-by-line, ignore obvious strings/comments
        for line in src.lines() {
            let l = line.trim();
            let looks_like_code = !(l.starts_with("//")
                || l.starts_with("/*")
                || l.starts_with('*')
                || l.contains('"'));

            if looks_like_code && looks_like_secret_context_line(l) {
                any_secret_context = true;
            }

            if looks_like_code && l.contains("thread_rng()") && looks_like_secret_context_line(l) {
                uses_thread_rng_for_secret = true;
            }
            if looks_like_code
                && (l.contains("rand::rngs::OsRng") || l.contains("rand_core::OsRng"))
            {
                saw_osrng = true;
            }
            if looks_like_code
                && (l.contains("subtle::ConstantTimeEq")
                    || l.contains(".ct_eq(")
                    || l.contains("ct_eq("))
            {
                saw_const_time_eq = true;
            }
            if looks_like_code
                && (l.contains("derive(Zeroize") || l.contains("zeroize::Zeroize"))
            {
                saw_zeroize = true;
            }
        }
    }

    // Enforce crypto hygiene only if we detect crypto-ish code
    // (either PQC crates present OR secret-like code found).
    let require_crypto_hygiene = saw_pqc || any_secret_context;

    if !require_crypto_hygiene {
        // No crypto in this repo right now -> nothing to enforce.
        println!("ok");
        return;
    }

    if uses_thread_rng_for_secret && !saw_osrng {
        println!("err: avoid `thread_rng()` for key/nonce; use OsRng");
        return;
    }
    if !saw_osrng {
        println!("err: no OsRng usage detected for secrets");
        return;
    }
    if !saw_const_time_eq {
        println!("err: no constant-time equality found (use subtle::ConstantTimeEq / ct_eq)");
        return;
    }
    if !saw_zeroize {
        println!("err: no `zeroize` usage detected for secret types");
        return;
    }

    println!("ok");
}

fn collect_rs_files(root: &str) -> Vec<String> {
    let mut out = Vec::new();
    visit(Path::new(root), &mut out);
    out
}

fn visit(p: &Path, out: &mut Vec<String>) {
    if p.file_name().and_then(|s| s.to_str()) == Some("target") {
        return;
    }
    let Ok(read) = fs::read_dir(p) else { return };
    for entry in read.flatten() {
        let path = entry.path();
        if path.is_dir() {
            visit(&path, out);
        } else if path.extension().and_then(|e| e.to_str()) == Some("rs") {
            if let Some(s) = path.to_str() {
                out.push(s.to_string());
            }
        }
    }
}

// Narrow per-line check to avoid strings/comments.
// Tweak these tokens to match your codebase’s crypto hotspots.
fn looks_like_secret_context_line(l: &str) -> bool {
    l.contains("keygen")
        || l.contains("generate_key")
        || l.contains("seal")
        || l.contains("sign(")
        || l.contains("nonce")
        || l.contains("secret")
        || l.contains("private_key")
        || l.contains("shared_secret")
}