From 84abc3d0004e033af744739b768deac2102ac8d2 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 22 Jul 2026 10:56:54 +0000 Subject: [PATCH 1/2] fix: apply custom headers by target origin, not hardcoded host Replace the hardcoded http://appwrite/ prefix check so screenshots and reports inject custom headers for whatever origin is being captured. This unblocks k8s setups where the router is appwrite-api (via _APP_WORKER_SCREENSHOTS_ROUTER) while still keeping auth headers off third-party subresources. Co-authored-by: Chirag Aggarwal --- src/routes/reports.ts | 5 +++-- src/routes/screenshots.ts | 5 +++-- src/utils/headers.ts | 14 +++++++++++++ tests/unit/headers.test.ts | 40 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 60 insertions(+), 4 deletions(-) create mode 100644 src/utils/headers.ts create mode 100644 tests/unit/headers.test.ts diff --git a/src/routes/reports.ts b/src/routes/reports.ts index dd8dc01..87dfd69 100755 --- a/src/routes/reports.ts +++ b/src/routes/reports.ts @@ -2,6 +2,7 @@ import type { BrowserContext, BrowserContextOptions } from "playwright-core"; import { playAudit } from "playwright-lighthouse"; import { browser, defaultContext, lighthouseConfigs } from "../config"; import { lighthouseSchema } from "../schemas"; +import { isSameOrigin } from "../utils/headers"; export async function handleReportsRequest(req: Request): Promise { let context: BrowserContext | undefined; @@ -30,11 +31,11 @@ export async function handleReportsRequest(req: Request): Promise { const page = await context.newPage(); - // Override headers if provided + // Apply custom headers only to the target origin (not third-party assets) if (body.headers) { await page.route("**/*", async (route, request) => { const url = request.url(); - if (url.startsWith("http://appwrite/")) { + if (isSameOrigin(url, body.url)) { return await route.continue({ headers: { ...request.headers(), diff --git a/src/routes/screenshots.ts b/src/routes/screenshots.ts index 6752612..10512ea 100755 --- a/src/routes/screenshots.ts +++ b/src/routes/screenshots.ts @@ -5,6 +5,7 @@ import type { } from "playwright-core"; import { browser, defaultContext } from "../config"; import { screenshotSchema } from "../schemas"; +import { isSameOrigin } from "../utils/headers"; export async function handleScreenshotsRequest( req: Request, @@ -40,10 +41,10 @@ export async function handleScreenshotsRequest( const page = await context.newPage(); - // Override headers + // Apply custom headers only to the target origin (not third-party assets) await page.route("**/*", async (route, request) => { const url = request.url(); - if (url.startsWith("http://appwrite/")) { + if (isSameOrigin(url, body.url)) { return await route.continue({ headers: { ...request.headers(), diff --git a/src/utils/headers.ts b/src/utils/headers.ts new file mode 100644 index 0000000..d7dde17 --- /dev/null +++ b/src/utils/headers.ts @@ -0,0 +1,14 @@ +/** + * Returns true when `requestUrl` targets the same origin as `targetUrl`. + * Used so custom headers are only applied to the screenshot/report target, + * not to third-party subresources the page may load. + */ +export function isSameOrigin(requestUrl: string, targetUrl: string): boolean { + try { + const targetOrigin = new URL(targetUrl).origin; + const requestOrigin = new URL(requestUrl).origin; + return requestOrigin === targetOrigin; + } catch { + return false; + } +} diff --git a/tests/unit/headers.test.ts b/tests/unit/headers.test.ts new file mode 100644 index 0000000..4ae02b4 --- /dev/null +++ b/tests/unit/headers.test.ts @@ -0,0 +1,40 @@ +import { describe, expect, test } from "bun:test"; +import { isSameOrigin } from "../../src/utils/headers"; + +describe("isSameOrigin", () => { + test("matches same origin with different paths", () => { + expect( + isSameOrigin( + "http://appwrite-api/assets/app.js", + "http://appwrite-api/?appwrite-preview=1", + ), + ).toBe(true); + }); + + test("matches default docker compose host", () => { + expect( + isSameOrigin("http://appwrite/", "http://appwrite/?appwrite-theme=dark"), + ).toBe(true); + }); + + test("rejects different hosts", () => { + expect( + isSameOrigin("https://cdn.example.com/style.css", "http://appwrite-api/"), + ).toBe(false); + }); + + test("rejects different schemes on the same host", () => { + expect(isSameOrigin("https://appwrite/", "http://appwrite/")).toBe(false); + }); + + test("rejects different ports", () => { + expect(isSameOrigin("http://appwrite:8080/", "http://appwrite/")).toBe( + false, + ); + }); + + test("returns false for invalid URLs", () => { + expect(isSameOrigin("not-a-url", "http://appwrite/")).toBe(false); + expect(isSameOrigin("http://appwrite/", "also-not-a-url")).toBe(false); + }); +}); From 42a44e8135e6b101e2889786d4e020ca499d28d4 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 22 Jul 2026 11:00:50 +0000 Subject: [PATCH 2/2] fix: apply custom headers by target origin, not hardcoded host Replace the hardcoded http://appwrite/ prefix so headers follow the screenshot/report URL origin (e.g. http://appwrite-api on k8s). Co-authored-by: Chirag Aggarwal --- src/routes/reports.ts | 6 +++--- src/routes/screenshots.ts | 6 +++--- src/utils/headers.ts | 14 ------------- tests/unit/headers.test.ts | 40 -------------------------------------- 4 files changed, 6 insertions(+), 60 deletions(-) delete mode 100644 src/utils/headers.ts delete mode 100644 tests/unit/headers.test.ts diff --git a/src/routes/reports.ts b/src/routes/reports.ts index 87dfd69..6d7f6a9 100755 --- a/src/routes/reports.ts +++ b/src/routes/reports.ts @@ -2,7 +2,6 @@ import type { BrowserContext, BrowserContextOptions } from "playwright-core"; import { playAudit } from "playwright-lighthouse"; import { browser, defaultContext, lighthouseConfigs } from "../config"; import { lighthouseSchema } from "../schemas"; -import { isSameOrigin } from "../utils/headers"; export async function handleReportsRequest(req: Request): Promise { let context: BrowserContext | undefined; @@ -31,11 +30,12 @@ export async function handleReportsRequest(req: Request): Promise { const page = await context.newPage(); - // Apply custom headers only to the target origin (not third-party assets) + // Override headers if provided if (body.headers) { + const targetOrigin = new URL(body.url).origin; await page.route("**/*", async (route, request) => { const url = request.url(); - if (isSameOrigin(url, body.url)) { + if (url === targetOrigin || url.startsWith(`${targetOrigin}/`)) { return await route.continue({ headers: { ...request.headers(), diff --git a/src/routes/screenshots.ts b/src/routes/screenshots.ts index 10512ea..0ebf6c4 100755 --- a/src/routes/screenshots.ts +++ b/src/routes/screenshots.ts @@ -5,7 +5,6 @@ import type { } from "playwright-core"; import { browser, defaultContext } from "../config"; import { screenshotSchema } from "../schemas"; -import { isSameOrigin } from "../utils/headers"; export async function handleScreenshotsRequest( req: Request, @@ -40,11 +39,12 @@ export async function handleScreenshotsRequest( } const page = await context.newPage(); + const targetOrigin = new URL(body.url).origin; - // Apply custom headers only to the target origin (not third-party assets) + // Override headers only for the target origin await page.route("**/*", async (route, request) => { const url = request.url(); - if (isSameOrigin(url, body.url)) { + if (url === targetOrigin || url.startsWith(`${targetOrigin}/`)) { return await route.continue({ headers: { ...request.headers(), diff --git a/src/utils/headers.ts b/src/utils/headers.ts deleted file mode 100644 index d7dde17..0000000 --- a/src/utils/headers.ts +++ /dev/null @@ -1,14 +0,0 @@ -/** - * Returns true when `requestUrl` targets the same origin as `targetUrl`. - * Used so custom headers are only applied to the screenshot/report target, - * not to third-party subresources the page may load. - */ -export function isSameOrigin(requestUrl: string, targetUrl: string): boolean { - try { - const targetOrigin = new URL(targetUrl).origin; - const requestOrigin = new URL(requestUrl).origin; - return requestOrigin === targetOrigin; - } catch { - return false; - } -} diff --git a/tests/unit/headers.test.ts b/tests/unit/headers.test.ts deleted file mode 100644 index 4ae02b4..0000000 --- a/tests/unit/headers.test.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { describe, expect, test } from "bun:test"; -import { isSameOrigin } from "../../src/utils/headers"; - -describe("isSameOrigin", () => { - test("matches same origin with different paths", () => { - expect( - isSameOrigin( - "http://appwrite-api/assets/app.js", - "http://appwrite-api/?appwrite-preview=1", - ), - ).toBe(true); - }); - - test("matches default docker compose host", () => { - expect( - isSameOrigin("http://appwrite/", "http://appwrite/?appwrite-theme=dark"), - ).toBe(true); - }); - - test("rejects different hosts", () => { - expect( - isSameOrigin("https://cdn.example.com/style.css", "http://appwrite-api/"), - ).toBe(false); - }); - - test("rejects different schemes on the same host", () => { - expect(isSameOrigin("https://appwrite/", "http://appwrite/")).toBe(false); - }); - - test("rejects different ports", () => { - expect(isSameOrigin("http://appwrite:8080/", "http://appwrite/")).toBe( - false, - ); - }); - - test("returns false for invalid URLs", () => { - expect(isSameOrigin("not-a-url", "http://appwrite/")).toBe(false); - expect(isSameOrigin("http://appwrite/", "also-not-a-url")).toBe(false); - }); -});