import * as csstree from "css-tree"; const blockedProperties = new Set(["behavior", "-moz-binding"]); const dangerousValue = /expression\s*\(|javascript:|vbscript:/i; /** * Sanitise user-supplied badge-wall CSS at the trust boundary (write time), so * the stored value is safe regardless of how it is later rendered. Parsing with * css-tree (leniently, like a browser) drops anything that isn't valid CSS — * including `` break-out attempts — and we additionally remove the few * constructs that can load resources or (in legacy engines) run code: * `@import`, `behavior`/`-moz-binding`, and `expression()`/`javascript:` values. * * This is defence in depth: rendering goes through `textContent` (no HTML * parsing, so no break-out) and the CSP blocks inline script regardless. */ export const sanitizeBadgeWallCss = (css: string): string => { if (!css) return ""; let ast: csstree.CssNode; try { ast = csstree.parse(css, { onParseError: () => {} }); } catch { return ""; } csstree.walk(ast, (node, item, list) => { if (!list || !item) return; if (node.type === "Atrule" && node.name.toLowerCase() === "import") { list.remove(item); } else if ( node.type === "Rule" && csstree.generate(node.prelude).includes("<") ) { // css-tree keeps an unparseable selector as a Raw prelude, so a // `