Files
SkyMoney/api/tests/security-logging-monitoring-failures.test.ts
Ricearoni1245 15e0c0a88a
Some checks failed
Deploy / deploy (push) Successful in 1m28s
Security Tests / security-non-db (push) Failing after 18s
Security Tests / security-db (push) Failing after 22s
feat: implement forgot password, added security updates
2026-03-01 21:47:15 -06:00

94 lines
3.2 KiB
TypeScript

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<Record<string, unknown>> = [];
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<string, unknown>;
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);
});
});