fix: resolve TODO.md security/perf/test-coverage backlog

Fixes the 13-item codebase health scan backlog: wraps meal-plan
generation in a transaction, adds missing userId/GIN indexes, fixes
an IPv6-parsing gap in the webhook SSRF guard (and an identical
duplicated bug in the AI URL-import path, now consolidated onto one
implementation), paginates the collections list, dedupes the AI
recipe Zod schemas, wires up Stripe tier sync, rate-limits AI key
rotation, gets `pnpm typecheck` actually working, and adds test
coverage for the previously-untested admin/webhooks routes.

Two flagged issues (collection removeRecipeId IDOR, tier-limit race)
turned out to already be fixed/non-issues on inspection — noted in
TODO.md rather than silently dropped.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Arnaud
2026-07-02 12:12:42 +02:00
parent 2154512e54
commit d2faf98ac1
38 changed files with 7598 additions and 315 deletions
@@ -0,0 +1,80 @@
import { describe, it, expect, vi, beforeEach } from "vitest";
import { NextRequest } from "next/server";
const mockAdminSession = { user: { id: "admin-1", role: "admin" } };
vi.mock("next/headers", () => ({
headers: vi.fn().mockResolvedValue(new Headers()),
}));
vi.mock("@/lib/auth/server", () => ({
auth: { api: { getSession: vi.fn() } },
}));
vi.mock("@/lib/site-settings", () => ({
setSiteSetting: vi.fn().mockResolvedValue(undefined),
}));
const { mockSelectChain, mockInsertValues } = vi.hoisted(() => {
const mockSelectChain = {
from: vi.fn().mockReturnThis(),
where: vi.fn().mockResolvedValue([{ role: "admin" }]),
};
return { mockSelectChain, mockInsertValues: vi.fn().mockResolvedValue(undefined) };
});
vi.mock("@epicure/db", () => ({
db: {
select: vi.fn(() => mockSelectChain),
insert: vi.fn(() => ({ values: mockInsertValues })),
},
users: { id: "id", role: "role" },
auditLogs: {},
eq: vi.fn((a, b) => ({ a, b, op: "eq" })),
}));
const { auth } = await import("@/lib/auth/server");
const { setSiteSetting } = await import("@/lib/site-settings");
import { PUT } from "../route";
function makeRequest(body: unknown) {
return new NextRequest("http://localhost/api/v1/admin/settings", {
method: "PUT",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(body),
});
}
beforeEach(() => {
vi.clearAllMocks();
vi.mocked(auth.api.getSession).mockResolvedValue(mockAdminSession as never);
mockSelectChain.where.mockResolvedValue([{ role: "admin" }]);
});
describe("PUT /api/v1/admin/settings", () => {
it("returns 403 when caller is not an admin", async () => {
mockSelectChain.where.mockResolvedValue([{ role: "user" }]);
const res = await PUT(makeRequest({ OPENAI_API_KEY: "sk-1" }));
expect(res.status).toBe(403);
});
it("returns 403 when there is no session", async () => {
vi.mocked(auth.api.getSession).mockResolvedValue(null as never);
const res = await PUT(makeRequest({ OPENAI_API_KEY: "sk-1" }));
expect(res.status).toBe(403);
});
it("updates allowed keys and writes an audit log", async () => {
const res = await PUT(makeRequest({ OPENAI_API_KEY: "sk-1" }));
expect(res.status).toBe(200);
expect(vi.mocked(setSiteSetting)).toHaveBeenCalledWith("OPENAI_API_KEY", "sk-1", "admin-1");
expect(mockInsertValues).toHaveBeenCalled();
});
it("silently ignores keys not in the allow-list", async () => {
const res = await PUT(makeRequest({ NOT_A_REAL_KEY: "x" }));
expect(res.status).toBe(200);
expect(vi.mocked(setSiteSetting)).not.toHaveBeenCalled();
expect(mockInsertValues).not.toHaveBeenCalled();
});
});
@@ -0,0 +1,70 @@
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
import { NextRequest } from "next/server";
const mockAdminSession = { user: { id: "admin-1", role: "admin" } };
vi.mock("@/lib/api-auth", () => ({
requireAdmin: vi.fn(),
}));
vi.mock("@/lib/email", () => ({
sendEmail: vi.fn().mockResolvedValue(undefined),
verifyEmailHtml: vi.fn((url: string) => `<a href="${url}">verify</a>`),
}));
const { requireAdmin } = await import("@/lib/api-auth");
const { sendEmail } = await import("@/lib/email");
import { POST } from "../route";
function makeRequest(body: unknown) {
return new NextRequest("http://localhost/api/v1/admin/test-email", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(body),
});
}
const ORIGINAL_ENV = process.env["BETTER_AUTH_URL"];
beforeEach(() => {
vi.clearAllMocks();
vi.mocked(requireAdmin).mockResolvedValue({ session: mockAdminSession as never, response: null });
process.env["BETTER_AUTH_URL"] = "https://epicure.example.com";
});
afterEach(() => {
if (ORIGINAL_ENV === undefined) delete process.env["BETTER_AUTH_URL"];
else process.env["BETTER_AUTH_URL"] = ORIGINAL_ENV;
});
describe("POST /api/v1/admin/test-email", () => {
it("returns 403 when caller is not an admin", async () => {
vi.mocked(requireAdmin).mockResolvedValue({
session: null,
response: new Response(JSON.stringify({ error: "Forbidden" }), { status: 403 }),
} as never);
const res = await POST(makeRequest({ to: "user@example.com" }));
expect(res.status).toBe(403);
});
it("returns 400 when 'to' is missing", async () => {
const res = await POST(makeRequest({}));
expect(res.status).toBe(400);
});
it("returns 500 when BETTER_AUTH_URL is not configured", async () => {
delete process.env["BETTER_AUTH_URL"];
const res = await POST(makeRequest({ to: "user@example.com" }));
expect(res.status).toBe(500);
expect(sendEmail).not.toHaveBeenCalled();
});
it("sends the test email using the configured base URL", async () => {
const res = await POST(makeRequest({ to: "user@example.com" }));
expect(res.status).toBe(200);
expect(sendEmail).toHaveBeenCalledWith(
expect.objectContaining({ to: "user@example.com" })
);
});
});
@@ -9,11 +9,16 @@ export async function POST(req: NextRequest) {
const { to } = await req.json() as { to: string };
if (!to) return NextResponse.json({ error: "Missing 'to'" }, { status: 400 });
const baseUrl = process.env["BETTER_AUTH_URL"];
if (!baseUrl) {
return NextResponse.json({ error: "BETTER_AUTH_URL is not configured" }, { status: 500 });
}
try {
await sendEmail({
to,
subject: "Epicure — test email",
html: verifyEmailHtml(`${process.env["BETTER_AUTH_URL"] ?? "http://localhost:3001"}/verify-email?token=test`),
html: verifyEmailHtml(`${baseUrl}/verify-email?token=test`),
});
return NextResponse.json({ ok: true });
} catch (err) {
@@ -0,0 +1,84 @@
import { describe, it, expect, vi, beforeEach } from "vitest";
import { NextRequest } from "next/server";
const mockAdminSession = { user: { id: "admin-1", role: "admin" } };
vi.mock("@/lib/api-auth", () => ({
requireAdmin: vi.fn(),
}));
const { mockUpdateChain, mockInsertValues } = vi.hoisted(() => {
const mockUpdateChain = {
set: vi.fn().mockReturnThis(),
where: vi.fn().mockReturnThis(),
returning: vi.fn().mockResolvedValue([{ id: "target-1", role: "moderator", tier: "free" }]),
};
return { mockUpdateChain, mockInsertValues: vi.fn().mockResolvedValue(undefined) };
});
vi.mock("@epicure/db", () => ({
db: {
update: vi.fn(() => mockUpdateChain),
insert: vi.fn(() => ({ values: mockInsertValues })),
},
users: { id: "id", role: "role", tier: "tier" },
auditLogs: {},
eq: vi.fn((a, b) => ({ a, b, op: "eq" })),
}));
const { requireAdmin } = await import("@/lib/api-auth");
import { PATCH } from "../route";
function makeRequest(body: unknown) {
return new NextRequest("http://localhost/api/v1/admin/users/target-1", {
method: "PATCH",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(body),
});
}
const ctx = { params: Promise.resolve({ id: "target-1" }) };
beforeEach(() => {
vi.clearAllMocks();
vi.mocked(requireAdmin).mockResolvedValue({ session: mockAdminSession as never, response: null });
mockUpdateChain.returning.mockResolvedValue([{ id: "target-1", role: "moderator", tier: "free" }]);
});
describe("PATCH /api/v1/admin/users/[id]", () => {
it("returns 403 when caller is not an admin", async () => {
vi.mocked(requireAdmin).mockResolvedValue({
session: null,
response: new Response(JSON.stringify({ error: "Forbidden" }), { status: 403 }),
} as never);
const res = await PATCH(makeRequest({ role: "admin" }), ctx);
expect(res.status).toBe(403);
});
it("returns 400 for an invalid role", async () => {
const res = await PATCH(makeRequest({ role: "superuser" }), ctx);
expect(res.status).toBe(400);
});
it("returns 400 for an invalid tier", async () => {
const res = await PATCH(makeRequest({ tier: "enterprise" }), ctx);
expect(res.status).toBe(400);
});
it("returns 404 when the target user does not exist", async () => {
mockUpdateChain.returning.mockResolvedValue([]);
const res = await PATCH(makeRequest({ role: "moderator" }), ctx);
expect(res.status).toBe(404);
});
it("updates the user and writes an audit log", async () => {
const res = await PATCH(makeRequest({ role: "moderator" }), ctx);
expect(res.status).toBe(200);
const body = await res.json() as { user: { id: string } };
expect(body.user.id).toBe("target-1");
expect(mockInsertValues).toHaveBeenCalledWith(
expect.objectContaining({ action: "admin.user.update", targetId: "target-1" })
);
});
});