Files
arah2/web/test/no-storage.test.mjs
T
araharah-executor 2659b7f352 feat(03-02): D-11 Mock-Telegram harness + the four requirement gates (17 cases green)
- telegram-mock.ts: fake window.Telegram.WebApp injected into the SAME window
  (never an iframe — Bot API 10.2 origin restriction, Pitfall 6); Web-Crypto
  dev signer mirroring bot/src/security/initdata.ts's two-step HMAC (async
  makeDevInitData — node:crypto stays out of the browser bundle path);
  controllable LocationManager stream, themeChanged emitter, BackButton clicks
- location-chain.test.mjs (10 cases): GEO-01 version gate / null-WebApp guard
  / null-denial / hard timeout / stale-detector + one-read nudge; GEO-02 pin
  pickup / bounded 60s poll / Desktop no-geolocation leg ' same denial keys
- no-storage.test.mjs (3 cases): GEO-03 comment-stripped grep gate over
  web/src/**/*.{ts,html}, no-SW invariant, store.ts storage-assignment probe
- theme.test.mjs (4 cases): PX-02 colorScheme + luminance fallback + setStyle
  source-introspection (Pitfall 7) + themeChanged re-apply; document stub
  installed and restored per case
- Rule 1 fix in telegram.ts: looksStale/nudgeAndRetryTelegram accept an
  omitted lastAccepted (undefined normalized to null) ' acquireFix(deps)
  first-call contract
2026-10-22 23:46:17 +08:00

80 lines
3.6 KiB
JavaScript
Raw 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 { 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");
});