Skip to content

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

// privacy-score — composite privacy scoring engine.
//
// Language: Swift (5.x, 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.
enum Grade: Character {
    case a = "A", b = "B", c = "C", f = "F"
}

/// Round to the nearest point and pin into the 0...25 window.
func clamp25(_ points: Double) -> Int {
    min(25, max(0, Int(points.rounded())))
}

/// 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.
func fingerprintPoints(signalCount: Int, highRisk: Int, mediumRisk: Int) -> Int {
    let surface = Double(max(0, signalCount - 12)) * 0.5
    return clamp25(25 - Double(highRisk) * 4 - Double(mediumRisk) * 1.5 - surface)
}

func passwordPoints(score: Int, breached: Bool) -> Int {
    let base = Double(min(4, max(0, 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(breached ? base * 0.32 : base)
}

func headersPoints(_ grades: [Grade]) -> Int {
    if grades.contains(.f) { return 0 }
    if grades.contains(.c) { return 10 }
    if grades.contains(.b) { return 18 }
    return grades.isEmpty ? 0 : 25
}

func breachPoints(pwned: Bool) -> Int {
    pwned ? 0 : 25
}

func letterFor(percent: Int, max: Int) -> String {
    if max == 0 { return "—" }
    if percent >= 85 { return "A" }
    if percent >= 70 { return "B" }
    if percent >= 50 { return "C" }
    return "D"
}

/// Renormalize over the checks that ran and grade the overall letter.
func scorePrivacy(_ points: [Int]) -> (total: Int, max: Int, percent: Int, letter: String) {
    let total = points.reduce(0, +)
    let max = points.count * 25
    let percent = max == 0 ? 0 : Int((Double(total) / Double(max) * 100).rounded())
    return (total, max, percent, letterFor(percent: percent, max: max))
}

let checks = [
    fingerprintPoints(signalCount: 18, highRisk: 2, mediumRisk: 4),  // 8
    passwordPoints(score: 4, breached: true),                        // 8 (breach floor)
    headersPoints([.a, .a, .b]),                                     // 18
    breachPoints(pwned: false),                                      // 25
]
let result = scorePrivacy(checks)
print("checks:", checks)
print("total \(result.total)/\(result.max) = \(result.percent)% -> \(result.letter)")

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 →