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);