import { afterEach, beforeEach, describe, expect, it } from "vitest"; import { mkdtempSync, rmSync, statSync } from "node:fs"; import { join } from "node:path"; import { tmpdir } from "node:os"; import { Storage } from "../src/storage.js"; describe("storage file permissions", () => { let dir: string; beforeEach(() => { dir = mkdtempSync(join(tmpdir(), "oppi-server-storage-perms-")); }); afterEach(() => { rmSync(dir, { recursive: true, force: true }); }); it("writes config file as owner-only", () => { const storage = new Storage(dir); storage.ensurePaired(); const configMode = statSync(join(dir, "config.json")).mode & 0o777; expect(configMode).toBe(0o600); }); it("writes session sqlite database and workspace records as owner-only", () => { const storage = new Storage(dir); storage.ensurePaired(); const session = storage.createSession("security-check", "anthropic/claude-sonnet-4-0"); const sessionDbPath = join(dir, "session-state.db"); const sessionsDir = join(dir, "sessions"); const sessionPath = join(sessionsDir, `${session.id}.json`); const workspace = storage.createWorkspace({ name: "default" }); const workspacePath = join(dir, "workspaces", `${workspace.id}.json`); expect(() => statSync(sessionsDir)).toThrow(); expect(statSync(join(dir, "workspaces")).mode & 0o777).toBe(0o700); expect(statSync(sessionDbPath).mode & 0o777).toBe(0o600); expect(() => statSync(sessionPath)).toThrow(); expect(statSync(workspacePath).mode & 0o777).toBe(0o600); }); });