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:
@@ -1,7 +1,7 @@
|
||||
import React, { CSSProperties, useState } from 'react';
|
||||
import { useNode, UserComponent } from '@craftjs/core';
|
||||
import { cssPropsToString } from '../../utils/style-helpers';
|
||||
import { escapeHtml, escapeAttr, safeUrl, scopeId } from '../../utils/escape';
|
||||
import { escapeHtml, escapeAttr, safeUrl, scopeId, cssValue } from '../../utils/escape';
|
||||
|
||||
/* ---------- Types ---------- */
|
||||
|
||||
@@ -125,14 +125,18 @@ Menu.craft = {
|
||||
/* ---------- HTML export ---------- */
|
||||
|
||||
(Menu as any).toHtml = (props: MenuProps, _childrenHtml: string, nodeId?: string) => {
|
||||
const linkCol = props.linkColor || '#3f3f46';
|
||||
const hoverCol = props.linkHoverColor || '#3b82f6';
|
||||
const ctaBg = props.ctaBgColor || '#3b82f6';
|
||||
const ctaText = props.ctaTextColor || '#ffffff';
|
||||
const gap = props.gap || '24px';
|
||||
// Sanitized once here -- linkCol/hoverCol/ctaBg/ctaText/gap/fSize are raw
|
||||
// string-interpolation sinks below (hoverCol goes into a <style> block,
|
||||
// the worst case: </style> breakout -> arbitrary <script>), see
|
||||
// task-cssxss-brief.md.
|
||||
const linkCol = cssValue(props.linkColor) || '#3f3f46';
|
||||
const hoverCol = cssValue(props.linkHoverColor) || '#3b82f6';
|
||||
const ctaBg = cssValue(props.ctaBgColor) || '#3b82f6';
|
||||
const ctaText = cssValue(props.ctaTextColor) || '#ffffff';
|
||||
const gap = cssValue(props.gap) || '24px';
|
||||
const orientation = props.orientation || 'horizontal';
|
||||
const alignment = props.alignment || 'right';
|
||||
const fSize = props.fontSize || '14px';
|
||||
const fSize = cssValue(props.fontSize) || '14px';
|
||||
|
||||
const justifyMap: Record<string, string> = { left: 'flex-start', center: 'center', right: 'flex-end' };
|
||||
|
||||
|
||||
Reference in New Issue
Block a user