import { afterAll, beforeAll, expect, test } from "vitest"; import { createLocalAuthFixture, type LocalAuthFixture, localPublicUrl } from "./auth-test-fixtures.js"; interface StateChangingRoute { family: string; method: "POST" | "PUT" | "DELETE"; url: string; } const stateChangingRoutes: readonly StateChangingRoute[] = [ { family: "auth logout", method: "POST", url: "/auth/logout" }, { family: "runtime prewarm", method: "POST", url: "/runtime/prewarm" }, { family: "session create", method: "POST", url: "/sessions" }, { family: "session mutation", method: "POST", url: "/sessions/session-1/response" }, { family: "session mutation", method: "POST", url: "/sessions/session-1/steer" }, { family: "session mutation", method: "POST", url: "/sessions/session-1/resume" }, { family: "session mutation", method: "POST", url: "/sessions/session-1/close" }, { family: "session mutation", method: "POST", url: "/sessions/session-1/rename" }, { family: "session mutation", method: "POST", url: "/sessions/session-1/group" }, { family: "session archive", method: "POST", url: "/sessions/session-1/archive" }, { family: "session archive", method: "POST", url: "/sessions/session-1/unarchive" }, { family: "session delete", method: "DELETE", url: "/sessions/session-1" }, { family: "SQL preview", method: "POST", url: "/sessions/session-1/sql/preview" }, { family: "SQL export", method: "POST", url: "/sessions/session-1/sql/export" }, { family: "workspace registry", method: "POST", url: "/workspace-registry/pull" }, { family: "workspace validation", method: "POST", url: "/workspaces/validate" }, { family: "workspace secrets", method: "PUT", url: "/workspaces/workspace-1/secrets" }, { family: "workspace secrets", method: "DELETE", url: "/workspaces/workspace-1/secrets/secret-1" }, { family: "workspace diagnostics", method: "POST", url: "/workspaces/workspace-1/test" }, { family: "settings", method: "PUT", url: "/settings" }, { family: "Pi management", method: "PUT", url: "/pi-management/config" }, { family: "Pi management", method: "POST", url: "/pi-management/test" }, { family: "maintenance", method: "POST", url: "/internal/maintenance/activate" }, { family: "maintenance", method: "POST", url: "/internal/maintenance/deactivate" }, ]; let fixture: LocalAuthFixture; beforeAll(async () => { fixture = await createLocalAuthFixture(); }); afterAll(async () => { await fixture.close(); }); test.each(stateChangingRoutes)( "$family $method $url rejects every production CSRF/session-boundary failure before downstream code", async ({ method, url }) => { const failures = [ fixture.sessionHeaders({ "x-thothii-csrf": undefined }), fixture.sessionHeaders({ "x-thothii-csrf": "malformed" }), fixture.sessionHeaders({ origin: undefined }), fixture.sessionHeaders({ origin: "http://wrong.example.test" }), fixture.sessionHeaders({ "sec-fetch-site": "cross-site" }), ]; for (const headers of failures) { fixture.resetDownstreamHits(); const response = await fixture.app.inject({ method, url, headers, payload: {} }); expect(response.statusCode).toBe(403); expect(response.json()).toEqual({ code: "csrf_failed", error: "Request origin validation failed" }); expect(fixture.downstreamHits()).toBe(0); } }, ); test("a valid real local session cookie and derived CSRF token cross the same production boundary", async () => { fixture.resetDownstreamHits(); const response = await fixture.app.inject({ method: "PUT", url: "/settings", headers: fixture.sessionHeaders(), payload: {}, }); expect(response.statusCode).toBe(200); expect(fixture.downstreamHits()).toBe(1); expect(localPublicUrl).toBe("http://127.0.0.1:8787"); });