Copy, from origin/master 386ae94 (site audit). The home and miner pages named "PC 1" in the figure captions, the
page-by-page lead, two alt texts, the Ember Tune table title, the Ember row's source line and the home pill; they now
say "a Windows rig with an RTX 5090, RTX 4070 and RX 9070 XT" at the first mention on each page and "the Windows rig"
after. The generated pages carried the same names from the bench-log (80 on /bench, 7 on /evidence, the inlined
journey on the home page): site/scrub.mjs now maps PC 1 to "the three-card Windows rig (RTX 5090, RTX 4070, RX 9070
XT)" and PC 2 to "the RTX 5090 Windows rig", build.mjs re-scrubs the stored journey entries, two /bench anchors on the
miner page follow the renamed headings, and \bPC [12]\b joins site/forbidden-strings.txt so the build fails if a number
returns. The scrub also covers the audit's other page-leak shapes (a pid, 0.0.0.0:port, ~/.config paths, --rpclisten=),
which the bench page carried and which now fail the build if they return.
CI: tools/ci/forbidden-strings.txt's appended audit block sat on one physical line with literal \n text, so none of its
patterns was active; \bPC [12]\b is now a real line there and the identity check's export scrub maps the two machines
the same way (igneum-public/tools/sync.sh must carry the same two rules). The other four audit patterns moved to
site/forbidden-strings.txt, since the public export carries simulator schedule logs where a pid is a pid. The identity
check gains a second pass over every served file under site/ (html, json, txt, xml, webmanifest, css, js; not api/ or
the build scripts), unscrubbed, with both pattern lists; dl\.igneum is narrowed to the tokened path so the public
download buttons pass. Shown to fire on a page naming PC 1 (exit 1) and to pass on the tree (0 hits over 232 export
files and 32 served site files).
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
70 lines
3.8 KiB
JavaScript
70 lines
3.8 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 (6 October 2026, site audit: the two Windows machines are described, never numbered)
|
|
[/\bPC 1's\b/g, "the three-card Windows rig's"],
|
|
[/\bPC 1\b/g, 'the three-card Windows rig (RTX 5090, RTX 4070, RX 9070 XT)'],
|
|
[/\bPC 2's\b/g, "the RTX 5090 Windows rig's"],
|
|
[/\bPC 2\b/g, 'the RTX 5090 Windows rig'],
|
|
[/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'],
|
|
// the site audit's page-leak shapes (6 October 2026): process ids, listen addresses, config paths, the node's listen flag
|
|
[/\bpid \d+\b/g, 'pid <n>'],
|
|
[/0\.0\.0\.0:\d+/g, '<listen address>'],
|
|
[/--rpclisten=\S*/g, 'the RPC listen flag'],
|
|
[/~\/\.config\/[A-Za-z0-9_./-]*/g, '<config path>'],
|
|
[/~\/\.config/g, '<config path>'],
|
|
[/~\/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;
|
|
}
|