Spreading props from untrusted sources can introduce XSS vulnerabilities when malicious attributes like dangerouslySetInnerHTML are injected. This security risk occurs when user-provided data is spread directly onto components without validation.
This warning typically appears when using ESLint's react/jsx-props-no-spreading rule, which flags the spread operator (...props) in JSX. While prop spreading itself is a legitimate React pattern, it becomes a security vulnerability when spreading props from untrusted sources like user input or API responses. The core issue is that React allows any prop to be passed to components, including special props like dangerouslySetInnerHTML. If an attacker can control the shape of an object being spread, they can inject malicious props that bypass React's XSS protections. For example, spreading user-provided JSON could inject {"dangerouslySetInnerHTML": {"__html": "<script>alert('XSS')</script>"}} directly into your component. This vulnerability is particularly dangerous because it's not immediately obvious from the code that a security issue exists. The spread operator abstracts away which props are being passed, making it easy to accidentally forward dangerous attributes without realizing it.
Search for all instances of prop spreading to identify potential vulnerabilities:
# Find all prop spreading in JSX
grep -r "....*}" src/ --include="*.tsx" --include="*.jsx"Review each instance to determine if the spread source could be influenced by user input or external data.
Replace spread operators with explicit prop passing for better security and clarity:
Before (vulnerable):
function UserCard({ userProps }: { userProps: any }) {
// userProps could contain dangerouslySetInnerHTML
return <div {...userProps} />;
}After (secure):
interface UserCardProps {
name: string;
email: string;
className?: string;
}
function UserCard({ name, email, className }: UserCardProps) {
return <div className={className}>{name} - {email}</div>;
}If you must spread props, create a whitelist of safe attributes:
function SafeWrapper({ children, ...props }: any) {
// Whitelist only safe props
const safeProps = {
className: props.className,
style: props.style,
id: props.id,
'aria-label': props['aria-label'],
};
return <div {...safeProps}>{children}</div>;
}Or use object destructuring with explicit omission:
function SafeComponent({ dangerouslySetInnerHTML, ...safeProps }: any) {
// Explicitly exclude dangerous props
return <div {...safeProps} />;
}Use TypeScript interfaces and runtime validation to prevent prop injection:
import { z } from 'zod';
// Define strict prop schema
const UserPropsSchema = z.object({
name: z.string(),
email: z.string().email(),
age: z.number().optional(),
});
type UserProps = z.infer<typeof UserPropsSchema>;
function UserProfile(rawProps: unknown) {
// Validate at runtime
const props = UserPropsSchema.parse(rawProps);
return (
<div>
<h2>{props.name}</h2>
<p>{props.email}</p>
</div>
);
}Configure ESLint to warn about prop spreading in your .eslintrc file:
{
"rules": {
"react/jsx-props-no-spreading": [
"warn",
{
"html": "enforce",
"custom": "enforce",
"explicitSpread": "ignore"
}
]
}
}For stricter enforcement, change "warn" to "error". This prevents accidentally introducing spreading in new code.
If you legitimately need to render HTML, always sanitize it with DOMPurify:
npm install dompurify
npm install --save-dev @types/dompurifyimport DOMPurify from 'dompurify';
function SafeHtmlRenderer({ html }: { html: string }) {
const sanitizedHtml = DOMPurify.sanitize(html);
return <div dangerouslySetInnerHTML={{ __html: sanitizedHtml }} />;
}Never spread props that could contain dangerouslySetInnerHTML from untrusted sources.
Security Context:
The prop spreading vulnerability became more prominent with React Server Components (CVE-2025-55182, CVSS 10.0) affecting React 19.x, where malicious props could be injected during server-side rendering. Always update to the latest React versions (19.0.1+, 19.1.2+, or 19.2.1+) for critical security patches.
Edge Cases:
- URL Props: Even without dangerouslySetInnerHTML, spreading props containing href attributes can enable javascript: protocol attacks. Always validate URLs before rendering.
- Event Handlers: Spreading props could inject malicious event handlers (onClick, onLoad, etc.) that execute arbitrary code.
- Style Injection: The style prop accepts objects that could be manipulated to exfiltrate data via CSS injection attacks.
Performance Considerations:
Explicit prop passing also improves React DevTools performance and makes components easier to debug, as you can see exactly which props are being used without inspecting the spread source.
Framework-Specific Guidance:
- Next.js: Use Server Actions with input validation libraries like Zod, and implement CSRF protection with @edge-csrf/nextjs
- React Router: Validate all route parameters before spreading them as props
- Component Libraries: When building reusable components that forward props, document which props are safe to spread and filter out dangerous ones
Testing:
Test for XSS vulnerabilities by attempting to inject dangerouslySetInnerHTML through your prop spreading code paths:
// Security test
const maliciousProps = {
dangerouslySetInnerHTML: {
__html: '<img src=x onerror=alert(1)>'
}
};
// This should NOT execute the alert
render(<YourComponent {...maliciousProps} />);Error: error:0308010C:digital envelope routines::unsupported
Error: error:0308010C:digital envelope routines::unsupported
React Hook "useEffect" is called conditionally. React Hooks must be called in the exact same order in every component render.
React Hook useEffect placed inside a condition
Hook can only be called inside the body of a function component
Hook can only be called inside the body of a function component
Rollup failed to resolve import during build
How to fix "Rollup failed to resolve import" in React
React Hook useCallback has a missing dependency: 'variable'. Either include it or remove the dependency array react-hooks/exhaustive-deps
React Hook useCallback has a missing dependency