diff --git a/internal/public-hygiene-check.mjs b/internal/public-hygiene-check.mjs index 1f4c34be6..16a40b338 100644 --- a/internal/public-hygiene-check.mjs +++ b/internal/public-hygiene-check.mjs @@ -85,6 +85,25 @@ const LEAK_STRICT = [ [/\bpersecution\b/, 'operational-context vocabulary (allowed only inside data CSVs)'], [/\.forgejo\/workflows/, 'CI workflow path — process, never ships in a public tree'], ] +// ★ THE PRAYER, WORD FOR WORD (Driver decision 2026-09-28): "Persecution happens globally, so the prayer is +// for the globe, and the packets carry the same prayer for all packets." The operational-vocabulary rule +// skips EXACTLY that prayer: the text at packet-strings.json .points[0].text (the packets' prayer +// point 1, which UPG-100's Two-Cross packet shows too), read from its source in every locale, removed from +// a scanned file before the rule looks at it. Scoped by file and key, never by the word: the same word +// anywhere else still counts. +const PRAYER_ALLOWED = (() => { + try { + const src = JSON.parse(readFileSync(join(ROOT, 'library/scripts/posters/packet-strings.json'), 'utf8')) + const out = [] + for (const [lc, v] of Object.entries(src)) { + const text = !lc.startsWith('_') && v?.points?.[0]?.text + if (typeof text === 'string' && text) out.push(text, JSON.stringify(text).slice(1, -1)) // raw, and as a JS/JSON string literal + } + return out + } catch { return [] } +})() +const withoutPrayer = (t) => PRAYER_ALLOWED.reduce((acc, s) => acc.split(s).join(''), t) + // ★ WIDENED 2026-09-21. This held ONE pattern (the Mapbox JWT), on the reasoning that public/ // is a tree we GENERATE, so the only realistic leak is the token the build itself writes. // THAT REASONING WAS WRONG: 855 files under public/ are TRACKED, 95 of them text — boundaries, @@ -131,7 +150,8 @@ for (const f of files) { if (!isText || f.size > 40 * 1024 * 1024) continue const t = readFileSync(f.abs, 'utf8') for (const [re, why] of LEAK) if (re.test(t)) hit(findings, 'leaks', f.rel, why) - for (const [re, why] of LEAK_STRICT) if (re.test(t) && !/\.csv$/.test(f.rel)) hit(STRICT ? findings : warnings, 'leaks', f.rel, why) + const tNoPrayer = PRAYER_ALLOWED.length && /persecution/.test(t) ? withoutPrayer(t) : t + for (const [re, why] of LEAK_STRICT) if (re.test(re.source.includes('persecution') ? tNoPrayer : t) && !/\.csv$/.test(f.rel)) hit(STRICT ? findings : warnings, 'leaks', f.rel, why) // ★ P2 (2026-09-24): a credential is a DOCUMENT, not a string. Every JWT-shaped value gets // its payload DECODED and its claims reviewed: 'it is a public token' is not sufficient — // the pk. accepted by the 2026-09-21 ruling turned out to carry a `u` claim naming a real,