From c2e52f6ae740a9acdafd4245cc7b557be5aafbf4 Mon Sep 17 00:00:00 2001 From: Hazelnoot Date: Thu, 18 Sep 2025 14:15:59 -0400 Subject: [PATCH] prevent packing renotes at infinite depth --- .../backend/src/core/entities/NoteEntityService.ts | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/packages/backend/src/core/entities/NoteEntityService.ts b/packages/backend/src/core/entities/NoteEntityService.ts index 4e7ac59f41..95b464c56f 100644 --- a/packages/backend/src/core/entities/NoteEntityService.ts +++ b/packages/backend/src/core/entities/NoteEntityService.ts @@ -508,6 +508,8 @@ export class NoteEntityService implements OnModuleInit { me?: { id: MiUser['id'] } | null | undefined, options?: { detail?: boolean; + recurseReply?: boolean; // Defaults to the value of detail, which defaults to true. + recurseRenote?: boolean; // Defaults to the value of detail, which defaults to true. skipHide?: boolean; withReactionAndUserPairCache?: boolean; bypassSilence?: boolean; @@ -535,6 +537,8 @@ export class NoteEntityService implements OnModuleInit { skipHide: false, withReactionAndUserPairCache: false, }, options); + opts.recurseRenote ??= opts.detail; + opts.recurseReply ??= opts.detail; const meId = me ? me.id : null; const note = typeof src === 'object' ? src : await this.noteLoader.load(src); @@ -647,8 +651,9 @@ export class NoteEntityService implements OnModuleInit { ...(opts.detail ? { clippedCount: note.clippedCount, processErrors: note.processErrors, + } : {}), - reply: note.replyId ? this.pack(note.reply ?? opts._hint_?.notes.get(note.replyId) ?? note.replyId, me, { + reply: opts.recurseReply && note.replyId ? this.pack(note.reply ?? opts._hint_?.notes.get(note.replyId) ?? note.replyId, me, { detail: false, skipHide: opts.skipHide, withReactionAndUserPairCache: opts.withReactionAndUserPairCache, @@ -658,8 +663,11 @@ export class NoteEntityService implements OnModuleInit { bypassSilence: bypassSilence || note.userId === note.replyUserId, }) : undefined, - renote: note.renoteId ? this.pack(note.renote ?? opts._hint_?.notes.get(note.renoteId) ?? note.renoteId, me, { - detail: true, + // The renote target needs to be packed with the reply, but we *must not* recurse any further. + // Pass detail=false and recurseReply=true to make sure we only include the right data. + renote: opts.recurseRenote && note.renoteId ? this.pack(note.renote ?? opts._hint_?.notes.get(note.renoteId) ?? note.renoteId, me, { + detail: false, + recurseReply: true, skipHide: opts.skipHide, withReactionAndUserPairCache: opts.withReactionAndUserPairCache, _hint_: options?._hint_,