From 05893d7ab9817f34a525f7c99369ae0b013972a2 Mon Sep 17 00:00:00 2001 From: Sebastion Date: Tue, 24 Mar 2026 12:50:51 +0000 Subject: [PATCH] fix: block IPv4-mapped IPv6 and IPv6 all-zeros in SSRF check MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The assertNotPrivateUrl() function blocked standard loopback and link-local addresses but could be bypassed using IPv4-mapped IPv6 representations: - http://[::ffff:127.0.0.1]:8080/ → loopback bypass - http://[::ffff:169.254.169.254]:8080/ → metadata endpoint bypass - http://[::]:8080/ → all-interfaces bypass Node.js normalises these to [::ffff:7f00:1], [::ffff:a9fe:a9fe], and [::] respectively, none of which matched the existing regex patterns. Add two patterns to close the gap: - /^\[::ffff:/i catches all IPv4-mapped IPv6 addresses - /^\[::\]$/ catches the IPv6 all-zeros address Legitimate RFC1918 LAN URLs (192.168.x, 10.x, 172.16-31.x) remain allowed. --- admin/app/validators/common.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/admin/app/validators/common.ts b/admin/app/validators/common.ts index 024c206..8fe78bd 100644 --- a/admin/app/validators/common.ts +++ b/admin/app/validators/common.ts @@ -22,6 +22,8 @@ export function assertNotPrivateUrl(urlString: string): void { /^169\.254\.\d+\.\d+$/, // Link-local / cloud metadata /^\[::1\]$/, /^\[?fe80:/i, // IPv6 link-local + /^\[::ffff:/i, // IPv4-mapped IPv6 (e.g. [::ffff:7f00:1] = 127.0.0.1) + /^\[::\]$/, // IPv6 all-zeros (equivalent to 0.0.0.0) ] if (blockedPatterns.some((re) => re.test(hostname))) {