Changeset fc41e04 in Klonkt


Ignore:
Timestamp:
08/11/2026 05:41:49 PM (4 weeks ago)
Author:
Robin <roboburr@…>
Branches:
main
Children:
cb3001e
Parents:
26b1848
Message:

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@…>

Files:
1 added
1 edited

Legend:

Unmodified
Added
Removed
  • src/services/ActivityPubService.js

    r26b1848 rfc41e04  
    21812181    // handshake (a ward and a co-guardian may both live here). Gather candidate
    21822182    // local slugs from the inbox owner, the `to` list, and the ward.
     2183    // MET localSlugOf en niet met slugFromActorUrl. Dat laatste knipt alleen de
     2184    // staart van een pad af, zonder naar de HOST te kijken -- en deze uri's
     2185    // komen uit `to` en uit de relatie, dus van de afzender. Een Offer gericht
     2186    // aan https://elders.example/ap/users/dev leverde zo de slug "dev" op, en
     2187    // die bestaat hier. Dan draait onze dev de afhandeling van een activiteit
     2188    // die nooit aan hem geadresseerd was. localSlugOf eist dat de uri met onze
     2189    // eigen basis begint en dat de site echt bestaat.
    21832190    const cand = new Set();
    21842191    if (slugParam) cand.add(slugParam);
    21852192    for (const t of (Array.isArray(act.to) ? act.to : (act.to ? [act.to] : []))) {
    2186       if (typeof t === 'string') { const s = slugFromActorUrl(t); if (s) cand.add(s); }
     2193      if (typeof t === 'string') { const s = localSlugOf(t); if (s) cand.add(s); }
    21872194    }
    21882195    if (type === 'Offer' || type === 'Undo') {
    21892196      const rel = type === 'Undo' ? Guardianship.parseUndoRelationship(act) : Guardianship.parseRelationship(act.object);
    2190       if (rel) { const s = slugFromActorUrl(rel.ward); if (s) cand.add(s); }
     2197      if (rel) { const s = localSlugOf(rel.ward); if (s) cand.add(s); }
    21912198    }
    21922199    let consumed = false;
     
    22062213    const noteIds = [];
    22072214    for (const u of objectUris) {
    2208       const s = slugFromActorUrl(u);        // one of our actors?
     2215      const s = localSlugOf(u);             // one of OURS -- host meegewogen
    22092216      if (s) { targetSlug = targetSlug || s; continue; }
    22102217      const pid = postIdFromNoteUrl(u, base); // one of our notes?
     
    22342241  if (type === 'Follow') {
    22352242    const who = typeof act.actor === 'string' ? act.actor : (act.actor && act.actor.id);
    2236     const slug = slugParam || slugFromActorUrl(typeof act.object === 'string' ? act.object : (act.object && act.object.id));
     2243    // slugParam is de eigenaar van een per-actor inbox; op de GEDEELDE inbox is
     2244    // die er niet en werd de slug uit act.object geraden. Zonder hostcontrole
     2245    // kon een Follow op andermans actor met dezelfde padstaart hier een volger
     2246    // opleveren.
     2247    const slug = slugParam || localSlugOf(typeof act.object === 'string' ? act.object : (act.object && act.object.id));
    22372248    if (!who || !slug) return 400;
    22382249    const remote = await fetchActor(who);
     
    63646375// Which local site (if any) hosts this actor URI — used by the handshake to
    63656376// apply the local side of a commit and to derive a ward's existing guardians.
    6366 function localSlugOf(actorUri) {
     6377export function localSlugOf(actorUri) {
    63676378  const base = (process.env.PUBLIC_BASE_URL || '').replace(/\/+$/, '');
    63686379  if (!actorUri || !actorUri.startsWith(`${base}/ap/users/`)) return null;
Note: See TracChangeset for help on using the changeset viewer.