nexus/server/src/__tests__/board-mutation-guard.test.ts
dotta 37c2c4acc4 Add browser-based board CLI auth flow
Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-03-23 08:46:05 -05:00

105 lines
3.1 KiB
TypeScript

import { describe, expect, it, vi } from "vitest";
import express from "express";
import request from "supertest";
import { boardMutationGuard } from "../middleware/board-mutation-guard.js";
function createApp(
actorType: "board" | "agent",
boardSource: "session" | "local_implicit" | "board_key" = "session",
) {
const app = express();
app.use(express.json());
app.use((req, _res, next) => {
req.actor = actorType === "board"
? { type: "board", userId: "board", source: boardSource }
: { type: "agent", agentId: "agent-1" };
next();
});
app.use(boardMutationGuard());
app.post("/mutate", (_req, res) => {
res.status(204).end();
});
app.get("/read", (_req, res) => {
res.status(204).end();
});
return app;
}
describe("boardMutationGuard", () => {
it("allows safe methods for board actor", async () => {
const app = createApp("board");
const res = await request(app).get("/read");
expect(res.status).toBe(204);
});
it("blocks board mutations without trusted origin", () => {
const middleware = boardMutationGuard();
const req = {
method: "POST",
actor: { type: "board", userId: "board", source: "session" },
header: () => undefined,
} as any;
const res = {
status: vi.fn().mockReturnThis(),
json: vi.fn(),
} as any;
const next = vi.fn();
middleware(req, res, next);
expect(next).not.toHaveBeenCalled();
expect(res.status).toHaveBeenCalledWith(403);
expect(res.json).toHaveBeenCalledWith({
error: "Board mutation requires trusted browser origin",
});
});
it("allows local implicit board mutations without origin", async () => {
const app = createApp("board", "local_implicit");
const res = await request(app).post("/mutate").send({ ok: true });
expect(res.status).toBe(204);
});
it("allows board bearer-key mutations without origin", async () => {
const app = createApp("board", "board_key");
const res = await request(app).post("/mutate").send({ ok: true });
expect(res.status).toBe(204);
});
it("allows board mutations from trusted origin", async () => {
const app = createApp("board");
const res = await request(app)
.post("/mutate")
.set("Origin", "http://localhost:3100")
.send({ ok: true });
expect(res.status).toBe(204);
});
it("allows board mutations from trusted referer origin", async () => {
const app = createApp("board");
const res = await request(app)
.post("/mutate")
.set("Referer", "http://localhost:3100/issues/abc")
.send({ ok: true });
expect(res.status).toBe(204);
});
it("does not block authenticated agent mutations", async () => {
const middleware = boardMutationGuard();
const req = {
method: "POST",
actor: { type: "agent", agentId: "agent-1" },
header: () => undefined,
} as any;
const res = {
status: vi.fn().mockReturnThis(),
json: vi.fn(),
} as any;
const next = vi.fn();
middleware(req, res, next);
expect(next).toHaveBeenCalledOnce();
expect(res.status).not.toHaveBeenCalled();
});
});