diff --git a/src/activitypub/actors.js b/src/activitypub/actors.js index 23bcc2b19e..989656970c 100644 --- a/src/activitypub/actors.js +++ b/src/activitypub/actors.js @@ -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)); diff --git a/src/activitypub/helpers.js b/src/activitypub/helpers.js index 009290b8f2..4a8b2b08fa 100644 --- a/src/activitypub/helpers.js +++ b/src/activitypub/helpers.js @@ -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 () => { diff --git a/test/activitypub/actors.js b/test/activitypub/actors.js index ed2bebfc1a..18d65a3fe0 100644 --- a/test/activitypub/actors.js +++ b/test/activitypub/actors.js @@ -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, []); }) }); }); diff --git a/test/activitypub/helpers.js b/test/activitypub/helpers.js index f557b90007..f5929ecb46 100644 --- a/test/activitypub/helpers.js +++ b/test/activitypub/helpers.js @@ -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 }; };