source: Klonkt/test/gated-propose-c2s.test.js@ dc0da72

main
Last change on this file since dc0da72 was dc0da72, checked in by roboburr <roboburr@…>, 4 weeks ago

Alleen claimen wat je herkent (regressie uit shaer-8ru)

Barts honderd aanbiedingen liepen meteen stuk op "400 unsupported_offer", en dat
was mijn fout van een uur eerder.

Ik zette het gate-voorstel als case 'Offer' in de outbox-switch. Die case
claimde ELKE Offer: wat geen shaer:GatedSetting was kreeg 400 -- dus ook de
adoptie-handshake, en straks elke Offer-vorm die we nog toevoegen. De
guardianship-afhandeling stond ervoor en ving het meestal op, maar zodra die iets
niet herkende viel het in mijn 400 in plaats van door te lopen.

Nu voor de switch, naast de andere Offer-afhandeling, en alleen als
parseGatedSetting iets teruggeeft. De toets die 'unsupported_offer' verwachtte is
vervangen: die legde mijn verkeerde aanname vast. Er staat nu een toets op dat een
adoptie-Offer NIET door de gate-tak wordt afgevangen.

Twee kleinere dingen die dezelfde ronde boven kwamen:

  • Het aanbod-script gebruikte een verzonnen relatie-URI en werd daarom stil geweigerd. isGuardianRelationship accepteert precies twee waarden.
  • De SSRF-toets hing af van wat er toevallig draaide: hij ging ervan uit dat er niets op [::1]:3060 luisterde, en viel om zodra de testkudde daar stond. Nu een poort waar met opzet niets luistert. Een toets die afhangt van wat er draait bewijst niets.

Suite 756/756.

  • Property mode set to 100644
File size: 4.3 KB
Line 
1// Een gate voorstellen vanuit de app (shaer-8ru, FEP-633c 5.6).
2//
3// De PWA stelde al voor over een eigen route; de apps konden alleen KIJKEN. Wat
4// hier bewaakt wordt is niet dat het werkt, maar dat het langs DEZELFDE weg gaat:
5// een tweede implementatie ernaast geeft vroeg of laat een ander antwoord, en bij
6// de antwoordpoort was dat vandaag precies het gat (de innamepoort zat alleen in
7// C2S, het webpad liep eromheen).
8//
9// In-memory SQLite. Run: npm test
10import { test } from 'node:test';
11import assert from 'node:assert/strict';
12
13process.env.DATABASE_PATH = ':memory:';
14process.env.PUBLIC_BASE_URL = 'https://oma.test';
15
16const dbMod = await import('../src/config/database.js');
17const db = dbMod.default;
18dbMod.initializeDatabase();
19const AP = await import('../src/services/ActivityPubService.js');
20const Guardianship = await import('../src/services/guardianship/index.js');
21const rel = await import('../src/services/guardianship/relations.js');
22
23db.prepare('INSERT INTO users (id, username, email, password_hash, role) VALUES (?,?,?,?,?)')
24 .run('u1', 'u1', 'u1@t', 'x', 'god');
25db.prepare('INSERT INTO sites (id, slug, title, owner_id) VALUES (?,?,?,?)').run('s1', 'oma', 'Oma', 'u1');
26const site = () => db.prepare('SELECT * FROM sites WHERE id = ?').get('s1');
27const user = { id: 'u1', username: 'u1' };
28const KIND = 'https://elders.test/ap/users/kind';
29rel.commitWardForGuardian('oma', KIND, { handle: '@kind' });
30
31const offer = (ward, feature, value) => ({
32 type: 'Offer',
33 object: { type: 'shaer:GatedSetting', 'shaer:ward': ward, 'shaer:feature': feature, 'shaer:value': value },
34});
35
36test('een guardian stelt een gate voor vanuit de app', async () => {
37 const uit = await AP.ingestOutboxActivity(site(), user, offer(KIND, 'shaer:images', true));
38 assert.equal(uit.status, 201);
39 assert.equal(uit.state, 'open');
40 // Het spoor van wat we stuurden: zonder rij kan het antwoord van de
41 // ward-server nergens landen, en zegt het scherm nooit meer dan een knoptekst.
42 const sent = Guardianship.gated.listSent('oma', KIND);
43 assert.ok(sent.some((p) => p.feature === 'shaer:images'), 'het voorstel is vastgelegd');
44});
45
46test('niet voor een kind dat niet van jou is', async () => {
47 // Zonder deze regel kan iedereen met een token een instelling van een vreemd
48 // kind aanvragen.
49 const uit = await AP.ingestOutboxActivity(site(), user, offer('https://elders.test/ap/users/vreemde', 'shaer:images', true));
50 assert.equal(uit.status, 403);
51 assert.equal(uit.error, 'not_your_ward');
52});
53
54test('een onbekende feature wordt GEWEIGERD, niet herschreven', () => {
55 // De oude route herschreef een onbekende naam stilletjes naar externalEmbeds.
56 // Een voorstel voor de ene poort dat op de andere landt is het soort fout dat
57 // een guardian nooit mag overkomen.
58 const uit = AP.proposeGate(site(), KIND, 'shaer:ietsNieuws', true);
59 assert.equal(uit.status, 400);
60 assert.equal(uit.error, 'unknown_feature');
61});
62
63test('een Offer dat GEEN gate-voorstel is wordt NIET geclaimd', async () => {
64 // WAS: dit gaf 400 unsupported_offer, en dat was fout. Deze tak claimde elke
65 // Offer, dus ook de adoptie-handshake -- Barts honderd aanbiedingen liepen er
66 // meteen op stuk. Alleen claimen wat je herkent.
67 const uit = await AP.ingestOutboxActivity(site(), user, { type: 'Offer', object: { type: 'Note', content: 'hoi' } });
68 assert.notEqual(uit && uit.error, 'unsupported_offer');
69});
70
71test('en een adoptie-Offer komt bij de handshake terecht', async () => {
72 // De regressie die Bart ving, als toets. Een Relationship-offer hoort NOOIT
73 // door de gate-tak afgevangen te worden.
74 const uit = await AP.ingestOutboxActivity(site(), user, {
75 type: 'Offer', to: [KIND],
76 object: { type: 'Relationship', subject: KIND, object: 'https://oma.test/ap/users/oma', relationship: 'shaer:Guardian' },
77 });
78 assert.notEqual(uit && uit.error, 'unsupported_offer');
79});
80
81test('de app en de PWA lopen door DEZELFDE functie', async () => {
82 // Niet dat ze hetzelfde doen, maar dat er maar een plek is die het doet: de
83 // C2S-weg boekt zijn voorstel in hetzelfde register waar de PWA uit leest.
84 const voor = Guardianship.gated.listSent('oma', KIND).length;
85 await AP.ingestOutboxActivity(site(), user, offer(KIND, 'shaer:music', true));
86 const na = Guardianship.gated.listSent('oma', KIND);
87 assert.equal(na.length, voor + 1);
88 assert.ok(na.some((p) => p.feature === 'shaer:music'));
89});
Note: See TracBrowser for help on using the repository browser.