diff options
| author | Fuwn <[email protected]> | 2026-06-02 00:25:29 +0000 |
|---|---|---|
| committer | Fuwn <[email protected]> | 2026-06-02 00:25:29 +0000 |
| commit | 31b825b183bfae702c8ceb2fa48b29a4b830cf73 (patch) | |
| tree | 6c14ae74185897bbf0957dc77fe214d4b022c12a /src/routes/user | |
| parent | fix(updates): isolate feed failures so one feed can't break the page (diff) | |
| download | due.moe-31b825b183bfae702c8ceb2fa48b29a4b830cf73.tar.xz due.moe-31b825b183bfae702c8ceb2fa48b29a4b830cf73.zip | |
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.svelte | 13 |
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); } |