[backend] Use a semaphore around populateMentions

This fixes a user-generated DoS payload for giant webring-style trees of mentions in user bios that could cause backend stalls.
This commit is contained in:
Laura Hausmann 2023-10-20 20:06:25 +02:00
parent 9b96b54c3c
commit 544b5a1678
No known key found for this signature in database
GPG key ID: D044E84C5BE01605
6 changed files with 56 additions and 5 deletions

22
.pnp.cjs generated
View file

@ -1722,6 +1722,10 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
"async-lock",\
"npm:1.4.0"\
],\
[\
"async-mutex",\
"npm:0.4.0"\
],\
[\
"async-settle",\
"npm:1.0.0"\
@ -13423,6 +13427,16 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
"linkType": "HARD"\
}]\
]],\
["async-mutex", [\
["npm:0.4.0", {\
"packageLocation": "./.yarn/cache/async-mutex-npm-0.4.0-f5a25d4255-813a71728b.zip/node_modules/async-mutex/",\
"packageDependencies": [\
["async-mutex", "npm:0.4.0"],\
["tslib", "npm:2.6.2"]\
],\
"linkType": "HARD"\
}]\
]],\
["async-settle", [\
["npm:1.0.0", {\
"packageLocation": "./.yarn/cache/async-settle-npm-1.0.0-5d08fbf926-d2382ad4b9.zip/node_modules/async-settle/",\
@ -13862,6 +13876,7 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
["archiver", "npm:5.3.1"],\
["argon2", "npm:0.30.3"],\
["async-lock", "npm:1.4.0"],\
["async-mutex", "npm:0.4.0"],\
["autolinker", "npm:4.0.0"],\
["autwh", "npm:0.1.0"],\
["aws-sdk", "npm:2.1413.0"],\
@ -30575,6 +30590,13 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
["tslib", "npm:2.6.1"]\
],\
"linkType": "HARD"\
}],\
["npm:2.6.2", {\
"packageLocation": "./.yarn/cache/tslib-npm-2.6.2-4fc8c068d9-329ea56123.zip/node_modules/tslib/",\
"packageDependencies": [\
["tslib", "npm:2.6.2"]\
],\
"linkType": "HARD"\
}]\
]],\
["tsscmp", [\

BIN
.yarn/cache/async-mutex-npm-0.4.0-f5a25d4255-813a71728b.zip (Stored with Git LFS) vendored Normal file

Binary file not shown.

BIN
.yarn/cache/tslib-npm-2.6.2-4fc8c068d9-329ea56123.zip (Stored with Git LFS) vendored Normal file

Binary file not shown.

View file

@ -41,6 +41,7 @@
"archiver": "5.3.1",
"argon2": "^0.30.3",
"async-lock": "1.4.0",
"async-mutex": "^0.4.0",
"autolinker": "4.0.0",
"autwh": "0.1.0",
"aws-sdk": "2.1413.0",

View file

@ -6,8 +6,12 @@ import { resolveMentionToUserAndProfile } from "@/remote/resolve-user.js";
import { IMentionedRemoteUsers } from "@/models/entities/note.js";
import { unique } from "@/prelude/array.js";
import config from "@/config/index.js";
import { Semaphore } from "async-mutex";
const queue = new Semaphore(10);
export const UserProfileRepository = db.getRepository(UserProfile).extend({
// We must never await this without promiseEarlyReturn, otherwise giant webring-style profile mention trees will cause the queue to stop working
async updateMentions(id: UserProfile["userId"]){
const profile = await this.findOneBy({ userId: id });
if (!profile) return;
@ -18,11 +22,12 @@ export const UserProfileRepository = db.getRepository(UserProfile).extend({
if (profile.fields.length > 0)
tokens.push(...profile.fields.map(p => mfm.parse(p.value).concat(mfm.parse(p.name))).flat());
const partial = {
mentions: await populateMentions(tokens, profile.userHost)
};
return UserProfileRepository.update(profile.userId, partial)
return queue.runExclusive(async () => {
const partial = {
mentions: await populateMentions(tokens, profile.userHost)
};
return UserProfileRepository.update(profile.userId, partial);
});
},
});

View file

@ -4779,6 +4779,15 @@ __metadata:
languageName: node
linkType: hard
"async-mutex@npm:^0.4.0":
version: 0.4.0
resolution: "async-mutex@npm:0.4.0"
dependencies:
tslib: ^2.4.0
checksum: 813a71728b35a4fbfd64dba719f04726d9133c67b577fcd951b7028c4a675a13ee34e69beb82d621f87bf81f5d4f135c4c44be0448550c7db728547244ef71fc
languageName: node
linkType: hard
"async-settle@npm:^1.0.0":
version: 1.0.0
resolution: "async-settle@npm:1.0.0"
@ -5129,6 +5138,7 @@ __metadata:
archiver: 5.3.1
argon2: ^0.30.3
async-lock: 1.4.0
async-mutex: ^0.4.0
autolinker: 4.0.0
autwh: 0.1.0
aws-sdk: 2.1413.0
@ -19712,6 +19722,13 @@ __metadata:
languageName: node
linkType: hard
"tslib@npm:^2.4.0":
version: 2.6.2
resolution: "tslib@npm:2.6.2"
checksum: 329ea56123005922f39642318e3d1f0f8265d1e7fcb92c633e0809521da75eeaca28d2cf96d7248229deb40e5c19adf408259f4b9640afd20d13aecc1430f3ad
languageName: node
linkType: hard
"tsscmp@npm:1.0.6":
version: 1.0.6
resolution: "tsscmp@npm:1.0.6"