32f40921567a21c3c174d3f0a3adc2d6a4d2e5b1
<style> was previously in FORBID_TAGS and stripped entirely. It's now allowed, but its CSS is rewritten by a new hand-rolled scoper (src/utils/scope-css.ts) so a customer's rules only match inside their own block's wrapper -- never leak out and restyle the rest of the page. The wrapper div (class="whp-html-<hash>") is only emitted when a block actually has surviving <style> content, so blocks that don't use it stay byte-identical to before this change. Key findings, both covered by tests: - DOMPurify's body-only serialization silently drops a <style> tag that appears before any other content in a block (the HTML5 parser implicitly places it in <head>, which DOMPurify never looks at). Fixed with FORCE_BODY: true. - DOMPurify does not sanitize CSS declaration values at all (expression(), behavior:, url() to any host all pass through verbatim) -- @import is stripped explicitly by scopeCss() since it's the one CSS-level exfiltration/fetch vector in scope here. Scope identifier reuses the existing djb2 stableHash() from utils/escape.ts (already used for this exact class of problem) over the block's own `code` string -- deterministic, no node id, no Math.random/Date.now. 1141/1141 tests passing (was 1077), tsc clean. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Languages
TypeScript
75.9%
JavaScript
12.3%
HTML
8.6%
CSS
2.6%
PHP
0.4%
Other
0.2%