aboutsummaryrefslogtreecommitdiff
path: root/src/routes/user
diff options
context:
space:
mode:
authorFuwn <[email protected]>2026-06-02 00:25:29 +0000
committerFuwn <[email protected]>2026-06-02 00:25:29 +0000
commit31b825b183bfae702c8ceb2fa48b29a4b830cf73 (patch)
tree6c14ae74185897bbf0957dc77fe214d4b022c12a /src/routes/user
parentfix(updates): isolate feed failures so one feed can't break the page (diff)
downloaddue.moe-31b825b183bfae702c8ceb2fa48b29a4b830cf73.tar.xz
due.moe-31b825b183bfae702c8ceb2fa48b29a4b830cf73.zip
fix(security): sanitize badge_wall_css server-side, render via textContentHEADmain
Custom badge-wall CSS was sanitised only client-side with a fragile regex and injected via innerHTML, while the stored value stayed raw. Sanitise at the write boundary instead (setCSS, covering both the REST and GraphQL paths) with a css-tree pass that parses leniently and drops @import, behavior/-moz-binding, expression()/javascript: values, and </style> break-out attempts; render with textContent instead of innerHTML so break-out is impossible by construction (CSP already blocks inline script). css-tree stays server-only. A behaviour-gate test confirms ordinary CSS (backdrop-filter, content, url(), @media, @keyframes) is preserved while the dangerous constructs are removed. The previous regex also silently stripped all `content:` declarations; those now render correctly.
Diffstat (limited to 'src/routes/user')
-rw-r--r--src/routes/user/[user]/badges/+page.svelte13
1 files changed, 1 insertions, 12 deletions
diff --git a/src/routes/user/[user]/badges/+page.svelte b/src/routes/user/[user]/badges/+page.svelte
index dc43fc08..605f5675 100644
--- a/src/routes/user/[user]/badges/+page.svelte
+++ b/src/routes/user/[user]/badges/+page.svelte
@@ -40,21 +40,10 @@ $: preferences = $BadgeWallUser.fetching
: ($BadgeWallUser.data?.User?.preferences as Preferences | undefined);
$: if (browser && preferences && preferences.badge_wall_css) {
- const sanitise = (css: string) =>
- css
- .replace(/\/\*[\s\S]*?\*\//g, "")
- .replace(/<\/?[^>]+(>|$)/g, "")
- .replace(
- /(expression|javascript|vbscript|onerror|onload|onclick|onmouseover|onmouseout|onmouseup|onmousedown|onkeydown|onkeyup|onkeypress|onblur|onfocus|onsubmit|onreset|onselect|onchange|ondblclick):/gi,
- "",
- )
- .replace(/(behaviour|behavior|moz-binding|content):/gi, "")
- .replace(/\s+/g, " ")
- .trim();
const style = document.createElement("style");
style.dataset.badgeWall = "true";
- style.innerHTML = sanitise(preferences.badge_wall_css);
+ style.textContent = preferences.badge_wall_css;
document.head.appendChild(style);
}