storage-access.test.ts

2.9 KB · raw

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    // The forbidden level name appears in this file's own assertion — match66    // only real setAccessLevel calls, which is the shape a violation takes.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});