Files
SkyMoney/api/tests/access-control.account-delete.test.ts
Ricearoni1245 1645896e54
Some checks failed
Deploy / deploy (push) Has been cancelled
chore: ran security check for OWASP top 10
2026-03-01 20:44:55 -06:00

84 lines
2.3 KiB
TypeScript

import { createHash, randomUUID } from "node:crypto";
import { afterAll, beforeAll, describe, expect, it } from "vitest";
import request from "supertest";
import type { FastifyInstance } from "fastify";
import { PrismaClient } from "@prisma/client";
import argon2 from "argon2";
import { buildApp } from "../src/server";
const prisma = new PrismaClient();
let app: FastifyInstance;
beforeAll(async () => {
app = await buildApp({ AUTH_DISABLED: true, SEED_DEFAULT_BUDGET: false });
await app.ready();
});
afterAll(async () => {
if (app) {
await app.close();
}
await prisma.$disconnect();
});
describe("Account delete access control", () => {
it("rejects confirm-delete when payload email targets another user", async () => {
const attackerEmail = `attacker-${Date.now()}@test.dev`;
const victimEmail = `victim-${Date.now()}@test.dev`;
const victimPassword = "VictimPass123!";
const deleteCode = "654321";
const [attacker, victim] = await Promise.all([
prisma.user.create({
data: {
email: attackerEmail,
passwordHash: await argon2.hash("AttackerPass123!"),
emailVerified: true,
},
}),
prisma.user.create({
data: {
email: victimEmail,
passwordHash: await argon2.hash(victimPassword),
emailVerified: true,
},
}),
]);
try {
await prisma.emailToken.create({
data: {
userId: victim.id,
type: "delete",
tokenHash: createHash("sha256").update(deleteCode).digest("hex"),
expiresAt: new Date(Date.now() + 60_000),
},
});
const csrf = randomUUID().replace(/-/g, "");
const res = await request(app.server)
.post("/account/confirm-delete")
.set("x-user-id", attacker.id)
.set("x-csrf-token", csrf)
.set("Cookie", `csrf=${csrf}`)
.send({
email: victim.email,
code: deleteCode,
password: victimPassword,
});
expect(res.status).toBe(403);
const victimStillExists = await prisma.user.findUnique({
where: { id: victim.id },
select: { id: true },
});
expect(victimStillExists?.id).toBe(victim.id);
} finally {
await prisma.user.deleteMany({
where: { id: { in: [attacker.id, victim.id] } },
});
}
});
});