Changeset 26c5f71 in Klonkt


Ignore:
Timestamp:
08/02/2026 07:23:10 AM (5 weeks ago)
Author:
Bart <bart@â€Ļ>
Branches:
main
Children:
679924e
Parents:
17546af
git-author:
Bart <bart@â€Ļ> (08/02/2026 07:20:46 AM)
git-committer:
Bart <bart@â€Ļ> (08/02/2026 07:23:10 AM)
Message:

WebFinger: een kale host vindt de primaire actor

acct:<host>@<host> is hoe Shaer een Ward adresseert zonder iemands slug te
kennen, maar WebFinger zocht het gebruikersdeel alleen op als site-slug. Een
kale host gaf dus altijd een 404, hoe je hem ook spelde.

Daarbovenop: đŸŠĩ.is.wildenvrij.nl en xn--zz9h.is.wildenvrij.nl zijn ÊÊn host.
Een geplakte URL wordt door elke URL-parser stil gepunycode, een getypte
handle niet. asciiHost() vergelijkt via WHATWG URL, dus beide spellingen
komen bij dezelfde actor uit, en PUBLIC_BASE_URL mag ook beide vormen.

Zes tests, waarvan twee de terugval bewaken: een onbekende gebruiker blijft
404 en een kale host die niet van ons is ook. Anders koppelt een typefout een
kind stilletjes aan het verkeerde account.

Co-Authored-By: Claude Opus 5 <claude@â€Ļ>

Files:
1 added
1 edited

Legend:

Unmodified
Added
Removed
  • src/routes/activitypub.js

    r17546af r26c5f71  
    4444const publicSite = (slug) => db.prepare('SELECT * FROM sites WHERE slug = ? AND (is_public IS NULL OR is_public = 1)').get(slug);
    4545const primarySlug = () => { const r = db.prepare('SELECT slug FROM sites WHERE is_primary = 1').get(); return r && r.slug; };
     46// A hostname as a human types it and as DNS stores it are the same host:
     47// `đŸŠĩ.is.wildenvrij.nl` IS `xn--zz9h.is.wildenvrij.nl`. WHATWG URL does the IDNA,
     48// so compare the ASCII form and never the bytes the client happened to send.
     49const asciiHost = (h) => {
     50  try { return new URL(`https://${h}`).host.toLowerCase(); } catch { return String(h).trim().toLowerCase(); }
     51};
    4652
    4753// ── WebFinger ─────────────────────────────────────────────────────
     
    4955  const m = String(req.query.resource || '').match(/^acct:([^@]+)@(.+)$/i);
    5056  if (!m) return res.status(400).type('text/plain').send('bad resource');
    51   const site = publicSite(m[1]);
     57  const user = m[1];
     58  let site = publicSite(user);
     59  // `acct:<host>@<host>` asks for this server's primary actor — the convention
     60  // Shaer's Handle relies on so a Ward is reachable without knowing anyone's
     61  // slug. Typing `đŸŠĩ.is.wildenvrij.nl`, pasting `https://đŸŠĩ.is.wildenvrij.nl`
     62  // (which the client's URL parser silently punycodes) and sending the xn--
     63  // form by hand are three spellings of one address; all arrive here with the
     64  // host sitting in the user position, and all must find the same actor.
     65  if (!site && asciiHost(user) === asciiHost(hostOf(req))) {
     66    const slug = primarySlug();
     67    if (slug) site = publicSite(slug);
     68  }
    5269  if (!site) return res.status(404).end();
    5370  res.type('application/jrd+json; charset=utf-8');
Note: See TracChangeset for help on using the changeset viewer.