From 4cc11853887e60d19728ace6b50d25c9a687f3bb Mon Sep 17 00:00:00 2001 From: Matthew Gallon Date: Sun, 23 Aug 2026 22:54:36 +0200 Subject: [PATCH] Potential fix for code scanning alert no. 2: Server-side request forgery Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> --- src/server/proxy.ts | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/server/proxy.ts b/src/server/proxy.ts index d887d9f..da01594 100644 --- a/src/server/proxy.ts +++ b/src/server/proxy.ts @@ -28,6 +28,18 @@ const BLOCKED_RESPONSE_HEADERS = new Set([ 'set-cookie', ]); +const ALLOWED_PROXY_HOSTS = new Set( + (process.env.PROXY_ALLOWED_HOSTS ?? '') + .split(',') + .map((h) => h.trim().toLowerCase()) + .filter(Boolean), +); + +function isAllowedTargetHostname(hostname: string): boolean { + if (ALLOWED_PROXY_HOSTS.size === 0) return false; + return ALLOWED_PROXY_HOSTS.has(hostname.toLowerCase()); +} + // Forwarded by blocklist, not allowlist, since sites rely on custom headers; this excludes hop-by-hop headers, computed ones, and IP-leaking proxy metadata. const REQUEST_HEADER_BLOCKLIST = new Set([ 'host', @@ -250,6 +262,11 @@ export async function handleProxy(req: Request, res: Response): Promise { return; } + if (!isAllowedTargetHostname(parsedTarget.hostname)) { + res.status(403).type('text').send('Proxying to this host is not allowed.'); + return; + } + targetUrl = parsedTarget.href; if (parsedTarget.protocol !== 'http:' && parsedTarget.protocol !== 'https:') { @@ -327,6 +344,11 @@ export async function handleProxy(req: Request, res: Response): Promise { return; } + if (!isAllowedTargetHostname(next.hostname)) { + res.status(403).type('text').send('Proxying to this host is not allowed.'); + return; + } + if (readFlagCookie(rawCookie, 'w2_block_ads') && isAdRequest(next)) { respondBlocked(req, res, next.href); return;