Files
HiveGrid-Wireframes/hivegrid-cloud/scripts/rotate-lighthouse-session-cookie.test.mjs
T

177 lines
5.8 KiB
JavaScript

import assert from "node:assert/strict";
import { spawn, spawnSync } from "node:child_process";
import fs from "node:fs";
import http from "node:http";
import os from "node:os";
import path from "node:path";
import { afterEach, describe, it } from "vitest";
const scriptPath = path.resolve("scripts/rotate-lighthouse-session-cookie.mjs");
const fixtures = [];
function createTempDir() {
const dir = fs.mkdtempSync(path.join(os.tmpdir(), "lighthouse-cookie-"));
fixtures.push(dir);
return dir;
}
function spawnScript(env) {
return new Promise((resolve) => {
const child = spawn(process.execPath, [scriptPath], {
env,
stdio: ["ignore", "pipe", "pipe"],
});
let stdout = "";
let stderr = "";
child.stdout.setEncoding("utf8");
child.stderr.setEncoding("utf8");
child.stdout.on("data", (chunk) => {
stdout += chunk;
});
child.stderr.on("data", (chunk) => {
stderr += chunk;
});
child.on("close", (status) => {
resolve({ status, stdout, stderr });
});
});
}
function startLoginServer() {
const requests = [];
const assertionErrors = [];
const server = http.createServer((req, res) => {
requests.push(req);
if (req.method === "GET" && req.url === "/login") {
res.writeHead(200, {
"content-type": "text/html",
"set-cookie": "RJ_session=seed-cookie; Path=/; HttpOnly",
});
res.end('<form><input value="csrf-token-123" name="csrf" type="hidden"></form>');
return;
}
if (req.method === "POST" && req.url === "/login") {
let body = "";
req.setEncoding("utf8");
req.on("data", (chunk) => {
body += chunk;
});
req.on("end", () => {
try {
const params = new URLSearchParams(body);
assert.equal(req.headers["x-csrf-token"], "csrf-token-123");
assert.equal(req.headers.cookie, "RJ_session=seed-cookie");
assert.equal(params.get("action"), "login");
assert.equal(params.get("csrf"), "csrf-token-123");
assert.equal(params.get("email"), "lighthouse@example.test");
assert.equal(params.get("password"), "super-secret-password");
res.writeHead(302, {
location: "/",
"set-cookie": "RJ_session=fresh-cookie; Path=/; HttpOnly; Secure",
});
res.end();
} catch (error) {
assertionErrors.push(error);
res.writeHead(500);
res.end("assertion failed");
}
});
return;
}
res.writeHead(404);
res.end("not found");
});
return new Promise((resolve, reject) => {
server.once("error", reject);
server.listen(0, "127.0.0.1", () => {
resolve({
url: `http://127.0.0.1:${server.address().port}/login`,
assertionErrors,
requests,
close: () => new Promise((closeResolve) => server.close(closeResolve)),
});
});
});
}
afterEach(() => {
while (fixtures.length > 0) {
fs.rmSync(fixtures.pop(), { recursive: true, force: true });
}
});
describe("rotate-lighthouse-session-cookie", () => {
it("logs in with CSRF protection and writes the fresh cookie to GITHUB_ENV", async () => {
const server = await startLoginServer();
const tempDir = createTempDir();
const githubEnv = path.join(tempDir, "github-env");
try {
const result = await spawnScript({
...process.env,
GITHUB_ENV: githubEnv,
LIGHTHOUSE_LOGIN_URL: server.url,
LIGHTHOUSE_LOGIN_EMAIL: "lighthouse@example.test",
LIGHTHOUSE_LOGIN_PASSWORD: "super-secret-password",
LIGHTHOUSE_SESSION_COOKIE_NAME: "RJ_session",
LIGHTHOUSE_COOKIE_DOMAIN: "boiler.saas-ctrl.com",
});
assert.equal(result.status, 0, result.stderr);
assert.match(result.stdout, /Minted Lighthouse session cookie RJ_session/);
assert.doesNotMatch(result.stdout, /fresh-cookie|super-secret-password/);
assert.doesNotMatch(result.stderr, /fresh-cookie|super-secret-password/);
const envFile = fs.readFileSync(githubEnv, "utf8");
assert.match(envFile, /^LIGHTHOUSE_SESSION_COOKIE=fresh-cookie$/m);
assert.match(envFile, /^LIGHTHOUSE_SESSION_COOKIE_NAME=RJ_session$/m);
assert.match(envFile, /^LIGHTHOUSE_COOKIE_DOMAIN=boiler\.saas-ctrl\.com$/m);
assert.equal(server.requests.length, 2);
assert.deepEqual(server.assertionErrors, []);
} finally {
await server.close();
}
});
it("fails fast when credentials are missing without printing secret material", () => {
const env = { ...process.env };
delete env.LIGHTHOUSE_SESSION_COOKIE;
env.LIGHTHOUSE_LOGIN_EMAIL = "";
env.LIGHTHOUSE_LOGIN_PASSWORD = "";
const result = spawnSync(process.execPath, [scriptPath], {
encoding: "utf8",
env,
});
assert.equal(result.status, 1);
assert.match(result.stderr, /missing required env var LIGHTHOUSE_LOGIN_EMAIL/);
assert.doesNotMatch(result.stderr, /password/i);
});
it("falls back to a pre-provisioned cookie while login credentials are being rolled out", () => {
const tempDir = createTempDir();
const githubEnv = path.join(tempDir, "github-env");
const result = spawnSync(process.execPath, [scriptPath], {
encoding: "utf8",
env: {
...process.env,
GITHUB_ENV: githubEnv,
LIGHTHOUSE_LOGIN_EMAIL: "",
LIGHTHOUSE_LOGIN_PASSWORD: "",
LIGHTHOUSE_SESSION_COOKIE: "existing-cookie",
LIGHTHOUSE_SESSION_COOKIE_NAME: "RJ_session",
},
});
assert.equal(result.status, 0, result.stderr);
assert.match(result.stdout, /Using pre-provisioned Lighthouse session cookie RJ_session/);
assert.doesNotMatch(result.stdout, /existing-cookie/);
assert.match(fs.readFileSync(githubEnv, "utf8"), /^LIGHTHOUSE_SESSION_COOKIE=existing-cookie$/m);
});
});