Skip to content

CVE-2026-11380: Verified Repro With Script Download

CVE-2026-11380: JetWidgets For Elementor Stored XSS via Animated Box animation effect

CVE-2026-11380 is verified against the affected target. Vulnerability class: XSS. This medium reproduction includes runnable sandbox proof, artifacts, and a plain-text agent view under REPRO-2026-00203.

REPRO-2026-00203 XSS Variant found Jul 2, 2026 CVE entry ↗ .txt
Severity
MEDIUM
CVSS
6.4
Confidence
HIGH
Reproduced in
29m 27s
Tool calls
176
Spend
$2.03
01 · Overview

What Is CVE-2026-11380?

CVE-2026-11380 is a medium-severity stored cross-site scripting (XSS) vulnerability in the JetWidgets For Elementor WordPress plugin (versions up to and including 1.0.21), in the Animated Box widget's animation_effect setting. Pruva reproduced it (reproduction REPRO-2026-00203).

02 · Severity & CVSS

CVE-2026-11380 Severity & CVSS Score

CVE-2026-11380 is rated medium severity, with a CVSS base score of 6.4 out of 10.

MEDIUM threat level
6.4 / 10 CVSS base
Weakness CWE-79 — Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')

Medium — meaningful risk under specific conditions. Schedule a fix in the normal cycle.

How to Reproduce CVE-2026-11380

$ pruva-verify REPRO-2026-00203
or curl -O https://pruva.dev/api/v1/reproductions/REPRO-2026-00203/artifacts/bundle/repro/reproduction_steps.sh && chmod +x reproduction_steps.sh && ./reproduction_steps.sh
Run in a VM or disposable container. This exploits a real vulnerability.
06 · Proof of Reproduction

Proof of Reproduction for CVE-2026-11380

Security impact — reproduced
  • reached the target end-to-end
  • on the real production code path
  • high confidence
  • the upstream fix blocks the same trigger
Trigger

animation_effect widget setting containing a double-quote that closes the class attribute and injects onmouseover/style attributes

Attack chain
  1. Published WordPress page rendered by Elementor + JetWidgets Animated Box widget
Variants tested

Stored XSS bypass in JetWidgets For Elementor 1.0.22: the Pricing Table widget's features_list/item_text setting is rendered raw through __loop_item() without escaping, allowing an author to inject <script> tags that execute for any page visitor.

How the agent worked 446 events · 176 tool calls · 29 min
29 minDuration
176Tool calls
131Reasoning steps
446Events
2Dead-ends
Agent activity over 29 min
Support
30
Hypothesis
2
Repro
172
Judge
26
Variant
212
0:0029:27

Root Cause and Exploit Chain for CVE-2026-11380

Versions: 1.0.21 and earlier (the WordPress.org repository still lists 1.0.21 as the current stable release at the time of the CVE).

The JetWidgets For Elementor WordPress plugin (versions up to and including 1.0.21) is vulnerable to a stored cross-site scripting (Stored XSS) flaw in the Animated Box widget. The widget's animation_effect setting is printed directly into the HTML class attribute of the rendered widget without output escaping or server-side validation. An attacker who can supply a value containing a double quote can break out of the class attribute and inject arbitrary attributes, including event handlers such as onmouseover="alert(1)". Because the payload is stored in the page and rendered whenever anyone visits the page, this is a stored XSS issue. The reproduction confirmed the payload in the published page HTML by deploying a real WordPress + Elementor + JetWidgets stack and viewing the rendered page.

  • Product / component: jetwidgets-for-elementor (Crocoblock / jetmonsters) — specifically the jw-animated-box widget.
  • Affected versions: 1.0.21 and earlier (the WordPress.org repository still lists 1.0.21 as the current stable release at the time of the CVE).
  • Risk level: Medium. The vendor/Wordfence CVSS is reported as 6.4.
  • Consequences: Any authenticated user with at least the author role can save a page containing a malicious Animated Box widget. The injected JavaScript is stored in the page and executes in the browser of any visitor who views the page.

Impact Parity

  • Disclosed / claimed maximum impact: Stored XSS via an author-level account in the Animated Box widget's animation_effect setting.
  • Reproduced impact from this run: A real WordPress environment was deployed with Elementor and JetWidgets 1.0.21. A published page was created with a malicious animation_effect value. The rendered page HTML contains the escaped attribute boundary break and the injected style and onmouseover="alert(1)" attributes.
  • Parity: full — the reproduction reaches the same rendered-page sink described in the advisory.
  • Not demonstrated: The reproduction does not run a full browser engine, so the JavaScript payload itself is not executed in the test environment. The proof relies on the presence of the injected event handler in the server-rendered HTML, which is the root cause of the stored XSS.

