mirror of
https://github.com/misskey-dev/misskey.git
synced 2026-08-01 04:45:51 +00:00
fix: RSS系ウィジェットにフィードURL検査機構を追加 (#17831)
This commit is contained in:
@@ -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);
|
||||
|
||||
@@ -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++;
|
||||
|
||||
@@ -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;
|
||||
});
|
||||
|
||||
@@ -29,15 +29,16 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
<script lang="ts" setup>
|
||||
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 type { WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js';
|
||||
import MkMarqueeText from '@/components/MkMarqueeText.vue';
|
||||
import type { FormWithDefault, GetFormResultType } from '@/utility/form.js';
|
||||
import MkMarqueeText from '@/components/MkMarqueeText.vue';
|
||||
import MkContainer from '@/components/MkContainer.vue';
|
||||
import { shuffle } from '@/utility/shuffle.js';
|
||||
import { i18n } from '@/i18n.js';
|
||||
import { url as base } from '@@/js/config.js';
|
||||
import { useInterval } from '@@/js/use-interval.js';
|
||||
|
||||
const name = 'rssTicker';
|
||||
|
||||
@@ -123,8 +124,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;
|
||||
key.value++;
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: syuilo and misskey-project
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
import { describe, expect, test } from 'vitest';
|
||||
import { tryParseUrl } from '@@/js/url.js';
|
||||
|
||||
describe('tryParseUrl', () => {
|
||||
test('parses an absolute URL', () => {
|
||||
expect(tryParseUrl('https://example.com/path')?.href).toBe('https://example.com/path');
|
||||
});
|
||||
|
||||
test('parses a relative URL against a base URL', () => {
|
||||
expect(tryParseUrl('/path', 'https://example.com/base')?.href).toBe('https://example.com/path');
|
||||
});
|
||||
|
||||
test('returns null for an invalid URL', () => {
|
||||
expect(tryParseUrl('https://[invalid')).toBeNull();
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user