From 66970e2455004b15893aa495120784be33e197f6 Mon Sep 17 00:00:00 2001 From: SandObserver <260779319+SandObserver@users.noreply.github.com> Date: Sat, 17 Oct 2026 14:38:25 -0400 Subject: [PATCH] Treat trailing-dot and IPv4-mapped loopback hosts as unreachable --- ui/js/service-link.js | 9 +++++++-- ui/test/service-link.test.mjs | 5 +++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/ui/js/service-link.js b/ui/js/service-link.js index 7110d473..bede46fc 100644 --- a/ui/js/service-link.js +++ b/ui/js/service-link.js @@ -17,9 +17,14 @@ export function browserLink(raw) { return ''; } if (u.protocol !== 'http:' && u.protocol !== 'https:') return ''; - const host = u.hostname.toLowerCase().replace(/^\[|\]$/g, ''); + const host = u.hostname + .toLowerCase() + .replace(/^\[|\]$/g, '') + .replace(/\.$/, ''); if (host === 'localhost' || host.endsWith('.localhost') || host === 'host.docker.internal') return ''; - if (host === '::1' || host === '0.0.0.0' || /^127\./.test(host)) return ''; + if (host === '::1' || host === '::' || host === '0.0.0.0' || /^127\./.test(host)) return ''; + const mapped = /^::ffff:([0-9a-f]{1,4}):[0-9a-f]{1,4}$/.exec(host); + if (mapped && [0, 127].includes(parseInt(mapped[1], 16) >> 8)) return ''; if (!host.includes('.') && !host.includes(':')) return ''; u.username = ''; u.password = ''; diff --git a/ui/test/service-link.test.mjs b/ui/test/service-link.test.mjs index 9b0a2a68..75649a8d 100644 --- a/ui/test/service-link.test.mjs +++ b/ui/test/service-link.test.mjs @@ -18,6 +18,11 @@ test('an address only the server can reach gives no link', () => { 'http://127.0.0.1:3000', 'http://[::1]:80', 'http://host.docker.internal:9000', + 'http://localhost.:8080', + 'http://foo.localhost.', + 'http://[::ffff:127.0.0.1]:8080', + 'http://[::ffff:0.0.0.0]', + 'http://[::]:80', '', null, 'ftp://10.0.0.1',