Root Cause

The vulnerability is in the widget's render template:

<!-- templates/jw-animated-box/global/index.php (v1.0.21) -->
<div class="jw-animated-box <?php $this->__html( 'animation_effect', '%s' ); ?>">

The helper method chain ends up in includes/base/class-jet-widgets-base.php:

public function __render_html( $setting = null, $format = '%s' ) {
    ...
    printf( wp_kses_post( $format ), $val );
}

wp_kses_post() is applied to the format string '%s', not to the value. Therefore the raw animation_effect value is printed verbatim inside the HTML class attribute. The UI control in includes/addons/jet-widgets-animated-box.php is a <select> with a fixed set of animation effects, but the server never validates the stored value before rendering it. A malicious author can supply a value such as:

jw-box-effect-1" style="position:fixed;width:100%;height:100%;top:0;left:0" onmouseover="alert(1)" data-x="

The first double quote closes the class attribute, the injected style covers the viewport, and the onmouseover handler will execute when a visitor moves the mouse over the page.

The vendor fixed the issue in commit 49952dd92b2bbd59e6627e7b67b0b3621c3852a0 (released as 1.0.22). The fix:

  1. Introduces a server-side sanitize_animation_effect() method that rejects any value not in the allowed list of animation effects and falls back to the default.
  2. Updates the template to use esc_attr() on the sanitized value:
$animation_effect = $this->sanitize_animation_effect( $this->get_settings_for_display( 'animation_effect' ) );
?>
<div class="jw-animated-box <?php echo esc_attr( $animation_effect ); ?>">

Reproduction Steps

