Skip to content

Privacy Score — Rust source

One number for your privacy health: your browser fingerprint, a test password's strength and breach exposure, and a site's security headers — four checks, one score, concrete fixes. Runs in your browser; only a 5-character hash prefix ever leaves it.

This is the Rust implementation — the same logic the interactive tool runs, in a shareable, citable form.

//! privacy-score — composite privacy scoring engine.
//!
//! Language: Rust (edition 2021, standard library only)
//! Source:   CosmoDev polyglot showcase port of the Privacy Score tool,
//!           ported from src/lib/privacy-score.ts (canonical TypeScript).
//! License:  display source — part of CosmoDev's polyglot tool pages.
//!
//! Four category checks, each scored out of 25; the overall percent is
//! renormalized over the categories that actually ran — skipping a check
//! never lowers your score. Letter bands: >=85 A, >=70 B, >=50 C, else D.

/// Header grade from a security-headers scan.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum Grade { A, B, C, F }

/// Round to the nearest point and pin into the 0..25 window.
fn clamp25(points: f64) -> i32 {
    points.round().clamp(0.0, 25.0) as i32
}

/// More distinguishable signals make a browser more unique: high-risk
/// signals cost 4 each, medium 1.5, and a large surface costs a little more.
fn fingerprint_points(signal_count: i32, high_risk: i32, medium_risk: i32) -> i32 {
    let surface = (signal_count - 12).max(0) as f64 * 0.5;
    clamp25(25.0 - high_risk as f64 * 4.0 - medium_risk as f64 * 1.5 - surface)
}

fn password_points(score: i32, breached: bool) -> i32 {
    let base = score.clamp(0, 4) as f64 / 4.0 * 25.0;
    // Breached is urgent regardless of strength: x0.32 floors even a
    // score-4 password at 8 points -> bad -> the change-it recommendation.
    clamp25(if breached { base * 0.32 } else { base })
}

fn headers_points(grades: &[Grade]) -> i32 {
    let has = |g: Grade| grades.iter().any(|&x| x == g);
    if has(Grade::F) { return 0; }
    if has(Grade::C) { return 10; }
    if has(Grade::B) { return 18; }
    if grades.is_empty() { 0 } else { 25 }
}

fn breach_points(pwned: bool) -> i32 {
    if pwned { 0 } else { 25 }
}

fn letter_for(percent: i32, max: i32) -> &'static str {
    match (max == 0, percent >= 85, percent >= 70, percent >= 50) {
        (true, ..) => "—",
        (_, true, ..) => "A",
        (_, _, true, _) => "B",
        (_, _, _, true) => "C",
        _ => "D",
    }
}

/// Renormalize over the checks that ran and grade the overall letter.
fn score_privacy(points: &[i32]) -> (i32, i32, i32, &'static str) {
    let total: i32 = points.iter().sum();
    let max = points.len() as i32 * 25;
    let percent = if max == 0 { 0 } else { (total as f64 / max as f64 * 100.0).round() as i32 };
    (total, max, percent, letter_for(percent, max))
}

fn main() {
    let checks = [
        fingerprint_points(18, 2, 4),                    // 8
        password_points(4, true),                        // 8 (breach floor)
        headers_points(&[Grade::A, Grade::A, Grade::B]), // 18
        breach_points(false),                            // 25
    ];
    println!("{:?}", checks);
    println!("{:?}", score_privacy(&checks));
}

Also available in 13 other languages

Every CosmoDev tool ships its pure logic in TypeScript (web) and Go (CLI), with authored implementations in a dozen-plus languages — the same contract, ported. Compare all languages side by side →