mirror of
https://github.com/snapotter-hq/SnapOtter.git
synced 2026-08-03 07:46:42 +02:00
# Conflicts: # tests/integration/generated/settings-matrix.test.ts # tests/integration/platform/api.test.ts # tests/integration/platform/concurrent.test.ts # tests/integration/platform/factory-multi-input.test.ts # tests/integration/security/adversarial-comprehensive.test.ts # tests/integration/security/adversarial-coverage-gaps.test.ts # tests/integration/security/adversarial-extended.test.ts # tests/integration/security/adversarial-final-gaps.test.ts # tests/integration/security/adversarial-matrix.test.ts # tests/integration/security/adversarial-security.test.ts # tests/integration/security/adversarial.test.ts # tests/integration/tools/image/color-adjustments.test.ts
157 lines
4.9 KiB
TypeScript
157 lines
4.9 KiB
TypeScript
/**
|
|
* Unit tests for security hardening of auth-related env defaults and Zod schemas.
|
|
*
|
|
* These tests verify that:
|
|
* - Rate limit and login attempt defaults were lowered to secure values
|
|
* - Auth Zod schemas enforce max length on username/password fields
|
|
* - New storage env vars have correct defaults
|
|
*/
|
|
import { describe, expect, it, vi } from "vitest";
|
|
|
|
vi.mock("../../../apps/api/src/db/index.js", () => ({
|
|
db: {},
|
|
pool: {},
|
|
closeDb: async () => {},
|
|
schema: {},
|
|
}));
|
|
|
|
import { loadEnv } from "../../../apps/api/src/lib/env.js";
|
|
import {
|
|
changePasswordSchema,
|
|
loginSchema,
|
|
registerSchema,
|
|
resetPasswordSchema,
|
|
} from "../../../apps/api/src/plugins/auth.js";
|
|
|
|
// ── Env defaults ─────────────────────────────────────────────────────────────
|
|
|
|
describe("Security: env defaults", () => {
|
|
it("LOGIN_ATTEMPT_LIMIT defaults to 10", () => {
|
|
const env = loadEnv();
|
|
expect(env.LOGIN_ATTEMPT_LIMIT).toBe(10);
|
|
});
|
|
|
|
it("RATE_LIMIT_PER_MIN is parsed correctly (test env overrides to 10000)", () => {
|
|
// vitest.config.ts sets RATE_LIMIT_PER_MIN=10000 for tests
|
|
const env = loadEnv();
|
|
expect(env.RATE_LIMIT_PER_MIN).toBe(10000);
|
|
});
|
|
|
|
it("MAX_STORAGE_PER_USER_MB defaults to 5000", () => {
|
|
const env = loadEnv();
|
|
expect(env.MAX_STORAGE_PER_USER_MB).toBe(5000);
|
|
});
|
|
|
|
it("MAX_WORKSPACE_SIZE_GB defaults to 10", () => {
|
|
const env = loadEnv();
|
|
expect(env.MAX_WORKSPACE_SIZE_GB).toBe(10);
|
|
});
|
|
});
|
|
|
|
// ── Auth Zod schema max length enforcement ───────────────────────────────────
|
|
|
|
describe("Security: loginSchema max lengths", () => {
|
|
it("rejects username longer than 255 chars", () => {
|
|
const result = loginSchema.safeParse({
|
|
username: "a".repeat(256),
|
|
password: "ValidPass1",
|
|
});
|
|
expect(result.success).toBe(false);
|
|
if (!result.success) {
|
|
expect(result.error.issues.some((i) => i.message === "Username too long")).toBe(true);
|
|
}
|
|
});
|
|
|
|
it("accepts username at exactly 255 chars", () => {
|
|
const result = loginSchema.safeParse({
|
|
username: "a".repeat(255),
|
|
password: "ValidPass1",
|
|
});
|
|
expect(result.success).toBe(true);
|
|
});
|
|
|
|
it("rejects password longer than 1024 chars", () => {
|
|
const result = loginSchema.safeParse({
|
|
username: "testuser",
|
|
password: "a".repeat(1025),
|
|
});
|
|
expect(result.success).toBe(false);
|
|
if (!result.success) {
|
|
expect(result.error.issues.some((i) => i.message === "Password too long")).toBe(true);
|
|
}
|
|
});
|
|
|
|
it("accepts password at exactly 1024 chars", () => {
|
|
const result = loginSchema.safeParse({
|
|
username: "testuser",
|
|
password: "a".repeat(1024),
|
|
});
|
|
expect(result.success).toBe(true);
|
|
});
|
|
});
|
|
|
|
describe("Security: changePasswordSchema max lengths", () => {
|
|
it("rejects oversized currentPassword", () => {
|
|
const result = changePasswordSchema.safeParse({
|
|
currentPassword: "a".repeat(1025),
|
|
newPassword: "ValidPass1",
|
|
});
|
|
expect(result.success).toBe(false);
|
|
});
|
|
|
|
it("rejects oversized newPassword", () => {
|
|
const result = changePasswordSchema.safeParse({
|
|
currentPassword: "ValidPass1",
|
|
newPassword: "a".repeat(1025),
|
|
});
|
|
expect(result.success).toBe(false);
|
|
});
|
|
});
|
|
|
|
describe("Security: registerSchema max lengths", () => {
|
|
it("rejects username longer than 255 chars", () => {
|
|
const result = registerSchema.safeParse({
|
|
username: "a".repeat(256),
|
|
password: "ValidPass1",
|
|
});
|
|
expect(result.success).toBe(false);
|
|
if (!result.success) {
|
|
expect(result.error.issues.some((i) => i.message === "Username too long")).toBe(true);
|
|
}
|
|
});
|
|
|
|
it("rejects password longer than 1024 chars", () => {
|
|
const result = registerSchema.safeParse({
|
|
username: "testuser",
|
|
password: "a".repeat(1025),
|
|
});
|
|
expect(result.success).toBe(false);
|
|
});
|
|
});
|
|
|
|
describe("Security: resetPasswordSchema max lengths", () => {
|
|
it("rejects newPassword longer than 1024 chars", () => {
|
|
const result = resetPasswordSchema.safeParse({
|
|
newPassword: "a".repeat(1025),
|
|
});
|
|
expect(result.success).toBe(false);
|
|
if (!result.success) {
|
|
expect(result.error.issues.some((i) => i.message === "Password too long")).toBe(true);
|
|
}
|
|
});
|
|
});
|
|
|
|
describe("Security: all schemas accept valid input", () => {
|
|
it("all schemas pass with normal-length fields", () => {
|
|
expect(loginSchema.safeParse({ username: "admin", password: "Pass1234" }).success).toBe(true);
|
|
expect(
|
|
changePasswordSchema.safeParse({ currentPassword: "Pass1234", newPassword: "NewPass1" })
|
|
.success,
|
|
).toBe(true);
|
|
expect(registerSchema.safeParse({ username: "newuser", password: "Pass1234" }).success).toBe(
|
|
true,
|
|
);
|
|
expect(resetPasswordSchema.safeParse({ newPassword: "Pass1234" }).success).toBe(true);
|
|
});
|
|
});
|