From 155ce4094fb00984fd715432f4790145d6fa4478 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=82=E3=82=8F=E3=82=8F=E3=82=8F=E3=81=A8=E3=83=BC?= =?UTF-8?q?=E3=81=AB=E3=82=85?= <17376330+u1-liquid@users.noreply.github.com> Date: Sun, 2 Feb 2025 04:32:39 +0900 Subject: [PATCH] =?UTF-8?q?spec(MisskeyIO#922):=20'https://'=E3=82=92?= =?UTF-8?q?=E7=9C=81=E7=95=A5=E3=81=99=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB?= =?UTF-8?q?=20(MisskeyIO#931)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/core/entities/DriveFileEntityService.ts | 4 ++-- packages/backend/src/misc/prelude/url.ts | 8 +++++++- packages/backend/src/server/FileServerService.ts | 12 ++++++------ packages/backend/src/server/ServerService.ts | 6 +++--- packages/backend/src/server/web/UrlPreviewService.ts | 4 ++-- packages/frontend/src/scripts/media-proxy.ts | 6 +++--- packages/frontend/src/scripts/url.ts | 8 +++++++- 7 files changed, 30 insertions(+), 18 deletions(-) diff --git a/packages/backend/src/core/entities/DriveFileEntityService.ts b/packages/backend/src/core/entities/DriveFileEntityService.ts index 97484b1efb83..90e13153bff2 100644 --- a/packages/backend/src/core/entities/DriveFileEntityService.ts +++ b/packages/backend/src/core/entities/DriveFileEntityService.ts @@ -12,7 +12,7 @@ import type { Packed } from '@/misc/json-schema.js'; import { awaitAll } from '@/misc/prelude/await-all.js'; import type { MiUser } from '@/models/User.js'; import type { MiDriveFile } from '@/models/DriveFile.js'; -import { appendQuery, query } from '@/misc/prelude/url.js'; +import { appendQuery, omitHttps, query } from '@/misc/prelude/url.js'; import { deepClone } from '@/misc/clone.js'; import { bindThis } from '@/decorators.js'; import { isMimeImage } from '@/misc/is-mime-image.js'; @@ -77,7 +77,7 @@ export class DriveFileEntityService { @bindThis private getProxiedUrl(url: string, mode?: 'static' | 'avatar'): string { return appendQuery( - `${this.config.mediaProxy}/${mode ?? 'image'}/${encodeURIComponent(url)}`, + `${this.config.mediaProxy}/${mode ?? 'image'}/${encodeURIComponent(omitHttps(url))}`, query({ ...(mode ? { [mode]: '1' } : {}), }), diff --git a/packages/backend/src/misc/prelude/url.ts b/packages/backend/src/misc/prelude/url.ts index 270a0750754e..10e5a060416c 100644 --- a/packages/backend/src/misc/prelude/url.ts +++ b/packages/backend/src/misc/prelude/url.ts @@ -14,10 +14,16 @@ export function query(obj: Record): string { .reduce((a, [k, v]) => (a[k] = v, a), {} as Record); return Object.entries(params) - .map((e) => `${e[0]}=${encodeURIComponent(e[1])}`) + .map((p) => `${p[0]}=${encodeURIComponent(p[1])}`) .join('&'); } export function appendQuery(url: string, query: string): string { return `${url}${/\?/.test(url) ? url.endsWith('?') ? '' : '&' : '?'}${query}`; } + +export function omitHttps(url: string): string { + if (url.startsWith('https://')) return url.slice(8); + if (url.startsWith('https%3A%2F%2F')) return url.slice(14); + return url; +} diff --git a/packages/backend/src/server/FileServerService.ts b/packages/backend/src/server/FileServerService.ts index e1f86fd27715..d9ba5a3a7b6c 100644 --- a/packages/backend/src/server/FileServerService.ts +++ b/packages/backend/src/server/FileServerService.ts @@ -26,7 +26,7 @@ import { FileInfoService } from '@/core/FileInfoService.js'; import { LoggerService } from '@/core/LoggerService.js'; import { bindThis } from '@/decorators.js'; import { isMimeImage } from '@/misc/is-mime-image.js'; -import { appendQuery, query } from '@/misc/prelude/url.js'; +import { appendQuery, omitHttps, query } from '@/misc/prelude/url.js'; import { correctFilename } from '@/misc/correct-filename.js'; import { handleRequestRedirectToOmitSearch } from '@/misc/fastify-hook-handlers.js'; import type { FastifyInstance, FastifyRequest, FastifyReply, FastifyPluginOptions } from 'fastify'; @@ -162,7 +162,7 @@ export class FileServerService { reply.header('Cache-Control', 'max-age=31536000, immutable'); const url = appendQuery( - `${this.config.mediaProxy}/static/${encodeURIComponent(file.url)}`, + `${this.config.mediaProxy}/static/${encodeURIComponent(omitHttps(file.url))}`, query({ static: '1', }), @@ -185,7 +185,7 @@ export class FileServerService { if (['image/svg+xml'].includes(file.mime)) { reply.header('Cache-Control', 'max-age=31536000, immutable'); - const url = `${this.config.mediaProxy}/svg/${encodeURIComponent(file.url)}`; + const url = `${this.config.mediaProxy}/svg/${encodeURIComponent(omitHttps(file.url))}`; file.cleanup(); return await reply.redirect(url, 301); @@ -342,13 +342,13 @@ export class FileServerService { reply.header('Cache-Control', 'public, max-age=259200'); // 3 days - const url = appendQuery( - `${this.config.mediaProxy}/redirect/${encodeURIComponent(request.params.url)}`, + const redirectUrl = appendQuery( + `${this.config.mediaProxy}/redirect/${encodeURIComponent(omitHttps(url))}`, query(transformQuery as Record), ); return reply.redirect( - url, + redirectUrl, 301, ); } diff --git a/packages/backend/src/server/ServerService.ts b/packages/backend/src/server/ServerService.ts index 999812172270..a2c47b7a1f1b 100644 --- a/packages/backend/src/server/ServerService.ts +++ b/packages/backend/src/server/ServerService.ts @@ -19,7 +19,7 @@ import { DI } from '@/di-symbols.js'; import type Logger from '@/logger.js'; import * as Acct from '@/misc/acct.js'; import { genIdenticon } from '@/misc/gen-identicon.js'; -import { appendQuery, query } from '@/misc/prelude/url.js'; +import { appendQuery, omitHttps, query } from '@/misc/prelude/url.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { LoggerService } from '@/core/LoggerService.js'; import { bindThis } from '@/decorators.js'; @@ -167,7 +167,7 @@ export class ServerService implements OnApplicationShutdown { if ('badge' in request.query) { url = appendQuery( // || emoji.originalUrl してるのは後方互換性のため(publicUrlはstringなので??はだめ) - `${this.config.mediaProxy}/emoji/${encodeURIComponent(emoji.publicUrl || emoji.originalUrl)}`, + `${this.config.mediaProxy}/emoji/${encodeURIComponent(omitHttps(emoji.publicUrl || emoji.originalUrl))}`, query({ badge: '1', }), @@ -175,7 +175,7 @@ export class ServerService implements OnApplicationShutdown { } else { url = appendQuery( // || emoji.originalUrl してるのは後方互換性のため(publicUrlはstringなので??はだめ) - `${this.config.mediaProxy}/emoji/${encodeURIComponent(emoji.publicUrl || emoji.originalUrl)}`, + `${this.config.mediaProxy}/emoji/${encodeURIComponent(omitHttps(emoji.publicUrl || emoji.originalUrl))}`, query({ emoji: '1', ...('static' in request.query ? { static: '1' } : {}), diff --git a/packages/backend/src/server/web/UrlPreviewService.ts b/packages/backend/src/server/web/UrlPreviewService.ts index d018106a5eae..04b2089093ed 100644 --- a/packages/backend/src/server/web/UrlPreviewService.ts +++ b/packages/backend/src/server/web/UrlPreviewService.ts @@ -12,7 +12,7 @@ import type { Config } from '@/config.js'; import { MetaService } from '@/core/MetaService.js'; import { HttpRequestService } from '@/core/HttpRequestService.js'; import type Logger from '@/logger.js'; -import { appendQuery, query } from '@/misc/prelude/url.js'; +import { appendQuery, omitHttps, query } from '@/misc/prelude/url.js'; import { LoggerService } from '@/core/LoggerService.js'; import { bindThis } from '@/decorators.js'; import { ApiError } from '@/server/api/error.js'; @@ -40,7 +40,7 @@ export class UrlPreviewService { if (!RegExp(/^https?:\/\//).exec(url)) return url; return appendQuery( - `${this.config.mediaProxy}/preview/${encodeURIComponent(url)}`, + `${this.config.mediaProxy}/preview/${encodeURIComponent(omitHttps(url))}`, query({ preview: '1', }), diff --git a/packages/frontend/src/scripts/media-proxy.ts b/packages/frontend/src/scripts/media-proxy.ts index 9bd22609b5e7..62e90192b249 100644 --- a/packages/frontend/src/scripts/media-proxy.ts +++ b/packages/frontend/src/scripts/media-proxy.ts @@ -3,7 +3,7 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { appendQuery, query } from '@/scripts/url.js'; +import { appendQuery, omitHttps, query } from '@/scripts/url.js'; import { url } from '@/config.js'; import { instance } from '@/instance.js'; @@ -25,7 +25,7 @@ export function getProxiedImageUrl(imageUrl: string, type?: 'preview' | 'emoji' } return appendQuery( - `${mustOrigin ? localProxy : instance.mediaProxy}/${type === 'preview' ? 'preview' : 'image'}/${encodeURIComponent(imageUrl)}`, + `${mustOrigin ? localProxy : instance.mediaProxy}/${type === 'preview' ? 'preview' : 'image'}/${encodeURIComponent(omitHttps(imageUrl))}`, query({ ...(!noFallback ? { 'fallback': '1' } : {}), ...(type ? { [type]: '1' } : {}), @@ -55,7 +55,7 @@ export function getStaticImageUrl(baseUrl: string): string { } return appendQuery( - `${instance.mediaProxy}/static/${encodeURIComponent(u.href)}`, + `${instance.mediaProxy}/static/${encodeURIComponent(omitHttps(u.href))}`, query({ static: '1' }), ); } diff --git a/packages/frontend/src/scripts/url.ts b/packages/frontend/src/scripts/url.ts index e3072b3b7d2b..10e5a060416c 100644 --- a/packages/frontend/src/scripts/url.ts +++ b/packages/frontend/src/scripts/url.ts @@ -8,7 +8,7 @@ * 2. プロパティがundefinedの時はクエリを付けない * (new URLSearchParams(obj)ではそこまで丁寧なことをしてくれない) */ -export function query(obj: Record): string { +export function query(obj: Record): string { const params = Object.entries(obj) .filter(([, v]) => Array.isArray(v) ? v.length : v !== undefined) .reduce((a, [k, v]) => (a[k] = v, a), {} as Record); @@ -21,3 +21,9 @@ export function query(obj: Record): string { export function appendQuery(url: string, query: string): string { return `${url}${/\?/.test(url) ? url.endsWith('?') ? '' : '&' : '?'}${query}`; } + +export function omitHttps(url: string): string { + if (url.startsWith('https://')) return url.slice(8); + if (url.startsWith('https%3A%2F%2F')) return url.slice(14); + return url; +}