SPB Git

Repository Public

studio-security.spec.ts 3.0 KB 48 lines typescript

1import { expect, test } from "@playwright/test";23/** The studio is closed to anonymous visitors: pages redirect, APIs refuse, cross-origin writes are rejected. */45test("studio pages redirect anonymous visitors to the sign-in page", async ({ page }) => {6  for (const path of ["/studio", "/studio/projects", "/studio/projects/new", "/studio/costs", "/studio/settings", "/studio/audit", "/studio/jobs", "/studio/films"]) {7    await page.goto(path);8    await expect(page, path).toHaveURL(/\/studio\/login/);9  }10  await expect(page.getByRole("heading", { name: "Studio sign-in" })).toBeVisible();11  await expect(page.locator('meta[name="robots"]')).toHaveAttribute("content", /noindex/);12});1314test("studio APIs refuse anonymous callers", async ({ request, baseURL }) => {15  const origin = new URL(baseURL!).origin;16  const post = (path: string, data: unknown) => request.post(path, { data, headers: { Origin: origin } });17  expect((await request.get("/api/studio/costs")).status()).toBe(401);18  expect((await request.get("/api/studio/projects")).status()).toBe(401);19  expect((await request.get("/api/studio/jobs/00000000-0000-4000-8000-000000000000")).status()).toBe(401);20  expect((await post("/api/studio/concepts", { theme: "anonymous attempt" })).status()).toBe(401);21  expect((await post("/api/studio/kill-switch", { on: false })).status()).toBe(401);22  expect((await post("/api/studio/projects/00000000-0000-4000-8000-000000000000/generate", {})).status()).toBe(401);23  expect((await post("/api/studio/films/00000000-0000-4000-8000-000000000000/publish", {})).status()).toBe(401);24  expect((await post("/api/studio/shots/00000000-0000-4000-8000-000000000000/regenerate", { what: "video" })).status()).toBe(401);25});2627test("cross-origin writes are rejected before authentication is even considered", async ({ request }) => {28  const r = await request.post("/api/studio/kill-switch", { data: { on: true }, headers: { Origin: "https://evil.example" } });29  expect(r.status()).toBe(403);30  const like = await request.post("/api/films/anything/react", { data: { like: true }, headers: { Origin: "https://evil.example" } });31  expect(like.status()).toBe(403);32});3334test("a wrong password is refused with a generic message", async ({ page }) => {35  await page.goto("/studio/login");36  await page.fill("#email", "nobody@example.com");37  await page.fill("#password", "definitely-not-the-password");38  await page.getByRole("button", { name: "Sign in" }).click();39  await expect(page.locator("main [role=alert]")).toContainText(/Wrong email or password|Too many attempts/);40  await expect(page).toHaveURL(/\/studio\/login/);41});4243test("public write endpoints validate their input", async ({ request, baseURL }) => {44  const origin = new URL(baseURL!).origin;45  expect((await request.post("/api/events", { data: { filmId: "x", kind: "view" }, headers: { Origin: origin } })).status()).toBe(400);46  expect((await request.post("/api/reports", { data: { detail: "short" }, headers: { Origin: origin } })).status()).toBe(400);47});48
Files main
Menu

Theme