mirror of
				https://codeberg.org/yeentown/barkey.git
				synced 2025-10-31 13:34:12 +00:00 
			
		
		
		
	fix: primitives 21, 22, and 23: reuse resolver
This also increases the default `recursionLimit` for `Resolver`, as it theoretically will go higher that it previously would and could possibly fail on non-malicious collection activities.
This commit is contained in:
		
							parent
							
								
									408e782507
								
							
						
					
					
						commit
						74565f67f7
					
				
					 2 changed files with 55 additions and 37 deletions
				
			
		|  | @ -93,19 +93,26 @@ export class ApInboxService { | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	@bindThis | 	@bindThis | ||||||
| 	public async performActivity(actor: MiRemoteUser, activity: IObject): Promise<string | void> { | 	public async performActivity(actor: MiRemoteUser, activity: IObject, resolver?: Resolver): Promise<string | void> { | ||||||
| 		let result = undefined as string | void; | 		let result = undefined as string | void; | ||||||
| 		if (isCollectionOrOrderedCollection(activity)) { | 		if (isCollectionOrOrderedCollection(activity)) { | ||||||
| 			const results = [] as [string, string | void][]; | 			const results = [] as [string, string | void][]; | ||||||
| 			const resolver = this.apResolverService.createResolver(); | 			// eslint-disable-next-line no-param-reassign
 | ||||||
| 			for (const item of toArray(isCollection(activity) ? activity.items : activity.orderedItems)) { | 			resolver ??= this.apResolverService.createResolver(); | ||||||
|  | 
 | ||||||
|  | 			const items = toArray(isCollection(activity) ? activity.items : activity.orderedItems); | ||||||
|  | 			if (items.length >= resolver.getRecursionLimit()) { | ||||||
|  | 				throw new Error(`skipping activity: collection would surpass recursion limit: ${this.utilityService.extractDbHost(actor.uri)}`); | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			for (const item of items) { | ||||||
| 				const act = await resolver.resolve(item); | 				const act = await resolver.resolve(item); | ||||||
| 				if (act.id == null || this.utilityService.extractDbHost(act.id) !== this.utilityService.extractDbHost(actor.uri)) { | 				if (act.id == null || this.utilityService.extractDbHost(act.id) !== this.utilityService.extractDbHost(actor.uri)) { | ||||||
| 					this.logger.debug('skipping activity: activity id is null or mismatching'); | 					this.logger.debug('skipping activity: activity id is null or mismatching'); | ||||||
| 					continue; | 					continue; | ||||||
| 				} | 				} | ||||||
| 				try { | 				try { | ||||||
| 					results.push([getApId(item), await this.performOneActivity(actor, act)]); | 					results.push([getApId(item), await this.performOneActivity(actor, act, resolver)]); | ||||||
| 				} catch (err) { | 				} catch (err) { | ||||||
| 					if (err instanceof Error || typeof err === 'string') { | 					if (err instanceof Error || typeof err === 'string') { | ||||||
| 						this.logger.error(err); | 						this.logger.error(err); | ||||||
|  | @ -120,7 +127,7 @@ export class ApInboxService { | ||||||
| 				result = results.map(([id, reason]) => `${id}: ${reason}`).join('\n'); | 				result = results.map(([id, reason]) => `${id}: ${reason}`).join('\n'); | ||||||
| 			} | 			} | ||||||
| 		} else { | 		} else { | ||||||
| 			result = await this.performOneActivity(actor, activity); | 			result = await this.performOneActivity(actor, activity, resolver); | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		// ついでにリモートユーザーの情報が古かったら更新しておく
 | 		// ついでにリモートユーザーの情報が古かったら更新しておく
 | ||||||
|  | @ -135,37 +142,37 @@ export class ApInboxService { | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	@bindThis | 	@bindThis | ||||||
| 	public async performOneActivity(actor: MiRemoteUser, activity: IObject): Promise<string | void> { | 	public async performOneActivity(actor: MiRemoteUser, activity: IObject, resolver?: Resolver): Promise<string | void> { | ||||||
| 		if (actor.isSuspended) return; | 		if (actor.isSuspended) return; | ||||||
| 
 | 
 | ||||||
| 		if (isCreate(activity)) { | 		if (isCreate(activity)) { | ||||||
| 			return await this.create(actor, activity); | 			return await this.create(actor, activity, resolver); | ||||||
| 		} else if (isDelete(activity)) { | 		} else if (isDelete(activity)) { | ||||||
| 			return await this.delete(actor, activity); | 			return await this.delete(actor, activity); | ||||||
| 		} else if (isUpdate(activity)) { | 		} else if (isUpdate(activity)) { | ||||||
| 			return await this.update(actor, activity); | 			return await this.update(actor, activity, resolver); | ||||||
| 		} else if (isFollow(activity)) { | 		} else if (isFollow(activity)) { | ||||||
| 			return await this.follow(actor, activity); | 			return await this.follow(actor, activity); | ||||||
| 		} else if (isAccept(activity)) { | 		} else if (isAccept(activity)) { | ||||||
| 			return await this.accept(actor, activity); | 			return await this.accept(actor, activity, resolver); | ||||||
| 		} else if (isReject(activity)) { | 		} else if (isReject(activity)) { | ||||||
| 			return await this.reject(actor, activity); | 			return await this.reject(actor, activity, resolver); | ||||||
| 		} else if (isAdd(activity)) { | 		} else if (isAdd(activity)) { | ||||||
| 			return await this.add(actor, activity); | 			return await this.add(actor, activity, resolver); | ||||||
| 		} else if (isRemove(activity)) { | 		} else if (isRemove(activity)) { | ||||||
| 			return await this.remove(actor, activity); | 			return await this.remove(actor, activity, resolver); | ||||||
| 		} else if (isAnnounce(activity)) { | 		} else if (isAnnounce(activity)) { | ||||||
| 			return await this.announce(actor, activity); | 			return await this.announce(actor, activity, resolver); | ||||||
| 		} else if (isLike(activity)) { | 		} else if (isLike(activity)) { | ||||||
| 			return await this.like(actor, activity); | 			return await this.like(actor, activity); | ||||||
| 		} else if (isUndo(activity)) { | 		} else if (isUndo(activity)) { | ||||||
| 			return await this.undo(actor, activity); | 			return await this.undo(actor, activity, resolver); | ||||||
| 		} else if (isBlock(activity)) { | 		} else if (isBlock(activity)) { | ||||||
| 			return await this.block(actor, activity); | 			return await this.block(actor, activity); | ||||||
| 		} else if (isFlag(activity)) { | 		} else if (isFlag(activity)) { | ||||||
| 			return await this.flag(actor, activity); | 			return await this.flag(actor, activity); | ||||||
| 		} else if (isMove(activity)) { | 		} else if (isMove(activity)) { | ||||||
| 			return await this.move(actor, activity); | 			return await this.move(actor, activity, resolver); | ||||||
| 		} else { | 		} else { | ||||||
| 			return `unrecognized activity type: ${activity.type}`; | 			return `unrecognized activity type: ${activity.type}`; | ||||||
| 		} | 		} | ||||||
|  | @ -207,12 +214,13 @@ export class ApInboxService { | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	@bindThis | 	@bindThis | ||||||
| 	private async accept(actor: MiRemoteUser, activity: IAccept): Promise<string> { | 	private async accept(actor: MiRemoteUser, activity: IAccept, resolver?: Resolver): Promise<string> { | ||||||
| 		const uri = activity.id ?? activity; | 		const uri = activity.id ?? activity; | ||||||
| 
 | 
 | ||||||
| 		this.logger.info(`Accept: ${uri}`); | 		this.logger.info(`Accept: ${uri}`); | ||||||
| 
 | 
 | ||||||
| 		const resolver = this.apResolverService.createResolver(); | 		// eslint-disable-next-line no-param-reassign
 | ||||||
|  | 		resolver ??= this.apResolverService.createResolver(); | ||||||
| 
 | 
 | ||||||
| 		const object = await resolver.resolve(activity.object).catch(err => { | 		const object = await resolver.resolve(activity.object).catch(err => { | ||||||
| 			this.logger.error(`Resolution failed: ${err}`); | 			this.logger.error(`Resolution failed: ${err}`); | ||||||
|  | @ -249,7 +257,7 @@ export class ApInboxService { | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	@bindThis | 	@bindThis | ||||||
| 	private async add(actor: MiRemoteUser, activity: IAdd): Promise<string | void> { | 	private async add(actor: MiRemoteUser, activity: IAdd, resolver?: Resolver): Promise<string | void> { | ||||||
| 		if (actor.uri !== activity.actor) { | 		if (actor.uri !== activity.actor) { | ||||||
| 			return 'invalid actor'; | 			return 'invalid actor'; | ||||||
| 		} | 		} | ||||||
|  | @ -260,7 +268,7 @@ export class ApInboxService { | ||||||
| 
 | 
 | ||||||
| 		if (activity.target === actor.featured) { | 		if (activity.target === actor.featured) { | ||||||
| 			const object = fromTuple(activity.object); | 			const object = fromTuple(activity.object); | ||||||
| 			const note = await this.apNoteService.resolveNote(object); | 			const note = await this.apNoteService.resolveNote(object, { resolver }); | ||||||
| 			if (note == null) return 'note not found'; | 			if (note == null) return 'note not found'; | ||||||
| 			await this.notePiningService.addPinned(actor, note.id); | 			await this.notePiningService.addPinned(actor, note.id); | ||||||
| 			return; | 			return; | ||||||
|  | @ -270,12 +278,13 @@ export class ApInboxService { | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	@bindThis | 	@bindThis | ||||||
| 	private async announce(actor: MiRemoteUser, activity: IAnnounce): Promise<string | void> { | 	private async announce(actor: MiRemoteUser, activity: IAnnounce, resolver?: Resolver): Promise<string | void> { | ||||||
| 		const uri = getApId(activity); | 		const uri = getApId(activity); | ||||||
| 
 | 
 | ||||||
| 		this.logger.info(`Announce: ${uri}`); | 		this.logger.info(`Announce: ${uri}`); | ||||||
| 
 | 
 | ||||||
| 		const resolver = this.apResolverService.createResolver(); | 		// eslint-disable-next-line no-param-reassign
 | ||||||
|  | 		resolver ??= this.apResolverService.createResolver(); | ||||||
| 
 | 
 | ||||||
| 		const activityObject = fromTuple(activity.object); | 		const activityObject = fromTuple(activity.object); | ||||||
| 		if (!activityObject) return 'skip: activity has no object property'; | 		if (!activityObject) return 'skip: activity has no object property'; | ||||||
|  | @ -293,7 +302,7 @@ export class ApInboxService { | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	@bindThis | 	@bindThis | ||||||
| 	private async announceNote(actor: MiRemoteUser, activity: IAnnounce, target: IPost): Promise<string | void> { | 	private async announceNote(actor: MiRemoteUser, activity: IAnnounce, target: IPost, resolver?: Resolver): Promise<string | void> { | ||||||
| 		const uri = getApId(activity); | 		const uri = getApId(activity); | ||||||
| 
 | 
 | ||||||
| 		if (actor.isSuspended) { | 		if (actor.isSuspended) { | ||||||
|  | @ -315,7 +324,7 @@ export class ApInboxService { | ||||||
| 			// Announce対象をresolve
 | 			// Announce対象をresolve
 | ||||||
| 			let renote; | 			let renote; | ||||||
| 			try { | 			try { | ||||||
| 				renote = await this.apNoteService.resolveNote(target); | 				renote = await this.apNoteService.resolveNote(target, { resolver }); | ||||||
| 				if (renote == null) return 'announce target is null'; | 				if (renote == null) return 'announce target is null'; | ||||||
| 			} catch (err) { | 			} catch (err) { | ||||||
| 				// 対象が4xxならスキップ
 | 				// 対象が4xxならスキップ
 | ||||||
|  | @ -334,7 +343,7 @@ export class ApInboxService { | ||||||
| 
 | 
 | ||||||
| 			this.logger.info(`Creating the (Re)Note: ${uri}`); | 			this.logger.info(`Creating the (Re)Note: ${uri}`); | ||||||
| 
 | 
 | ||||||
| 			const activityAudience = await this.apAudienceService.parseAudience(actor, activity.to, activity.cc); | 			const activityAudience = await this.apAudienceService.parseAudience(actor, activity.to, activity.cc, resolver); | ||||||
| 			const createdAt = activity.published ? new Date(activity.published) : null; | 			const createdAt = activity.published ? new Date(activity.published) : null; | ||||||
| 
 | 
 | ||||||
| 			if (createdAt && createdAt < this.idService.parse(renote.id).date) { | 			if (createdAt && createdAt < this.idService.parse(renote.id).date) { | ||||||
|  | @ -372,7 +381,7 @@ export class ApInboxService { | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	@bindThis | 	@bindThis | ||||||
| 	private async create(actor: MiRemoteUser, activity: ICreate): Promise<string | void> { | 	private async create(actor: MiRemoteUser, activity: ICreate, resolver?: Resolver): Promise<string | void> { | ||||||
| 		const uri = getApId(activity); | 		const uri = getApId(activity); | ||||||
| 
 | 
 | ||||||
| 		this.logger.info(`Create: ${uri}`); | 		this.logger.info(`Create: ${uri}`); | ||||||
|  | @ -398,7 +407,8 @@ export class ApInboxService { | ||||||
| 			activityObject.attributedTo = activity.actor; | 			activityObject.attributedTo = activity.actor; | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		const resolver = this.apResolverService.createResolver(); | 		// eslint-disable-next-line no-param-reassign
 | ||||||
|  | 		resolver ??= this.apResolverService.createResolver(); | ||||||
| 
 | 
 | ||||||
| 		const object = await resolver.resolve(activityObject).catch(e => { | 		const object = await resolver.resolve(activityObject).catch(e => { | ||||||
| 			this.logger.error(`Resolution failed: ${e}`); | 			this.logger.error(`Resolution failed: ${e}`); | ||||||
|  | @ -574,12 +584,13 @@ export class ApInboxService { | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	@bindThis | 	@bindThis | ||||||
| 	private async reject(actor: MiRemoteUser, activity: IReject): Promise<string> { | 	private async reject(actor: MiRemoteUser, activity: IReject, resolver?: Resolver): Promise<string> { | ||||||
| 		const uri = activity.id ?? activity; | 		const uri = activity.id ?? activity; | ||||||
| 
 | 
 | ||||||
| 		this.logger.info(`Reject: ${uri}`); | 		this.logger.info(`Reject: ${uri}`); | ||||||
| 
 | 
 | ||||||
| 		const resolver = this.apResolverService.createResolver(); | 		// eslint-disable-next-line no-param-reassign
 | ||||||
|  | 		resolver ??= this.apResolverService.createResolver(); | ||||||
| 
 | 
 | ||||||
| 		const object = await resolver.resolve(activity.object).catch(e => { | 		const object = await resolver.resolve(activity.object).catch(e => { | ||||||
| 			this.logger.error(`Resolution failed: ${e}`); | 			this.logger.error(`Resolution failed: ${e}`); | ||||||
|  | @ -616,7 +627,7 @@ export class ApInboxService { | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	@bindThis | 	@bindThis | ||||||
| 	private async remove(actor: MiRemoteUser, activity: IRemove): Promise<string | void> { | 	private async remove(actor: MiRemoteUser, activity: IRemove, resolver?: Resolver): Promise<string | void> { | ||||||
| 		if (actor.uri !== activity.actor) { | 		if (actor.uri !== activity.actor) { | ||||||
| 			return 'invalid actor'; | 			return 'invalid actor'; | ||||||
| 		} | 		} | ||||||
|  | @ -627,7 +638,7 @@ export class ApInboxService { | ||||||
| 
 | 
 | ||||||
| 		if (activity.target === actor.featured) { | 		if (activity.target === actor.featured) { | ||||||
| 			const activityObject = fromTuple(activity.object); | 			const activityObject = fromTuple(activity.object); | ||||||
| 			const note = await this.apNoteService.resolveNote(activityObject); | 			const note = await this.apNoteService.resolveNote(activityObject, { resolver }); | ||||||
| 			if (note == null) return 'note not found'; | 			if (note == null) return 'note not found'; | ||||||
| 			await this.notePiningService.removePinned(actor, note.id); | 			await this.notePiningService.removePinned(actor, note.id); | ||||||
| 			return; | 			return; | ||||||
|  | @ -637,7 +648,7 @@ export class ApInboxService { | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	@bindThis | 	@bindThis | ||||||
| 	private async undo(actor: MiRemoteUser, activity: IUndo): Promise<string> { | 	private async undo(actor: MiRemoteUser, activity: IUndo, resolver?: Resolver): Promise<string> { | ||||||
| 		if (actor.uri !== activity.actor) { | 		if (actor.uri !== activity.actor) { | ||||||
| 			return 'invalid actor'; | 			return 'invalid actor'; | ||||||
| 		} | 		} | ||||||
|  | @ -646,7 +657,8 @@ export class ApInboxService { | ||||||
| 
 | 
 | ||||||
| 		this.logger.info(`Undo: ${uri}`); | 		this.logger.info(`Undo: ${uri}`); | ||||||
| 
 | 
 | ||||||
| 		const resolver = this.apResolverService.createResolver(); | 		// eslint-disable-next-line no-param-reassign
 | ||||||
|  | 		resolver ??= this.apResolverService.createResolver(); | ||||||
| 
 | 
 | ||||||
| 		const object = await resolver.resolve(activity.object).catch(e => { | 		const object = await resolver.resolve(activity.object).catch(e => { | ||||||
| 			this.logger.error(`Resolution failed: ${e}`); | 			this.logger.error(`Resolution failed: ${e}`); | ||||||
|  | @ -770,14 +782,15 @@ export class ApInboxService { | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	@bindThis | 	@bindThis | ||||||
| 	private async update(actor: MiRemoteUser, activity: IUpdate): Promise<string> { | 	private async update(actor: MiRemoteUser, activity: IUpdate, resolver?: Resolver): Promise<string> { | ||||||
| 		if (actor.uri !== activity.actor) { | 		if (actor.uri !== activity.actor) { | ||||||
| 			return 'skip: invalid actor'; | 			return 'skip: invalid actor'; | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		this.logger.debug('Update'); | 		this.logger.debug('Update'); | ||||||
| 
 | 
 | ||||||
| 		const resolver = this.apResolverService.createResolver(); | 		// eslint-disable-next-line no-param-reassign
 | ||||||
|  | 		resolver ??= this.apResolverService.createResolver(); | ||||||
| 
 | 
 | ||||||
| 		const object = await resolver.resolve(activity.object).catch(e => { | 		const object = await resolver.resolve(activity.object).catch(e => { | ||||||
| 			this.logger.error(`Resolution failed: ${e}`); | 			this.logger.error(`Resolution failed: ${e}`); | ||||||
|  | @ -799,11 +812,11 @@ export class ApInboxService { | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	@bindThis | 	@bindThis | ||||||
| 	private async move(actor: MiRemoteUser, activity: IMove): Promise<string> { | 	private async move(actor: MiRemoteUser, activity: IMove, resolver?: Resolver): Promise<string> { | ||||||
| 		// fetch the new and old accounts
 | 		// fetch the new and old accounts
 | ||||||
| 		const targetUri = getApHrefNullable(activity.target); | 		const targetUri = getApHrefNullable(activity.target); | ||||||
| 		if (!targetUri) return 'skip: invalid activity target'; | 		if (!targetUri) return 'skip: invalid activity target'; | ||||||
| 
 | 
 | ||||||
| 		return await this.apPersonService.updatePerson(actor.uri) ?? 'skip: nothing to do'; | 		return await this.apPersonService.updatePerson(actor.uri, resolver) ?? 'skip: nothing to do'; | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -42,7 +42,7 @@ export class Resolver { | ||||||
| 		private apRendererService: ApRendererService, | 		private apRendererService: ApRendererService, | ||||||
| 		private apDbResolverService: ApDbResolverService, | 		private apDbResolverService: ApDbResolverService, | ||||||
| 		private loggerService: LoggerService, | 		private loggerService: LoggerService, | ||||||
| 		private recursionLimit = 100, | 		private recursionLimit = 256, | ||||||
| 	) { | 	) { | ||||||
| 		this.history = new Set(); | 		this.history = new Set(); | ||||||
| 		this.logger = this.loggerService.getLogger('ap-resolve'); | 		this.logger = this.loggerService.getLogger('ap-resolve'); | ||||||
|  | @ -53,6 +53,11 @@ export class Resolver { | ||||||
| 		return Array.from(this.history); | 		return Array.from(this.history); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
|  | 	public getRecursionLimit(): number { | ||||||
|  | 		return this.recursionLimit; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	@bindThis | 	@bindThis | ||||||
| 	public async resolveCollection(value: string | IObject): Promise<ICollection | IOrderedCollection> { | 	public async resolveCollection(value: string | IObject): Promise<ICollection | IOrderedCollection> { | ||||||
| 		const collection = typeof value === 'string' | 		const collection = typeof value === 'string' | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		
		Reference in a new issue