igneum/site/scrub.mjs
2026-10-04 13:54:16 +00:00

60 lines
3.2 KiB
JavaScript

// Scrub for the public /bench page (review round 4, R4.6.1). The same rules the public repository export applies
// (its sync.sh generic scrub and bench-log rules, and the maintainers' local rules), plus site-only ones, in plain Node
// so the build runs the same on this Mac, on Vercel and on the CI runner. The build fails if any pattern in
// site/forbidden-strings.txt survives, so a private name, host or address can never reach the page.
import { readFileSync } from 'node:fs';
import { join, dirname } from 'node:path';
import { fileURLToPath } from 'node:url';
const here = dirname(fileURLToPath(import.meta.url));
const utc = (h, m, s) => `${String((Number(h) + 23) % 24).padStart(2, '0')}:${m}${s || ''} UTC`;
const RULES = [
// machines: model names only
[/the PC node at 192\.168\.[0-9.]+/g, 'the RTX 5090 node on the LAN'],
[/\bthe PC node\b/g, 'the RTX 5090 node'],
[/\bWindows PC\b/g, 'an RTX 5090 on Windows'],
[/\bthe PC's\b/g, "the RTX 5090 machine's"],
[/\bthe PC\b/g, 'the RTX 5090 machine'],
[/\bPC (joins|start|period)\b/g, 'RTX 5090 $1'],
[/\(Mac side only;/g, '(Apple M5 Max side only;'],
[/\bthe Mac's\b/g, "the Apple M5 Max's"],
[/\bthe Mac\b/g, 'the Apple M5 Max'],
[/\bJosh's\b/g, "the maintainers'"],
[/\bJosh\b/g, 'the maintainers'],
[/ Not deployed to Vercel tonight\./g, ''],
// addresses, hosts, paths
[/192\.168\.[0-9]+\.[0-9]+/g, '<lan-ip>'],
[/\b100\.[0-9]+\.[0-9]+\.[0-9]+\b/g, '<overlay-ip>'],
[/DESKTOP-[A-Z0-9]{7}/g, '<pc-hostname>'],
[/igneum-seed-\d+/g, 'the seed node'],
[/\bHetzner\b/g, 'the cloud provider'],
[/\bhcloud\b/g, 'the cloud CLI'],
[/\/root\/[A-Za-z0-9_./-]*/g, '<server path>'],
[/\/opt\/igneum[A-Za-z0-9_./-]*/g, '<server path>'],
[/dl\.igneum\.network[^ )`]*/g, 'the downloads host'],
[/,? ?log intake id \d+/g, ''],
[/\bintake id \d+\b/g, 'intake'],
[/~\/Desktop\//g, '`'], [/``/g, '`'],
[/~\/\.cargo\/bin\/cargo/g, 'cargo'],
[/\/Users\/[A-Za-z0-9_.-]+/g, '~'],
[/C:\\Users\\[A-Za-z0-9_.-]+/g, '%USERPROFILE%'],
// unpublished documents
[/`docs\/fud-ledger\.md`/g, 'the break ledger (kept by the maintainers, not yet published)'],
[/\(CLAUDE\.md, /g, '(the design document, '],
[/\bCLAUDE\.md\b/g, 'the design document'],
// local times to UTC (the log is written in UTC+1)
[/2,193 blocks at 22:35;/g, '2,193 blocks at 21:35 UTC;'],
[/(\d{1,2}:\d{2}(?::\d{2})? UTC) = \d{1,2}:\d{2} B[S]T/g, '$1'],
[/(\d{1,2}):(\d{2})(:\d{2})? to (\d{1,2}):(\d{2})(:\d{2})? B[S]T/g, (_, h1, m1, s1, h2, m2, s2) => `${utc(h1, m1, s1).replace(' UTC', '')} to ${utc(h2, m2, s2)}`],
[/(\d{1,2}):(\d{2})(:\d{2})? B[S]T/g, (_, h, m, s) => utc(h, m, s)],
[/\(local time, UTC\+1\)/g, '(UTC)'],
[/\bB[S]T\b/g, 'UTC'],
];
export function scrubBench(text) {
let out = text;
for (const [re, rep] of RULES) out = out.replace(re, rep);
const pats = readFileSync(join(here, 'forbidden-strings.txt'), 'utf8').split('\n').map(l => l.trim()).filter(l => l && !l.startsWith('#'));
const hits = [];
out.split('\n').forEach((line, i) => { for (const p of pats) if (new RegExp(p).test(line)) { hits.push(`${i + 1}: ${p}`); break; } });
if (hits.length) throw new Error(`scrub: forbidden strings remain on the bench page:\n${hits.slice(0, 20).join('\n')}`);
return out;
}