fix(builder): sanitize CSS-value sinks to prevent style/<style> breakout XSS
Adds a single cssValue() sanitizer (src/utils/escape.ts) that strips
<>{};"'\ and neutralizes url(), safe for both style="..." attribute and
<style>...</style> element contexts. Applies it at every raw user-prop
CSS-value interpolation sink found via grep across src/components (colors,
sizes, gaps interpolated directly into style strings/<style> blocks),
including the highest-risk <style>-context sinks: ColumnLayout gap,
Menu/Navbar hover and background colors. Also Number()-coerces the
`columns` grid-template-columns sinks in Gallery/Testimonials/NumberCounter
as defense in depth. Regression tests assert </style><script> payloads are
neutralized and normal colors still render.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -21,3 +21,22 @@ describe('StarRating.toHtml accessibility (F2.2)', () => {
|
||||
expect(html).toContain('aria-label="Rating: 2 out of 10"');
|
||||
});
|
||||
});
|
||||
|
||||
describe('StarRating.toHtml XSS hardening (filledColor/emptyColor/size into style=)', () => {
|
||||
test('a filledColor value containing a quote breakout is neutralized', () => {
|
||||
const malicious = '#f00" onmouseover="alert(1)';
|
||||
const { html } = toHtml({ rating: 3, maxStars: 5, filledColor: malicious }, '');
|
||||
expect(html).not.toMatch(/style="[^"]*"[^>]*onmouseover/);
|
||||
});
|
||||
|
||||
test('a size value containing </style><script> is neutralized', () => {
|
||||
const malicious = '24px</style><script>alert(1)</script>';
|
||||
const { html } = toHtml({ rating: 3, maxStars: 5, size: malicious }, '');
|
||||
expect(html).not.toContain('<script>alert(1)</script>');
|
||||
});
|
||||
|
||||
test('a normal filled color still renders', () => {
|
||||
const { html } = toHtml({ rating: 5, maxStars: 5, filledColor: '#ff9900' }, '');
|
||||
expect(html).toContain('color:#ff9900');
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user