diff --git a/CHANGES.md b/CHANGES.md index 4734d2c4b..1cee497c4 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -21,6 +21,17 @@ To be released. [#849]: https://github.com/fedify-dev/fedify/issues/849 [#851]: https://github.com/fedify-dev/fedify/pull/851 +### @fedify/cli + + - Fixed `fedify nodeinfo` choosing SVG favicons whose filenames use + uppercase `.SVG` extensions or include query strings or fragments. The + command now ignores those SVG favicon links and falls back to + `/favicon.ico` before rendering terminal art. [[#891], [#918] by Junghoon + Ban] + +[#891]: https://github.com/fedify-dev/fedify/issues/891 +[#918]: https://github.com/fedify-dev/fedify/pull/918 + ### @fedify/vocab - Fixed Activity Vocabulary parsing so malformed language tags in remote diff --git a/packages/cli/src/nodeinfo.test.ts b/packages/cli/src/nodeinfo.test.ts index 0a954428a..fa8853c55 100644 --- a/packages/cli/src/nodeinfo.test.ts +++ b/packages/cli/src/nodeinfo.test.ts @@ -81,6 +81,56 @@ test("getFaviconUrl - svg icons only falls back to /favicon.ico", async () => { fetchMock.hardReset(); }); +const HTML_WITH_UPPERCASE_SVG_ONLY = ` + + + + Test Site + + +Test + +`; + +test("getFaviconUrl - uppercase svg icons only falls back to /favicon.ico", async () => { + fetchMock.spyGlobal(); + + fetchMock.get("https://example.com/", { + body: HTML_WITH_UPPERCASE_SVG_ONLY, + headers: { "Content-Type": "text/html" }, + }); + + const result = await getFaviconUrl("https://example.com/"); + assert.equal(result.href, "https://example.com/favicon.ico"); + + fetchMock.hardReset(); +}); + +const HTML_WITH_UPPERCASE_SVG_WITH_QUERY = ` + + + + Test Site + + +Test + +`; + +test("getFaviconUrl - uppercase svg icons with query and hash fall back to /favicon.ico", async () => { + fetchMock.spyGlobal(); + + fetchMock.get("https://example.com/", { + body: HTML_WITH_UPPERCASE_SVG_WITH_QUERY, + headers: { "Content-Type": "text/html" }, + }); + + const result = await getFaviconUrl("https://example.com/"); + assert.equal(result.href, "https://example.com/favicon.ico"); + + fetchMock.hardReset(); +}); + const HTML_WITHOUT_ICON = ` diff --git a/packages/cli/src/nodeinfo.ts b/packages/cli/src/nodeinfo.ts index 21b4058ad..2ef9cbe63 100644 --- a/packages/cli/src/nodeinfo.ts +++ b/packages/cli/src/nodeinfo.ts @@ -326,8 +326,9 @@ export async function getFaviconUrl( if (w < 38 || h < 19) continue; } if ("href" in attrs) { - if (attrs.href.endsWith(".svg")) continue; - return new URL(attrs.href, response.url); + const parsedUrl = new URL(attrs.href, response.url); + if (parsedUrl.pathname.toLowerCase().endsWith(".svg")) continue; + return parsedUrl; } } return new URL("/favicon.ico", response.url);