The full reproduction is automated by bundle/repro/reproduction_steps.sh. At a high level the script:

  1. Creates a Docker network and starts a mysql:8.0 container and a wordpress:6.7-php8.1-apache container.
  2. Waits until the WordPress install page is reachable inside the Docker network.
  3. Installs WP-CLI, completes WordPress core installation, and sets the site URL to the internal Docker hostname (http://jetwidgets-repro-wp).
  4. Installs and activates Elementor 3.27.5 and JetWidgets For Elementor 1.0.21.
  5. Creates an author role user.
  6. Runs a PHP script inside the WordPress container that inserts a published page containing an Elementor jw-animated-box widget whose animation_effect setting is the malicious payload.
  7. Fetches the rendered page from inside the Docker network using curl and copies the body and headers to bundle/repro/artifacts/.
  8. Checks that the rendered HTML contains onmouseover="alert(1)" and the injected style attribute.

Expected evidence of reproduction: bundle/repro/artifacts/page.html contains a line similar to:

<div class="elementor-jw-animated-box jet-widgets"><div class="jw-animated-box jw-box-effect-1" style="position:fixed;width:100%;height:100%;top:0;left:0" onmouseover="alert(1)" data-x="">

Evidence

  • bundle/logs/reproduction_steps.log — step-by-step console output from the reproduction run.
  • bundle/logs/insert.log — page ID and permalink produced by the PHP insertion script.
  • bundle/logs/wordpress.log — Apache/WordPress container logs.
  • bundle/logs/mysql.log — MySQL container logs.
  • bundle/repro/artifacts/page.html — full HTML body of the rendered published page.
  • bundle/repro/artifacts/page.headers — HTTP response headers from the page request.
  • bundle/repro/artifacts/insert_page.php — the PHP helper used to create the malicious page.

Key excerpt from bundle/repro/artifacts/page.html (line 229 in the reproduced output):

<div class="elementor-jw-animated-box jet-widgets"><div class="jw-animated-box jw-box-effect-1" style="position:fixed;width:100%;height:100%;top:0;left:0" onmouseover="alert(1)" data-x="">

This shows that the animation_effect value broke out of the class attribute and added new attributes, including the onmouseover event handler.

Environment details captured in the runtime manifest:

  • WordPress 6.7 (PHP 8.1 / Apache)
  • Elementor 3.27.5
  • JetWidgets For Elementor 1.0.21
  • MySQL 8.0

Recommendations / Next Steps

  1. Upgrade: Site owners should upgrade JetWidgets For Elementor to version 1.0.22 or later, which contains commit 49952dd92b2bbd59e6627e7b67b0b3621c3852a0.
  2. Fix approach:
    • Server-side validation: reject any animation_effect value that is not in the defined list of allowed effects (e.g., jw-box-effect-1 through jw-box-effect-8).
    • Output escaping: render the value with esc_attr() before placing it inside the HTML class attribute.
  3. Testing recommendations:
    • Add unit/integration tests that save payloads containing ", <, >, and javascript: in the animation_effect setting and assert that the rendered HTML does not contain an unescaped attribute break or event handler.
    • Test with the Elementor editor save flow as well as direct post-meta injection to ensure both paths are sanitized.
  4. Further verification: The script could be extended to include a real browser engine (e.g., Playwright/Chromium) to demonstrate actual JavaScript execution on mouseover. The current proof is sufficient to confirm the server-side sink but not a live browser exploit.

Additional Notes

  • Idempotency: The reproduction script was executed twice consecutively from a clean state and succeeded both times. It cleans up all Docker containers and networks on exit via a trap.
  • Limitations: The reproduction does not exercise the Elementor editor UI or the admin-ajax.php save endpoint. It sets the widget data directly in post meta. This is acceptable for proving the rendered-page vulnerability sink, but it does not reproduce the full author-workflow UI. The root cause is the same regardless of how the malicious value reaches the database.
  • No custom exploit code: The reproduction uses the real WordPress, Elementor, and JetWidgets plugin code; no reimplementation of the vulnerability was used.

Variant Analysis & Alternative Triggers for CVE-2026-11380

Versions: 1.0.21 (vulnerable) and 1.0.22 (still vulnerable to this variant). 1.0.22 is the latest release at the time of analysis.

The JetWidgets For Elementor 1.0.22 release was intended to fix CVE-2026-11380, a stored XSS in the Animated Box widget's animation_effect setting. The patch validates the animation_effect value against an allow-list and escapes it with esc_attr() before rendering it in the HTML class attribute. However, the patch does not address the same underlying pattern in other widgets. Specifically, the Pricing Table widget's features_list repeater still renders the item_text value through __loop_item() without any sanitization, and the final printf() does not escape the value. As a result, an authenticated author can store a feature item containing <script>alert(1)</script> and that script will be rendered verbatim on the published page. This variant reproduces on the patched version 1.0.22, making it a true bypass of the 1.0.22 fix.

Fix Coverage / Assumptions

The 1.0.22 fix relies on the assumption that the vulnerability is limited to enumerated SELECT controls whose values are rendered into HTML class attributes. The patch explicitly covers:

  • Animated Box animation_effect (includes/addons/jet-widgets-animated-box.php + templates/jw-animated-box/global/index.php)
  • Animated Box button_icon_position (allow-listed)
  • Pricing Table button_size and button_icon_position (templates/jw-pricing-table/global/button.php + includes/addons/jet-widgets-pricing-table.php)

The fix does not cover:

  • The shared __html() / __get_html() / __loop_item() helper methods, which still print raw stored values by default.
  • Other Pricing Table templates such as features-loop-item.php, heading.php, price.php, and action.php that use these helpers for text-like content.
  • Any other widget template that calls __loop_item() without a sanitize callback.

Variant / Alternate Trigger

  • Entry point: A published WordPress page created by an author-level user containing a JetWidgets Pricing Table (jw-pricing-table) widget.

  • Attacker-controlled input: The item_text field of the features_list repeater in the Pricing Table widget settings.

  • Data path: The malicious string is stored in Elementor post meta (_elementor_data) and rendered when any visitor views the page.

  • Sink: templates/jw-pricing-table/global/features-loop-item.php:

    printf( '<span class="pricing-feature__text">%s</span>', $this->__loop_item( array( 'item_text' ) ) ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
    
  • Helper involved: Jet_Widgets_Base::__loop_item()Jet_Widgets_Base::__render_loop_item() in includes/base/class-jet-widgets-base.php. The helper applies a sanitizer only if the caller provides one; otherwise it returns sprintf( $format, $value ) with the raw value.

  • Why this is a bypass: This file is unchanged between 1.0.21 and 1.0.22. The 1.0.22 patch only fixes the Animated Box and the Pricing Table button controls, leaving the feature-text path open.

  • Product / component: jetwidgets-for-elementor (Crocoblock / jetmonsters), specifically the jw-pricing-table widget and the features_list repeater.

  • Affected versions: 1.0.21 (vulnerable) and 1.0.22 (still vulnerable to this variant). 1.0.22 is the latest release at the time of analysis.

  • Risk level: Medium (same as the parent CVE: stored XSS via author-level access).

  • Consequences: Any authenticated user with at least the author role can save a page containing a malicious Pricing Table feature. The injected JavaScript is stored in the page and executes in the browser of any visitor who views the page.

Impact Parity

  • Disclosed / claimed maximum impact for the parent CVE: Stored XSS via an author-level account in the Animated Box widget's animation_effect setting.
  • Reproduced impact from this variant run: A real WordPress environment was deployed with Elementor 3.27.5 and JetWidgets 1.0.22. A published page was created with a malicious Pricing Table item_text value. The rendered page HTML contains the raw <script>alert(1)</script> inside the feature text span.
  • Parity: full — the variant reaches the same stored XSS impact class (author → stored script → viewer execution) through a different widget and setting.
  • Not demonstrated: We did not demonstrate a different impact class (e.g., code execution on the server). The reproduced impact is the same as the parent claim.

Root Cause

The root cause is the same as the parent CVE: a widget setting that is intended to be plain text is printed into the page without output escaping or server-side validation. The original fix added allow-list validation and esc_attr() only for the specific Animated Box control and two Pricing Table button controls. Because the underlying __loop_item() helper does not escape by default and the features-loop-item.php template does not call esc_html() or wp_kses_post() on the item_text value, the same stored XSS can be reached from a different entry point.

The vendor's fix commit referenced for the parent CVE is 49952dd92b2bbd59e6627e7b67b0b3621c3852a0 (released as 1.0.22). The tested 1.0.22 source is WordPress.org SVN tag tags/1.0.22, revision 3594346.

Reproduction Steps

The full reproduction is automated by bundle/vuln_variant/reproduction_steps.sh. At a high level the script:

  1. Spins up two isolated Docker stacks (WordPress 6.7 + PHP 8.1 + MySQL 8.0 + Elementor 3.27.5).
  2. Installs JetWidgets For Elementor 1.0.21 in the first stack and 1.0.22 in the second stack.
  3. Creates an author-level user in each stack.
  4. Runs a PHP script inside each WordPress container that inserts a published page containing a Pricing Table widget with a malicious features_list item:
    'features_list' => array(
        array(
            'item_text'     => '<script>alert(1)</script>',
            'item_included' => 'item-included',
        ),
    ),
    
  5. Fetches the rendered page from inside each Docker network and copies the body and headers to bundle/vuln_variant/artifacts/.
  6. Checks whether the raw <script>alert(1)</script> string appears in each rendered page.
  7. Reports BYPASS CONFIRMED and exits 0 if the payload appears in the 1.0.22 rendered page.

Expected evidence of reproduction: bundle/vuln_variant/artifacts/page_fixed.html contains a line similar to:

<div class="pricing-feature__inner"><span class="jet-widgets-icon item-bullet">...</span><span class="pricing-feature__text"><script>alert(1)</script></span></div>

The same payload appears in bundle/vuln_variant/artifacts/page_vuln.html for the 1.0.21 stack.

Evidence

  • bundle/logs/vuln_variant/variant_reproduction.log — step-by-step console output from the variant run (both 1.0.21 and 1.0.22).
  • bundle/logs/vuln_variant/insert_vuln.log and bundle/logs/vuln_variant/insert_fixed.log — page IDs and permalinks produced by the PHP insertion scripts.
  • bundle/logs/vuln_variant/wordpress_vuln.log / mysql_vuln.log — container logs for the vulnerable stack.
  • bundle/logs/vuln_variant/wordpress_fixed.log / mysql_fixed.log — container logs for the patched stack.
  • bundle/vuln_variant/artifacts/page_vuln.html and bundle/vuln_variant/artifacts/page_vuln.headers — rendered page from the 1.0.21 stack.
  • bundle/vuln_variant/artifacts/page_fixed.html and bundle/vuln_variant/artifacts/page_fixed.headers — rendered page from the 1.0.22 stack.
  • bundle/vuln_variant/artifacts/insert_vuln.php and bundle/vuln_variant/artifacts/insert_fixed.php — the PHP helpers used to create the malicious pages.

Key excerpt from bundle/vuln_variant/artifacts/page_fixed.html (line 232 in the reproduced output):

<div class="pricing-feature__inner"><span class="jet-widgets-icon item-bullet"><svg aria-hidden="true" class="e-font-icon-svg e-fas-check" viewBox="0 0 512 512" xmlns="http://www.w3.org/2000/svg"><path d="M173.898 439.404l-166.4-166.4c-9.997-9.997-9.997-26.206 0-36.204l36.203-36.204c9.997-9.998 26.207-9.998 36.204 0L192 312.69 432.095 72.596c9.997-9.997 26.207-9.997 36.204 0l36.203 36.204c9.997 9.997 9.997 26.206 0 36.204l-294.4 294.401c-9.998 9.997-26.207 9.997-36.204-.001z"></path></svg></span><span class="pricing-feature__text"><script>alert(1)</script></span></div>

Environment details:

  • WordPress 6.7 (PHP 8.1 / Apache)
  • Elementor 3.27.5
  • JetWidgets For Elementor 1.0.21 (vulnerable) and 1.0.22 (patched, but bypassed)
  • MySQL 8.0

Recommendations / Next Steps

  1. Extend the fix to the Pricing Table feature text: In templates/jw-pricing-table/global/features-loop-item.php, escape the item_text value before printing. Since the control is a plain text field, the appropriate fix is:

    printf( '<span class="pricing-feature__text">%s</span>', esc_html( $this->__loop_item( array( 'item_text' ) ) ) );
    
  2. Audit all remaining __html() / __loop_item() calls: Walk every widget template and helper call to ensure stored values are escaped according to their output context. Pay special attention to text content that should use esc_html() and URLs that should use esc_url().

  3. Harden the base helpers: Consider making __html(), __get_html(), and __loop_item() apply context-aware escaping by default, with an explicit false opt-out for callers that have already escaped. This prevents the same pattern from recurring every time a new control is added.

  4. Regression tests: Add tests that save payloads containing <script>, ", >, and javascript: into each widget's text-like controls and assert that the rendered page escapes them. Test both the Elementor editor save flow and direct post-meta injection.

Additional Notes

  • Idempotency: The reproduction script was executed twice consecutively from a clean state and produced the bypass both times.
  • Clean state: The script creates fresh Docker containers and networks for each run, then removes them via a trap on exit. It does not mutate the repro stage's artifacts or the plugin source checked out in bundle/artifacts/.
  • No custom exploit code: The reproduction uses the real WordPress, Elementor, and JetWidgets plugin code; no reimplementation of the vulnerability was used.
  • Limitations: The reproduction does not exercise the Elementor editor UI or the admin-ajax.php save endpoint. It sets the widget data directly in post meta. This is acceptable for proving the rendered-page vulnerability sink, but it does not reproduce the full author-workflow UI. The root cause is the same regardless of how the malicious value reaches the database.
  • Trust boundary: The input is supplied by an authenticated author and rendered to anonymous visitors. This crosses the same trust boundary as the parent CVE and is within the plugin's stated security scope (the changelog repeatedly documents "sanitize & escape output" fixes).
  • Not a documented limitation: The item_text control is registered as a plain TEXT control, not a WYSIWYG/HTML control. Rendering raw HTML tags from it is therefore not expected behavior.

CVE-2026-11380 Reproduction Transcript

The agent's step-by-step process — every tool call, every handoff, the moment the exploit fired.

Full session Replay every step — scrub the timeline or play it back.

Event 1/40
0:002:58
0:00
session startedaccounts/fireworks/models/kimi-k2p7-code · CVE-2026-11380 · REPRO-20
0:02
0:04
web search
0:08
0:09
web search
0:15
0:16
web search
0:19
0:21
web search
0:23
0:24
web search
0:26
0:27
web search
0:35
0:36
web search
0:43
0:44
web search
0:59
0:59
extract_facts
no facts extracted
1:00
1:00
1:00
supportrepro
2:49
2:57
2:57
2:57
2:58

Artifacts and Evidence for CVE-2026-11380

Scripts, logs, diffs, and output captured during the reproduction.

08 · How to Fix

How to Fix CVE-2026-11380

Coming soon

Step-by-step mitigation and hardening guidance for CVE-2026-11380 — configuration checks, workarounds where no patch exists, and how to verify you're protected — is on the way.

10 · FAQ

FAQ: CVE-2026-11380

How does the CVE-2026-11380 stored-XSS attack work?

An authenticated attacker sets animation_effect to a value containing a double quote followed by an injected attribute such as onmouseover="alert(1)"; because the value is stored and rendered unescaped whenever the page loads, the injected script executes in the browser of any visitor who views that page.

How severe is CVE-2026-11380?

Medium severity; the vendor/Wordfence CVSS is reported as 6.4.

Does exploiting CVE-2026-11380 require authentication?

Yes - exploitation requires an authenticated WordPress account with at least the author role. It is not exploitable by anonymous visitors, though the injected script then runs against any visitor who later views the affected page.

How can I reproduce CVE-2026-11380?

Download the verified script and run it in an isolated environment against a WordPress + Elementor + JetWidgets For Elementor (<= 1.0.21) stack; it saves a page with a crafted Animated Box animation_effect value and confirms the unescaped payload appears in the rendered page HTML.
11 · References

References for CVE-2026-11380

Authoritative sources for CVE-2026-11380 — official vulnerability databases and the upstream advisory. Pruva's reproduction verifies the issue firsthand; these are the primary records to corroborate it.