Fix C1/C2: XSS via unsanitized style keys and animation attrs
C1: cssPropsToString emitted the camelToKebab'd style-object KEY unsanitized while only sanitizing the VALUE. A malicious style key containing a quote (reachable via AI update_props or deserialized saved state) could close the style="..." attribute and inject a live element. Now validates each key against a CSS property/custom-prop allowlist and drops anything that doesn't match. C2: buildDataAttrs (html-export.ts) interpolated props.animation and props.animationDelay directly into data-animation="..."/ data-animation-delay="..." with no escaping, for every exported node. Now routes both through escapeAttr. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import { componentResolver } from '../components/resolver';
|
||||
import { cssPropsToString } from './style-helpers';
|
||||
import { escapeHtml } from './escape';
|
||||
import { escapeHtml, escapeAttr } from './escape';
|
||||
|
||||
export interface ExportOptions {
|
||||
title?: string;
|
||||
@@ -24,9 +24,9 @@ function buildDataAttrs(props: Record<string, any>): string {
|
||||
if (props.hideOnTablet) attrs += ' data-hide-tablet';
|
||||
if (props.hideOnMobile) attrs += ' data-hide-mobile';
|
||||
if (props.animation && props.animation !== 'none') {
|
||||
attrs += ` data-animation="${props.animation}"`;
|
||||
attrs += ` data-animation="${escapeAttr(String(props.animation))}"`;
|
||||
if (props.animationDelay && props.animationDelay !== '0') {
|
||||
attrs += ` data-animation-delay="${props.animationDelay}"`;
|
||||
attrs += ` data-animation-delay="${escapeAttr(String(props.animationDelay))}"`;
|
||||
}
|
||||
}
|
||||
return attrs;
|
||||
|
||||
Reference in New Issue
Block a user