Something went wrong. Try again.
[READ-ONLY] Mirror of https://github.com/openstatusHQ/openstatus. ๐ซ Status page with uptime monitoring & API monitoring as code ๐ซ openstatus.dev
bun drizzle-orm monitoring monitoring-as-code nextjs observability on-call open-source shadcn-ui status-page statuspage synthetic-monitoring tinybird turso uptime uptime-checker uptime-monitor
Something went wrong. Try again.
8.9 kB ยท 250 lines
TypeScript
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251import { db, eq } from "@openstatus/db";import { page, pageComponent, statusReport } from "@openstatus/db/src/schema";import { createPage, createPageComponent, createTestWorkspace,} from "@openstatus/db/src/test/factories";import { expect } from "@std/expect";import { afterAll, beforeAll, describe, test } from "@std/testing/bdd";import { TRPCError } from "@trpc/server";import { fetchRequestHandler } from "@trpc/server/adapters/fetch";import { NextRequest } from "next/server.js";
import { appRouter } from "../root";import { createInnerTRPCContext, createTRPCContext } from "../trpc";
const PASSWORD = "s3cret-pw";let workspaceId: number;const slugs = {} as Record< "public" | "password" | "email-domain" | "ip-restriction", string>;const reportIds = {} as Record<keyof typeof slugs, number>;const pageIds = {} as Record<keyof typeof slugs, number>;
function caller(opts?: { headers?: Record<string, string>; email?: string }) { return appRouter.createCaller( createInnerTRPCContext({ req: new NextRequest("http://rsc.internal", { headers: opts?.headers }), session: opts?.email ? { user: { email: opts.email } } : null, }), );}
beforeAll(async () => { const fixture = await createTestWorkspace(); workspaceId = fixture.workspace.id;
const variants = { public: {}, password: { password: PASSWORD }, "email-domain": { authEmailDomains: "acme.com" }, "ip-restriction": { allowedIpRanges: "10.0.0.0/8" }, } as const;
for (const accessType of Object.keys(variants) as (keyof typeof slugs)[]) { const _page = await createPage(workspaceId, { accessType, ...variants[accessType], }); slugs[accessType] = _page.slug; pageIds[accessType] = _page.id; await createPageComponent(workspaceId, _page.id); const report = await db .insert(statusReport) .values({ workspaceId, pageId: _page.id, title: "confidential incident", status: "investigating", }) .returning() .get(); reportIds[accessType] = report.id; }});
afterAll(async () => { await db .delete(statusReport) .where(eq(statusReport.workspaceId, workspaceId)); await db .delete(pageComponent) .where(eq(pageComponent.workspaceId, workspaceId)); await db.delete(page).where(eq(page.workspaceId, workspaceId));});
const authorized = { public: () => caller(), password: () => caller({ headers: { cookie: `secured-${slugs.password}=${PASSWORD}` } }), "email-domain": () => caller({ email: "jane@ACME.com" }), "ip-restriction": () => caller({ headers: { "x-real-ip": "10.1.2.3" } }),};
const denied = { password: () => caller({ headers: { cookie: `secured-${slugs.password}=wrong` } }), "email-domain": () => caller({ email: "jane@evil.com" }), "ip-restriction": () => caller({ headers: { "x-real-ip": "8.8.8.8" } }),};
// The reported exploit: anonymous HTTP call with a spoofed `x-trpc-source`.function httpCall(path: string, input: unknown, headers?: HeadersInit) { const url = new URL(`http://status.test/api/trpc/lambda/${path}`); url.searchParams.set("input", JSON.stringify({ json: input })); const req = new NextRequest(url, { headers: { "x-trpc-source": "client", ...headers }, }); return fetchRequestHandler({ endpoint: "/api/trpc/lambda", router: appRouter, req, createContext: () => createTRPCContext({ req }), });}
describe("statusPage over HTTP", () => { test("anonymous callers get no protected content by slug", async () => { const slug = slugs.password; // chrome-only procedures answer 200 with a redacted page, the rest deny const calls: [string, unknown, number][] = [ ["statusPage.get", { slug }, 200], ["statusPage.get", { slug: slug.toUpperCase() }, 200], ["statusPage.get", { slug, pw: "guess" }, 200], ["statusPage.get", { slug, pw: "" }, 200], ["statusPage.getLight", { slug }, 200], ["statusPage.getGate", { slug }, 200], ["statusPage.getReport", { slug, id: reportIds.password }, 401], ["statusPage.getMonitors", { slug }, 401], ["statusPage.getMonitors", { slug, pw: "guess" }, 401], ["statusPage.getUptime", { slug, pageComponentIds: [] }, 401], ]; for (const [path, input, status] of calls) { const res = await httpCall(path, input); expect(res.status).toBe(status); const body = await res.text(); expect(body).not.toContain("confidential"); expect(body).not.toContain(PASSWORD); expect(body).not.toContain("test-component-"); } });
test("the right cookie or `pw` still gets the content", async () => { const slug = slugs.password; const viaCookie = await httpCall( "statusPage.getReport", { slug, id: reportIds.password }, { cookie: `secured-${slug}=${PASSWORD}` }, ); expect(viaCookie.status).toBe(200); expect(await viaCookie.text()).toContain("confidential"); const viaPw = await httpCall("statusPage.get", { slug, pw: PASSWORD }); expect(viaPw.status).toBe(200); expect(await viaPw.text()).toContain("confidential"); });});
describe("statusPage access gate", () => { for (const accessType of Object.keys(authorized) as (keyof typeof slugs)[]) { test(`${accessType}: authorized caller gets the full page`, async () => { const c = authorized[accessType](); const slug = slugs[accessType]; const data = await c.statusPage.get({ slug }); expect(data?.statusReports.length).toBe(1); expect(data?.pageComponents.length).toBe(1); const report = await c.statusPage.getReport({ slug, id: reportIds[accessType], }); expect(report?.title).toBe("confidential incident"); }); }
for (const accessType of Object.keys(denied) as (keyof typeof denied)[]) { for (const [label, make] of [ ["anonymous", () => caller()], ["wrong credentials", denied[accessType]], ] as const) { test(`${accessType}: ${label} caller gets chrome only`, async () => { const c = make(); const slug = slugs[accessType];
for (const data of [ await c.statusPage.get({ slug }), await c.statusPage.getLight({ slug }), ]) { expect(data?.title).toBe("Test Page"); expect(data?.accessType).toBe(accessType); expect(data?.statusReports).toEqual([]); expect(data?.maintenances).toEqual([]); expect(data?.pageComponents).toEqual([]); expect(data?.monitors).toEqual([]); expect(JSON.stringify(data)).not.toContain("confidential"); expect(JSON.stringify(data)).not.toContain(PASSWORD); }
const attempts = [ () => c.statusPage.getReport({ slug, id: reportIds[accessType] }), () => c.statusPage.getMaintenance({ slug, id: 1 }), () => c.statusPage.getMonitors({ slug }), () => c.statusPage.getMonitor({ slug, id: 1 }), () => c.statusPage.getUptime({ slug, pageComponentIds: [] }), () => c.statusPage.subscribe({ slug, email: "outsider@example.com", subscribeComponents: false, pageComponents: [], }), ]; for (const attempt of attempts) { const error = await attempt().catch((e) => e); expect(error).toBeInstanceOf(TRPCError); expect(["UNAUTHORIZED", "FORBIDDEN"]).toContain( (error as TRPCError).code, ); } }); } }
for (const accessType of Object.keys(denied) as (keyof typeof denied)[]) { test(`${accessType}: pageSubscriber.upsert by page id is gated`, async () => { const error = await caller() .pageSubscriber.upsert({ email: "outsider@example.com", pageId: pageIds[accessType], }) .catch((e) => e); expect(error).toBeInstanceOf(TRPCError); expect(["UNAUTHORIZED", "FORBIDDEN"]).toContain( (error as TRPCError).code, ); }); }
test("password: `pw` input authorizes a cookie-less caller", async () => { const data = await caller().statusPage.get({ slug: slugs.password, pw: PASSWORD, }); expect(data?.statusReports.length).toBe(1); });
test("password: a wrong `pw` does not fall through to a valid cookie", async () => { const data = await authorized.password().statusPage.get({ slug: slugs.password, pw: "wrong", }); expect(data?.statusReports).toEqual([]); });
test("ip-restriction: spoofed x-forwarded-for loses to x-real-ip", async () => { const data = await caller({ headers: { "x-real-ip": "8.8.8.8", "x-forwarded-for": "10.1.2.3" }, }).statusPage.get({ slug: slugs["ip-restriction"] }); expect(data?.statusReports).toEqual([]); });});