From 31b825b183bfae702c8ceb2fa48b29a4b830cf73 Mon Sep 17 00:00:00 2001 From: Fuwn Date: Tue, 2 Jun 2026 00:25:29 +0000 Subject: fix(security): sanitize badge_wall_css server-side, render via textContent 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 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. --- src/lib/Database/SB/User/preferences.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src/lib/Database') diff --git a/src/lib/Database/SB/User/preferences.ts b/src/lib/Database/SB/User/preferences.ts index d1f03ee8..755f21b2 100644 --- a/src/lib/Database/SB/User/preferences.ts +++ b/src/lib/Database/SB/User/preferences.ts @@ -1,3 +1,4 @@ +import { sanitizeBadgeWallCss } from "$lib/Utility/sanitizeCss"; import sb from "../../sb.server"; export interface UserPreferences { @@ -122,7 +123,7 @@ export const toggleHideAWCBadges = async (userId: number) => { export const setCSS = async (userId: number, css: string) => { return await setUserPreferences(userId, { updated_at: new Date().toISOString(), - badge_wall_css: css, + badge_wall_css: sanitizeBadgeWallCss(css), }); }; -- cgit v1.2.3