diff --git a/src/views/wiki.ts b/src/views/wiki.ts index ad528a9..c3e44f1 100644 --- a/src/views/wiki.ts +++ b/src/views/wiki.ts @@ -1,4 +1,5 @@ import { canEdit } from "../lib/access.ts"; +import { escapeHtml } from "../lib/html.ts"; import { t } from "../lib/i18n/index.ts"; import { newNoteUrl, noteUrl } from "../lib/urls.ts"; import type { WikiIdentity } from "../lib/wiki-identity.ts"; @@ -18,13 +19,13 @@ export function wikiPage( .map( (n) => `
  • - ${n.title} + ${escapeHtml(n.title)}
  • `, ) .join("\n"); const langBadge = wikiLanguage - ? ` ${wikiLanguage}` + ? ` ${escapeHtml(wikiLanguage)}` : ""; const newNoteButton = canEdit(options?.accessLevel ?? "none") @@ -32,8 +33,8 @@ export function wikiPage( : ""; const content = ` -

    ${wiki.name}${langBadge}

    -

    /${wiki.slug}

    +

    ${escapeHtml(wiki.name)}${langBadge}

    +

    /${escapeHtml(wiki.slug)}

    ${msg.wiki.notes}

    ${newNoteButton} diff --git a/tests/views/wiki.test.ts b/tests/views/wiki.test.ts new file mode 100644 index 0000000..3516056 --- /dev/null +++ b/tests/views/wiki.test.ts @@ -0,0 +1,37 @@ +import { describe, expect, test } from "bun:test"; +import { wikiPage } from "../../src/views/wiki.ts"; + +// Targeted exception to the "views only check status/content-type" rule: XSS +// inertness is a behavioral property worth asserting directly. The wiki-home +// fallback view renders wiki.name / note titles / language from records that +// may originate on a third-party PDS, so they must be escaped. +const baseWiki = { name: "Safe Wiki", handle: "alice.test", slug: "test" }; + +describe("wikiPage XSS escaping", () => { + test("escapes a malicious wiki name", () => { + const html = wikiPage( + { ...baseWiki, name: "" }, + { locale: "en" }, + ); + expect(html).not.toContain(" { + const html = wikiPage(baseWiki, { + locale: "en", + sidebarNotes: [{ slug: "n", title: "" }], + }); + expect(html).not.toContain(""); + expect(html).toContain("<script>"); + }); + + test("escapes a malicious wiki language badge", () => { + const html = wikiPage( + baseWiki, + { locale: "en" }, + 'en">', + ); + expect(html).not.toContain(""); + }); +});