source: Klonkt/test/inbox-vreemde-slug.test.js

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

Een slug is een lokale sleutel, geen naam op de draad (Robins vraag, 11-8)

Aanleiding: bij het rechttrekken van de help-handles bleek @${site.slug} als
handle de deur uit te gaan -- zonder host. Robin vroeg door: waar communiceren we
nog op <slug>, want op een AP-oppervlak mag dat niet voorkomen. Dat leverde een
scherpere vondst op dan de handle zelf.

IN DE INBOX WERD EEN SLUG GERADEN UIT EEN VREEMDE URI. slugFromActorUrl knipt de
staart van /ap/users/<x> af en kijkt NIET naar de host. Op drie plekken kwamen de
uri's van de AFZENDER:

handshake-routering uit to en uit de relatie (Offer/Accept/Reject/Undo)
Flag uit de gerapporteerde object-uri's
Follow uit act.object, op de GEDEELDE inbox (per-actor heeft

slugParam en was dus al veilig)

Een activiteit gericht aan https://elders.example/ap/users/dev leverde zo de
slug "dev" op, en die bestaat hier. Dan draaide onze dev de afhandeling van iets
dat nooit aan hem geadresseerd was -- een handshake, een rapport tegen zijn naam,
of een volger in zijn lijst.

localSlugOf deed het al goed: het eist dat de uri met onze eigen basis begint EN
dat de site bestaat. Die stond er, alleen niet op deze drie plekken. Nu wel, en
geexporteerd zodat een test hem kan vastleggen.

Vier tests, met de aanval als eerste erin: een vreemde actor met onze padstaart
geeft null, de onze geeft de slug, een niet-bestaande site geeft null, en een pad
dat er alleen op lijkt telt niet.

WAT ER NOG STAAT, en dat is bewust niet in deze commit: routes/activitypub.js
regel 448 en 756 vallen in hun catch terug op @${slug} -- een handle zonder
host. Dat gebeurt alleen als PUBLIC_BASE_URL onparseerbaar is, dus bij een kapotte
installatie, maar het is dezelfde fout: een halve naam op een AP-oppervlak.

Co-Authored-By: Claude Opus 5 <noreply@…>

  • Property mode set to 100644
File size: 1.9 KB
Line 
1// Een slug is een LOKALE sleutel en mag niet uit een vreemde URI komen.
2//
3// Robins vraag (11-8): waar communiceren we nog op <slug>, want op een
4// AP-oppervlak mag dat niet. In de inbox werd hij geraden met
5// slugFromActorUrl -- die knipt de staart van een pad af zonder naar de host te
6// kijken. De uri's komen daar uit `to`, uit de relatie en uit act.object, dus
7// van de AFZENDER. Een activiteit gericht aan andermans actor met dezelfde
8// padstaart als een van onze sites kwam zo bij ONZE site terecht.
9import { test } from 'node:test';
10import assert from 'node:assert/strict';
11
12process.env.DATABASE_PATH = ':memory:';
13process.env.PUBLIC_BASE_URL = 'https://ons.test';
14
15const dbMod = await import('../src/config/database.js');
16const db = dbMod.default;
17dbMod.initializeDatabase();
18const AP = await import('../src/services/ActivityPubService.js');
19
20db.prepare('INSERT INTO users (id, username, email, password_hash, role) VALUES (?,?,?,?,?)')
21 .run('u1', 'u1', 'u1@t', 'x', 'god');
22db.prepare('INSERT INTO sites (id, slug, title, owner_id) VALUES (?,?,?,?)').run('s1', 'dev', 'Dev', 'u1');
23
24test('een vreemde actor met onze padstaart levert GEEN lokale slug', () => {
25 // Dit is de aanval in een regel: elders heet ook iemand /ap/users/dev.
26 assert.equal(AP.localSlugOf('https://elders.example/ap/users/dev'), null);
27});
28
29test('onze eigen actor levert hem wel', () => {
30 assert.equal(AP.localSlugOf('https://ons.test/ap/users/dev'), 'dev');
31});
32
33test('en een site die niet bestaat ook niet, ook al is de host de onze', () => {
34 // De staart is niet genoeg: hij moet ook echt een site zijn.
35 assert.equal(AP.localSlugOf('https://ons.test/ap/users/bestaatniet'), null);
36});
37
38test('een pad dat er alleen op lijkt telt niet', () => {
39 assert.equal(AP.localSlugOf('https://ons.test/elders/ap/users/dev'), null);
40 assert.equal(AP.localSlugOf('https://kwaad.test/ons.test/ap/users/dev'), null);
41});
Note: See TracBrowser for help on using the repository browser.