merge: Trim padded Actor keys to avoid value too long error (resolves #806) (!913)

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

Closes #806

Approved-by: dakkar <dakkar@thenautilus.net>
Approved-by: Marie <github@yuugi.dev>
This commit is contained in:
Marie 2025-02-28 20:06:11 +00:00
commit 6e8ab00750
2 changed files with 71 additions and 2 deletions

View file

@ -447,7 +447,7 @@ export class ApPersonService implements OnModuleInit {
await transactionalEntityManager.save(new MiUserPublickey({
userId: user.id,
keyId: person.publicKey.id,
keyPem: person.publicKey.publicKeyPem,
keyPem: person.publicKey.publicKeyPem.trim(),
}));
}
});

View file

@ -22,7 +22,7 @@ import { CoreModule } from '@/core/CoreModule.js';
import { FederatedInstanceService } from '@/core/FederatedInstanceService.js';
import { LoggerService } from '@/core/LoggerService.js';
import type { IActor, IApDocument, ICollection, IObject, IPost } from '@/core/activitypub/type.js';
import { MiMeta, MiNote, MiUser, UserProfilesRepository } from '@/models/_.js';
import { MiMeta, MiNote, MiUser, UserProfilesRepository, UserPublickeysRepository } from '@/models/_.js';
import { DI } from '@/di-symbols.js';
import { secureRndstr } from '@/misc/secure-rndstr.js';
import { DownloadService } from '@/core/DownloadService.js';
@ -96,6 +96,7 @@ describe('ActivityPub', () => {
let jsonLdService: JsonLdService;
let resolver: MockResolver;
let idService: IdService;
let userPublickeysRepository: UserPublickeysRepository;
const metaInitial = {
cacheRemoteFiles: true,
@ -144,6 +145,7 @@ describe('ActivityPub', () => {
jsonLdService = app.get<JsonLdService>(JsonLdService);
resolver = new MockResolver(await app.resolve<LoggerService>(LoggerService));
idService = app.get<IdService>(IdService);
userPublickeysRepository = app.get<UserPublickeysRepository>(DI.userPublickeysRepository);
// Prevent ApPersonService from fetching instance, as it causes Jest import-after-test error
const federatedInstanceService = app.get<FederatedInstanceService>(FederatedInstanceService);
@ -620,4 +622,71 @@ describe('ActivityPub', () => {
});
});
});
describe(ApPersonService, () => {
describe('createPerson', () => {
it('should trim publicKey', async () => {
const actor = createRandomActor();
actor.publicKey = {
id: `${actor.id}#main-key`,
publicKeyPem: ' key material\t\n\r\n \n',
};
resolver.register(actor.id, actor);
const user = await personService.createPerson(actor.id, resolver);
const publicKey = await userPublickeysRepository.findOneBy({ userId: user.id });
expect(publicKey).not.toBeNull();
expect(publicKey?.keyPem).toBe('key material');
});
it('should accept SocialHome actor', async () => {
// This is taken from a real SocialHome actor, including the 13,905 newline characters in the public key.
const actor = {
'@context': ['https://www.w3.org/ns/activitystreams', 'https://w3id.org/security/v1', {
'pyfed': 'https://docs.jasonrobinson.me/ns/python-federation#',
'diaspora': 'https://diasporafoundation.org/ns/',
'manuallyApprovesFollowers': 'as:manuallyApprovesFollowers',
}],
id: 'https://socialhome.network/u/hq/',
type: 'Person',
inbox: 'https://socialhome.network/u/hq/inbox/',
'diaspora:guid': '7538bd1b-d3a8-49a5-bf00-db63fcc9114f',
'diaspora:handle': 'hq@socialhome.network',
publicKey: {
id: 'https://socialhome.network/u/hq/#main-key',
owner: 'https://socialhome.network/u/hq/',
publicKeyPem: '-----BEGIN PUBLIC KEY-----\nMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAg39sDmTAJ7l9bl5jYLmj\nKYnDZJgRiO/WR+V1HEMEsRoEPTxJzWe+Ou7YTUhOOvDRu5ncEn3ictF3/BxhhQC1\nQwUKYlfuU1R7PyGqWtGm6300mDAmbq+eyC+fwV9FbkCm9npRatZfnZXZWuCgA6f7\nWmmBw09QVZQ6Ypu+7CF/Q6bv0E5B2hieTSbRgavdSkEopMyJhPs5/X6Hh4XYSi7t\nYEg9vD0d0J9QJSnCTYIZT145cV1DANV/4KjhKkYgvt4hLNOKZ1v4QC57K+PFna9N\ntxm1nMxwjpBPus8LQeDii/MwKoiZ7LBjeflm0C9AMFlNPB9iq3rEXo3eyCEb7Lyr\nEp+oqYNfopFIRPNfhBxtkx5ioUXty3cx1WnZtehqGdpOcb1wUatW5IjV8tlfLIr7\nrDNCxgGnScR6h7++BHYDdDVBgGUkC5ELIxxSMqlYMiBGVmYdIoAGO6nuqw4bp5l3\nUf07d28GoZgcRBVZWC/xOtRb7E6PTzsE7xd51UijusRC79lnapzTWY9GAY0ZYu+w\nbAxO7u3+Knr6EXZkGkmrElKIT2N6SPJY3Xo91+PT1Y77JMFkkWlEX9IO08fALsqg\nbMSKNQ8WfyHCTjaiH3n4BdgTjP4kRm2OhczxvgCFvtcOK+M60YdwM6MOZDEOVtGU\nGIYA1mtQW7a8jb5QPTQu9GcCAwEAAQ==\n-----END PUBLIC KEY-----' + ''.padEnd(13905, '\n'),
},
endpoints: { 'sharedInbox': 'https://socialhome.network/receive/public/' },
followers: 'https://socialhome.network/u/hq/followers/',
following: 'https://socialhome.network/u/hq/following/',
icon: {
type: 'Image',
'pyfed:inlineImage': false,
mediaType: 'image/png',
url: 'https://socialhome.network/media/__sized__/profiles/Socialhome-dark-600-crop-c0-5__0-5-300x300.png',
},
manuallyApprovesFollowers: false,
name: 'Socialhome HQ',
outbox: 'https://socialhome.network/u/hq/outbox/',
preferredUsername: 'hq',
published: '2017-01-29T19:28:19+00:00',
updated: '2025-02-17T23:11:30+00:00',
url: 'https://socialhome.network/p/7538bd1b-d3a8-49a5-bf00-db63fcc9114f/',
};
resolver.register(actor.id, actor);
resolver.register(actor.publicKey.id, actor.publicKey);
resolver.register(actor.followers, { id: actor.followers, type: 'Collection', totalItems: 0, items: [] } satisfies ICollection);
resolver.register(actor.following, { id: actor.following, type: 'Collection', totalItems: 0, items: [] } satisfies ICollection);
resolver.register(actor.outbox, { id: actor.outbox, type: 'Collection', totalItems: 0, items: [] } satisfies ICollection);
const user = await personService.createPerson(actor.id, resolver);
const publicKey = await userPublickeysRepository.findOneBy({ userId: user.id });
expect(user.uri).toBe(actor.id);
expect(publicKey).not.toBeNull();
});
});
});
});