diff --git a/packages/backend/src/server/web/UrlPreviewService.ts b/packages/backend/src/server/web/UrlPreviewService.ts index 47cc09b067..19dac1dfb8 100644 --- a/packages/backend/src/server/web/UrlPreviewService.ts +++ b/packages/backend/src/server/web/UrlPreviewService.ts @@ -6,6 +6,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { summaly } from '@misskey-dev/summaly'; import { SummalyResult } from '@misskey-dev/summaly/built/summary.js'; +import * as Redis from 'ioredis'; import { DI } from '@/di-symbols.js'; import type { Config } from '@/config.js'; import { HttpRequestService } from '@/core/HttpRequestService.js'; @@ -15,9 +16,9 @@ import { LoggerService } from '@/core/LoggerService.js'; import { bindThis } from '@/decorators.js'; import { ApiError } from '@/server/api/error.js'; import { MiMeta } from '@/models/Meta.js'; -import type { FastifyRequest, FastifyReply } from 'fastify'; -import * as Redis from 'ioredis'; import { RedisKVCache } from '@/misc/cache.js'; +import { UtilityService } from '@/core/UtilityService.js'; +import type { FastifyRequest, FastifyReply } from 'fastify'; @Injectable() export class UrlPreviewService { @@ -36,12 +37,13 @@ export class UrlPreviewService { private httpRequestService: HttpRequestService, private loggerService: LoggerService, + private utilityService: UtilityService, ) { this.logger = this.loggerService.getLogger('url-preview'); this.previewCache = new RedisKVCache(this.redisClient, 'summaly', { lifetime: 1000 * 60 * 60 * 24, // 1d memoryCacheLifetime: 1000 * 60 * 10, // 10m - fetcher: (key: string) => { throw new Error('the UrlPreview cache should never fetch'); }, + fetcher: () => { throw new Error('the UrlPreview cache should never fetch'); }, toRedisConverter: (value) => JSON.stringify(value), fromRedisConverter: (value) => JSON.parse(value), }); @@ -65,7 +67,7 @@ export class UrlPreviewService { reply: FastifyReply, ): Promise { const url = request.query.url; - if (typeof url !== 'string') { + if (typeof url !== 'string' || !URL.canParse(url)) { reply.code(400); return; } @@ -87,6 +89,18 @@ export class UrlPreviewService { }; } + const host = new URL(url).host; + if (this.utilityService.isBlockedHost(this.meta.blockedHosts, host)) { + reply.code(403); + return { + error: new ApiError({ + message: 'URL is blocked', + code: 'URL_PREVIEW_BLOCKED', + id: '50294652-857b-4b13-9700-8e5c7a8deae8', + }), + }; + } + const key = `${url}@${lang}`; const cached = await this.previewCache.get(key); if (cached !== undefined) {