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