source: Klonkt/test/webfinger-hostmeta.test.js@ 2220c49

main
Last change on this file since 2220c49 was 2220c49, checked in by Robin <roboburr@…>, 5 weeks ago

Twee dichte deuren in de ontdekking opengezet

Gevonden bij het nalopen van waarom Funkwhale dev niet vond via zijn zoekbalk.
Die zoekbalk bleek gewoon lokaal -- onze kant antwoordde correct -- maar
onderweg stonden er twee deuren dicht die een andere client wel gebruikt.

HOST-META gaf 404. Dat is de klassieke eerste stap van WebFinger (RFC 6415):
een client die het webfinger-pad niet wil raden, vraagt daar de sjabloon op.
Kreeg hij bij ons een 404, dan gaf hij het op -- terwijl de webfinger eronder
prima werkte. Nu in beide vormen: XRD en JRD, want allebei worden gevraagd.

WEBFINGER WAS STRENG in wat hij accepteerde. Alleen acct:naam@host; al het
andere een 400, ook als we prima wisten wie er bedoeld werd. Nu ook zonder
schema en met het apenstaartje dat mensen intypen. Coulant zijn kost hier
niets: het antwoord noemt altijd de canonieke acct:-vorm terug, dus een
slordige vraag levert geen slordig antwoord.

WAT IK BEWUST NIET HEB GEDAAN. Ik had er ook de actor-URI als resource bij
gezet -- Mastodon accepteert die -- tot de suite me op mijn vingers tikte:
test/webfinger-bare-host.test.js legt vast dat juist die vorm een 400 hoort te
geven. Dat is een uitgesproken keuze van eerder, geen vergetelheid, en die
draai ik niet om als bijvangst van een coulance-fix. De tak is eruit en er
staat nu een test die de 400 vastlegt, met de reden erbij.

8 tests.

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

  • Property mode set to 100644
File size: 3.6 KB
Line 
1// Ontdekking: host-meta erbij, en WebFinger coulant in wat hij accepteert.
2//
3// Aanleiding (7/8 augustus): Funkwhale vond dev pas via zijn eigen "search via
4// the fediverse". Onze kant antwoordde correct, maar twee deuren stonden dicht
5// die een andere client wel gebruikt: host-meta gaf 404, en een resource zonder
6// `acct:` gaf 400 terwijl we prima wisten wie er bedoeld werd.
7import { test } from 'node:test';
8import assert from 'node:assert/strict';
9
10process.env.DATABASE_PATH = ':memory:';
11process.env.PUBLIC_BASE_URL = 'https://test.example';
12
13const dbMod = await import('../src/config/database.js');
14const db = dbMod.default;
15dbMod.initializeDatabase();
16const express = (await import('express')).default;
17const routes = (await import('../src/routes/activitypub.js')).default;
18
19db.prepare('INSERT INTO users (id, username, email, password_hash, role) VALUES (?,?,?,?,?)')
20 .run('u1', 'u1', 'u1@t', 'x', 'god');
21db.prepare('INSERT INTO sites (id, slug, title, owner_id, is_public, is_primary) VALUES (?,?,?,?,1,1)')
22 .run('s1', 'band', 'De Band', 'u1');
23
24const app = express(); app.use(routes);
25const server = app.listen(0);
26await new Promise((r) => server.once('listening', r));
27const base = `http://127.0.0.1:${server.address().port}`;
28const wf = async (resource) => {
29 const r = await fetch(`${base}/.well-known/webfinger?resource=${encodeURIComponent(resource)}`);
30 return { status: r.status, type: r.headers.get('content-type'), body: r.status === 200 ? await r.json() : null };
31};
32
33test('de nette vorm blijft werken, en het antwoord is canoniek', async () => {
34 const { status, type, body } = await wf('acct:band@test.example');
35 assert.equal(status, 200);
36 assert.match(type, /application\/jrd\+json/);
37 assert.equal(body.subject, 'acct:band@test.example');
38 assert.equal(body.links.find((l) => l.rel === 'self').href, 'https://test.example/ap/users/band');
39});
40
41test('zonder acct: mag ook -- en levert hetzelfde canonieke antwoord', async () => {
42 const { status, body } = await wf('band@test.example');
43 assert.equal(status, 200);
44 assert.equal(body.subject, 'acct:band@test.example', 'een slordige vraag geeft geen slordig antwoord');
45});
46
47test('met het apenstaartje dat mensen intypen', async () => {
48 const { status, body } = await wf('@band@test.example');
49 assert.equal(status, 200);
50 assert.equal(body.subject, 'acct:band@test.example');
51});
52
53test('de actor-URI blijft een 400 -- vastgelegd in webfinger-bare-host', async () => {
54 // Niet vergeten maar bewust: die keuze staat elders vast en draaien we niet
55 // om als bijvangst van een coulance-fix.
56 assert.equal((await wf('https://test.example/ap/users/band')).status, 400);
57});
58
59test('echte onzin blijft een 400, een onbekende gebruiker een 404', async () => {
60 assert.equal((await wf('kaas')).status, 400);
61 assert.equal((await wf('')).status, 400);
62 assert.equal((await wf('acct:bestaatniet@test.example')).status, 404);
63});
64
65test('host-meta wijst naar de webfinger-sjabloon (XRD)', async () => {
66 const r = await fetch(`${base}/.well-known/host-meta`);
67 assert.equal(r.status, 200);
68 assert.match(r.headers.get('content-type'), /application\/xrd\+xml/);
69 const xml = await r.text();
70 assert.match(xml, /rel="lrdd"/);
71 assert.match(xml, /template="https:\/\/test\.example\/\.well-known\/webfinger\?resource=\{uri\}"/);
72});
73
74test('en in JSON, want beide vormen worden gevraagd', async () => {
75 const r = await fetch(`${base}/.well-known/host-meta.json`);
76 assert.equal(r.status, 200);
77 assert.match(r.headers.get('content-type'), /application\/jrd\+json/);
78 const j = await r.json();
79 assert.equal(j.links[0].rel, 'lrdd');
80 assert.match(j.links[0].template, /\{uri\}$/);
81});
82
83test.after(() => server.close());
Note: See TracBrowser for help on using the repository browser.