Loading CHANGES.md +70 −1 Original line number Diff line number Diff line Loading @@ -74,6 +74,19 @@ To be released. [multibase]: https://github.com/multiformats/js-multibase Version 1.4.7 ------------- Released on March 20, 2025. - Fixed a bug of WebFinger handler where it had failed to match `acct:` URIs with a host having a port number. [[#218], [#219] by Revath S Kumar] - Fixed a server error thrown when an invalid URL was passed to the `base-url` parameter of the followers collection. [[#217]] Version 1.4.6 ------------- Loading Loading @@ -237,6 +250,19 @@ Released on February 5, 2025. [#195]: https://github.com/fedify-dev/fedify/issues/195 Version 1.3.14 -------------- Released on March 20, 2025. - Fixed a bug of WebFinger handler where it had failed to match `acct:` URIs with a host having a port number. [[#218], [#219] by Revath S Kumar] - Fixed a server error thrown when an invalid URL was passed to the `base-url` parameter of the followers collection. [[#217]] Version 1.3.13 -------------- Loading Loading @@ -516,6 +542,19 @@ Released on November 30, 2024. [#193]: https://github.com/fedify-dev/fedify/issues/193 Version 1.2.18 -------------- Released on March 20, 2025. - Fixed a bug of WebFinger handler where it had failed to match `acct:` URIs with a host having a port number. [[#218], [#219] by Revath S Kumar] - Fixed a server error thrown when an invalid URL was passed to the `base-url` parameter of the followers collection. [[#217]] Version 1.2.17 -------------- Loading Loading @@ -844,6 +883,19 @@ Released on October 31, 2024. [#118]: https://github.com/fedify-dev/fedify/issues/118 Version 1.1.18 -------------- Released on March 20, 2025. - Fixed a bug of WebFinger handler where it had failed to match `acct:` URIs with a host having a port number. [[#218], [#219] by Revath S Kumar] - Fixed a server error thrown when an invalid URL was passed to the `base-url` parameter of the followers collection. [[#217]] Version 1.1.17 -------------- Loading Loading @@ -1213,6 +1265,23 @@ Released on October 20, 2024. [#150]: https://github.com/fedify-dev/fedify/issues/150 Version 1.0.21 -------------- Released on March 20, 2025. - Fixed a bug of WebFinger handler where it had failed to match `acct:` URIs with a host having a port number. [[#218], [#219] by Revath S Kumar] - Fixed a server error thrown when an invalid URL was passed to the `base-url` parameter of the followers collection. [[#217]] [#217]: https://github.com/fedify-dev/fedify/issues/217 [#218]: https://github.com/fedify-dev/fedify/issues/218 [#219]: https://github.com/fedify-dev/fedify/pull/219 Version 1.0.20 -------------- Loading Loading @@ -3378,4 +3447,4 @@ Version 0.1.0 Initial release. Released on March 8, 2024. <!-- cSpell: ignore Dogeon Fabien Wressell Emelia Fróði Karlsson --> <!-- cSpell: ignore Hana Heesun Kyunghee Jiyu --> <!-- cSpell: ignore Hana Heesun Kyunghee Jiyu Revath Kumar --> src/federation/middleware.ts +6 −2 Original line number Diff line number Diff line Loading @@ -2499,8 +2499,12 @@ export class FederationImpl<TContextData> implements Federation<TContextData> { case "followers": { let baseUrl = url.searchParams.get("base-url"); if (baseUrl != null) { const u = new URL(baseUrl); baseUrl = `${u.origin}/`; try { baseUrl = `${new URL(baseUrl).origin}/`; } catch { // If base-url is invalid, set to null to behave as if it wasn't provided baseUrl = null; } } return await handleCollection(request, { name: "followers", Loading src/webfinger/handler.test.ts +92 −7 Original line number Diff line number Diff line Loading @@ -19,7 +19,7 @@ test("handleWebFinger()", async (t) => { url, data: undefined, getActorUri(identifier) { return new URL(`https://example.com/users/${identifier}`); return new URL(`${url.origin}/users/${identifier}`); }, async getActor(handle): Promise<Actor | null> { return await actorDispatcher( Loading Loading @@ -47,8 +47,9 @@ test("handleWebFinger()", async (t) => { const actorDispatcher: ActorDispatcher<void> = (ctx, identifier) => { if (identifier !== "someone" && identifier !== "someone2") return null; const actorUri = ctx.getActorUri(identifier); return new Person({ id: ctx.getActorUri(identifier), id: actorUri, name: identifier === "someone" ? "Someone" : "Someone 2", preferredUsername: identifier === "someone" ? null Loading @@ -56,13 +57,13 @@ test("handleWebFinger()", async (t) => { ? "bar" : null, icon: new Image({ url: new URL("https://example.com/icon.jpg"), url: new URL(`${actorUri.origin}/icon.jpg`), mediaType: "image/jpeg", }), urls: [ new URL("https://example.com/@" + identifier), new URL(`${actorUri.origin}/@${identifier}`), new Link({ href: new URL("https://example.org/@" + identifier), href: new URL(`${actorUri.origin}/@${identifier}`), rel: "alternate", mediaType: "text/html", }), Loading Loading @@ -131,7 +132,7 @@ test("handleWebFinger()", async (t) => { rel: "http://webfinger.net/rel/profile-page", }, { href: "https://example.org/@someone", href: "https://example.com/@someone", rel: "alternate", type: "text/html", }, Loading Loading @@ -175,7 +176,7 @@ test("handleWebFinger()", async (t) => { rel: "http://webfinger.net/rel/profile-page", }, { href: "https://example.org/@someone2", href: "https://example.com/@someone2", rel: "alternate", type: "text/html", }, Loading Loading @@ -449,4 +450,88 @@ test("handleWebFinger()", async (t) => { subject: "https://example.com/users/someone2", }); }); const expectedForLocalhostWithPort = { subject: "acct:someone@localhost:8000", aliases: ["https://localhost:8000/users/someone"], links: [ { href: "https://localhost:8000/users/someone", rel: "self", type: "application/activity+json", }, { href: "https://localhost:8000/@someone", rel: "http://webfinger.net/rel/profile-page", }, { href: "https://localhost:8000/@someone", rel: "alternate", type: "text/html", }, { href: "https://localhost:8000/icon.jpg", rel: "http://webfinger.net/rel/avatar", type: "image/jpeg", }, ], }; await t.step("on localhost with port, ok: resource=acct:...", async () => { const u = new URL("https://localhost:8000/.well-known/webfinger"); u.searchParams.set("resource", "acct:someone@localhost:8000"); const context = createContext(u); const request = context.request; const response = await handleWebFinger(request, { context, actorDispatcher, onNotFound, }); assertEquals(response.status, 200); assertEquals(response.headers.get("Content-Type"), "application/jrd+json"); assertEquals(response.headers.get("Access-Control-Allow-Origin"), "*"); assertEquals(await response.json(), expectedForLocalhostWithPort); }); const expectedForHostnameWithPort = { subject: "acct:someone@example.com:8000", aliases: ["http://example.com:8000/users/someone"], links: [ { href: "http://example.com:8000/users/someone", rel: "self", type: "application/activity+json", }, { href: "http://example.com:8000/@someone", rel: "http://webfinger.net/rel/profile-page", }, { href: "http://example.com:8000/@someone", rel: "alternate", type: "text/html", }, { href: "http://example.com:8000/icon.jpg", rel: "http://webfinger.net/rel/avatar", type: "image/jpeg", }, ], }; await t.step("on hostname with port, ok: resource=acct:...", async () => { const u = new URL("http://example.com:8000/.well-known/webfinger"); u.searchParams.set("resource", "acct:someone@example.com:8000"); const context = createContext(u); const request = context.request; const response = await handleWebFinger(request, { context, actorDispatcher, onNotFound, }); assertEquals(response.status, 200); assertEquals(response.headers.get("Content-Type"), "application/jrd+json"); assertEquals(response.headers.get("Access-Control-Allow-Origin"), "*"); assertEquals(await response.json(), expectedForHostnameWithPort); }); }); src/webfinger/handler.ts +9 −4 Original line number Diff line number Diff line Loading @@ -171,12 +171,17 @@ async function handleWebFingerInternal<TContextData>( ); } } else { const resourceHost = domainToASCII(match[2].toLowerCase()); if (resourceHost != context.url.host && resourceHost != host) { const portMatch = /:\d+$/.exec(match[2]); const normalizedHost = portMatch == null ? domainToASCII(match[2].toLowerCase()) : domainToASCII(match[2].substring(0, portMatch.index).toLowerCase()) + portMatch[0]; if (normalizedHost != context.url.host && normalizedHost != host) { return await onNotFound(request); } } else { identifier = await mapUsernameToIdentifier(match[1]); resourceUrl = new URL(`acct:${match[1]}@${resourceHost}`); resourceUrl = new URL(`acct:${match[1]}@${normalizedHost}`); } } } else { identifier = uriParsed.identifier; Loading Loading
CHANGES.md +70 −1 Original line number Diff line number Diff line Loading @@ -74,6 +74,19 @@ To be released. [multibase]: https://github.com/multiformats/js-multibase Version 1.4.7 ------------- Released on March 20, 2025. - Fixed a bug of WebFinger handler where it had failed to match `acct:` URIs with a host having a port number. [[#218], [#219] by Revath S Kumar] - Fixed a server error thrown when an invalid URL was passed to the `base-url` parameter of the followers collection. [[#217]] Version 1.4.6 ------------- Loading Loading @@ -237,6 +250,19 @@ Released on February 5, 2025. [#195]: https://github.com/fedify-dev/fedify/issues/195 Version 1.3.14 -------------- Released on March 20, 2025. - Fixed a bug of WebFinger handler where it had failed to match `acct:` URIs with a host having a port number. [[#218], [#219] by Revath S Kumar] - Fixed a server error thrown when an invalid URL was passed to the `base-url` parameter of the followers collection. [[#217]] Version 1.3.13 -------------- Loading Loading @@ -516,6 +542,19 @@ Released on November 30, 2024. [#193]: https://github.com/fedify-dev/fedify/issues/193 Version 1.2.18 -------------- Released on March 20, 2025. - Fixed a bug of WebFinger handler where it had failed to match `acct:` URIs with a host having a port number. [[#218], [#219] by Revath S Kumar] - Fixed a server error thrown when an invalid URL was passed to the `base-url` parameter of the followers collection. [[#217]] Version 1.2.17 -------------- Loading Loading @@ -844,6 +883,19 @@ Released on October 31, 2024. [#118]: https://github.com/fedify-dev/fedify/issues/118 Version 1.1.18 -------------- Released on March 20, 2025. - Fixed a bug of WebFinger handler where it had failed to match `acct:` URIs with a host having a port number. [[#218], [#219] by Revath S Kumar] - Fixed a server error thrown when an invalid URL was passed to the `base-url` parameter of the followers collection. [[#217]] Version 1.1.17 -------------- Loading Loading @@ -1213,6 +1265,23 @@ Released on October 20, 2024. [#150]: https://github.com/fedify-dev/fedify/issues/150 Version 1.0.21 -------------- Released on March 20, 2025. - Fixed a bug of WebFinger handler where it had failed to match `acct:` URIs with a host having a port number. [[#218], [#219] by Revath S Kumar] - Fixed a server error thrown when an invalid URL was passed to the `base-url` parameter of the followers collection. [[#217]] [#217]: https://github.com/fedify-dev/fedify/issues/217 [#218]: https://github.com/fedify-dev/fedify/issues/218 [#219]: https://github.com/fedify-dev/fedify/pull/219 Version 1.0.20 -------------- Loading Loading @@ -3378,4 +3447,4 @@ Version 0.1.0 Initial release. Released on March 8, 2024. <!-- cSpell: ignore Dogeon Fabien Wressell Emelia Fróði Karlsson --> <!-- cSpell: ignore Hana Heesun Kyunghee Jiyu --> <!-- cSpell: ignore Hana Heesun Kyunghee Jiyu Revath Kumar -->
src/federation/middleware.ts +6 −2 Original line number Diff line number Diff line Loading @@ -2499,8 +2499,12 @@ export class FederationImpl<TContextData> implements Federation<TContextData> { case "followers": { let baseUrl = url.searchParams.get("base-url"); if (baseUrl != null) { const u = new URL(baseUrl); baseUrl = `${u.origin}/`; try { baseUrl = `${new URL(baseUrl).origin}/`; } catch { // If base-url is invalid, set to null to behave as if it wasn't provided baseUrl = null; } } return await handleCollection(request, { name: "followers", Loading
src/webfinger/handler.test.ts +92 −7 Original line number Diff line number Diff line Loading @@ -19,7 +19,7 @@ test("handleWebFinger()", async (t) => { url, data: undefined, getActorUri(identifier) { return new URL(`https://example.com/users/${identifier}`); return new URL(`${url.origin}/users/${identifier}`); }, async getActor(handle): Promise<Actor | null> { return await actorDispatcher( Loading Loading @@ -47,8 +47,9 @@ test("handleWebFinger()", async (t) => { const actorDispatcher: ActorDispatcher<void> = (ctx, identifier) => { if (identifier !== "someone" && identifier !== "someone2") return null; const actorUri = ctx.getActorUri(identifier); return new Person({ id: ctx.getActorUri(identifier), id: actorUri, name: identifier === "someone" ? "Someone" : "Someone 2", preferredUsername: identifier === "someone" ? null Loading @@ -56,13 +57,13 @@ test("handleWebFinger()", async (t) => { ? "bar" : null, icon: new Image({ url: new URL("https://example.com/icon.jpg"), url: new URL(`${actorUri.origin}/icon.jpg`), mediaType: "image/jpeg", }), urls: [ new URL("https://example.com/@" + identifier), new URL(`${actorUri.origin}/@${identifier}`), new Link({ href: new URL("https://example.org/@" + identifier), href: new URL(`${actorUri.origin}/@${identifier}`), rel: "alternate", mediaType: "text/html", }), Loading Loading @@ -131,7 +132,7 @@ test("handleWebFinger()", async (t) => { rel: "http://webfinger.net/rel/profile-page", }, { href: "https://example.org/@someone", href: "https://example.com/@someone", rel: "alternate", type: "text/html", }, Loading Loading @@ -175,7 +176,7 @@ test("handleWebFinger()", async (t) => { rel: "http://webfinger.net/rel/profile-page", }, { href: "https://example.org/@someone2", href: "https://example.com/@someone2", rel: "alternate", type: "text/html", }, Loading Loading @@ -449,4 +450,88 @@ test("handleWebFinger()", async (t) => { subject: "https://example.com/users/someone2", }); }); const expectedForLocalhostWithPort = { subject: "acct:someone@localhost:8000", aliases: ["https://localhost:8000/users/someone"], links: [ { href: "https://localhost:8000/users/someone", rel: "self", type: "application/activity+json", }, { href: "https://localhost:8000/@someone", rel: "http://webfinger.net/rel/profile-page", }, { href: "https://localhost:8000/@someone", rel: "alternate", type: "text/html", }, { href: "https://localhost:8000/icon.jpg", rel: "http://webfinger.net/rel/avatar", type: "image/jpeg", }, ], }; await t.step("on localhost with port, ok: resource=acct:...", async () => { const u = new URL("https://localhost:8000/.well-known/webfinger"); u.searchParams.set("resource", "acct:someone@localhost:8000"); const context = createContext(u); const request = context.request; const response = await handleWebFinger(request, { context, actorDispatcher, onNotFound, }); assertEquals(response.status, 200); assertEquals(response.headers.get("Content-Type"), "application/jrd+json"); assertEquals(response.headers.get("Access-Control-Allow-Origin"), "*"); assertEquals(await response.json(), expectedForLocalhostWithPort); }); const expectedForHostnameWithPort = { subject: "acct:someone@example.com:8000", aliases: ["http://example.com:8000/users/someone"], links: [ { href: "http://example.com:8000/users/someone", rel: "self", type: "application/activity+json", }, { href: "http://example.com:8000/@someone", rel: "http://webfinger.net/rel/profile-page", }, { href: "http://example.com:8000/@someone", rel: "alternate", type: "text/html", }, { href: "http://example.com:8000/icon.jpg", rel: "http://webfinger.net/rel/avatar", type: "image/jpeg", }, ], }; await t.step("on hostname with port, ok: resource=acct:...", async () => { const u = new URL("http://example.com:8000/.well-known/webfinger"); u.searchParams.set("resource", "acct:someone@example.com:8000"); const context = createContext(u); const request = context.request; const response = await handleWebFinger(request, { context, actorDispatcher, onNotFound, }); assertEquals(response.status, 200); assertEquals(response.headers.get("Content-Type"), "application/jrd+json"); assertEquals(response.headers.get("Access-Control-Allow-Origin"), "*"); assertEquals(await response.json(), expectedForHostnameWithPort); }); });
src/webfinger/handler.ts +9 −4 Original line number Diff line number Diff line Loading @@ -171,12 +171,17 @@ async function handleWebFingerInternal<TContextData>( ); } } else { const resourceHost = domainToASCII(match[2].toLowerCase()); if (resourceHost != context.url.host && resourceHost != host) { const portMatch = /:\d+$/.exec(match[2]); const normalizedHost = portMatch == null ? domainToASCII(match[2].toLowerCase()) : domainToASCII(match[2].substring(0, portMatch.index).toLowerCase()) + portMatch[0]; if (normalizedHost != context.url.host && normalizedHost != host) { return await onNotFound(request); } } else { identifier = await mapUsernameToIdentifier(match[1]); resourceUrl = new URL(`acct:${match[1]}@${resourceHost}`); resourceUrl = new URL(`acct:${match[1]}@${normalizedHost}`); } } } else { identifier = uriParsed.identifier; Loading