Skip to content

Privacy Score — Python 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 Python implementation — the same logic the interactive tool runs, in a shareable, citable form.

"""privacy-score — composite privacy scoring engine.

Language: Python (3.9+, 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.
"""

from __future__ import annotations


def clamp25(points: float) -> int:
    """Round to the nearest point and pin into the 0..25 window."""
    return max(0, min(25, round(points)))


def status_for(points: int) -> str:
    return "ok" if points >= 20 else "warn" if points >= 10 else "bad"


def fingerprint_points(signal_count: int, high: int, medium: int) -> int:
    # 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.
    surface = max(0, signal_count - 12) * 0.5
    return clamp25(25 - high * 4 - medium * 1.5 - surface)


def password_points(score: int, breached: bool) -> int:
    base = max(0, min(4, score)) / 4 * 25
    # Breached is urgent regardless of strength: x0.32 floors even a
    # score-4 password at 8 points -> "bad" -> the change-it recommendation.
    return clamp25(base * 0.32 if breached else base)


def headers_points(grades: list[str]) -> int:
    counts = {g: grades.count(g) for g in "ABCF"}
    if counts["F"] > 0:
        return 0
    if counts["C"] > 0:
        return 10
    if counts["B"] > 0:
        return 18
    return 25 if grades else 0


def breach_points(pwned: bool) -> int:
    return 0 if pwned else 25


def letter_for(percent: int, maximum: int) -> str:
    return ("—" if maximum == 0 else "A" if percent >= 85 else "B"
            if percent >= 70 else "C" if percent >= 50 else "D")


def score_privacy(points: list[int]) -> dict:
    """Renormalize over the checks that ran and grade the overall letter."""
    total = sum(points)
    maximum = len(points) * 25
    percent = round(total / maximum * 100) if maximum else 0
    return {
        "total": total, "max": maximum, "percent": percent,
        "letter": letter_for(percent, maximum),
        "statuses": [status_for(p) for p in points],
    }


if __name__ == "__main__":
    checks = [
        fingerprint_points(signal_count=18, high=2, medium=4),  # 8
        password_points(score=4, breached=True),                # 8 (breach floor)
        headers_points(list("AAB")),                            # 18
        breach_points(pwned=False),                             # 25
    ]
    print(checks)
    print(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 →