From 472891a42f82ba4a6c2e6449f370959f09d86c9e Mon Sep 17 00:00:00 2001 From: fly_mc Date: Tue, 19 Nov 2024 02:16:35 +0800 Subject: [PATCH] backend: some validation fixes?? --- packages/backend/src/core/UtilityService.ts | 10 ++++++++-- .../src/core/activitypub/ApRequestService.ts | 9 ++++++++- .../src/core/activitypub/ApResolverService.ts | 16 ++++++++++++++-- .../core/activitypub/models/ApPersonService.ts | 6 ------ .../backend/src/server/api/endpoints/ap/show.ts | 7 ++++++- 5 files changed, 36 insertions(+), 12 deletions(-) diff --git a/packages/backend/src/core/UtilityService.ts b/packages/backend/src/core/UtilityService.ts index 86082ccdc..009dd4665 100644 --- a/packages/backend/src/core/UtilityService.ts +++ b/packages/backend/src/core/UtilityService.ts @@ -49,7 +49,7 @@ export class UtilityService { @bindThis public isMediaSilencedHost(silencedHosts: string[] | undefined, host: string | null): boolean { if (!silencedHosts || host == null) return false; - return silencedHosts.some(x => host.toLowerCase() === x); + return silencedHosts.some(x => `.${host.toLowerCase()}`.endsWith(`.${x}`)); } @bindThis @@ -96,7 +96,7 @@ export class UtilityService { @bindThis public extractDbHost(uri: string): string { const url = new URL(uri); - return this.toPuny(url.hostname); + return this.toPuny(url.host); } @bindThis @@ -111,6 +111,12 @@ export class UtilityService { } @bindThis + public punyHost(url: string): string { + const urlObj = new URL(url); + const host = `${this.toPuny(urlObj.hostname)}${urlObj.port.length > 0 ? ':' + urlObj.port : ''}`; + return host; + } + public isFederationAllowedHost(host: string): boolean { if (this.meta.federation === 'none') return false; if (this.meta.federation === 'specified' && !this.meta.federationHosts.some(x => `.${host.toLowerCase()}`.endsWith(`.${x}`))) return false; diff --git a/packages/backend/src/core/activitypub/ApRequestService.ts b/packages/backend/src/core/activitypub/ApRequestService.ts index c7d19adfd..38c78cf90 100644 --- a/packages/backend/src/core/activitypub/ApRequestService.ts +++ b/packages/backend/src/core/activitypub/ApRequestService.ts @@ -15,7 +15,9 @@ import { HttpRequestService } from '@/core/HttpRequestService.js'; import { LoggerService } from '@/core/LoggerService.js'; import { bindThis } from '@/decorators.js'; import type Logger from '@/logger.js'; +import type { IObject } from './type.js'; import { validateContentTypeSetAsActivityPub } from '@/core/activitypub/misc/validator.js'; +import { assertActivityMatchesUrls } from '@/core/activitypub/misc/check-against-url.js'; type Request = { url: string; @@ -252,6 +254,11 @@ export class ApRequestService { validateContentTypeSetAsActivityPub(res); - return await res.json(); + const finalUrl = res.url; // redirects may have been involved + const activity = await res.json() as IObject; + + assertActivityMatchesUrls(activity, [url, finalUrl]); + + return activity; } } diff --git a/packages/backend/src/core/activitypub/ApResolverService.ts b/packages/backend/src/core/activitypub/ApResolverService.ts index d38fb71f5..b6026c4b8 100644 --- a/packages/backend/src/core/activitypub/ApResolverService.ts +++ b/packages/backend/src/core/activitypub/ApResolverService.ts @@ -16,6 +16,7 @@ import { UtilityService } from '@/core/UtilityService.js'; import { bindThis } from '@/decorators.js'; import { LoggerService } from '@/core/LoggerService.js'; import type Logger from '@/logger.js'; +import { fromTuple } from '@/misc/from-tuple.js'; import { isCollectionOrOrderedCollection } from './type.js'; import { ApDbResolverService } from './ApDbResolverService.js'; import { ApRendererService } from './ApRendererService.js'; @@ -67,7 +68,10 @@ export class Resolver { } @bindThis - public async resolve(value: string | IObject): Promise { + public async resolve(value: string | IObject | [string | IObject]): Promise { + // eslint-disable-next-line no-param-reassign + value = fromTuple(value); + if (typeof value !== 'string') { return value; } @@ -95,7 +99,7 @@ export class Resolver { } if (!this.utilityService.isFederationAllowedHost(host)) { - throw new Error('Instance is blocked'); + throw new Bull.UnrecoverableError('Instance is blocked'); } if (this.config.signToActivityPubGet && !this.user) { @@ -114,6 +118,14 @@ export class Resolver { throw new Error('invalid response'); } + // HttpRequestService / ApRequestService have already checked that + // `object.id` or `object.url` matches the URL used to fetch the + // object after redirects; here we double-check that no redirects + // bounced between hosts + if (object.id && (this.utilityService.punyHost(object.id) !== this.utilityService.punyHost(value))) { + throw new Error(`invalid AP object ${value}: id ${object.id} has different host`); + } + return object; } diff --git a/packages/backend/src/core/activitypub/models/ApPersonService.ts b/packages/backend/src/core/activitypub/models/ApPersonService.ts index 76e5dd262..9037542ab 100644 --- a/packages/backend/src/core/activitypub/models/ApPersonService.ts +++ b/packages/backend/src/core/activitypub/models/ApPersonService.ts @@ -129,12 +129,6 @@ export class ApPersonService implements OnModuleInit { this.logger = this.apLoggerService.logger; } - private punyHost(url: string): string { - const urlObj = new URL(url); - const host = `${this.utilityService.toPuny(urlObj.hostname)}${urlObj.port.length > 0 ? ':' + urlObj.port : ''}`; - return host; - } - /** * Validate and convert to actor object * @param x Fetched object diff --git a/packages/backend/src/server/api/endpoints/ap/show.ts b/packages/backend/src/server/api/endpoints/ap/show.ts index c52608cef..a877d1ce0 100644 --- a/packages/backend/src/server/api/endpoints/ap/show.ts +++ b/packages/backend/src/server/api/endpoints/ap/show.ts @@ -27,7 +27,7 @@ export const meta = { kind: 'read:account', limit: { - duration: ms('1hour'), + duration: ms('1minute'), max: 30, }, @@ -118,6 +118,11 @@ export default class extends Endpoint { // eslint- ])); if (local != null) return local; + const host = this.utilityService.extractDbHost(uri); + + // local object, not found in db? fail + if (this.utilityService.isSelfHost(host)) return null; + // リモートから一旦オブジェクトフェッチ const resolver = this.apResolverService.createResolver(); const object = await resolver.resolve(uri) as any;