fix(server): "forkbomb" DOS mitigation (#9247)
* Add recursion limit to resolver * Use shared resolver in featured and question * Changelog * Changelog fix * Update CHANGELOG.md Co-authored-by: Acid Chicken (硫酸鶏) <root@acid-chicken.com> * Add host to recursion limit error message Co-authored-by: Acid Chicken (硫酸鶏) <root@acid-chicken.com> Co-authored-by: syuilo <Syuilotan@yahoo.co.jp> Co-authored-by: Acid Chicken (硫酸鶏) <root@acid-chicken.com>
This commit is contained in:
parent
5decad9cf1
commit
66513b9893
|
@ -4,7 +4,7 @@
|
||||||
### Improvements
|
### Improvements
|
||||||
|
|
||||||
### Bugfixes
|
### Bugfixes
|
||||||
-
|
-
|
||||||
|
|
||||||
You should also include the user name that made the change.
|
You should also include the user name that made the change.
|
||||||
-->
|
-->
|
||||||
|
@ -25,6 +25,7 @@ You should also include the user name that made the change.
|
||||||
- Server: 引用内の文章がnyaizeされてしまう問題を修正 @kabo2468
|
- Server: 引用内の文章がnyaizeされてしまう問題を修正 @kabo2468
|
||||||
- Server: Bug fix for Pinned Users lookup on instance @squidicuzz
|
- Server: Bug fix for Pinned Users lookup on instance @squidicuzz
|
||||||
- Client: インスタンスティッカーのfaviconを読み込む際に偽サイト警告が出ることがあるのを修正 @syuilo
|
- Client: インスタンスティッカーのfaviconを読み込む際に偽サイト警告が出ることがあるのを修正 @syuilo
|
||||||
|
- Server: Mitigate AP reference chain DoS vector @skehmatics
|
||||||
|
|
||||||
## 12.119.0 (2022/09/10)
|
## 12.119.0 (2022/09/10)
|
||||||
|
|
||||||
|
|
|
@ -731,7 +731,7 @@ export class ApInboxService {
|
||||||
await this.apPersonService.updatePerson(actor.uri!, resolver, object);
|
await this.apPersonService.updatePerson(actor.uri!, resolver, object);
|
||||||
return 'ok: Person updated';
|
return 'ok: Person updated';
|
||||||
} else if (getApType(object) === 'Question') {
|
} else if (getApType(object) === 'Question') {
|
||||||
await this.apQuestionService.updateQuestion(object).catch(err => console.error(err));
|
await this.apQuestionService.updateQuestion(object, resolver).catch(err => console.error(err));
|
||||||
return 'ok: Question updated';
|
return 'ok: Question updated';
|
||||||
} else {
|
} else {
|
||||||
return `skip: Unknown type: ${getApType(object)}`;
|
return `skip: Unknown type: ${getApType(object)}`;
|
||||||
|
|
|
@ -76,6 +76,7 @@ export class Resolver {
|
||||||
private httpRequestService: HttpRequestService,
|
private httpRequestService: HttpRequestService,
|
||||||
private apRendererService: ApRendererService,
|
private apRendererService: ApRendererService,
|
||||||
private apDbResolverService: ApDbResolverService,
|
private apDbResolverService: ApDbResolverService,
|
||||||
|
private recursionLimit = 100
|
||||||
) {
|
) {
|
||||||
this.history = new Set();
|
this.history = new Set();
|
||||||
}
|
}
|
||||||
|
@ -116,6 +117,10 @@ export class Resolver {
|
||||||
throw new Error('cannot resolve already resolved one');
|
throw new Error('cannot resolve already resolved one');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.history.size > this.recursionLimit) {
|
||||||
|
throw new Error(`hit recursion limit: ${this.utilityService.extractDbHost(value)}`);
|
||||||
|
}
|
||||||
|
|
||||||
this.history.add(value);
|
this.history.add(value);
|
||||||
|
|
||||||
const host = this.utilityService.extractDbHost(value);
|
const host = this.utilityService.extractDbHost(value);
|
||||||
|
|
|
@ -390,7 +390,7 @@ export class ApPersonService implements OnModuleInit {
|
||||||
});
|
});
|
||||||
//#endregion
|
//#endregion
|
||||||
|
|
||||||
await this.updateFeatured(user!.id).catch(err => this.logger.error(err));
|
await this.updateFeatured(user!.id, resolver).catch(err => this.logger.error(err));
|
||||||
|
|
||||||
return user!;
|
return user!;
|
||||||
}
|
}
|
||||||
|
@ -503,7 +503,7 @@ export class ApPersonService implements OnModuleInit {
|
||||||
followerSharedInbox: person.sharedInbox ?? (person.endpoints ? person.endpoints.sharedInbox : undefined),
|
followerSharedInbox: person.sharedInbox ?? (person.endpoints ? person.endpoints.sharedInbox : undefined),
|
||||||
});
|
});
|
||||||
|
|
||||||
await this.updateFeatured(exist.id).catch(err => this.logger.error(err));
|
await this.updateFeatured(exist.id, resolver).catch(err => this.logger.error(err));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -551,14 +551,14 @@ export class ApPersonService implements OnModuleInit {
|
||||||
return { fields, services };
|
return { fields, services };
|
||||||
}
|
}
|
||||||
|
|
||||||
public async updateFeatured(userId: User['id']) {
|
public async updateFeatured(userId: User['id'], resolver?: Resolver) {
|
||||||
const user = await this.usersRepository.findOneByOrFail({ id: userId });
|
const user = await this.usersRepository.findOneByOrFail({ id: userId });
|
||||||
if (!this.userEntityService.isRemoteUser(user)) return;
|
if (!this.userEntityService.isRemoteUser(user)) return;
|
||||||
if (!user.featured) return;
|
if (!user.featured) return;
|
||||||
|
|
||||||
this.logger.info(`Updating the featured: ${user.uri}`);
|
this.logger.info(`Updating the featured: ${user.uri}`);
|
||||||
|
|
||||||
const resolver = this.apResolverService.createResolver();
|
if (resolver == null) resolver = this.apResolverService.createResolver();
|
||||||
|
|
||||||
// Resolve to (Ordered)Collection Object
|
// Resolve to (Ordered)Collection Object
|
||||||
const collection = await resolver.resolveCollection(user.featured);
|
const collection = await resolver.resolveCollection(user.featured);
|
||||||
|
|
|
@ -65,7 +65,7 @@ export class ApQuestionService {
|
||||||
* @param uri URI of AP Question object
|
* @param uri URI of AP Question object
|
||||||
* @returns true if updated
|
* @returns true if updated
|
||||||
*/
|
*/
|
||||||
public async updateQuestion(value: any) {
|
public async updateQuestion(value: any, resolver?: Resolver) {
|
||||||
const uri = typeof value === 'string' ? value : value.id;
|
const uri = typeof value === 'string' ? value : value.id;
|
||||||
|
|
||||||
// URIがこのサーバーを指しているならスキップ
|
// URIがこのサーバーを指しているならスキップ
|
||||||
|
@ -80,7 +80,7 @@ export class ApQuestionService {
|
||||||
//#endregion
|
//#endregion
|
||||||
|
|
||||||
// resolve new Question object
|
// resolve new Question object
|
||||||
const resolver = this.apResolverService.createResolver();
|
if (resolver == null) resolver = this.apResolverService.createResolver();
|
||||||
const question = await resolver.resolve(value) as IQuestion;
|
const question = await resolver.resolve(value) as IQuestion;
|
||||||
this.logger.debug(`fetched question: ${JSON.stringify(question, null, 2)}`);
|
this.logger.debug(`fetched question: ${JSON.stringify(question, null, 2)}`);
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue