Changeset cec72bc in Klonkt for test/forwarded-activities.test.js
- Timestamp:
- 08/06/2026 11:18:34 AM (5 weeks ago)
- Branches:
- main
- Children:
- f4f0a6d
- Parents:
- 434ba41
- git-author:
- Robin <roboburr@…> (08/06/2026 11:18:33 AM)
- git-committer:
- Claude (agent) <aiclaude@…> (08/06/2026 11:18:34 AM)
- File:
-
- 1 edited
-
test/forwarded-activities.test.js (modified) (8 diffs)
Legend:
- Unmodified
- Added
- Removed
-
test/forwarded-activities.test.js
r434ba41 rcec72bc 36 36 let opgehaald = []; 37 37 const echteFetch = globalThis.fetch; 38 globalThis.fetch = async (url, opts ) => {38 globalThis.fetch = async (url, opts = {}) => { 39 39 const u = String(url); 40 opgehaald.push( u);40 opgehaald.push({ url: u, ondertekend: !!(opts.headers && (opts.headers.Signature || opts.headers.signature)) }); 41 41 if (u === NOTE_ID && bron) { 42 42 return new Response(JSON.stringify(bron), { status: 200, headers: { 'content-type': 'application/activity+json' } }); … … 44 44 return new Response('not found', { status: 404 }); 45 45 }; 46 47 // Een note die WIJ kennen: doorsturen gebeurt omdat wij in de thread zitten, en 48 // sinds shaer-drf dereferencen we alleen als inReplyTo daarheen wijst. 49 const ONZE_NOTE = 'https://203.0.113.10/notes/van-ons'; 50 db.prepare(`INSERT OR IGNORE INTO ap_timeline (id, slug, author_uri, author_name, content, created_at) 51 VALUES (?,?,?,?,?,?)`).run(ONZE_NOTE, 'me', AUTEUR, 'Anna', '<p>x</p>', '2026-08-06 09:00:00'); 46 52 47 53 /** Een doorgestuurde Create: ondertekend door de doorstuurder, geschreven door de auteur. */ … … 51 57 actor: AUTEUR, 52 58 object: objectOverride !== undefined ? objectOverride : { 53 id: NOTE_ID, type: 'Note', attributedTo: AUTEUR, 59 id: NOTE_ID, type: 'Note', attributedTo: AUTEUR, inReplyTo: ONZE_NOTE, 54 60 content: '<p>BEZORGDE versie</p>', to: ['https://www.w3.org/ns/activitystreams#Public'], 55 61 }, … … 65 71 const status = await AP.handleInbox(req(act), 'me', alsDoorstuurder); 66 72 assert.notEqual(status, 401, 'een doorgestuurde reactie hoort niet meer geweigerd te worden'); 67 assert.ok(opgehaald. includes(NOTE_ID), 'het object hoort bij de bron opgehaald te zijn');73 assert.ok(opgehaald.some((v) => v.url === NOTE_ID), 'het object hoort bij de bron opgehaald te zijn'); 68 74 // Wat telt: de OPGEHAALDE inhoud wordt gebruikt, niet wat de doorstuurder gaf. 69 75 assert.match(act.object.content, /ECHTE versie/); … … 75 81 // is de waarheid; de payload wordt weggegooid. 76 82 bron = { id: NOTE_ID, type: 'Note', attributedTo: AUTEUR, content: '<p>ECHTE versie</p>' }; 77 const act = doorgestuurd({ id: NOTE_ID, type: 'Note', attributedTo: AUTEUR, content: '<p>KOOP MIJN MUNTEN</p>' });83 const act = doorgestuurd({ id: NOTE_ID, type: 'Note', attributedTo: AUTEUR, inReplyTo: ONZE_NOTE, content: '<p>KOOP MIJN MUNTEN</p>' }); 78 84 await AP.handleInbox(req(act), 'me', alsDoorstuurder); 79 85 assert.doesNotMatch(act.object.content, /MUNTEN/); … … 83 89 // Zonder deze ankereis wijst een doorsturer je naar een host die hij zelf 84 90 // beheert, waar attributedTo alles kan beweren. 85 bron = { id: 'https://203.0.113.66/notes/1', type: 'Note', attributedTo: AUTEUR, content: '<p>x</p>' };86 const act = doorgestuurd({ id: 'https://203.0.113.66/notes/1', type: 'Note', attributedTo: AUTEUR, content: '<p>x</p>' });91 bron = { id: 'https://203.0.113.66/notes/1', type: 'Note', attributedTo: AUTEUR, inReplyTo: ONZE_NOTE, content: '<p>x</p>' }; 92 const act = doorgestuurd({ id: 'https://203.0.113.66/notes/1', type: 'Note', attributedTo: AUTEUR, inReplyTo: ONZE_NOTE, content: '<p>x</p>' }); 87 93 const status = await AP.handleInbox(req(act), 'me', alsDoorstuurder); 88 94 assert.equal(status, 401); … … 116 122 const status = await AP.handleInbox({ body: doorgestuurd(), headers: {}, ip: '203.0.113.9' }, 'me', null); 117 123 assert.equal(status, 401); 118 assert.equal(opgehaald. includes(NOTE_ID), false, 'er hoort niet eens opgehaald te worden');124 assert.equal(opgehaald.some((v) => v.url === NOTE_ID), false, 'er hoort niet eens opgehaald te worden'); 119 125 }); 120 126 … … 127 133 }); 128 134 135 test('een antwoord op iets dat we NIET kennen wordt niet opgehaald', async () => { 136 // De vernauwing uit shaer-drf. Zonder deze eis zijn claimedActor en object.id 137 // allebei door de aanvaller gekozen en eist het host-anker alleen dat ze aan 138 // elkaar gelijk zijn -- dan kan iedereen met een werkende actor ons naar elke 139 // URL sturen. 140 bron = { id: NOTE_ID, type: 'Note', attributedTo: AUTEUR, content: '<p>x</p>' }; 141 opgehaald = []; 142 const act = doorgestuurd({ 143 id: NOTE_ID, type: 'Note', attributedTo: AUTEUR, 144 inReplyTo: 'https://203.0.113.10/notes/kennen-we-niet', content: '<p>x</p>', 145 }); 146 const status = await AP.handleInbox(req(act), 'me', alsDoorstuurder); 147 assert.equal(status, 401); 148 assert.equal(opgehaald.some((v) => v.url === NOTE_ID), false, 'er hoort niet eens opgehaald te worden'); 149 }); 150 151 test('zonder inReplyTo wordt er niets opgehaald', async () => { 152 bron = { id: NOTE_ID, type: 'Note', attributedTo: AUTEUR, content: '<p>x</p>' }; 153 opgehaald = []; 154 const act = doorgestuurd({ id: NOTE_ID, type: 'Note', attributedTo: AUTEUR, content: '<p>x</p>' }); 155 assert.equal(await AP.handleInbox(req(act), 'me', alsDoorstuurder), 401); 156 assert.equal(opgehaald.some((v) => v.url === NOTE_ID), false); 157 }); 158 159 test('de dereference haalt ONBETEKEND op als dat volstaat', async () => { 160 // Anders kan een ander ons een ondertekend verzoek naar een adres van zijn 161 // keuze laten sturen, met onze identiteit eronder. 162 // 163 // Eigen id: NOTE_ID is in een eerdere test mislukt en zit dus in de negatieve 164 // cache -- die zou deze poging overslaan. Dat de test daarop stukliep, is het 165 // bewijs dat de cache doet wat hij moet. 166 const VERS = 'https://203.0.113.10/notes/vers'; 167 const stubOrig = globalThis.fetch; 168 globalThis.fetch = async (url, opts = {}) => { 169 if (String(url) === VERS) { 170 opgehaald.push({ url: VERS, ondertekend: !!(opts.headers && (opts.headers.Signature || opts.headers.signature)) }); 171 return new Response(JSON.stringify({ id: VERS, type: 'Note', attributedTo: AUTEUR, content: '<p>x</p>' }), 172 { status: 200, headers: { 'content-type': 'application/activity+json' } }); 173 } 174 return stubOrig(url, opts); 175 }; 176 opgehaald = []; 177 const act = doorgestuurd({ id: VERS, type: 'Note', attributedTo: AUTEUR, inReplyTo: ONZE_NOTE, content: '<p>y</p>' }); 178 const status = await AP.handleInbox(req(act), 'me', alsDoorstuurder); 179 globalThis.fetch = stubOrig; 180 assert.notEqual(status, 401); 181 const pogingen = opgehaald.filter((v) => v.url === VERS); 182 assert.equal(pogingen.length, 1, 'één poging'); 183 assert.equal(pogingen[0].ondertekend, false, 'en die was onbetekend'); 184 }); 185 186 test('een mislukte poging wordt onthouden, zodat een retry hem niet herhaalt', async () => { 187 // Mastodon herhaalt dagenlang; zonder cache doet elke herhaling de fetch 188 // opnieuw. De tweede bezorging hoort geen tweede fetch op te leveren. 189 const MIS = 'https://203.0.113.10/notes/mislukt'; 190 bron = null; // de stub geeft 404 191 const act = () => doorgestuurd({ id: MIS, type: 'Note', attributedTo: AUTEUR, inReplyTo: ONZE_NOTE, content: '<p>x</p>' }); 192 opgehaald = []; 193 assert.equal(await AP.handleInbox(req(act()), 'me', alsDoorstuurder), 401); 194 const na1 = opgehaald.filter((v) => v.url === MIS).length; 195 assert.ok(na1 >= 1, 'de eerste poging haalt wel op'); 196 opgehaald = []; 197 assert.equal(await AP.handleInbox(req(act()), 'me', alsDoorstuurder), 401); 198 assert.equal(opgehaald.filter((v) => v.url === MIS).length, 0, 'de tweede niet meer'); 199 }); 200 129 201 test.after(() => { globalThis.fetch = echteFetch; });
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)