import { describe, it, expect, beforeAll, afterAll } from "vitest"; import Fastify from "fastify"; import cookie from "@fastify/cookie"; import rateLimit from "@fastify/rate-limit"; import jwt from "jsonwebtoken"; import prisma from "../config/database"; import { config } from "../config/env"; import { securityHeaders } from "../middleware/security"; import usersRoutes from "../routes/admin/users"; /** * Pinning tests for audit finding M10: POST /users must strip role_id for * non-admin callers (mirroring the PUT privilege-escalation guard) — a bare * users.create holder must not be able to mint an account on a privileged * role the caller itself lacks. */ const N = "usr_roleguard_"; const stamp = Date.now().toString(36); let app: ReturnType; let adminToken: string; let creatorToken: string; let creatorRoleId: number; let privilegedRoleId: number; function token(user: { id: number; username: string; roleName: string }) { return jwt.sign( { sub: user.id, username: user.username, role: user.roleName }, config.jwt.secret, { expiresIn: "15m" }, ); } async function cleanup() { await prisma.users.deleteMany({ where: { username: { startsWith: N } } }); const roles = await prisma.roles.findMany({ where: { name: { startsWith: N } }, select: { id: true }, }); const roleIds = roles.map((r) => r.id); if (roleIds.length > 0) { await prisma.role_permissions.deleteMany({ where: { role_id: { in: roleIds } }, }); await prisma.roles.deleteMany({ where: { id: { in: roleIds } } }); } } beforeAll(async () => { await cleanup(); app = Fastify({ logger: false }); await app.register(cookie); await app.register(rateLimit, { max: 1000, timeWindow: "1 minute" }); app.addHook("onRequest", securityHeaders); await app.register(usersRoutes, { prefix: "/api/admin/users" }); const admin = await prisma.users.findFirst({ where: { roles: { name: "admin" } }, include: { roles: true }, }); if (!admin) throw new Error("Test setup: admin user not found"); adminToken = token({ id: admin.id, username: admin.username, roleName: "admin", }); // Caller role: ONLY users.create. const creatorRole = await prisma.roles.create({ data: { name: `${N}creator_${stamp}`, display_name: "Creator Only" }, }); creatorRoleId = creatorRole.id; const perm = await prisma.permissions.findFirst({ where: { name: "users.create" }, }); if (!perm) throw new Error("Test setup: users.create permission missing"); await prisma.role_permissions.create({ data: { role_id: creatorRoleId, permission_id: perm.id }, }); const creator = await prisma.users.create({ data: { username: `${N}creator_${stamp}`, email: `${N}creator_${stamp}@test.local`, password_hash: "$2a$10$invalidinvalidinvalidinvalidinvalidinvalidinvalidinvali", first_name: "Creator", last_name: "Only", is_active: true, role_id: creatorRoleId, }, }); creatorToken = token({ id: creator.id, username: creator.username, roleName: creatorRole.name, }); // Target role the caller does NOT hold — its content is irrelevant, the // guard must strip ANY role assignment from a non-admin create. const privilegedRole = await prisma.roles.create({ data: { name: `${N}privileged_${stamp}`, display_name: "Privileged" }, }); privilegedRoleId = privilegedRole.id; }); afterAll(async () => { if (app) await app.close(); await cleanup(); await prisma.$disconnect(); }); describe("POST /users role escalation guard (audit M10)", () => { it("strips role_id when the caller is not admin", async () => { const res = await app.inject({ method: "POST", url: "/api/admin/users", headers: { Authorization: `Bearer ${creatorToken}`, "Content-Type": "application/json", }, payload: { username: `${N}victim_${stamp}`, email: `${N}victim_${stamp}@test.local`, password: "Heslo12345", first_name: "Victim", last_name: "User", role_id: privilegedRoleId, }, }); expect(res.statusCode).toBe(201); const created = await prisma.users.findFirst({ where: { username: `${N}victim_${stamp}` }, }); expect(created).not.toBeNull(); expect(created?.role_id).not.toBe(privilegedRoleId); }); it("keeps role assignment for an admin caller", async () => { const res = await app.inject({ method: "POST", url: "/api/admin/users", headers: { Authorization: `Bearer ${adminToken}`, "Content-Type": "application/json", }, payload: { username: `${N}byadmin_${stamp}`, email: `${N}byadmin_${stamp}@test.local`, password: "Heslo12345", first_name: "ByAdmin", last_name: "User", role_id: privilegedRoleId, }, }); expect(res.statusCode).toBe(201); const created = await prisma.users.findFirst({ where: { username: `${N}byadmin_${stamp}` }, }); expect(created?.role_id).toBe(privilegedRoleId); }); });