summaryrefslogtreecommitdiff
path: root/packages/backend/src
diff options
context:
space:
mode:
authorsyuilo <Syuilotan@yahoo.co.jp>2022-12-03 18:42:06 +0900
committersyuilo <Syuilotan@yahoo.co.jp>2022-12-03 18:42:06 +0900
commit58a3a0b7d42e7a91d332e11715f45e36c0a7bd56 (patch)
tree6fdeffc7edc02fbae16e4ed4ccea91c77b9cffde /packages/backend/src
parentMerge branch 'develop' (diff)
downloadmisskey-58a3a0b7d42e7a91d332e11715f45e36c0a7bd56.tar.gz
misskey-58a3a0b7d42e7a91d332e11715f45e36c0a7bd56.tar.bz2
misskey-58a3a0b7d42e7a91d332e11715f45e36c0a7bd56.zip
forkbomb DOS mitigation
Diffstat (limited to 'packages/backend/src')
-rw-r--r--packages/backend/src/remote/activitypub/kernel/update/index.ts8
-rw-r--r--packages/backend/src/remote/activitypub/models/person.ts8
-rw-r--r--packages/backend/src/remote/activitypub/models/question.ts8
-rw-r--r--packages/backend/src/remote/activitypub/resolver.ts15
4 files changed, 22 insertions, 17 deletions
diff --git a/packages/backend/src/remote/activitypub/kernel/update/index.ts b/packages/backend/src/remote/activitypub/kernel/update/index.ts
index 9e8a81bb39..a142cb46ef 100644
--- a/packages/backend/src/remote/activitypub/kernel/update/index.ts
+++ b/packages/backend/src/remote/activitypub/kernel/update/index.ts
@@ -10,7 +10,7 @@ import { updatePerson } from '../../models/person.js';
*/
export default async (actor: CacheableRemoteUser, activity: IUpdate): Promise<string> => {
if ('actor' in activity && actor.uri !== activity.actor) {
- return `skip: invalid actor`;
+ return 'skip: invalid actor';
}
apLogger.debug('Update');
@@ -24,10 +24,10 @@ export default async (actor: CacheableRemoteUser, activity: IUpdate): Promise<st
if (isActor(object)) {
await updatePerson(actor.uri!, resolver, object);
- return `ok: Person updated`;
+ return 'ok: Person updated';
} else if (getApType(object) === 'Question') {
- await updateQuestion(object).catch(e => console.log(e));
- return `ok: Question updated`;
+ 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 6097e3b6ed..5ef04588e9 100644
--- a/packages/backend/src/remote/activitypub/models/person.ts
+++ b/packages/backend/src/remote/activitypub/models/person.ts
@@ -271,7 +271,7 @@ export async function createPerson(uri: string, resolver?: Resolver): Promise<Us
});
//#endregion
- await updateFeatured(user!.id).catch(err => logger.error(err));
+ await updateFeatured(user!.id, resolver).catch(err => logger.error(err));
return user!;
}
@@ -384,7 +384,7 @@ export async function updatePerson(uri: string, resolver?: Resolver | null, hint
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));
}
/**
@@ -462,14 +462,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 f0321fdf2f..57070fb1e7 100644
--- a/packages/backend/src/remote/activitypub/models/question.ts
+++ b/packages/backend/src/remote/activitypub/models/question.ts
@@ -1,9 +1,9 @@
import config from '@/config/index.js';
+import { Notes, Polls } from '@/models/index.js';
+import { IPoll } from '@/models/entities/poll.js';
import Resolver from '../resolver.js';
import { IObject, IQuestion, isQuestion } from '../type.js';
import { apLogger } from '../logger.js';
-import { Notes, Polls } from '@/models/index.js';
-import { IPoll } from '@/models/entities/poll.js';
export async function extractPollFromQuestion(source: string | IObject, resolver?: Resolver): Promise<IPoll> {
if (resolver == null) resolver = new Resolver();
@@ -40,7 +40,7 @@ export async function extractPollFromQuestion(source: string | IObject, resolver
* @param uri URI of AP Question object
* @returns true if updated
*/
-export async function updateQuestion(value: any) {
+export async function updateQuestion(value: any, resolver?: Resolver) {
const uri = typeof value === 'string' ? value : value.id;
// URIがこのサーバーを指しているならスキップ
@@ -55,7 +55,7 @@ export async function updateQuestion(value: any) {
//#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)}`);
diff --git a/packages/backend/src/remote/activitypub/resolver.ts b/packages/backend/src/remote/activitypub/resolver.ts
index 2f9af43c0c..ad0df0c97a 100644
--- a/packages/backend/src/remote/activitypub/resolver.ts
+++ b/packages/backend/src/remote/activitypub/resolver.ts
@@ -4,10 +4,7 @@ import { ILocalUser } from '@/models/entities/user.js';
import { getInstanceActor } from '@/services/instance-actor.js';
import { fetchMeta } from '@/misc/fetch-meta.js';
import { extractDbHost, isSelfHost } from '@/misc/convert-host.js';
-import { signedGet } from './request.js';
-import { IObject, isCollectionOrOrderedCollection, ICollection, IOrderedCollection } from './type.js';
import { FollowRequests, Notes, NoteReactions, Polls, Users } from '@/models/index.js';
-import { parseUri } from './db-resolver.js';
import renderNote from '@/remote/activitypub/renderer/note.js';
import { renderLike } from '@/remote/activitypub/renderer/like.js';
import { renderPerson } from '@/remote/activitypub/renderer/person.js';
@@ -15,12 +12,16 @@ import renderQuestion from '@/remote/activitypub/renderer/question.js';
import renderCreate from '@/remote/activitypub/renderer/create.js';
import { renderActivity } from '@/remote/activitypub/renderer/index.js';
import renderFollow from '@/remote/activitypub/renderer/follow.js';
+import { parseUri } from './db-resolver.js';
+import { IObject, isCollectionOrOrderedCollection, ICollection, IOrderedCollection } from './type.js';
+import { signedGet } from './request.js';
export default class Resolver {
private history: Set<string>;
private user?: ILocalUser;
+ private recursionLimit?: number;
- constructor() {
+ constructor(recursionLimit = 100) {
this.history = new Set();
}
@@ -60,6 +61,10 @@ export default class Resolver {
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);
@@ -123,7 +128,7 @@ export default class Resolver {
if (parsed.rest == null || !/^\w+$/.test(parsed.rest)) throw new Error('resolveLocal: invalid follow URI');
return Promise.all(
- [parsed.id, parsed.rest].map(id => Users.findOneByOrFail({ id }))
+ [parsed.id, parsed.rest].map(id => Users.findOneByOrFail({ id })),
)
.then(([follower, followee]) => renderActivity(renderFollow(follower, followee, url)));
default: