import assert from "node:assert/strict"; import { createServer } from "node:http"; import { spawn } from "node:child_process"; import { once } from "node:events"; import { fileURLToPath } from "node:url"; const host = "http://127.0.0.1:3005"; const upstream = createServer((req, res) => { res.setHeader("Content-Type", "application/json"); if (req.url.endsWith("/auth/login")) { res.end( JSON.stringify({ access_token: "test-only-session", expires_in: 28800, token_type: "Bearer", }), ); } else if (req.url.endsWith("/profile")) { res.statusCode = req.headers.authorization === "Bearer test-only-session" ? 200 : 401; res.end( JSON.stringify( res.statusCode === 200 ? { username: "test" } : { error: "invalid_session" }, ), ); } else { res.statusCode = 404; res.end("{}"); } }); await new Promise((r) => upstream.listen(0, "127.0.0.1", r)); const port = upstream.address().port; const app = spawn( process.execPath, [ "node_modules/next/dist/bin/next", "start", "--hostname", "127.0.0.1", "--port", "3005", ], { cwd: fileURLToPath(new URL("../", import.meta.url)), env: { ...process.env, FOUNDRY_API_ORIGIN: "http://127.0.0.1:" + port }, stdio: ["ignore", "pipe", "pipe"], }, ); let startup = ""; app.stdout.on("data", (x) => (startup += x)); app.stderr.on("data", (x) => (startup += x)); try { let ready = false; for (let n = 0; n < 100; n++) { if (app.exitCode !== null) throw new Error("Frontend failed to start: " + startup); try { if ((await fetch(host)).ok) { ready = true; break; } } catch {} await new Promise((r) => setTimeout(r, 100)); } assert(ready, "Frontend did not start"); const res = await fetch(host + "/api/foundry/auth/login", { method: "POST", headers: { "Content-Type": "application/json", Origin: host }, body: "{}", }); assert.equal(res.status, 200); assert.match(res.headers.get("server-timing") || "", /upstream;dur=\d/); const body = await res.json(); assert.equal(body.access_token, undefined); const cookie = res.headers.get("set-cookie"); assert(cookie?.includes("HttpOnly")); assert(cookie.includes("SameSite=Lax")); assert(cookie.includes("Max-Age=28800")); const session = cookie.split(";")[0]; assert.equal( ( await fetch(host + "/api/foundry/profile", { headers: { Cookie: session }, }) ).status, 200, ); assert.equal((await fetch(host + "/api/foundry/profile")).status, 401); for (const origin of ["https://example.com", "null"]) assert.equal( ( await fetch(host + "/api/foundry/auth/login", { method: "POST", headers: { Origin: origin, "Content-Type": "application/json" }, body: "{}", }) ).status, 403, ); assert.equal((await fetch(host + "/api/foundry/unlisted")).status, 404); assert.equal( (await fetch(host + "/api/oauth/slack?challenge=bad")).status, 400, ); console.log( "PASS: session cookie, token removal, authenticated proxy, invalid session, cross-origin rejection, path allowlist, OAuth validation", ); } finally { const exited = once(app, "exit"); app.kill("SIGTERM"); await exited; await new Promise((r) => upstream.close(r)); }