Changeset 0bafe9e in Klonkt
- Timestamp:
- 08/06/2026 10:49:06 AM (5 weeks ago)
- Branches:
- main
- Children:
- d74a461
- Parents:
- 5d0d41d
- git-author:
- Robin <roboburr@…> (08/06/2026 10:49:05 AM)
- git-committer:
- Claude (agent) <aiclaude@…> (08/06/2026 10:49:06 AM)
- Files:
-
- 2 edited
-
src/services/ActivityPubService.js (modified) (1 diff)
-
test/authorized-fetch.test.js (modified) (1 diff)
Legend:
- Unmodified
- Added
- Removed
-
src/services/ActivityPubService.js
r5d0d41d r0bafe9e 1295 1295 // Geen kip-ei: om ONZE handtekening te controleren haalt de andere kant ons 1296 1296 // actor-document op, en dat serveert Klonkt publiek. 1297 if (opts.asSlug) { 1298 const signed = await signedGetJson(opts.asSlug, url).catch(() => null); 1299 if (signed && signed.id) return signed; 1300 // Geen terugkeer bij mislukking: een instance zonder secure mode moet 1301 // blijven werken, en niet elke 401 komt van authorized fetch. 1302 } 1297 // 1298 // ONBETEKEND EERST, en dat is een veiligheidskeuze en geen optimalisatie. 1299 // verifyRequest haalt de keyId-URL op VOORDAT er iets geverifieerd is, en die 1300 // URL komt uit een header die iedereen mag sturen. Tekenden we dat verzoek 1301 // standaard, dan kan een volslagen onbekende ons een ONDERTEKEND verzoek laten 1302 // sturen naar een adres van zijn keuze -- met onze identiteit eronder. Dat is 1303 // precies hoe een instance op een blocklist belandt. Ondertekenen doen we dus 1304 // pas als het onbetekend niet lukt, en dan alleen voor deze ene URL. 1305 let doc = null; 1303 1306 try { 1304 1307 const r = await safeFetch(url, { headers: { Accept: 'application/activity+json' } }); 1305 if (!r.ok) return null; 1306 const len = Number(r.headers.get('content-length') || 0); 1307 if (len > 2_000_000) return null; // refuse oversized actor docs 1308 return await r.json(); 1309 } catch { return null; } 1308 if (r.ok) { 1309 const len = Number(r.headers.get('content-length') || 0); 1310 if (len > 2_000_000) return null; // refuse oversized actor docs 1311 doc = await r.json(); 1312 } 1313 } catch { /* val door naar de ondertekende poging */ } 1314 // Genoeg? Dan klaar. Sommige instances serveren onbetekend wel een document 1315 // maar zonder sleutel; voor een verificatie hebben we daar niets aan, dus die 1316 // telt als mislukt. 1317 if (doc && (!opts.asSlug || (doc.publicKey && doc.publicKey.publicKeyPem))) return doc; 1318 if (!opts.asSlug) return doc; 1319 const signed = await signedGetJson(opts.asSlug, url).catch(() => null); 1320 return (signed && signed.id) ? signed : doc; 1310 1321 } 1311 1322 -
test/authorized-fetch.test.js
r5d0d41d r0bafe9e 66 66 }); 67 67 68 test('mislukt ondertekend ophalen, dan volgt de onbetekende poging alsnog', async () => { 69 // Niet elke 401 komt van secure mode, en een instance die geen handtekening 70 // verwacht mag er niet door stukgaan. OPEN_ACTOR antwoordt op allebei, dus we 71 // meten dat er ECHT twee pogingen zijn wanneer de eerste niets oplevert. 68 test('een OPEN instance wordt NIET ondertekend opgehaald', () => { 69 // De veiligheidskant van shaer-afq: verifyRequest haalt de keyId-URL op 70 // voordat er iets geverifieerd is, en die URL komt uit een header die iedereen 71 // mag sturen. Tekenden we standaard, dan kan een vreemde ons een ondertekend 72 // verzoek naar een adres van zijn keuze laten sturen, met onze identiteit 73 // eronder. Onbetekend eerst dus, en alleen tekenen als het anders niet lukt. 72 74 verzoeken = []; 75 return AP.fetchActor(OPEN_ACTOR, { asSlug: 'me' }).then((actor) => { 76 assert.ok(actor); 77 assert.equal(verzoeken.length, 1, 'één poging, geen tweede'); 78 assert.equal(verzoeken[0].ondertekend, false, 'en die was onbetekend'); 79 }); 80 }); 81 82 test('een document zonder sleutel telt als mislukt en leidt tot een ondertekende poging', async () => { 83 // Sommige instances serveren onbetekend wel iets, maar zonder publicKey. Voor 84 // een verificatie hebben we daar niets aan. 85 const KAAL = 'https://203.0.113.50/users/kaal'; 73 86 const stubOrig = globalThis.fetch; 74 let eerste = true;75 87 globalThis.fetch = async (url, opts = {}) => { 76 88 const ondertekend = !!(opts.headers && (opts.headers.Signature || opts.headers.signature)); 77 if (String(url) === OPEN_ACTOR && ondertekend && eerste) { eerste = false; return new Response('nee', { status: 500 }); } 89 if (String(url) === KAAL) { 90 verzoeken.push({ url: String(url), ondertekend }); 91 const body = ondertekend 92 ? { id: KAAL, type: 'Person', publicKey: { id: `${KAAL}#k`, owner: KAAL, publicKeyPem: 'x' } } 93 : { id: KAAL, type: 'Person' }; // kaal: geen sleutel 94 return new Response(JSON.stringify(body), { status: 200, headers: { 'content-type': 'application/activity+json' } }); 95 } 78 96 return stubOrig(url, opts); 79 97 }; 80 const actor = await AP.fetchActor(OPEN_ACTOR, { asSlug: 'me' }); 98 verzoeken = []; 99 const actor = await AP.fetchActor(KAAL, { asSlug: 'me' }); 81 100 globalThis.fetch = stubOrig; 82 assert.ok(actor, 'de terugval hoort hem alsnog op te halen'); 101 assert.ok(actor.publicKey && actor.publicKey.publicKeyPem, 'de ondertekende poging levert de sleutel'); 102 assert.deepEqual(verzoeken.map((v) => v.ondertekend), [false, true], 'eerst onbetekend, daarna pas ondertekend'); 83 103 }); 84 104
Note:
See TracChangeset
for help on using the changeset viewer.
![(please configure the [header_logo] section in trac.ini)](/chrome/site/your_project_logo.png)