69 lines
2.7 KiB
TypeScript
69 lines
2.7 KiB
TypeScript
import { readFile } from "node:fs/promises";
|
|
|
|
import { describe, expect, it } from "vitest";
|
|
|
|
const protectedEntryPoints = [
|
|
"app/admin/actions.ts",
|
|
"app/api/admin/media/presign/route.ts",
|
|
"app/api/admin/media/[id]/complete/route.ts",
|
|
"app/api/admin/media/[id]/route.ts",
|
|
"app/api/events/admin/route.ts",
|
|
] as const;
|
|
|
|
describe("administrative security boundaries", () => {
|
|
for (const path of protectedEntryPoints) {
|
|
it(`${path} repeats server-side admin authorization`, async () => {
|
|
const source = await readFile(
|
|
new URL(`../${path}`, import.meta.url),
|
|
"utf8",
|
|
);
|
|
expect(source).toContain("requireAdmin");
|
|
expect(source).toMatch(/await requireAdmin\(\)/u);
|
|
});
|
|
}
|
|
|
|
it("same-origin media checks visibility before falling back to admin auth", async () => {
|
|
const source = await readFile(
|
|
new URL("../app/media/[id]/route.ts", import.meta.url),
|
|
"utf8",
|
|
);
|
|
expect(source).toContain("getMediaVisibility");
|
|
expect(source).toContain("getAdminSession");
|
|
expect(source.indexOf("getMediaVisibility")).toBeLessThan(
|
|
source.lastIndexOf("getAdminSession"),
|
|
);
|
|
});
|
|
|
|
it("enforces Cloudflare Turnstile on email sign-in", async () => {
|
|
const source = await readFile(
|
|
new URL("../lib/auth/server.ts", import.meta.url),
|
|
"utf8",
|
|
);
|
|
expect(source).toContain('provider: "cloudflare-turnstile"');
|
|
expect(source).toContain('secretKey: required("TURNSTILE_SECRET_KEY")');
|
|
expect(source).toContain('endpoints: ["/sign-in/email", "/sign-up/email"]');
|
|
});
|
|
|
|
it("requires a Turnstile token before submitting account login outside development", async () => {
|
|
const source = await readFile(
|
|
new URL("../components/auth/account-form.tsx", import.meta.url),
|
|
"utf8",
|
|
);
|
|
expect(source).toContain('const captcha = process.env.NODE_ENV !== "development";');
|
|
const loginGuard = "if (busy || (!register && captcha && !token)) return;";
|
|
const registrationGuard = "if (!form?.reportValidity() || (captcha && !token)) return;";
|
|
expect(source).toContain(loginGuard);
|
|
expect(source).toContain(registrationGuard);
|
|
expect(source.indexOf(loginGuard)).toBeLessThan(
|
|
source.indexOf("await authClient.signIn.email("),
|
|
);
|
|
expect(source.indexOf(registrationGuard)).toBeLessThan(
|
|
source.indexOf("await authClient.signUp.email("),
|
|
);
|
|
expect(source).toContain('"x-captcha-response": token');
|
|
expect(source).toContain("disabled={busy || (captcha && !token && !pendingEmail)}");
|
|
expect(source).toMatch(/"expired-callback":\s*\(\)\s*=>\s*\{[^}]*setToken\(""\)/u);
|
|
expect(source).toContain("window.turnstile?.reset(widget.current)");
|
|
});
|
|
});
|