diff --git a/src/lib/components/chat/Messages/Citations/CitationModal.svelte b/src/lib/components/chat/Messages/Citations/CitationModal.svelte index 57ea654364..a56e409b27 100644 --- a/src/lib/components/chat/Messages/Citations/CitationModal.svelte +++ b/src/lib/components/chat/Messages/Citations/CitationModal.svelte @@ -6,6 +6,7 @@ import { WEBUI_API_BASE_URL } from '$lib/constants'; import { settings, config } from '$lib/stores'; import { injectCsp } from '$lib/utils/csp'; + import { isValidHttpUrl } from '$lib/utils'; import XMark from '$lib/components/icons/XMark.svelte'; import Textarea from '$lib/components/common/Textarea.svelte'; @@ -71,7 +72,7 @@ const baseUrl = file_id ? `${WEBUI_API_BASE_URL}/files/${file_id}/content${page !== undefined ? `#page=${page + 1}` : ''}` - : sourceUrl?.includes('http') + : isValidHttpUrl(sourceUrl) ? sourceUrl : null; @@ -101,10 +102,10 @@
{#if citation?.source?.name} {@const document = mergedDocuments?.[0]} - {#if document?.metadata?.file_id || document.source?.url?.includes('http')} + {#if document?.metadata?.file_id || isValidHttpUrl(document.source?.url)} - {#if document.source?.url?.includes('http')} + {#if isValidHttpUrl(document.source?.url)} {@const snippetUrl = getTextFragmentUrl(document)} {#if snippetUrl} {:else if token.type === 'link'} {@const noteId = getNoteIdFromHref(token.href)} + {@const safeHref = safeLinkUrl(token.href)} {#if noteId} {:else if token.tokens} {:else} + import { safeLinkUrl } from '$lib/utils'; + import ChevronDown from '$lib/components/icons/ChevronDown.svelte'; import ChevronUp from '$lib/components/icons/ChevronUp.svelte'; import Search from '$lib/components/icons/Search.svelte'; @@ -59,7 +61,7 @@ {#if status?.items} {#each status.items as item, itemIdx} @@ -99,7 +101,7 @@ {:else if status?.urls} {#each status.urls as url, urlIdx} diff --git a/src/lib/utils/index.ts b/src/lib/utils/index.ts index 91b66f8524..7073503aa9 100644 --- a/src/lib/utils/index.ts +++ b/src/lib/utils/index.ts @@ -981,6 +981,20 @@ export const isValidHttpUrl = (string: string) => { return url.protocol === 'http:' || url.protocol === 'https:'; }; +const SAFE_LINK_PROTOCOLS = ['http:', 'https:', 'mailto:', 'tel:']; + +export const safeLinkUrl = (url: string): string | undefined => { + let protocol; + try { + protocol = new URL(url).protocol; + } catch (_) { + // No scheme to parse, so the browser resolves it against our own origin. + return url; + } + + return SAFE_LINK_PROTOCOLS.includes(protocol) ? url : undefined; +}; + export const isYoutubeUrl = (url: string) => { return ( url.startsWith('https://www.youtube.com') ||