/** * Beyond our own code: after Chromium renders a document, nothing it left on disk may contain that document. Chromium * keeps a profile, a cache and crash data in temp folders; this searches all of them for a unique marker while the * browser is still running, and again after it closes. (Review follow-up, 2026-11-07.) */ import { existsSync, readdirSync, readFileSync, statSync } from "node:fs"; import { tmpdir } from "node:os"; import path from "node:path"; import { afterAll, describe, expect, it } from "vitest"; import { Renderer } from "@pagenull/renderer"; const renderer = new Renderer({ concurrency: 1 }); afterAll(() => renderer.close()); /** Every file under `dir` (skipping anything unreadable), up to a sane size. */ function files(dir: string, out: string[] = []): string[] { let entries: string[] = []; try { entries = readdirSync(dir); } catch { return out; } for (const e of entries) { const p = path.join(dir, e); let s; try { s = statSync(p); } catch { continue; } if (s.isDirectory()) files(p, out); else if (s.isFile() && s.size < 64 * 1024 * 1024) out.push(p); } return out; } /** Files holding the marker as UTF-8 or UTF-16 (Chromium stores some text as UTF-16). */ function holding(marker: string, paths: string[]): string[] { const needles = [Buffer.from(marker, "utf8"), Buffer.from(marker, "utf16le")]; return paths.filter((p) => { try { const buf = readFileSync(p); return needles.some((n) => buf.includes(n)); } catch { return false; } }); } describe("no trace on disk", () => { it("Chromium's profile, cache and temp files never hold the document, during or after a render", async () => { const tmp = tmpdir(); const before = new Set(existsSync(tmp) ? readdirSync(tmp) : []); const marker = `CANARY-${Math.random().toString(36).slice(2)}-DOCUMENT-TEXT`; const out = await renderer.pdf({ html: `
${marker}
`, allowNetwork: false }); expect(out.body.subarray(0, 5).toString()).toBe("%PDF-"); // While the browser is still up: everything it created in the temp folder. const created = readdirSync(tmp).filter((e) => !before.has(e) || /playwright|chrom/i.test(e)); const during = created.flatMap((e) => files(path.join(tmp, e))); expect(holding(marker, during)).toEqual([]); // And after it closes, in whatever is left. await renderer.close(); const left = readdirSync(tmp).filter((e) => !before.has(e) || /playwright|chrom/i.test(e)); expect(holding(marker, left.flatMap((e) => files(path.join(tmp, e))))).toEqual([]); }, 120_000); });