From edfcec2ac1d12b73a0eebee388dde0c5094c4525 Mon Sep 17 00:00:00 2001 From: James Pepper Date: Wed, 27 May 2026 19:20:18 +0100 Subject: [PATCH] Potential fix for code scanning alert no. 12: Server-side request forgery Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> --- src/lib/blockchain-api.ts | 35 ++++++++++++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/src/lib/blockchain-api.ts b/src/lib/blockchain-api.ts index f2fbd3a..1508f9d 100644 --- a/src/lib/blockchain-api.ts +++ b/src/lib/blockchain-api.ts @@ -43,6 +43,38 @@ function sanitizeTxid(txid: string): string { return encodeURIComponent(trimmed); } +function sanitizeProviderPathname(pathname: string): string { + const trimmed = pathname.trim(); + if (!trimmed) { + throw new Error('Disallowed provider URL path.'); + } + + // Block absolute/protocol-relative URLs and backslash path confusion. + if (/^[a-zA-Z][a-zA-Z0-9+.-]*:/.test(trimmed) || trimmed.startsWith('//') || trimmed.includes('\\')) { + throw new Error('Disallowed provider URL path.'); + } + + if (!trimmed.startsWith('/')) { + throw new Error('Disallowed provider URL path.'); + } + + // Block obvious and percent-encoded traversal indicators before URL construction. + const lowered = trimmed.toLowerCase(); + if ( + lowered.includes('/./') || + lowered.includes('/../') || + lowered.endsWith('/.') || + lowered.endsWith('/..') || + lowered.includes('%2e') || + lowered.includes('%2f') || + lowered.includes('%5c') + ) { + throw new Error('Disallowed provider URL path.'); + } + + return trimmed; +} + export async function fetchJson( host: AllowedHost, pathname: string, @@ -51,7 +83,8 @@ export async function fetchJson( revalidate?: number, ): Promise { const origin = TRUSTED_ORIGINS[host]; - const url = new URL(pathname, origin); + const safePathname = sanitizeProviderPathname(pathname); + const url = new URL(safePathname, origin); if (query) { for (const [key, value] of Object.entries(query)) {