diff --git a/packages/frontend-shared/js/url.ts b/packages/frontend-shared/js/url.ts index 1f3550d951..b70c7cfee4 100644 --- a/packages/frontend-shared/js/url.ts +++ b/packages/frontend-shared/js/url.ts @@ -27,6 +27,14 @@ export function extractDomain(url: string) { return match ? match[1] : null; } +export function tryParseUrl(url: string | URL, base?: string | URL): URL | null { + try { + return new URL(url, base); + } catch { + return null; + } +} + export function maybeMakeRelative(urlStr: string, baseStr: string): string { try { const baseObj = new URL(baseStr); diff --git a/packages/frontend/src/ui/_common_/statusbar-rss.vue b/packages/frontend/src/ui/_common_/statusbar-rss.vue index 1c4c29769a..13f69e74fa 100644 --- a/packages/frontend/src/ui/_common_/statusbar-rss.vue +++ b/packages/frontend/src/ui/_common_/statusbar-rss.vue @@ -31,6 +31,7 @@ import { ref } from 'vue'; import * as Misskey from 'misskey-js'; import { useInterval } from '@@/js/use-interval.js'; import { url as baseUrl } from '@@/js/config.js'; +import { tryParseUrl } from '@@/js/url.js'; import MkMarqueeText from '@/components/MkMarqueeText.vue'; import { shuffle } from '@/utility/shuffle.js'; @@ -56,8 +57,8 @@ const tick = () => { } items.value = feed.items.filter((item) => { if (!item.link) return false; - const itemUrl = new URL(item.link, baseUrl); - return ['http:', 'https:'].includes(itemUrl.protocol); + const itemUrl = tryParseUrl(item.link, baseUrl); + return itemUrl != null && ['http:', 'https:'].includes(itemUrl.protocol); }); fetching.value = false; key.value++; diff --git a/packages/frontend/src/widgets/WidgetRss.vue b/packages/frontend/src/widgets/WidgetRss.vue index 9cc5d8ad39..510de0234a 100644 --- a/packages/frontend/src/widgets/WidgetRss.vue +++ b/packages/frontend/src/widgets/WidgetRss.vue @@ -24,10 +24,11 @@ import { ref, watch, computed } from 'vue'; import * as Misskey from 'misskey-js'; import { url as base } from '@@/js/config.js'; import { useInterval } from '@@/js/use-interval.js'; +import { tryParseUrl } from '@@/js/url.js'; import { useWidgetPropsManager } from './widget.js'; -import { i18n } from '@/i18n.js'; import type { WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js'; import type { FormWithDefault, GetFormResultType } from '@/utility/form.js'; +import { i18n } from '@/i18n.js'; import MkContainer from '@/components/MkContainer.vue'; const name = 'rss'; @@ -83,8 +84,8 @@ const tick = () => { .then((feed: Misskey.entities.FetchRssResponse) => { rawItems.value = feed.items.filter((item) => { if (!item.link) return false; - const itemUrl = new URL(item.link, base); - return ['http:', 'https:'].includes(itemUrl.protocol); + const itemUrl = tryParseUrl(item.link, base); + return itemUrl != null && ['http:', 'https:'].includes(itemUrl.protocol); }); fetching.value = false; }); diff --git a/packages/frontend/src/widgets/WidgetRssTicker.vue b/packages/frontend/src/widgets/WidgetRssTicker.vue index 7b0ee60e63..7e339d1dd7 100644 --- a/packages/frontend/src/widgets/WidgetRssTicker.vue +++ b/packages/frontend/src/widgets/WidgetRssTicker.vue @@ -29,15 +29,16 @@ SPDX-License-Identifier: AGPL-3.0-only