diff --git a/packages/backend/package.json b/packages/backend/package.json index 64a64650c6..cd8ab7121f 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -77,7 +77,7 @@ "@fastify/static": "8.1.1", "@fastify/view": "10.0.2", "@misskey-dev/sharp-read-bmp": "1.3.0", - "@misskey-dev/summaly": "5.2.0", + "@misskey-dev/summaly": "5.2.1", "@nestjs/common": "11.0.16", "@nestjs/core": "11.0.15", "@nestjs/testing": "11.0.15", diff --git a/packages/backend/src/core/activitypub/ApRendererService.ts b/packages/backend/src/core/activitypub/ApRendererService.ts index cbd5465945..f41eeba39f 100644 --- a/packages/backend/src/core/activitypub/ApRendererService.ts +++ b/packages/backend/src/core/activitypub/ApRendererService.ts @@ -562,30 +562,10 @@ export class ApRendererService { this.userProfilesRepository.findOneByOrFail({ userId: user.id }), ]); - // TODO remove this when we merge the MFM change - const tryRewriteUrl = (maybeUrl: string) => { - const urlSafeRegex = /^(?:http[s]?:\/\/.)?(?:www\.)?[-a-zA-Z0-9@%._\+~#=]{2,256}\.[a-z]{2,6}\b(?:[-a-zA-Z0-9@:%_\+.~#?&\/\/=]*)/; - try { - const match = maybeUrl.match(urlSafeRegex); - if (!match) { - return maybeUrl; - } - const urlPart = match[0]; - const urlPartParsed = new URL(urlPart); - const restPart = maybeUrl.slice(match[0].length); - - return `${urlPart}${restPart}`; - } catch (e) { - return maybeUrl; - } - }; - const attachment = profile.fields.map(field => ({ type: 'PropertyValue', name: field.name, - value: (field.value.startsWith('http://') || field.value.startsWith('https://')) - ? tryRewriteUrl(field.value) - : 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 cf130a21ec..aa8fcd0c2a 100644 --- a/packages/backend/src/server/web/UrlPreviewService.ts +++ b/packages/backend/src/server/web/UrlPreviewService.ts @@ -54,12 +54,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-embed/package.json b/packages/frontend-embed/package.json index 472e4d7c94..40e8802ab4 100644 --- a/packages/frontend-embed/package.json +++ b/packages/frontend-embed/package.json @@ -38,7 +38,7 @@ "vue": "3.5.13" }, "devDependencies": { - "@misskey-dev/summaly": "5.2.0", + "@misskey-dev/summaly": "5.2.1", "@testing-library/vue": "8.1.0", "@types/estree": "1.0.7", "@types/micromatch": "4.0.9", 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..ed36704d92 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 (error) { + return ''; + } +} diff --git a/packages/frontend/package.json b/packages/frontend/package.json index 1718dd93a1..d3453fb8cb 100644 --- a/packages/frontend/package.json +++ b/packages/frontend/package.json @@ -84,7 +84,7 @@ "cypress": "13.15.2" }, "devDependencies": { - "@misskey-dev/summaly": "5.2.0", + "@misskey-dev/summaly": "5.2.1", "@storybook/addon-actions": "8.6.12", "@storybook/addon-essentials": "8.6.12", "@storybook/addon-interactions": "8.6.12", diff --git a/packages/frontend/src/aiscript/api.ts b/packages/frontend/src/aiscript/api.ts index e7e396023d..08ba89dd9d 100644 --- a/packages/frontend/src/aiscript/api.ts +++ b/packages/frontend/src/aiscript/api.ts @@ -68,7 +68,7 @@ export function createAiScriptEnv(opts: { storageKey: string, token?: string }) }), 'Mk:api': values.FN_NATIVE(async ([ep, param, token]) => { utils.assertString(ep); - if (ep.value.includes('://')) { + if (ep.value.includes('://') || ep.value.includes('..')) { throw new errors.AiScriptRuntimeError('invalid endpoint'); } if (token) { diff --git a/packages/frontend/src/components/MkLink.vue b/packages/frontend/src/components/MkLink.vue index 4b128fc167..3a942b03dc 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
- -
+ +
@@ -104,6 +104,7 @@ import { prefer } from '@/preferences.js'; import { misskeyApi } from '@/utility/misskey-api.js'; import { warningExternalWebsite } from '@/utility/warning-external-website.js'; import DynamicNoteSimple from '@/components/DynamicNoteSimple.vue'; +import { maybeMakeRelative } from '@@/js/url.js'; type SummalyResult = Awaited>; @@ -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 0c59c7748a..df358ae3c3 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