chore: ran security check for OWASP top 10
Some checks failed
Deploy / deploy (push) Has been cancelled

This commit is contained in:
2026-03-01 20:44:55 -06:00
parent 023587c48c
commit 1645896e54
20 changed files with 1916 additions and 168 deletions

View File

@@ -0,0 +1,83 @@
import { afterAll, beforeEach, describe, expect, it, vi } from "vitest";
const ORIGINAL_ENV = { ...process.env };
beforeEach(() => {
vi.resetModules();
vi.resetAllMocks();
process.env = { ...ORIGINAL_ENV };
});
afterAll(() => {
process.env = ORIGINAL_ENV;
});
function setRequiredBaseEnv() {
process.env.DATABASE_URL = "postgres://app:app@127.0.0.1:5432/skymoney";
process.env.JWT_SECRET = "test-jwt-secret-32-chars-min-abcdef";
process.env.COOKIE_SECRET = "test-cookie-secret-32-chars-abcdef";
process.env.CORS_ORIGINS = "https://allowed.example.com";
process.env.AUTH_DISABLED = "0";
process.env.SEED_DEFAULT_BUDGET = "0";
}
describe("A04 Cryptographic Failures", () => {
it("rejects non-https APP_ORIGIN in production", async () => {
setRequiredBaseEnv();
process.env.NODE_ENV = "production";
process.env.APP_ORIGIN = "http://allowed.example.com";
await expect(import("../src/env")).rejects.toThrow(
"APP_ORIGIN must use https:// in production."
);
});
it("applies secure JWT defaults for issuer and audience", async () => {
setRequiredBaseEnv();
process.env.NODE_ENV = "production";
process.env.APP_ORIGIN = "https://allowed.example.com";
delete process.env.JWT_ISSUER;
delete process.env.JWT_AUDIENCE;
const envModule = await import("../src/env");
expect(envModule.env.JWT_ISSUER).toBe("skymoney-api");
expect(envModule.env.JWT_AUDIENCE).toBe("skymoney-web");
});
it("registers fastify-jwt with explicit algorithm and claim validation", async () => {
const jwtPlugin = vi.fn(async () => undefined);
vi.doMock("@fastify/jwt", () => ({
default: jwtPlugin,
}));
const { buildApp } = await import("../src/server");
const app = await buildApp({
NODE_ENV: "test",
DATABASE_URL: "postgres://app:app@127.0.0.1:5432/skymoney",
JWT_SECRET: "test-jwt-secret-32-chars-min-abcdef",
COOKIE_SECRET: "test-cookie-secret-32-chars-abcdef",
AUTH_DISABLED: true,
SEED_DEFAULT_BUDGET: false,
APP_ORIGIN: "http://localhost:5173",
});
try {
await app.ready();
expect(jwtPlugin.mock.calls.length).toBeGreaterThan(0);
const jwtCall = jwtPlugin.mock.calls.find((call) => {
const opts = call[1] as Record<string, any> | undefined;
return !!opts?.sign && !!opts?.verify;
});
expect(jwtCall).toBeTruthy();
const jwtOptions = jwtCall?.[1] as Record<string, any>;
expect(jwtOptions.sign.algorithm).toBe("HS256");
expect(jwtOptions.sign.iss).toBe("skymoney-api");
expect(jwtOptions.sign.aud).toBe("skymoney-web");
expect(jwtOptions.verify.algorithms).toEqual(["HS256"]);
expect(jwtOptions.verify.allowedIss).toBe("skymoney-api");
expect(jwtOptions.verify.allowedAud).toBe("skymoney-web");
} finally {
await app.close();
}
});
});