derive-privacy-scores.js raw
1 #!/usr/bin/env node
2 /**
3 * Reads data/privacytests-android.json and writes a small derived TS file
4 * with per-browser score summaries. The app imports the small TS — the raw
5 * JSON is kept in the repo as the auditable source-of-truth, but is not
6 * bundled into the APK.
7 *
8 * To refresh:
9 * 1. curl -o data/privacytests-android.json https://privacytests.org/android.json
10 * 2. node scripts/derive-privacy-scores.js
11 * 3. commit both files
12 */
13
14 const fs = require('fs');
15 const path = require('path');
16
17 const ROOT = path.resolve(__dirname, '..');
18 const SRC = path.join(ROOT, 'data', 'privacytests-android.json');
19 const OUT = path.join(ROOT, 'constants', 'privacy-scores.ts');
20
21 const data = JSON.parse(fs.readFileSync(SRC, 'utf8'));
22
23 // privacytests browser-name -> our Android package id
24 const PKG_FOR_NAME = {
25 brave: 'com.brave.browser',
26 chrome: 'com.android.chrome',
27 cromite: 'org.cromite.cromite',
28 duckduckgo: 'com.duckduckgo.mobile.android',
29 edge: 'com.microsoft.emmx',
30 firefox: 'org.mozilla.firefox',
31 focus: 'org.mozilla.focus',
32 opera: 'com.opera.browser',
33 samsung: 'com.sec.android.app.sbrowser',
34 tor: 'org.torproject.torbrowser',
35 vivaldi: 'com.vivaldi.browser',
36 // yandex intentionally omitted — not surfaced by us
37 };
38
39 const SECTIONS = [
40 'session_1p',
41 'session_3p',
42 'supercookies',
43 'trackers',
44 'query',
45 'https',
46 'misc',
47 'navigation',
48 ];
49
50 function summarize(testResults) {
51 const out = { totalPassed: 0, totalTests: 0, bySection: {} };
52 for (const section of SECTIONS) {
53 const entries = Object.values(testResults[section] || {});
54 const total = entries.length;
55 const passed = entries.filter((e) => e && e.passed === true).length;
56 out.totalPassed += passed;
57 out.totalTests += total;
58 out.bySection[section] = { passed, total };
59 }
60 return out;
61 }
62
63 // Dedupe — privacytests runs each browser twice; take the first occurrence.
64 const seen = new Set();
65 const scores = {};
66 for (const entry of data.all_tests) {
67 const name = entry.browser;
68 const pkg = PKG_FOR_NAME[name];
69 if (!pkg || seen.has(pkg)) continue;
70 seen.add(pkg);
71 scores[pkg] = {
72 name,
73 version: entry.reportedVersion,
74 ...summarize(entry.testResults),
75 };
76 }
77
78 const generated = new Date(data.timeStarted).toISOString().slice(0, 10);
79 const banner =
80 `// AUTO-GENERATED by scripts/derive-privacy-scores.js — do not edit.\n` +
81 `// Source: data/privacytests-android.json (${generated})\n` +
82 `// privacytests.org platform: ${data.platform}\n\n`;
83
84 const body =
85 `export type PrivacyTestScore = {\n` +
86 ` name: string;\n` +
87 ` version: string;\n` +
88 ` totalPassed: number;\n` +
89 ` totalTests: number;\n` +
90 ` bySection: Record<string, { passed: number; total: number }>;\n` +
91 `};\n\n` +
92 `export const PRIVACY_TEST_DATE = '${generated}';\n\n` +
93 `export const PRIVACY_TEST_SCORES: Record<string, PrivacyTestScore> = ` +
94 JSON.stringify(scores, null, 2) +
95 ';\n';
96
97 fs.writeFileSync(OUT, banner + body);
98 console.log(`Wrote ${OUT}`);
99 console.log(`Snapshot date: ${generated}`);
100 console.log(`Browsers with scores: ${Object.keys(scores).length}`);
101