merge: Use secureResolve for Actor collections (resolves #1087) (!1087)

View MR for information: https://activitypub.software/TransFem-org/Sharkey/-/merge_requests/1087

Closes #1087

Approved-by: dakkar <dakkar@thenautilus.net>
Approved-by: Marie <github@yuugi.dev>
This commit is contained in:
Hazelnoot 2025-06-01 20:39:40 +00:00
commit a5d49c8bbf

View file

@ -356,8 +356,8 @@ export class ApPersonService implements OnModuleInit, OnApplicationShutdown {
const [followingVisibility, followersVisibility] = await Promise.all( const [followingVisibility, followersVisibility] = await Promise.all(
[ [
this.isPublicCollection(person.following, resolver), this.isPublicCollection(person.following, resolver, uri),
this.isPublicCollection(person.followers, resolver), this.isPublicCollection(person.followers, resolver, uri),
].map((p): Promise<'public' | 'private'> => p ].map((p): Promise<'public' | 'private'> => p
.then(isPublic => isPublic ? 'public' : 'private') .then(isPublic => isPublic ? 'public' : 'private')
.catch(err => { .catch(err => {
@ -393,10 +393,15 @@ export class ApPersonService implements OnModuleInit, OnApplicationShutdown {
//#endregion //#endregion
//#region resolve counts //#region resolve counts
const _resolver = resolver ?? this.apResolverService.createResolver(); const outboxCollection = person.outbox
const outboxcollection = await _resolver.resolveCollection(person.outbox).catch(() => { return null; }); ? await resolver.resolveCollection(person.outbox, true, uri).catch(() => { return null; })
const followerscollection = await _resolver.resolveCollection(person.followers!).catch(() => { return null; }); : null;
const followingcollection = await _resolver.resolveCollection(person.following!).catch(() => { return null; }); const followersCollection = person.followers
? await resolver.resolveCollection(person.followers, true, uri).catch(() => { return null; })
: null;
const followingCollection = person.following
? await resolver.resolveCollection(person.following, true, uri).catch(() => { return null; })
: null;
// Register the instance first, to avoid FK errors // Register the instance first, to avoid FK errors
await this.federatedInstanceService.fetchOrRegister(host); await this.federatedInstanceService.fetchOrRegister(host);
@ -426,9 +431,9 @@ export class ApPersonService implements OnModuleInit, OnApplicationShutdown {
host, host,
inbox: person.inbox, inbox: person.inbox,
sharedInbox: person.sharedInbox ?? person.endpoints?.sharedInbox ?? null, sharedInbox: person.sharedInbox ?? person.endpoints?.sharedInbox ?? null,
notesCount: outboxcollection?.totalItems ?? 0, notesCount: outboxCollection?.totalItems ?? 0,
followersCount: followerscollection?.totalItems ?? 0, followersCount: followersCollection?.totalItems ?? 0,
followingCount: followingcollection?.totalItems ?? 0, followingCount: followingCollection?.totalItems ?? 0,
followersUri: person.followers ? getApId(person.followers) : undefined, followersUri: person.followers ? getApId(person.followers) : undefined,
featured: person.featured ? getApId(person.featured) : undefined, featured: person.featured ? getApId(person.featured) : undefined,
uri: person.id, uri: person.id,
@ -577,8 +582,8 @@ export class ApPersonService implements OnModuleInit, OnApplicationShutdown {
const [followingVisibility, followersVisibility] = await Promise.all( const [followingVisibility, followersVisibility] = await Promise.all(
[ [
this.isPublicCollection(person.following, resolver), this.isPublicCollection(person.following, resolver, exist.uri),
this.isPublicCollection(person.followers, resolver), this.isPublicCollection(person.followers, resolver, exist.uri),
].map((p): Promise<'public' | 'private' | undefined> => p ].map((p): Promise<'public' | 'private' | undefined> => p
.then(isPublic => isPublic ? 'public' : 'private') .then(isPublic => isPublic ? 'public' : 'private')
.catch(err => { .catch(err => {
@ -802,13 +807,13 @@ export class ApPersonService implements OnModuleInit, OnApplicationShutdown {
const _resolver = resolver ?? this.apResolverService.createResolver(); const _resolver = resolver ?? this.apResolverService.createResolver();
// Resolve to (Ordered)Collection Object // Resolve to (Ordered)Collection Object
const collection = await _resolver.resolveCollection(user.featured).catch(err => { const collection = user.featured ? await _resolver.resolveCollection(user.featured, true, user.uri).catch(err => {
if (err instanceof AbortError || err instanceof StatusError) { if (err instanceof AbortError || err instanceof StatusError) {
this.logger.warn(`Failed to update featured notes: ${err.name}: ${err.message}`); this.logger.warn(`Failed to update featured notes: ${err.name}: ${err.message}`);
} else { } else {
this.logger.error('Failed to update featured notes:', err); this.logger.error('Failed to update featured notes:', err);
} }
}); }) : null;
if (!collection) return; if (!collection) return;
if (!isCollectionOrOrderedCollection(collection)) throw new UnrecoverableError(`featured ${user.featured} is not Collection or OrderedCollection in ${user.uri}`); if (!isCollectionOrOrderedCollection(collection)) throw new UnrecoverableError(`featured ${user.featured} is not Collection or OrderedCollection in ${user.uri}`);
@ -894,13 +899,15 @@ export class ApPersonService implements OnModuleInit, OnApplicationShutdown {
} }
@bindThis @bindThis
private async isPublicCollection(collection: string | ICollection | IOrderedCollection | undefined, resolver: Resolver): Promise<boolean> { private async isPublicCollection(collection: string | ICollection | IOrderedCollection | undefined, resolver: Resolver, sentFrom: string): Promise<boolean> {
if (collection) { if (collection) {
const resolved = await resolver.resolveCollection(collection); const resolved = await resolver.resolveCollection(collection, true, sentFrom).catch(() => null);
if (resolved) {
if (resolved.first || (resolved as ICollection).items || (resolved as IOrderedCollection).orderedItems) { if (resolved.first || (resolved as ICollection).items || (resolved as IOrderedCollection).orderedItems) {
return true; return true;
} }
} }
}
return false; return false;
} }