From 0bb4e57b0c646a20aa46e6cac545b37682629e89 Mon Sep 17 00:00:00 2001 From: Julia Johannesen Date: Sun, 27 Apr 2025 13:05:09 -0400 Subject: [PATCH 1/5] Security fixes Co-Authored-By: dakkar --- .../src/core/activitypub/ApRendererService.ts | 4 +--- .../backend/src/server/web/UrlPreviewService.ts | 10 ++++------ packages/frontend-shared/js/math.ts | 10 ++++++++++ packages/frontend-shared/js/url.ts | 17 +++++++++++++++++ packages/frontend/src/components/MkLink.vue | 6 ++++-- .../frontend/src/components/MkUrlPreview.vue | 8 +++++--- packages/frontend/src/components/MkUserInfo.vue | 2 +- .../frontend/src/components/MkUserPopup.vue | 2 +- .../src/components/MkUserSetupDialog.User.vue | 2 +- .../frontend/src/components/global/MkMfm.ts | 7 ++++--- .../frontend/src/components/global/MkUrl.vue | 6 ++++-- packages/frontend/src/scripts/aiscript/api.ts | 2 +- packages/frontend/src/widgets/WidgetPhotos.vue | 2 +- .../src/widgets/server-metric/cpu-mem.vue | 4 ++-- 14 files changed, 56 insertions(+), 26 deletions(-) create mode 100644 packages/frontend-shared/js/math.ts diff --git a/packages/backend/src/core/activitypub/ApRendererService.ts b/packages/backend/src/core/activitypub/ApRendererService.ts index cb9b74f6d7..44eb029a35 100644 --- a/packages/backend/src/core/activitypub/ApRendererService.ts +++ b/packages/backend/src/core/activitypub/ApRendererService.ts @@ -496,9 +496,7 @@ export class ApRendererService { const attachment = profile.fields.map(field => ({ type: 'PropertyValue', name: field.name, - value: (field.value.startsWith('http://') || field.value.startsWith('https://')) - ? `${new URL(field.value).href}` - : field.value, + value: this.mfmService.toHtml(mfm.parse(field.value)), })); const emojis = await this.getEmojis(user.emojis); diff --git a/packages/backend/src/server/web/UrlPreviewService.ts b/packages/backend/src/server/web/UrlPreviewService.ts index 19dac1dfb8..f2a93e0958 100644 --- a/packages/backend/src/server/web/UrlPreviewService.ts +++ b/packages/backend/src/server/web/UrlPreviewService.ts @@ -52,12 +52,10 @@ export class UrlPreviewService { @bindThis private wrap(url?: string | null): string | null { return url != null - ? url.match(/^https?:\/\//) - ? `${this.config.mediaProxy}/preview.webp?${query({ - url, - preview: '1', - })}` - : url + ? `${this.config.mediaProxy}/preview.webp?${query({ + url, + preview: '1', + })}` : null; } diff --git a/packages/frontend-shared/js/math.ts b/packages/frontend-shared/js/math.ts new file mode 100644 index 0000000000..528f3b08bf --- /dev/null +++ b/packages/frontend-shared/js/math.ts @@ -0,0 +1,10 @@ +/* + * SPDX-FileCopyrightText: dakkar and other Sharkey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + +export function clamp(value: number, min: number, max: number) { + if (value > max) return max; + if (value < min) return min; + return value; +} diff --git a/packages/frontend-shared/js/url.ts b/packages/frontend-shared/js/url.ts index eb830b1eea..e4f9ca513d 100644 --- a/packages/frontend-shared/js/url.ts +++ b/packages/frontend-shared/js/url.ts @@ -26,3 +26,20 @@ export function extractDomain(url: string) { const match = url.match(/^(?:https?:)?(?:\/\/)?(?:[^@\n]+@)?([^:\/\n]+)/im); return match ? match[1] : null; } + +export function maybeMakeRelative(urlStr: string, baseStr: string): string { + try { + const baseObj = new URL(baseStr); + const urlObj = new URL(urlStr); + /* in all places where maybeMakeRelative is used, baseStr is the + * instance's public URL, which can't have path components, so the + * relative URL will always have the whole path from the urlStr + */ + if (urlObj.origin === baseObj.origin) { + return urlObj.pathname + urlObj.search + urlObj.hash; + } + return urlStr; + } catch (e) { + return ''; + } +} diff --git a/packages/frontend/src/components/MkLink.vue b/packages/frontend/src/components/MkLink.vue index 263cd95eb1..ad54e1b00e 100644 --- a/packages/frontend/src/components/MkLink.vue +++ b/packages/frontend/src/components/MkLink.vue @@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only
- -
+ +
@@ -98,6 +98,7 @@ import MkButton from '@/components/MkButton.vue'; import { transformPlayerUrl } from '@/scripts/player-url-transform.js'; import { defaultStore } from '@/store.js'; import { misskeyApi } from '@/scripts/misskey-api.js'; +import { maybeMakeRelative } from '@@/js/url.js'; const XNoteSimple = defineAsyncComponent(() => defaultStore.state.noteDesign === 'misskey' @@ -126,7 +127,8 @@ const MOBILE_THRESHOLD = 500; const isMobile = ref(deviceKind === 'smartphone' || window.innerWidth <= MOBILE_THRESHOLD); const hidePreview = ref(false); -const self = props.url.startsWith(local); +const maybeRelativeUrl = maybeMakeRelative(props.url, local); +const self = maybeRelativeUrl !== props.url; const attr = self ? 'to' : 'href'; const target = self ? null : '_blank'; const fetching = ref(true); diff --git a/packages/frontend/src/components/MkUserInfo.vue b/packages/frontend/src/components/MkUserInfo.vue index a6bbacacee..7e805dc904 100644 --- a/packages/frontend/src/components/MkUserInfo.vue +++ b/packages/frontend/src/components/MkUserInfo.vue @@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only