From 33d895355405379ac8a8b8229d8756d2b0ab3707 Mon Sep 17 00:00:00 2001 From: mauriceboe Date: Sat, 4 Apr 2026 23:47:46 +0200 Subject: [PATCH] fix(security): harden Google Maps URL resolver against SSRF - Replace substring check with strict hostname validation (goo.gl, maps.app.goo.gl) - Add checkSsrf() guard with bypass=true to block private/internal IPs unconditionally - Prevents crafted URLs like https://evil.com/?foo=goo.gl from triggering server-side fetches --- server/src/services/mapsService.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/server/src/services/mapsService.ts b/server/src/services/mapsService.ts index de16e9c..5e38ffd 100644 --- a/server/src/services/mapsService.ts +++ b/server/src/services/mapsService.ts @@ -1,6 +1,7 @@ import fetch from 'node-fetch'; import { db } from '../db/database'; import { decrypt_api_key } from './apiKeyCrypto'; +import { checkSsrf } from '../utils/ssrfGuard'; // ── Interfaces ─────────────────────────────────────────────────────────────── @@ -474,8 +475,11 @@ export async function reverseGeocode(lat: string, lng: string, lang?: string): P export async function resolveGoogleMapsUrl(url: string): Promise<{ lat: number; lng: number; name: string | null; address: string | null }> { let resolvedUrl = url; - // Follow redirects for short URLs (goo.gl, maps.app.goo.gl) - if (url.includes('goo.gl') || url.includes('maps.app')) { + // Follow redirects for short URLs (goo.gl, maps.app.goo.gl) with SSRF protection + const parsed = new URL(url); + if (['goo.gl', 'maps.app.goo.gl'].includes(parsed.hostname)) { + const ssrf = await checkSsrf(url, true); + if (!ssrf.allowed) throw Object.assign(new Error('URL blocked by SSRF check'), { status: 403 }); const redirectRes = await fetch(url, { redirect: 'follow', signal: AbortSignal.timeout(10000) }); resolvedUrl = redirectRes.url; }