import { readdirSync, readFileSync, statSync } from "node:fs"; import { join } from "node:path"; import { fileURLToPath } from "node:url"; import { describe, expect, it, vi } from "vitest"; import { hardenExtensionStorage } from "./storage-access"; function area(setAccessLevel?: (options: { accessLevel: string }) => Promise) { return setAccessLevel ? { setAccessLevel } : {}; } describe("extension storage hardening (PB-26-203)", () => { it("requests trusted-only access for both local and session storage", async () => { const localSetAccessLevel = vi.fn(async () => undefined); const sessionSetAccessLevel = vi.fn(async () => undefined); vi.stubGlobal("chrome", { storage: { local: area(localSetAccessLevel), session: area(sessionSetAccessLevel), }, }); const result = await hardenExtensionStorage(); expect(localSetAccessLevel).toHaveBeenCalledWith({ accessLevel: "TRUSTED_CONTEXTS" }); expect(sessionSetAccessLevel).toHaveBeenCalledWith({ accessLevel: "TRUSTED_CONTEXTS" }); expect(result).toEqual({ localHardened: true, sessionHardened: true, sessionEscrowAvailable: true, }); }); it("fails closed on escrow when session hardening is rejected", async () => { const localSetAccessLevel = vi.fn(async () => undefined); const sessionSetAccessLevel = vi.fn(async () => { throw new Error("unsupported"); }); vi.stubGlobal("chrome", { storage: { local: area(localSetAccessLevel), session: area(sessionSetAccessLevel), }, }); const result = await hardenExtensionStorage(); expect(result).toEqual({ localHardened: true, sessionHardened: false, sessionEscrowAvailable: false, }); }); it("treats a missing session area as no escrow without failing startup", async () => { const localSetAccessLevel = vi.fn(async () => undefined); vi.stubGlobal("chrome", { storage: { local: area(localSetAccessLevel) } }); const result = await hardenExtensionStorage(); expect(result.sessionEscrowAvailable).toBe(false); expect(result.localHardened).toBe(true); }); it("never widens session storage to untrusted contexts", () => { const hits: string[] = []; // The forbidden level name appears in this file's own assertion — match // only real setAccessLevel calls, which is the shape a violation takes. const callPattern = /setAccessLevel\(\s*\{\s*accessLevel:\s*"TRUSTED_AND_UNTRUSTED_CONTEXTS"/; const walk = (dir: string) => { for (const name of readdirSync(dir)) { const path = join(dir, name); if (statSync(path).isDirectory()) { walk(path); } else if (path.endsWith(".ts") || path.endsWith(".tsx")) { if (callPattern.test(readFileSync(path, "utf8"))) hits.push(path); } } }; walk(join(fileURLToPath(new URL(".", import.meta.url)), "..")); expect(hits).toEqual([]); }); });