diff --git a/packages/backend/src/core/entities/EmojiEntityService.ts b/packages/backend/src/core/entities/EmojiEntityService.ts index 391d97232..8929d4e64 100644 --- a/packages/backend/src/core/entities/EmojiEntityService.ts +++ b/packages/backend/src/core/entities/EmojiEntityService.ts @@ -11,39 +11,15 @@ import type { } from '@/models/Blocking.js'; import type { MiEmoji } from '@/models/Emoji.js'; import { bindThis } from '@/decorators.js'; import { In } from 'typeorm'; -import type { Config } from '@/config.js'; @Injectable() export class EmojiEntityService { constructor( @Inject(DI.emojisRepository) private emojisRepository: EmojisRepository, - - @Inject(DI.config) - private config: Config, ) { } - private stripProxyIfOrigin(url: string): string { - try { - const u = new URL(url); - let origin = u.origin; - if (u.origin === new URL(this.config.mediaProxy).origin) { - const innerUrl = u.searchParams.get('url'); - if (innerUrl) { - origin = new URL(innerUrl).origin; - } - } - if (origin === u.origin) { - return url; - } - } catch (e) { - return url; - } - - return url; - } - @bindThis public packSimpleNoQuery( emoji: MiEmoji, @@ -53,7 +29,7 @@ export class EmojiEntityService { name: emoji.name, category: emoji.category, // || emoji.originalUrl してるのは後方互換性のため(publicUrlはstringなので??はだめ) - url: this.stripProxyIfOrigin(emoji.publicUrl || emoji.originalUrl), + url: emoji.publicUrl || emoji.originalUrl, localOnly: emoji.localOnly ? true : undefined, isSensitive: emoji.isSensitive ? true : undefined, roleIdsThatCanBeUsedThisEmojiAsReaction: emoji.roleIdsThatCanBeUsedThisEmojiAsReaction.length > 0 ? emoji.roleIdsThatCanBeUsedThisEmojiAsReaction : undefined, @@ -96,7 +72,7 @@ export class EmojiEntityService { category: emoji.category, host: emoji.host, // || emoji.originalUrl してるのは後方互換性のため(publicUrlはstringなので??はだめ) - url: this.stripProxyIfOrigin(emoji.publicUrl || emoji.originalUrl), + url: emoji.publicUrl || emoji.originalUrl, license: emoji.license, isSensitive: emoji.isSensitive, localOnly: emoji.localOnly, diff --git a/packages/backend/src/models/Following.ts b/packages/backend/src/models/Following.ts index a64d1a4ca..62cbc29f2 100644 --- a/packages/backend/src/models/Following.ts +++ b/packages/backend/src/models/Following.ts @@ -3,7 +3,7 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne, ViewEntity } from 'typeorm'; +import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm'; import { id } from './util/id.js'; import { MiUser } from './User.js'; @@ -98,4 +98,3 @@ export class MiFollowing { public followeeSharedInbox: string | null; //#endregion } - diff --git a/packages/backend/src/server/ServerService.ts b/packages/backend/src/server/ServerService.ts index 7961b7026..a733adbc4 100644 --- a/packages/backend/src/server/ServerService.ts +++ b/packages/backend/src/server/ServerService.ts @@ -35,11 +35,6 @@ import { makeHstsHook } from './hsts.js'; const _dirname = fileURLToPath(new URL('.', import.meta.url)); -// This function is used to determine if a path is safe to redirect to. -function redirectSafePath(path: string): boolean { - return ['/files/', '/identicon/', '/proxy/', '/static-assets/', '/vite/', '/embed_vite/'].some(prefix => path.startsWith(prefix)); -} - @Injectable() export class ServerService implements OnApplicationShutdown { private logger: Logger; @@ -144,7 +139,7 @@ export class ServerService implements OnApplicationShutdown { name: name, }); - reply.header('Content-Security-Policy', 'default-src \'none\''); + reply.header('Content-Security-Policy', 'default-src \'none\'; style-src \'unsafe-inline\''); if (emoji == null) { if ('fallback' in request.query) { @@ -155,26 +150,16 @@ export class ServerService implements OnApplicationShutdown { } } - const dbUrl = emoji?.publicUrl || emoji?.originalUrl; - const dbUrlParsed = new URL(dbUrl); - const instanceUrl = new URL(this.config.url); - if (dbUrlParsed.origin === instanceUrl.origin) { - if (!redirectSafePath(dbUrlParsed.pathname)) { - return await reply.status(508); - } - return await reply.redirect(dbUrl, 301); - } - let url: URL; if ('badge' in request.query) { url = new URL(`${this.config.mediaProxy}/emoji.png`); // || emoji.originalUrl してるのは後方互換性のため(publicUrlはstringなので??はだめ) - url.searchParams.set('url', dbUrl); + url.searchParams.set('url', emoji.publicUrl || emoji.originalUrl); url.searchParams.set('badge', '1'); } else { url = new URL(`${this.config.mediaProxy}/emoji.webp`); // || emoji.originalUrl してるのは後方互換性のため(publicUrlはstringなので??はだめ) - url.searchParams.set('url', dbUrl); + url.searchParams.set('url', emoji.publicUrl || emoji.originalUrl); url.searchParams.set('emoji', '1'); if ('static' in request.query) url.searchParams.set('static', '1'); } @@ -198,16 +183,6 @@ export class ServerService implements OnApplicationShutdown { reply.header('Cache-Control', 'public, max-age=86400'); if (user) { - const dbUrl = user?.avatarUrl ?? this.userEntityService.getIdenticonUrl(user); - const dbUrlParsed = new URL(dbUrl); - const instanceUrl = new URL(this.config.url); - if (dbUrlParsed.origin === instanceUrl.origin) { - if (!redirectSafePath(dbUrlParsed.pathname)) { - return await reply.status(508); - } - return await reply.redirect(dbUrl, 301); - } - reply.redirect(user.avatarUrl ?? this.userEntityService.getIdenticonUrl(user)); } else { reply.redirect('/static-assets/user-unknown.png');