import { afterAll, beforeAll, describe, expect, it } from "vitest"; import request from "supertest"; import type { FastifyInstance } from "fastify"; import { buildApp } from "../src/server"; let authApp: FastifyInstance; let csrfApp: FastifyInstance; const capturedEvents: Array> = []; function attachSecurityEventCapture(app: FastifyInstance) { const logger = app.log as any; const originalChild = logger.child.bind(logger); logger.child = (...args: any[]) => { const child = originalChild(...args); const originalWarn = child.warn.bind(child); child.warn = (obj: unknown, ...rest: unknown[]) => { if (obj && typeof obj === "object") { const payload = obj as Record; if (typeof payload.securityEvent === "string") capturedEvents.push(payload); } return originalWarn(obj, ...rest); }; return child; }; } beforeAll(async () => { authApp = await buildApp({ AUTH_DISABLED: false, SEED_DEFAULT_BUDGET: false }); await authApp.ready(); (authApp as any).ensureUser = async () => undefined; attachSecurityEventCapture(authApp); csrfApp = await buildApp({ AUTH_DISABLED: true, SEED_DEFAULT_BUDGET: false }); await csrfApp.ready(); (csrfApp as any).ensureUser = async () => undefined; attachSecurityEventCapture(csrfApp); }); afterAll(async () => { if (authApp) await authApp.close(); if (csrfApp) await csrfApp.close(); }); describe("A09 Security Logging and Monitoring Failures", () => { it("emits structured security log for unauthenticated protected-route access", async () => { capturedEvents.length = 0; const res = await request(authApp.server).get("/dashboard"); expect(res.status).toBe(401); const event = capturedEvents.find((payload) => { return payload.securityEvent === "auth.unauthenticated_request"; }); expect(event).toBeTruthy(); expect(event?.outcome).toBe("failure"); expect(typeof event?.requestId).toBe("string"); expect(typeof event?.ip).toBe("string"); }); it("emits structured security log for csrf validation failures", async () => { capturedEvents.length = 0; const res = await request(csrfApp.server) .post("/me") .set("x-user-id", `csrf-user-${Date.now()}`) .send({ displayName: "NoCsrf" }); expect(res.status).toBe(403); expect(res.body.code).toBe("CSRF"); const event = capturedEvents.find((payload) => payload.securityEvent === "csrf.validation"); expect(event).toBeTruthy(); expect(event?.outcome).toBe("failure"); expect(typeof event?.requestId).toBe("string"); expect(typeof event?.ip).toBe("string"); }); it("emits structured security log for forgot-password requests without raw token data", async () => { capturedEvents.length = 0; const res = await request(authApp.server) .post("/auth/forgot-password/request") .send({ email: `missing-${Date.now()}@test.dev` }); expect(res.status).toBe(200); const event = capturedEvents.find( (payload) => payload.securityEvent === "auth.password_reset.request" ); expect(event).toBeTruthy(); expect(event?.outcome).toBe("success"); expect(event && "token" in event).toBe(false); }); });