1import { readdirSync, readFileSync, statSync } from "node:fs";2import { join } from "node:path";3import { fileURLToPath } from "node:url";4import { describe, expect, it, vi } from "vitest";56import { hardenExtensionStorage } from "./storage-access";78function area(setAccessLevel?: (options: { accessLevel: string }) => Promise<void>) {9 return setAccessLevel ? { setAccessLevel } : {};10}1112describe("extension storage hardening (PB-26-203)", () => {13 it("requests trusted-only access for both local and session storage", async () => {14 const localSetAccessLevel = vi.fn(async () => undefined);15 const sessionSetAccessLevel = vi.fn(async () => undefined);16 vi.stubGlobal("chrome", {17 storage: {18 local: area(localSetAccessLevel),19 session: area(sessionSetAccessLevel),20 },21 });2223 const result = await hardenExtensionStorage();2425 expect(localSetAccessLevel).toHaveBeenCalledWith({ accessLevel: "TRUSTED_CONTEXTS" });26 expect(sessionSetAccessLevel).toHaveBeenCalledWith({ accessLevel: "TRUSTED_CONTEXTS" });27 expect(result).toEqual({28 localHardened: true,29 sessionHardened: true,30 sessionEscrowAvailable: true,31 });32 });3334 it("fails closed on escrow when session hardening is rejected", async () => {35 const localSetAccessLevel = vi.fn(async () => undefined);36 const sessionSetAccessLevel = vi.fn(async () => { throw new Error("unsupported"); });37 vi.stubGlobal("chrome", {38 storage: {39 local: area(localSetAccessLevel),40 session: area(sessionSetAccessLevel),41 },42 });4344 const result = await hardenExtensionStorage();4546 expect(result).toEqual({47 localHardened: true,48 sessionHardened: false,49 sessionEscrowAvailable: false,50 });51 });5253 it("treats a missing session area as no escrow without failing startup", async () => {54 const localSetAccessLevel = vi.fn(async () => undefined);55 vi.stubGlobal("chrome", { storage: { local: area(localSetAccessLevel) } });5657 const result = await hardenExtensionStorage();5859 expect(result.sessionEscrowAvailable).toBe(false);60 expect(result.localHardened).toBe(true);61 });6263 it("never widens session storage to untrusted contexts", () => {64 const hits: string[] = [];65 66 67 const callPattern = /setAccessLevel\(\s*\{\s*accessLevel:\s*"TRUSTED_AND_UNTRUSTED_CONTEXTS"/;68 const walk = (dir: string) => {69 for (const name of readdirSync(dir)) {70 const path = join(dir, name);71 if (statSync(path).isDirectory()) {72 walk(path);73 } else if (path.endsWith(".ts") || path.endsWith(".tsx")) {74 if (callPattern.test(readFileSync(path, "utf8"))) hits.push(path);75 }76 }77 };78 walk(join(fileURLToPath(new URL(".", import.meta.url)), ".."));7980 expect(hits).toEqual([]);81 });82});