diff --git a/server/src/__tests__/board-mutation-guard.test.ts b/server/src/__tests__/board-mutation-guard.test.ts index 62e1e68e..9a4789b2 100644 --- a/server/src/__tests__/board-mutation-guard.test.ts +++ b/server/src/__tests__/board-mutation-guard.test.ts @@ -84,6 +84,28 @@ describe("boardMutationGuard", () => { expect(res.status).toBe(204); }); + it("allows board mutations when x-forwarded-host matches origin", async () => { + const app = createApp("board"); + const res = await request(app) + .post("/mutate") + .set("Host", "127.0.0.1") + .set("X-Forwarded-Host", "10.90.10.20:3443") + .set("Origin", "https://10.90.10.20:3443") + .send({ ok: true }); + expect(res.status).toBe(204); + }); + + it("blocks board mutations when x-forwarded-host does not match origin", async () => { + const app = createApp("board"); + const res = await request(app) + .post("/mutate") + .set("Host", "127.0.0.1") + .set("X-Forwarded-Host", "10.90.10.20:3443") + .set("Origin", "https://evil.example.com") + .send({ ok: true }); + expect(res.status).toBe(403); + }); + it("does not block authenticated agent mutations", async () => { const middleware = boardMutationGuard(); const req = { diff --git a/server/src/middleware/board-mutation-guard.ts b/server/src/middleware/board-mutation-guard.ts index de66a4ce..feff3b40 100644 --- a/server/src/middleware/board-mutation-guard.ts +++ b/server/src/middleware/board-mutation-guard.ts @@ -18,7 +18,8 @@ function parseOrigin(value: string | undefined) { function trustedOriginsForRequest(req: Request) { const origins = new Set(DEFAULT_DEV_ORIGINS.map((value) => value.toLowerCase())); - const host = req.header("host")?.trim(); + const forwardedHost = req.header("x-forwarded-host")?.split(",")[0]?.trim(); + const host = forwardedHost || req.header("host")?.trim(); if (host) { origins.add(`http://${host}`.toLowerCase()); origins.add(`https://${host}`.toLowerCase());