Files
arah2/web/test/no-storage.test.mjs
araharah-executor 747eeb7b34 test(04-04): the BA7/BA8 drift gates + the search-tree GEO3 pin + the destination-preload capture leg
- BA7: the shipped JS must carry EVERY committed gazetteer id (the
  web half of bot/web id-set parity, T-04-39) — RED against the
  pre-gazetteer bundle, green after the resolver lands in the build
- BA8: the shipped JS carries NO deep-link crypto (JS assets only —
  the official Telegram SDK is external, D-11/T-04-40)
- no-storage: explicit web/src/search memory-only + no-crypto pin
- capture.mjs: the ARAHARAH-DEST signal race + the 'dest' wait mode
  (state=resolved asserted, then the rendered card's text is waited
  on — the card mounts at live, T-04-42) + the shell-dest-preload
  leg; 8/8 expected captures
2026-10-24 01:38:24 +08:00

105 lines
4.7 KiB
JavaScript
Raw Permalink Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env node
/*
* no-storage.test.mjs — the GEO-03 grep gate (zero persistence, source-introspection).
* -----------------------------------------------------------------------------------
* Inverted form of bot/test/privacy.test.mjs (PG1 pins writes to carry TTLs; this
* pin that web/src contains NO persistence surface at all — D-03 (memory-only
* token) + D-07 (coordinates live in memory only)).
*
* The gate MUST strip comments before scanning: web/src/mock/telegram-mock.ts's
* header documents the ban in prose, and prose is not a call site. Strip block
* comments (/* … *​/) and line comments (// …), THEN assert none of the banned
* tokens appear — code occurrences only.
*/
import { strict as assert } from "node:assert";
import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
import path from "node:path";
import { fileURLToPath } from "node:url";
import { test } from "./harness.mjs";
const SRC = path.join(path.dirname(fileURLToPath(new URL(import.meta.url))), "..", "src");
const BANNED = [
"localStorage", "sessionStorage", "indexedDB", "caches", "CacheStorage",
"document.cookie", "WebApp.DeviceStorage", "WebApp.SecureStorage", "WebApp.CloudStorage",
];
/* path-scrape + comment strip (the plan's contract for this gate) */
function listFiles(dir, exts, acc = []) {
for (const entry of readdirSync(dir)) {
const full = path.join(dir, entry);
if (statSync(full).isDirectory()) listFiles(full, exts, acc);
else if (exts.some((e) => entry.endsWith(e))) acc.push(full);
}
return acc;
}
function stripComments(src) {
return src
.replace(/\/\*[\s\S]*?\*\//g, " ") // block comments
.replace(/(^|[^:\\])\/\/[^\n]*/g, "$1"); // line comments (not URLs inside strings — benign over-stripping, gate is prefer-fail reading)
}
function codeText(file) {
return stripComments(readFileSync(file, "utf8"));
}
/* ————— GEO3 — the mechanical gate over every web/src source ————— */
test("GEO3 no banned storage token appears in web/src code (comments stripped, all *.ts/*.html)", () => {
const files = listFiles(SRC, [".ts", ".html"]);
assert.ok(files.length >= 12, `scanned a real tree (found ${files.length} files)`);
for (const file of files) {
const code = codeText(file);
for (const banned of BANNED) {
assert.ok(!code.includes(banned), `${path.relative(process.cwd(), file)} references ${banned} — GEO-03 violated`);
}
}
});
test("GEO3 the runtime-probe-preceding invariant: no service worker / registration call exists", () => {
// Vite emits no SW by default; this pins the "and none was added" half (RESEARCH GEO-03 §).
for (const file of listFiles(SRC, [".ts", ".html"])) {
const code = codeText(file);
for (const token of ["serviceWorker.register", "navigator.serviceWorker", "workbox"]) {
assert.ok(!code.includes(token), `${path.relative(process.cwd(), file)} references ${token} — GEO-03 surface added`);
}
}
});
test("GEO3 web/src/session/store.ts assigns nothing to any storage object (the D-03 half)", () => {
const code = codeText(path.join(SRC, "session", "store.ts"));
assert.ok(!/Storage\s*\.\s*setItem|Storage\s*\.\s*getItem/.test(code), "store.ts touches a Storage object");
assert.ok(!/indexedDB\s*\.\s*open/.test(code), "store.ts opens IndexedDB");
assert.ok(!/document\s*\.\s*cookie\s*=/.test(code), "store.ts writes a cookie");
assert.ok(!/caches\s*\.\s*open/.test(code), "store.ts opens a CacheStorage");
// ...and keeps the named TTL constant the client revalidates against (D-03).
assert.ok(code.includes("SESSION_TTL_SECONDS = 900"), "the 900s TTL mirror by name is present");
});
test("GEO3 web/src/search/ holds no persistence surface and no deep-link crypto", () => {
// The ONBD-02 tree (04-04): the resolved destination is user context and
// stays MEMORY-ONLY (GEO-03/D-07), and the deep-link path is a plain
// public id with NO crypto (D-11). The whole-tree GEO3 gate above already
// walks this directory; this explicit case pins the new tree + the
// no-crypto law so a future file there fails HERE, by name.
const searchDir = path.join(SRC, "search");
assert.ok(existsSync(searchDir), "web/src/search exists (the ONBD-02 resolver tree)");
for (const file of listFiles(searchDir, [".ts"])) {
const code = codeText(file);
for (const banned of BANNED) {
assert.ok(
!code.includes(banned),
`${path.relative(process.cwd(), file)} references ${banned} — GEO-03 violated (web/src/search must stay memory-only)`,
);
}
for (const token of ["createHmac", "crypto.subtle", "sha256"]) {
assert.ok(
!code.includes(token),
`${path.relative(process.cwd(), file)} references ${token} — D-11 forbids crypto in the deep-link path`,
);
}
}
});