fix: bug where disparate ids all claiming to be the same handle were causing duplicate remote users due to collisions, #13352

This commit is contained in:
Julian Lam
2025-04-22 15:12:56 -04:00
parent 3e508d6c65
commit c2a3ef817d
4 changed files with 39 additions and 0 deletions

View File

@@ -127,6 +127,13 @@ Actors.assert = async (ids, options = {}) => {
activitypub.helpers.log(`[activitypub/actors] Processing ${id}`);
const actor = (typeof id === 'object' && id.hasOwnProperty('id')) ? id : await activitypub.get('uid', 0, id, { cache: process.env.CI === 'true' });
// webfinger backreference check
const { hostname: domain } = new URL(id);
const { actorUri: canonicalId } = await activitypub.helpers.query(`${actor.preferredUsername}@${domain}`);
if (id !== canonicalId) {
return null;
}
let typeOk = false;
if (Array.isArray(actor.type)) {
typeOk = actor.type.some(type => activitypub._constants.acceptableActorTypes.has(type));

View File

@@ -28,6 +28,8 @@ const sha256 = payload => crypto.createHash('sha256').update(payload).digest('he
const Helpers = module.exports;
Helpers._webfingerCache = webfingerCache; // exported for tests
Helpers._test = (method, args) => {
// because I am lazy and I probably wrote some variant of this below code 1000 times already
setTimeout(async () => {

View File

@@ -46,6 +46,7 @@ describe('Actor asserton', () => {
publicKeyPem: 'somekey',
},
});
activitypub.helpers._webfingerCache.set('example@example.org', { actorUri })
});
it('should return true if successfully asserted', async () => {
@@ -194,6 +195,25 @@ describe('Actor asserton', () => {
const uid = await db.getObjectField('handle:uid', `${preferredUsername.toLowerCase()}@example.org`);
assert.strictEqual(uid, id);
});
it('should fail to assert if a passed-in ID\'s webfinger query does not respond with the same ID (gh#13352)', async () => {
const { id } = helpers.mocks.person({
preferredUsername: 'foobar',
});
const actorUri = `https://example.org/${utils.generateUUID()}`;
activitypub.helpers._webfingerCache.set('foobar@example.org', {
username: 'foobar',
hostname: 'example.org',
actorUri,
});
const { actorUri: confirm } = await activitypub.helpers.query('foobar@example.org');
assert.strictEqual(confirm, actorUri);
const response = await activitypub.actors.assert([id]);
assert.deepStrictEqual(response, []);
})
});
});

View File

@@ -40,6 +40,11 @@ Helpers.mocks.person = (override = {}) => {
};
activitypub._cache.set(`0;${id}`, actor);
activitypub.helpers._webfingerCache.set(`${actor.preferredUsername}@example.org`, {
actorUri: id,
username: id,
hostname: 'example.org',
});
return { id, actor };
};
@@ -51,6 +56,11 @@ Helpers.mocks.group = (override = {}) => {
});
activitypub._cache.set(`0;${id}`, actor);
activitypub.helpers._webfingerCache.set(`${actor.preferredUsername}@example.org`, {
actorUri: id,
username: id,
hostname: 'example.org',
});
return { id, actor };
};