From ac0c6841aa5a6f6da253235acb58d7f979dd520b Mon Sep 17 00:00:00 2001 From: dakkar Date: Thu, 2 Jan 2025 10:03:16 +0000 Subject: [PATCH] use the whole hostname to check remote links - fixes #866 the warning dialog's "trust this domain" toggle saves the whole hostname, so this code needs to use the whole hostname otherwise trusting a `www.example.com` will never work, because we'd be checking `example.com` against it, and fail while I was there, I also made the `trustedLinkUrlPatterns` correctly match sub-domains: previously, trusting `ple.com` would trust `example.com` --- .../src/scripts/warning-external-website.ts | 22 +++++++++++++------ 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/packages/frontend/src/scripts/warning-external-website.ts b/packages/frontend/src/scripts/warning-external-website.ts index 67158c6438..0c9b5ba806 100644 --- a/packages/frontend/src/scripts/warning-external-website.ts +++ b/packages/frontend/src/scripts/warning-external-website.ts @@ -8,13 +8,21 @@ import { defaultStore } from '@/store.js'; import * as os from '@/os.js'; import MkUrlWarningDialog from '@/components/MkUrlWarningDialog.vue'; -const extractDomain = /^(https?:\/\/|\/\/)?([^@/\s]+@)?(www\.)?([^:/\s]+)/i; const isRegExp = /^\/(.+)\/(.*)$/; -export async function warningExternalWebsite(url: string) { - const domain = extractDomain.exec(url)?.[4]; +function extractHostname(maybeUrl: string): URL | null { + try { + const url = new URL(maybeUrl); + return url.host; + } catch { + return null; + } +} - if (!domain) return false; +export async function warningExternalWebsite(url: string) { + const hostname = extractHostname(url); + + if (!hostname) return false; const isTrustedByInstance = instance.trustedLinkUrlPatterns.some(expression => { const r = isRegExp.exec(expression); @@ -24,11 +32,11 @@ export async function warningExternalWebsite(url: string) { } else if (expression.includes(' ')) { return expression.split(' ').every(keyword => url.includes(keyword)); } else { - return domain.endsWith(expression); + return `.${hostname}`.endsWith(`.${expression}`); } }); - const isTrustedByUser = defaultStore.reactiveState.trustedDomains.value.includes(domain); + const isTrustedByUser = defaultStore.reactiveState.trustedDomains.value.includes(hostname); const isDisabledByUser = !defaultStore.reactiveState.warnExternalUrl.value; if (!isTrustedByInstance && !isTrustedByUser && !isDisabledByUser) { @@ -44,7 +52,7 @@ export async function warningExternalWebsite(url: string) { }); if (confirm.canceled) return false; - + return window.open(url, '_blank', 'nofollow noopener popup=false'); }