From 6a7228c06d7af2a28ead1f4ae1830a258c05afae Mon Sep 17 00:00:00 2001 From: Fuwn Date: Mon, 1 Jun 2026 15:45:01 +0000 Subject: fix(security): sanitize third-party RSS HTML before {@html} The /updates page rendered manga/novel feed fields (content, titles, series names) from mangaupdates/syosetu/wlnupdates via {@html} with no sanitization. CSP already blocks script execution, but injected markup could still phish, redirect, or track. Add sanitizeFeedHtml (DOMPurify with a small safe allow-list) and apply it on ingest. A behaviour-gate test plus a check against the live mangaupdates feed confirm legitimate formatting (entities, //) is preserved while safe"); + expect(script).not.toContain("script"); + expect(script).toContain("safe"); + + const onerror = sanitizeFeedHtml("beforeafter"); + expect(onerror).not.toContain("onerror"); + expect(onerror).not.toContain("x'), + ).not.toContain("javascript:"); + expect( + sanitizeFeedHtml(''), + ).not.toContain("iframe"); + expect( + sanitizeFeedHtml( + '', + ), + ).not.toContain("meta"); + expect(sanitizeFeedHtml("")).not.toContain( + "style", + ); + expect(sanitizeFeedHtml('
text
')).toBe("text"); + }); +}); diff --git a/src/lib/Utility/sanitizeHtml.ts b/src/lib/Utility/sanitizeHtml.ts new file mode 100644 index 00000000..3d0229e4 --- /dev/null +++ b/src/lib/Utility/sanitizeHtml.ts @@ -0,0 +1,32 @@ +import DOMPurify from "dompurify"; + +const feedConfig = { + ALLOWED_TAGS: [ + "a", + "b", + "i", + "em", + "strong", + "u", + "s", + "br", + "p", + "span", + "small", + "sup", + "sub", + "code", + ], + ALLOWED_ATTR: ["href", "title"], + ALLOWED_URI_REGEXP: /^(?:https?|mailto):/i, +}; + +/** + * Sanitise HTML coming from third-party RSS feeds before it reaches an `{@html}` + * sink. Keeps the light formatting these feeds actually use (HTML entities, + * ``/``/``) and strips anything that could inject content or + * behaviour: `