diff --git a/app/components/admin/PostEditor.vue b/app/components/admin/PostEditor.vue index 949d64fc..f8bdb5aa 100644 --- a/app/components/admin/PostEditor.vue +++ b/app/components/admin/PostEditor.vue @@ -81,13 +81,14 @@ function buildMentionChip (rkey: string): HTMLElement { return span } -function isSafeUrl (url: string): boolean { +function safeHref (url: string): string { try { - const parsed = new URL(url, 'https://example.com') - return parsed.protocol === 'https:' || parsed.protocol === 'http:' + const parsed = new URL(url) + if (parsed.protocol !== 'https:' && parsed.protocol !== 'http:') return '#' + return parsed.href } catch { - return false + return '#' } } @@ -95,7 +96,7 @@ function buildLinkChip (label: string, url: string): HTMLElement { const a = document.createElement('a') a.dataset.linkUri = url a.contentEditable = 'false' - a.href = isSafeUrl(url) ? url : '#' + a.href = safeHref(url) a.target = '_blank' a.rel = 'noopener noreferrer' a.className = 'inline-block px-1.5 py-0.5 rounded bg-background text-primary underline underline-offset-2 text-sm mx-0.5' diff --git a/modules/router/index.ts b/modules/router/index.ts index 005ef5b2..9963c810 100644 --- a/modules/router/index.ts +++ b/modules/router/index.ts @@ -94,8 +94,11 @@ export default defineNuxtModule({ return { path: isDynamic ? `/${path - .replace(/\[(.*)\]/g, '(?<$1>.+)') - .replace(/\//g, '\\/')}/` + .split(/\[(.*?)\]/g) + .map((part, i) => + i % 2 ? `(?<${part}>.+)` : RegExp.escape(part), + ) + .join('')}/` : genString(path), component: isAdminFile(f) ? `defineAsyncComponent(${genDynamicImport(f, { interopDefault: true })})`