From d3af00a9120f5254f533fa3106d08c5c01b3c8da Mon Sep 17 00:00:00 2001 From: Derek Schmidt Date: Thu, 1 Dec 2022 00:46:05 -0500 Subject: [PATCH 1/3] server: Add recursion limit to resolver Changelog: Security --- packages/backend/src/remote/activitypub/resolver.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/backend/src/remote/activitypub/resolver.ts b/packages/backend/src/remote/activitypub/resolver.ts index 3cea4c44e..9937df43f 100644 --- a/packages/backend/src/remote/activitypub/resolver.ts +++ b/packages/backend/src/remote/activitypub/resolver.ts @@ -19,9 +19,11 @@ import { parseUri } from './db-resolver.js'; export default class Resolver { private history: Set; private user?: ILocalUser; + private recursionLimit?: number; - constructor() { + constructor(recursionLimit = 100) { this.history = new Set(); + this.recursionLimit = recursionLimit; } public getHistory(): string[] { @@ -59,7 +61,9 @@ export default class Resolver { if (this.history.has(value)) { throw new Error('cannot resolve already resolved one'); } - + if (this.recursionLimit && this.history.size > this.recursionLimit) { + throw new Error('hit recursion limit'); + } this.history.add(value); const host = extractDbHost(value); From 11a6e706f483ed5d7f2170e258b839ea1d92f2b1 Mon Sep 17 00:00:00 2001 From: Derek Schmidt Date: Thu, 1 Dec 2022 00:46:37 -0500 Subject: [PATCH 2/3] server: Use shared resolver in featured and question accept --- .../backend/src/remote/activitypub/kernel/update/index.ts | 2 +- packages/backend/src/remote/activitypub/models/person.ts | 8 ++++---- .../backend/src/remote/activitypub/models/question.ts | 5 +++-- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/packages/backend/src/remote/activitypub/kernel/update/index.ts b/packages/backend/src/remote/activitypub/kernel/update/index.ts index 153c86233..29a352f2f 100644 --- a/packages/backend/src/remote/activitypub/kernel/update/index.ts +++ b/packages/backend/src/remote/activitypub/kernel/update/index.ts @@ -26,7 +26,7 @@ export default async (actor: CacheableRemoteUser, activity: IUpdate): Promise console.log(e)); + await updateQuestion(object, resolver).catch(e => console.log(e)); return 'ok: Question updated'; } else { return `skip: Unknown type: ${getApType(object)}`; diff --git a/packages/backend/src/remote/activitypub/models/person.ts b/packages/backend/src/remote/activitypub/models/person.ts index d512c1870..f5bb0f280 100644 --- a/packages/backend/src/remote/activitypub/models/person.ts +++ b/packages/backend/src/remote/activitypub/models/person.ts @@ -269,7 +269,7 @@ export async function createPerson(uri: string, resolver?: Resolver = new Resolv }); //#endregion - await updateFeatured(user!.id).catch(err => logger.error(err)); + await updateFeatured(user!.id, resolver).catch(err => logger.error(err)); return user!; } @@ -380,7 +380,7 @@ export async function updatePerson(uri: string, resolver?: Resolver = new Resolv followerSharedInbox: person.sharedInbox || (person.endpoints ? person.endpoints.sharedInbox : undefined), }); - await updateFeatured(exist.id).catch(err => logger.error(err)); + await updateFeatured(exist.id, resolver).catch(err => logger.error(err)); } /** @@ -458,14 +458,14 @@ export function analyzeAttachments(attachments: IObject | IObject[] | undefined) return { fields, services }; } -export async function updateFeatured(userId: User['id']) { +export async function updateFeatured(userId: User['id'], resolver?: Resolver) { const user = await Users.findOneByOrFail({ id: userId }); if (!Users.isRemoteUser(user)) return; if (!user.featured) return; logger.info(`Updating the featured: ${user.uri}`); - const resolver = new Resolver(); + if (resolver == null) resolver = new Resolver(); // Resolve to (Ordered)Collection Object const collection = await resolver.resolveCollection(user.featured); diff --git a/packages/backend/src/remote/activitypub/models/question.ts b/packages/backend/src/remote/activitypub/models/question.ts index 404c3e0c0..17811b13c 100644 --- a/packages/backend/src/remote/activitypub/models/question.ts +++ b/packages/backend/src/remote/activitypub/models/question.ts @@ -36,9 +36,10 @@ export async function extractPollFromQuestion(source: string | IObject, resolver /** * Update votes of Question * @param value AP Question object or its id + * @param resolver Resolver to use * @returns true if updated */ -export async function updateQuestion(value: string | IObject) { +export async function updateQuestion(value: string | IObject, resolver?: Resolver) { const uri = typeof value === 'string' ? value : value.id; // URIがこのサーバーを指しているならスキップ @@ -53,7 +54,7 @@ export async function updateQuestion(value: string | IObject) { //#endregion // resolve new Question object - const resolver = new Resolver(); + if (resolver == null) resolver = new Resolver(); const question = await resolver.resolve(value) as IQuestion; apLogger.debug(`fetched question: ${JSON.stringify(question, null, 2)}`); From 936cbf900b7ad9ddce784cef465dd520062e8314 Mon Sep 17 00:00:00 2001 From: Johann150 Date: Thu, 1 Dec 2022 20:32:57 +0100 Subject: [PATCH 3/3] use default argument value This unifies the style with the other function in that file and fixes the lint "no-param-reassign". --- packages/backend/src/remote/activitypub/models/question.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/backend/src/remote/activitypub/models/question.ts b/packages/backend/src/remote/activitypub/models/question.ts index 17811b13c..9c0a0c52e 100644 --- a/packages/backend/src/remote/activitypub/models/question.ts +++ b/packages/backend/src/remote/activitypub/models/question.ts @@ -39,7 +39,7 @@ export async function extractPollFromQuestion(source: string | IObject, resolver * @param resolver Resolver to use * @returns true if updated */ -export async function updateQuestion(value: string | IObject, resolver?: Resolver) { +export async function updateQuestion(value: string | IObject, resolver?: Resolver = new Resolver()) { const uri = typeof value === 'string' ? value : value.id; // URIがこのサーバーを指しているならスキップ @@ -54,7 +54,6 @@ export async function updateQuestion(value: string | IObject, resolver?: Resolve //#endregion // resolve new Question object - if (resolver == null) resolver = new Resolver(); const question = await resolver.resolve(value) as IQuestion; apLogger.debug(`fetched question: ${JSON.stringify(question, null, 2)}`);