Atomic Edge Proof of Concept automated generator using AI diff analysis
Published : May 14, 2026

CVE-2026-5396: Fluent Forms <= 6.1.21 – Authenticated (Subscriber+) Authorization Bypass via 'form_id' Parameter (fluentform)

CVE ID CVE-2026-5396
Plugin fluentform
Severity High (CVSS 8.2)
CWE 639
Vulnerable Version 6.1.21
Patched Version 6.2.0
Disclosed May 12, 2026

Analysis Overview

Atomic Edge analysis of CVE-2026-5396:
The Fluent Forms plugin for WordPress, up to version 6.1.21, contains an Insecure Direct Object Reference (IDOR) vulnerability in the SubmissionPolicy class. This vulnerability allows authenticated attackers with form-specific Fluent Forms Manager access to bypass authorization and perform submission-level actions (read, modify status, add notes, permanently delete) on submissions belonging to any other form. The severity is high (CVSS 8.2) because it enables unauthorized data access and destruction with minimal privileges.

Root Cause:
The root cause lies in the SubmissionPolicy class authorizing actions based solely on a user-supplied `form_id` query parameter without verifying that the targeted submission actually belongs to that form. An attacker controls the `form_id` parameter in API requests. The submission ID is also attacker-controlled via the `entry_id` or `submission_id` parameter. The Authorization check only validates that the user has permissions for the supplied `form_id`, but does not cross-reference whether the given submission belongs to that form. This allows an attacker to supply a `form_id` of a form they are authorized for, while targeting a submission ID from any other form. Key affected endpoints include AJAX actions like `fluentform-change-entry-status`, `fluentform-update-entry-user`, and REST API routes under `fluentform` namespace handling submission reads, updates, deletions, and notes.

Exploitation:
An authenticated attacker with Fluent Forms Manager access restricted to a single form (e.g., Form ID 5) can target submissions from any other form (e.g., Form ID 10) by crafting requests that use their authorized form_id (5) but a different submission_id (e.g., 100 belonging to form 10). The attack vector is through the WordPress AJAX API (admin-ajax.php) or REST API endpoints. For example, a POST to `/wp-admin/admin-ajax.php` with `action=fluentform-change-entry-status&form_id=5&entry_id=100&status=trash` would successfully change the status of submission 100 (from form 10) because the check only verifies the user’s access to form 5. The attacker must know or guess the target submission ID and have the appropriate nonce (which may be obtained from the authenticated session).

Patch Analysis:
The patch modifies the authorization logic in the SubmissionPolicy class (not fully shown in the diff but referenced). The critical change is that the system now verifies the submission’s actual `form_id` from the database record before authorizing the action. The diff shows related hardening: In `fluentform/app/Http/Controllers/SubmissionController.php` line 162-165, the `updateSubmissionUser` function now retrieves the submission’s `form_id` from the database and uses it for authorization instead of trusting the `form_id` parameter. Also, `Eentry_id` replaces `submission_id` to ensure the correct parameter is used for authorization. The patch ensures the authorization check uses the form_id derived from the submission record, not the user-supplied parameter.

Impact:
Successful exploitation allows an attacker to read, modify status (trash, spam, unread), add notes, and permanently delete submissions from any form in the Fluent Forms installation. This can lead to exposure of sensitive user-submitted data (PII, order details, contact information), data loss through permanent deletion, and alteration of workflow statuses. In multi-site or user-role segmentation contexts, this represents a complete bypass of form-level access controls.

Differential between vulnerable and patched code

Below is a differential between the unpatched vulnerable code and the patched update, for reference.

Code Diff
--- a/fluentform/app/Api/Entry.php
+++ b/fluentform/app/Api/Entry.php
@@ -2,6 +2,8 @@

 namespace FluentFormAppApi;

+defined('ABSPATH') or die;
+
 use FluentFormAppModulesFormFormDataParser;
 use FluentFormAppModulesFormFormFieldsParser;
 use FluentFormAppServicesReportReportHelper;
--- a/fluentform/app/Api/Form.php
+++ b/fluentform/app/Api/Form.php
@@ -82,12 +82,12 @@
         $skip = $perPage * ($currentPage - 1);

         if ($is_filter_by_conv_or_step_form) {
-            $data = (array) $query->select('*')->get();
+            $data = $query->select('*')->get()->all();
         } else {
             if ($total < $skip) {
                 $skip = 0;
             }
-            $data = (array) $query->select('*')->limit($perPage)->offset($skip)->get();
+            $data = $query->select('*')->limit($perPage)->offset($skip)->get()->all();
         }

         // Fetch all counts in bulk before the loop
--- a/fluentform/app/Api/Submission.php
+++ b/fluentform/app/Api/Submission.php
@@ -2,6 +2,8 @@

 namespace FluentFormAppApi;

+use FluentFormAppModelsSubscription;
+use FluentFormAppModelsTransaction;
 use FluentFormFrameworkHelpersArrayHelper;

 class Submission
@@ -82,23 +84,17 @@

     public function transactions($columnValue, $column = 'submission_id')
     {
-        return wpFluent()->table('fluentform_transactions')
-            ->where($column, $columnValue)
-            ->get();
+        return Transaction::where($column, $columnValue)->get();
     }

     public function transaction($columnValue, $column = 'id')
     {
-        return wpFluent()->table('fluentform_transactions')
-            ->where($column, $columnValue)
-            ->first();
+        return Transaction::where($column, $columnValue)->first();
     }

     public function subscriptions($submissionId, $withTransactions = false)
     {
-        $subscriptions = wpFluent()->table('fluentform_subscriptions')
-            ->where('submission_id', $submissionId)
-            ->get();
+        $subscriptions = Subscription::bySubmission($submissionId)->get();

         if ($withTransactions) {
             foreach ($subscriptions as $subscription) {
@@ -111,9 +107,7 @@

     public function getSubscription($subscriptionId, $withTransactions = false)
     {
-        $subscription = wpFluent()->table('fluentform_subscriptions')
-            ->where('id', $subscriptionId)
-            ->first();
+        $subscription = Subscription::find($subscriptionId);

         if (!$subscription) {
             return false;
@@ -148,8 +142,7 @@
             'grouped'           => false,
         ]);

-        $query = wpFluent()->table('fluentform_transactions')
-            ->orderBy('id', 'DESC')
+        $query = Transaction::orderBy('id', 'DESC')
             ->where(function ($q) use ($user) {
                 $q->where('user_id', $user->ID)
                     ->orderBy('id', 'DESC')
@@ -174,17 +167,14 @@
     public function transactionsBySubscriptionId($subscriptionId)
     {

-        return wpFluent()->table('fluentform_transactions')
-            ->where('subscription_id', $subscriptionId)
+        return Transaction::where('subscription_id', $subscriptionId)
             ->orderBy('id', 'DESC')
             ->get();
     }

     public function transactionsBySubmissionId($submissionId)
     {
-        return wpFluent()->table('fluentform_transactions')
-            ->where('submission_id', $submissionId)
-            ->get();
+        return Transaction::bySubmission($submissionId)->get();
     }

     public function subscriptionsByUserId($userId = false, $args = [])
@@ -212,7 +202,7 @@
             ->where('payment_type', 'subscription')
             ->get();

-        if (!$submissions) {
+        if (count($submissions) === 0) {
             return [];
         }

@@ -223,8 +213,7 @@
             $currencyMaps[$submission->id] = $submission->currency;
         }

-        $query = wpFluent()->table('fluentform_subscriptions')
-            ->select(['fluentform_subscriptions.*'])
+        $query = Subscription::select(['fluentform_subscriptions.*'])
             ->orderBy('id', 'DESC')
             ->whereIn('submission_id', $submissionIds);

--- a/fluentform/app/Compat/ArrayHelper.php
+++ b/fluentform/app/Compat/ArrayHelper.php
@@ -0,0 +1,35 @@
+<?php
+
+namespace FluentFormFrameworkHelpers;
+
+use FluentFormFrameworkSupportArr;
+use FluentFormFrameworkSupportHelper;
+
+class ArrayHelper extends Arr
+{
+    /**
+     * Return the default value of the given value.
+     *
+     * @param  mixed $value
+     * @return mixed
+     */
+    public static function value($value)
+    {
+        return Helper::value($value);
+    }
+
+    public static function isTrue($array, $key)
+    {
+        $value = static::get($array, $key);
+
+        if (is_bool($value)) {
+            return $value;
+        }
+
+        if ('false' == $value || '0' == $value || !$value) {
+            return false;
+        }
+
+        return true;
+    }
+}
--- a/fluentform/app/Compat/FormSubmissions.php
+++ b/fluentform/app/Compat/FormSubmissions.php
@@ -0,0 +1,17 @@
+<?php
+
+namespace FluentFormAppDatabasesMigrations;
+
+defined('ABSPATH') or die;
+
+/**
+ * @deprecated since 6.2.0. Use FluentFormDatabaseMigrationsSubmissions instead.
+ * @todo Remove in 7.0
+ */
+class FormSubmissions extends FluentFormDatabaseMigrationsSubmissions
+{
+    public function __construct()
+    {
+        _deprecated_function(__CLASS__, '6.2.0', 'FluentFormDatabaseMigrationsSubmissions');
+    }
+}
--- a/fluentform/app/Compat/RequestFile.php
+++ b/fluentform/app/Compat/RequestFile.php
@@ -0,0 +1,10 @@
+<?php
+
+namespace FluentFormFrameworkRequest;
+
+use FluentFormFrameworkHttpRequestFile as BaseFile;
+
+class File extends BaseFile
+{
+    //
+}
--- a/fluentform/app/Compat/RequestRequest.php
+++ b/fluentform/app/Compat/RequestRequest.php
@@ -0,0 +1,10 @@
+<?php
+
+namespace FluentFormFrameworkRequest;
+
+use FluentFormFrameworkHttpRequestRequest as BaseRequest;
+
+class Request extends BaseRequest
+{
+    //
+}
--- a/fluentform/app/Compat/SubmissionDetails.php
+++ b/fluentform/app/Compat/SubmissionDetails.php
@@ -0,0 +1,17 @@
+<?php
+
+namespace FluentFormAppDatabasesMigrations;
+
+defined('ABSPATH') or die;
+
+/**
+ * @deprecated since 6.2.0. Use FluentFormDatabaseMigrationsSubmissionDetails instead.
+ * @todo Remove in 7.0
+ */
+class SubmissionDetails extends FluentFormDatabaseMigrationsSubmissionDetails
+{
+    public function __construct()
+    {
+        _deprecated_function(__CLASS__, '6.2.0', 'FluentFormDatabaseMigrationsSubmissionDetails');
+    }
+}
--- a/fluentform/app/ComposerScript.php
+++ b/fluentform/app/ComposerScript.php
@@ -36,6 +36,11 @@
             $fileName = $file->getPathname();

             $content = file_get_contents($fileName);
+
+            if (strpos($content, 'WPFluent\') === false) {
+                continue;
+            }
+
             $content = str_replace(
                 'WPFluent\',
                 $namespace . '\Framework\',
--- a/fluentform/app/Helpers/Helper.php
+++ b/fluentform/app/Helpers/Helper.php
@@ -744,7 +744,7 @@
         $ff_list = Form::select(['id', 'title'])->orderBy('id', 'DESC')->get();
         $forms = [];

-        if ($ff_list) {
+        if (count($ff_list) > 0) {
             $forms[0] = esc_html__('Select a Fluent Forms', 'fluentform');
             foreach ($ff_list as $form) {
                 $forms[$form->id] = esc_html($form->title) . ' (' . $form->id . ')';
@@ -1275,14 +1275,14 @@

     /**
      * Determine pro payment script is compatible or not
-     * Script is compatible if pro version is greater than or equal to 6.0.4
+     * Script is compatible if pro version meets the minimum required version
      *
      * @return bool
      */
     public static function isProPaymentScriptCompatible()
     {
         if (self::hasPro()) {
-            return version_compare(FLUENTFORMPRO_VERSION, '6.0.4', '>=');
+            return version_compare(FLUENTFORMPRO_VERSION, FLUENTFORM_MINIMUM_PRO_VERSION, '>=');
         }
         return false;
     }
--- a/fluentform/app/Helpers/Protector.php
+++ b/fluentform/app/Helpers/Protector.php
@@ -39,7 +39,7 @@

         $ciphertext_raw = openssl_encrypt($text, $cipher, $key, $options = OPENSSL_RAW_DATA, $iv);

-        $hmac = hash_hmac('sha256', $ciphertext_raw, $key, $as_binary = true);
+        $hmac = hash_hmac('sha256', $iv . $ciphertext_raw, $key, $as_binary = true);

         return base64_encode($iv . $hmac . $ciphertext_raw);
     }
@@ -55,24 +55,32 @@
     {
         $key = static::getSalt();

-        $c = base64_decode($text);
+        $c = base64_decode($text, true);

         $cipher = 'AES-128-CBC';

         $ivlen = openssl_cipher_iv_length($cipher);

+        $sha2len = 32;
+
+        if ($c === false || strlen($c) < $ivlen + $sha2len) {
+            return null;
+        }
+
         $iv = substr($c, 0, $ivlen);

-        $hmac = substr($c, $ivlen, $sha2len = 32);
+        $hmac = substr($c, $ivlen, $sha2len);

         $ciphertext_raw = substr($c, $ivlen + $sha2len);

-        $original_plaintext = openssl_decrypt($ciphertext_raw, $cipher, $key, $options = OPENSSL_RAW_DATA, $iv);
-
-        $calcmac = hash_hmac('sha256', $ciphertext_raw, $key, $as_binary = true);
+        $calcmac = hash_hmac('sha256', $iv . $ciphertext_raw, $key, $as_binary = true);

-        if (hash_equals($hmac, $calcmac)) { // timing attack safe comparison
-            return $original_plaintext;
+        if (!hash_equals($hmac, $calcmac)) {
+            return null;
         }
+
+        $original_plaintext = openssl_decrypt($ciphertext_raw, $cipher, $key, $options = OPENSSL_RAW_DATA, $iv);
+
+        return $original_plaintext !== false ? $original_plaintext : null;
     }
 }
--- a/fluentform/app/Hooks/Ajax.php
+++ b/fluentform/app/Hooks/Ajax.php
@@ -1,5 +1,7 @@
 <?php

+defined('ABSPATH') or die;
+
 /**
  * Add all ajax hooks
  */
@@ -88,124 +90,14 @@
 });


-$app->addAction('wp_ajax_fluentform-forms', function () use ($app) {
-    dd('wp_ajax_fluentform-forms');
-    Acl::verify('fluentform_dashboard_access');
-    (new FluentFormAppModulesFormForm($app))->index();
-});
-
-$app->addAction('wp_ajax_fluentform-form-store', function () use ($app) {
-    dd('wp_ajax_fluentform-form-store');
-    Acl::verify('fluentform_forms_manager');
-    (new FluentFormAppModulesFormForm($app))->store();
-});
-
-$app->addAction('wp_ajax_fluentform-form-find', function () use ($app) {
-    //No usage found
-    Acl::verify('fluentform_dashboard_access');
-    (new FluentFormAppModulesFormForm($app))->find();
-});
-
-$app->addAction('wp_ajax_fluentform-form-delete', function () use ($app) {
-    dd('wp_ajax_fluentform-form-delete');
-    Acl::verify('fluentform_forms_manager');
-    (new FluentFormAppModulesFormForm($app))->delete();
-});
-
-$app->addAction('wp_ajax_fluentform-form-duplicate', function () use ($app) {
-    dd('wp_ajax_fluentform-form-duplicate');
-    Acl::verify('fluentform_forms_manager');
-    (new FluentFormAppModulesFormForm($app))->duplicate();
-});
+// Legacy AJAX handlers removed — these routes are handled by the REST API.
+// Kept: fluentform-form-find-shortcode-locations (still in active use)
 $app->addAdminAjaxAction('fluentform-form-find-shortcode-locations', function () use ($app) {
     Acl::verify('fluentform_forms_manager');
     (new FluentFormAppModulesFormForm($app))->findFormLocations();
 });

-$app->addAction('wp_ajax_fluentform-convert-to-conversational', function () use ($app) {
-    dd('wp_ajax_fluentform-convert-to-conversational');
-    Acl::verify('fluentform_forms_manager');
-    (new FluentFormAppModulesFormForm($app))->convertToConversational();
-});
-
-
-$app->addAction('wp_ajax_fluentform-form-inputs', function () use ($app) {
-    dd('wp_ajax_fluentform-form-inputs');
-    Acl::verify('fluentform_forms_manager');
-    (new FluentFormAppModulesFormInputs($app))->index();
-});
-
-$app->addAction('wp_ajax_fluentform-load-editor-shortcodes', function () use ($app) {
-    dd('wp_ajax_fluentform-load-editor-shortcodes');
-    Acl::verify('fluentform_forms_manager');
-    (new FluentFormAppModulesComponentComponent($app))->getEditorShortcodes();
-});
-
-$app->addAction('wp_ajax_fluentform-load-all-editor-shortcodes', function () use ($app) {
-    dd('wp_ajax_fluentform-load-all-editor-shortcodes');
-    Acl::verify('fluentform_forms_manager');
-    (new FluentFormAppModulesComponentComponent($app))->getAllEditorShortcodes();
-});
-
-$app->addAction('wp_ajax_fluentform-settings-formSettings', function () use ($app) {
-    dd('wp_ajax_fluentform-settings-formSettings');
-    Acl::verify('fluentform_forms_manager');
-    (new FluentFormAppModulesFormSettingsFormSettings($app))->index();
-});
-
-$app->addAction('wp_ajax_fluentform-settings-general-formSettings', function () use ($app) {
-    dd('wp_ajax_fluentform-settings-general-formSettings');
-    Acl::verify('fluentform_forms_manager');
-    (new FluentFormAppModulesFormSettingsFormSettings($app))->getGeneralSettingsAjax();
-});
-
-$app->addAction('wp_ajax_fluentform-settings-formSettings-store', function () use ($app) {
-    dd('wp_ajax_fluentform-settings-formSettings-store');
-    Acl::verify('fluentform_forms_manager');
-    (new FluentFormAppModulesFormSettingsFormSettings($app))->store();
-});
-
-$app->addAction('wp_ajax_fluentform-settings-formSettings-remove', function () use ($app) {
-    dd('wp_ajax_fluentform-settings-formSettings-remove');
-    Acl::verify('fluentform_forms_manager');
-    (new FluentFormAppModulesFormSettingsFormSettings($app))->remove();
-});
-
-$app->addAction('wp_ajax_fluentform-get-form-custom_css_js', function () {
-    dd('wp_ajax_fluentform-get-form-custom_css_js');
-    Acl::verify('fluentform_forms_manager');
-    (new FluentFormAppModulesFormSettingsFormCssJs())->getSettingsAjax();
-});
-
-$app->addAction('wp_ajax_fluentform-save-form-custom_css_js', function () {
-    dd('wp_ajax_fluentform-save-form-custom_css_js');
-    Acl::verify('fluentform_forms_manager');
-    (new FluentFormAppModulesFormSettingsFormCssJs())->saveSettingsAjax();
-});
-
-$app->addAction('wp_ajax_fluentform-save-form-entry_column_view_settings', function () {
-    dd('wp_ajax_fluentform-save-form-entry_column_view_settings');
-    Acl::verify('fluentform_forms_manager');
-    (new FluentFormAppModulesFormSettingsEntryColumnViewSettings())->saveVisibleColumnsAjax();
-});
-
-$app->addAction('wp_ajax_fluentform-save-form-entry_column_order_settings', function () {
-    dd('wp_ajax_fluentform-save-form-entry_column_order_settings');
-    Acl::verify('fluentform_forms_manager');
-    (new FluentFormAppModulesFormSettingsEntryColumnViewSettings())->saveEntryColumnsOrderAjax();
-});
-
-$app->addAction('wp_ajax_fluentform-reset-form-entry_column_order_settings', function () {
-    dd('wp_ajax_fluentform-reset-form-entry_column_order_settings');
-    Acl::verify('fluentform_forms_manager');
-    (new FluentFormAppModulesFormSettingsEntryColumnViewSettings())->resetEntryDisplaySettings();
-});
-
-$app->addAction('wp_ajax_fluentform-load-editor-components', function () use ($app) {
-    dd('wp_ajax_fluentform-load-editor-components');
-    Acl::verify('fluentform_forms_manager');
-    (new FluentFormAppModulesComponentComponent($app))->index();
-});
+// Legacy AJAX handlers removed — these routes are now handled by the REST API.



@@ -215,9 +107,14 @@
 });

 $app->addAction('wp_ajax_fluentform-update-entry-user', function () use ($app) {
-    Acl::verify('fluentform_entries_viewer');
-    $userId = intval($app->request->get('user_id'));
     $submissionId = intval($app->request->get('submission_id'));
+    $formId = null;
+    if ($submissionId) {
+        $submission = FluentFormAppModelsSubmission::select('form_id')->find($submissionId);
+        $formId = $submission ? $submission->form_id : null;
+    }
+    Acl::verify('fluentform_manage_entries', $formId);
+    $userId = intval($app->request->get('user_id'));
     try {
         $result = (new FluentFormAppServicesSubmissionSubmissionService())->updateSubmissionUser($userId, $submissionId);
         wp_send_json_success($result);
@@ -244,55 +141,7 @@
 });


-$app->addAction('wp_ajax_fluentform-get-entry-logs', function () use ($app) {
-    dd('wp_ajax_fluentform-get-entry-logs');
-    Acl::verify('fluentform_entries_viewer');
-    $entry_id = intval($app->request->get('entry_id'));
-    $logType = sanitize_text_field($app->request->get('log_type'));
-    (new FluentFormAppModulesLoggerDataLogger($app))->getLogsByEntry($entry_id, $logType);
-});
-
-$app->addAction('wp_ajax_fluentform_get_activity_log_filters', function () use ($app) {
-    dd('wp_ajax_fluentform_get_activity_log_filters');
-    Acl::verify('fluentform_entries_viewer');
-    (new FluentFormAppModulesLoggerDataLogger($app))->getLogFilters();
-});
-
-$app->addAction('wp_ajax_fluentform_get_activity_api_log_filters', function () use ($app) {
-    dd('wp_ajax_fluentform_get_activity_api_log_filters');
-    Acl::verify('fluentform_entries_viewer');
-    (new FluentFormAppModulesLoggerDataLogger($app))->getApiLogFilters();
-});
-
-$app->addAction('wp_ajax_fluentform_get_all_logs', function () use ($app) {
-    dd('wp_ajax_fluentform_get_all_logs');
-    Acl::verify('fluentform_entries_viewer');
-    (new FluentFormAppModulesLoggerDataLogger($app))->getAllLogs();
-});
-
-$app->addAction('wp_ajax_fluentform_get_api_logs', function () use ($app) {
-    dd('wp_ajax_fluentform_get_api_logs');
-    Acl::verify('fluentform_entries_viewer');
-    (new FluentFormAppModulesLoggerDataLogger($app))->getApiLogs();
-});
-
-$app->addAction('wp_ajax_fluentform_retry_api_action', function () use ($app) {
-    // No usage found
-    Acl::verify('fluentform_entries_viewer');
-    (new FluentFormAppModulesLoggerDataLogger($app))->retryApiAction();
-});
-
-$app->addAction('wp_ajax_fluentform_delete_logs_by_ids', function () use ($app) {
-    dd('wp_ajax_fluentform_delete_logs_by_ids');
-    Acl::verify('fluentform_manage_entries');
-    (new FluentFormAppModulesLoggerDataLogger($app))->deleteLogsByIds();
-});
-
-$app->addAction('wp_ajax_fluentform_delete_api_logs_by_ids', function () use ($app) {
-    dd('wp_ajax_fluentform_delete_api_logs_by_ids');
-    Acl::verify('fluentform_manage_entries');
-    (new FluentFormAppModulesLoggerDataLogger($app))->deleteApiLogsByIds();
-});
+// Legacy log AJAX handlers removed — these routes are now handled by the REST API.

 $app->addAction('wp_ajax_fluentform-change-entry-status', function () use ($app) {
     Acl::verify('fluentform_manage_entries');
@@ -302,6 +151,7 @@
     ];
     $newStatus = (new FluentFormAppServicesSubmissionSubmissionService())->updateStatus($attributes);
     wp_send_json_success([
+        // translators: %s is the submission status name
         'message' => sprintf(__('Item has been marked as %s', 'fluentform'), $newStatus),
         'status'  => $newStatus,
     ], 200);
@@ -309,7 +159,7 @@


 $app->addAction('wp_ajax_fluentform_notice_action_track_yes', function () {
-    Acl::hasAnyFormPermission();
+    Acl::verify('fluentform_settings_manager');
     (new FluentFormAppModulesTrackTrackModule())->sendInitialInfo();
 });

@@ -334,6 +184,7 @@
  * Background Process Receiver
  */

+// $this refers to the Application instance (included via Application::requireCommonFiles → includes.php)
 $app->addAction('wp_ajax_fluentform_background_process', function () {
     $this->app['fluentFormAsyncRequest']->handleBackgroundCall();
 });
@@ -343,6 +194,20 @@
 });

 /*
+ * Background Report Data Migration
+ */
+$app->addAction('wp_ajax_fluentform_report_data_migrate', function () {
+    if (!wp_verify_nonce(sanitize_text_field(wpFluentForm('request')->get('nonce')), 'fluentform_report_data_migrate')) {
+        die('invalid');
+    }
+    $formId = intval(wpFluentForm('request')->get('form_id'));
+    if ($formId && Acl::hasPermission('fluentform_entries_viewer', $formId)) {
+        FluentFormAppServicesReportReportHelper::runMigrationBatch($formId);
+    }
+    die('done');
+});
+
+/*
  * For REST API Nonce Renewal
  */
 $app->addAction('wp_ajax_fluentform_renew_rest_nonce', function () {
--- a/fluentform/app/Hooks/Handlers/ActivationHandler.php
+++ b/fluentform/app/Hooks/Handlers/ActivationHandler.php
@@ -2,6 +2,8 @@

 namespace FluentFormAppHooksHandlers;

+defined('ABSPATH') or die;
+
 use FluentFormAppModulesAclAcl;
 use FluentFormDatabaseDBMigrator;
 use FluentFormDatabaseMigrationsLogs;
@@ -195,7 +197,7 @@
         add_filter('cron_schedules', function ($schedules) {
             $schedules['ff_every_five_minutes'] = [
                 'interval' => 300,
-                'display'  => esc_html__('Every 5 Minutes (FluentForm)', 'fluentform'),
+                'display'  => 'Every 5 Minutes (FluentForm)',
             ];

             return $schedules;
--- a/fluentform/app/Hooks/Handlers/GlobalNotificationHandler.php
+++ b/fluentform/app/Hooks/Handlers/GlobalNotificationHandler.php
@@ -61,7 +61,7 @@
         $feedMetaKeys = array_keys($feedKeys);
         $feeds = $this->globalNotificationService->getNotificationFeeds($form, $feedMetaKeys);

-        if (! $feeds) {
+        if (count($feeds) === 0) {
             do_action_deprecated(
                 'fluentform_global_notify_completed',
                 [
--- a/fluentform/app/Hooks/actions.php
+++ b/fluentform/app/Hooks/actions.php
@@ -1,5 +1,7 @@
 <?php

+defined('ABSPATH') or die;
+
 use FluentFormAppModulesComponentComponent;
 use FluentFormAppModulesAclAcl;
 use FluentFormAppHelpersHelper;
@@ -960,7 +962,7 @@
 add_filter('cron_schedules', function ($schedules) {
     $schedules['ff_every_five_minutes'] = [
         'interval' => 300,
-        'display'  => esc_html__('Every 5 minutes (FluentForm)', 'fluentform'),
+        'display'  => 'Every 5 minutes (FluentForm)',
     ];

     return $schedules;
@@ -1090,7 +1092,8 @@
     $forms = wpFluent()->table('fluentform_forms')
         ->select(['id', 'title'])
         ->orderBy('id', 'DESC')
-        ->get();
+        ->get()
+        ->toArray();

     array_unshift($forms, (object) [
         'id'    => '',
@@ -1115,7 +1118,7 @@
         'forms'                   => $forms,
         'style_presets'           => $presets,
         'theme_style'             => apply_filters('fluentform/load_theme_style', false) ? 'ffs_inherit_theme' : '',
-        'conversational_demo_img' => fluentformMix('img/conversational-form-demo.png'),
+        'conversational_demo_img' => fluentFormMix('img/conversational-form-demo.png'),
         'rest'                    => Helper::getRestInfo()
     ]);

--- a/fluentform/app/Hooks/filters.php
+++ b/fluentform/app/Hooks/filters.php
@@ -1,5 +1,7 @@
 <?php

+defined('ABSPATH') or die;
+
 /**
  * All registered filter's handlers should be in appHooksHandlers,
  * addFilter is similar to add_filter and addCustomFlter is just a
@@ -257,13 +259,11 @@


 $app->addFilter('fluentform/response_render_textarea', function ($value, $field, $formId, $isHtml) {
-    $value = $value ? nl2br($value) : $value;
-
-    if (!$isHtml || !$value) {
+    if (!$value || !is_string($value)) {
         return $value;
     }

-    return '<span style="white-space: pre-line">' . $value . '</span>';
+    return nl2br($value);
 }, 10, 4);

 $app->addFilter('fluentform/response_render_input_file', function ($response, $field, $form_id, $isHtml = false) {
--- a/fluentform/app/Hooks/includes.php
+++ b/fluentform/app/Hooks/includes.php
@@ -1,5 +1,7 @@
 <?php

+defined('ABSPATH') or die;
+
 /*
  * Require any extra files here. For example::
  * require_once "shortcodes.php";
--- a/fluentform/app/Http/Controllers/FormController.php
+++ b/fluentform/app/Http/Controllers/FormController.php
@@ -118,8 +118,9 @@
     public function update(FormService $formService)
     {
         try {
+            // Sanitization handled in Updater::update() — only title, status, form_id, formFields are extracted
             $attributes = $this->request->all();
-
+
             $formService->update($attributes);

             return $this->sendSuccess([
--- a/fluentform/app/Http/Controllers/FormIntegrationController.php
+++ b/fluentform/app/Http/Controllers/FormIntegrationController.php
@@ -25,7 +25,7 @@
         try {
             $integration = $integrationService->find($this->request->all());
             return $this->sendSuccess($integration);
-        } catch (Exception $e) {
+        } catch (Exception $e) {
             return $this->sendError([
                 'message' => $e->getMessage(),
             ], 422);
@@ -37,7 +37,7 @@
         try {
             $integration = $integrationService->update($this->request->all());
             return $this->sendSuccess($integration);
-        } catch (Exception $e) {
+        } catch (Exception $e) {
             return $this->sendError([
                 'message' => $e->getMessage()
             ], 422);
@@ -47,12 +47,13 @@
     public function delete(FormIntegrationService $integrationService)
     {
         try {
+            $formId = intval($this->request->get('form_id'));
             $id = intval($this->request->get('integration_id'));
-            $integrationService->delete($id);
+            $integrationService->delete($id, $formId);
             return $this->sendSuccess([
                 'message' => __('Successfully deleted the Integration.', 'fluentform'),
             ], 200);
-        } catch (Exception $e) {
+        } catch (Exception $e) {
             return $this->sendError([
                 'message' => $e->getMessage(),
             ], 422);
@@ -91,7 +92,7 @@
             return $this->sendSuccess([
                 'merge_fields' => $merge_fields,
             ]);
-        } catch (Exception $e) {
+        } catch (Exception $e) {
             return $this->sendError([
                 'message' => $e->getMessage(),
             ], 422);
--- a/fluentform/app/Http/Controllers/GlobalIntegrationController.php
+++ b/fluentform/app/Http/Controllers/GlobalIntegrationController.php
@@ -8,13 +8,6 @@

 class GlobalIntegrationController extends Controller
 {
-    /**
-     * Request object
-     *
-     * @var FluentFormFrameworkRequestRequest $request
-     */
-    protected $request;
-

     public function index(GlobalIntegrationService $globalIntegrationService)
     {
--- a/fluentform/app/Http/Controllers/IntegrationManagerController.php
+++ b/fluentform/app/Http/Controllers/IntegrationManagerController.php
@@ -2,6 +2,7 @@

 namespace FluentFormAppHttpControllers;

+defined('ABSPATH') or die;

 use FluentFormAppHelpersIntegrationManagerHelper;
 use FluentFormFrameworkFoundationApp;
--- a/fluentform/app/Http/Controllers/LogController.php
+++ b/fluentform/app/Http/Controllers/LogController.php
@@ -25,10 +25,11 @@
                 $logger->get($attributes)
             );
         } catch (Exception $e) {
-            return $this->sendError([
-                'message' => __('Something went wrong, please try again!', 'fluentform'),
-                'error'   => $e->getMessage(),
-            ]);
+            $response = ['message' => __('Something went wrong, please try again!', 'fluentform')];
+            if (defined('WP_DEBUG') && WP_DEBUG) {
+                $response['error'] = $e->getMessage();
+            }
+            return $this->sendError($response);
         }
     }

@@ -46,10 +47,11 @@
                 $logger->getFilters($attributes)
             );
         } catch (Exception $e) {
-            return $this->sendError([
-                'message' => __('Something went wrong, please try again!', 'fluentform'),
-                'error'   => $e->getMessage(),
-            ]);
+            $response = ['message' => __('Something went wrong, please try again!', 'fluentform')];
+            if (defined('WP_DEBUG') && WP_DEBUG) {
+                $response['error'] = $e->getMessage();
+            }
+            return $this->sendError($response);
         }
     }

@@ -67,9 +69,11 @@
                 $logger->remove($attributes)
             );
         } catch (Exception $e) {
-            return $this->sendError([
-                'message' => $e->getMessage(),
-            ]);
+            $response = ['message' => __('Something went wrong, please try again!', 'fluentform')];
+            if (defined('WP_DEBUG') && WP_DEBUG) {
+                $response['error'] = $e->getMessage();
+            }
+            return $this->sendError($response);
         }
     }
 }
--- a/fluentform/app/Http/Controllers/SubmissionController.php
+++ b/fluentform/app/Http/Controllers/SubmissionController.php
@@ -159,7 +159,9 @@
     {
         try {
             $userId = intval($this->request->get('user_id'));
-            $submissionId = intval($this->request->get('submission_id'));
+            // Use entry_id from route parameter — not submission_id from body —
+            // to ensure authorization target matches the mutation target.
+            $submissionId = intval($this->request->get('entry_id'));
             $response = $submissionService->updateSubmissionUser($userId, $submissionId);
             return $this->sendSuccess($response);
         } catch (Exception $e) {
--- a/fluentform/app/Http/Controllers/SubmissionNoteController.php
+++ b/fluentform/app/Http/Controllers/SubmissionNoteController.php
@@ -32,7 +32,14 @@
     {
         try {
             $attributes = $this->request->all();
-
+
+            if (isset($attributes['note']['content'])) {
+                $attributes['note']['content'] = wp_kses_post($attributes['note']['content']);
+            }
+            if (isset($attributes['note']['status'])) {
+                $attributes['note']['status'] = sanitize_text_field($attributes['note']['status']);
+            }
+
             return $this->sendSuccess(
                 $submissionService->storeNote($submissionId, $attributes)
             );
--- a/fluentform/app/Http/Policies/FormPolicy.php
+++ b/fluentform/app/Http/Policies/FormPolicy.php
@@ -3,7 +3,7 @@
 namespace FluentFormAppHttpPolicies;

 use FluentFormAppModulesAclAcl;
-use FluentFormFrameworkRequestRequest;
+use FluentFormFrameworkHttpRequestRequest;
 use FluentFormFrameworkFoundationPolicy;

 class FormPolicy extends Policy
@@ -29,10 +29,26 @@
         return Acl::hasAnyFormPermission($request->get('form_id'));
     }

+    public function find(Request $request)
+    {
+        return Acl::hasPermission('fluentform_forms_manager', $request->get('form_id'));
+    }
+
+    public function delete(Request $request)
+    {
+        return Acl::hasPermission('fluentform_forms_manager', $request->get('form_id'));
+    }
+
+    public function integrationListComponent(Request $request)
+    {
+        return Acl::hasPermission('fluentform_forms_manager', $request->get('form_id'));
+    }
+
     public function updateModuleStatus(Request $request)
     {
         return Acl::hasPermission('fluentform_settings_manager', $request->get('form_id'));
     }
+
     public function updateIntegration(Request $request)
     {
         return Acl::hasPermission('fluentform_settings_manager', $request->get('form_id'));
--- a/fluentform/app/Http/Policies/GlobalSettingsPolicy.php
+++ b/fluentform/app/Http/Policies/GlobalSettingsPolicy.php
@@ -4,7 +4,7 @@

 use FluentFormAppModulesAclAcl;
 use FluentFormFrameworkFoundationPolicy;
-use FluentFormFrameworkRequestRequest;
+use FluentFormFrameworkHttpRequestRequest;

 class GlobalSettingsPolicy extends Policy
 {
--- a/fluentform/app/Http/Policies/PublicPolicy.php
+++ b/fluentform/app/Http/Policies/PublicPolicy.php
@@ -3,7 +3,7 @@
 namespace FluentFormAppHttpPolicies;


-use FluentFormFrameworkRequestRequest;
+use FluentFormFrameworkHttpRequestRequest;
 use FluentFormFrameworkFoundationPolicy;

 class PublicPolicy extends Policy
--- a/fluentform/app/Http/Policies/ReportPolicy.php
+++ b/fluentform/app/Http/Policies/ReportPolicy.php
@@ -3,7 +3,7 @@
 namespace FluentFormAppHttpPolicies;

 use FluentFormAppModulesAclAcl;
-use FluentFormFrameworkRequestRequest;
+use FluentFormFrameworkHttpRequestRequest;
 use FluentFormFrameworkFoundationPolicy;
 use FluentFormFrameworkSupportArr;

--- a/fluentform/app/Http/Policies/RoleManagerPolicy.php
+++ b/fluentform/app/Http/Policies/RoleManagerPolicy.php
@@ -4,7 +4,7 @@

 use FluentFormAppModulesAclAcl;
 use FluentFormFrameworkFoundationPolicy;
-use FluentFormFrameworkRequestRequest;
+use FluentFormFrameworkHttpRequestRequest;

 class RoleManagerPolicy extends Policy
 {
--- a/fluentform/app/Http/Policies/SubmissionPolicy.php
+++ b/fluentform/app/Http/Policies/SubmissionPolicy.php
@@ -2,8 +2,9 @@

 namespace FluentFormAppHttpPolicies;

+use FluentFormAppModelsSubmission;
 use FluentFormAppModulesAclAcl;
-use FluentFormFrameworkRequestRequest;
+use FluentFormFrameworkHttpRequestRequest;
 use FluentFormFrameworkFoundationPolicy;

 class SubmissionPolicy extends Policy
@@ -16,13 +17,19 @@
      */
     public function verifyRequest(Request $request)
     {
-
-        return Acl::hasPermission('fluentform_entries_viewer', $request->get('form_id'));
+        $formId = $this->resolveFormId($request);
+        return Acl::hasPermission('fluentform_entries_viewer', $formId);
     }

     public function handleBulkActions(Request $request)
     {
-        return Acl::hasPermission('fluentform_manage_entries', $request->get('form_id'));
+        $formId = $this->resolveFormId($request);
+        return Acl::hasPermission('fluentform_manage_entries', $formId);
+    }
+
+    public function store(Request $request)
+    {
+        return $this->handleBulkActions($request);
     }

     public function updateStatus(Request $request)
@@ -44,4 +51,31 @@
     {
         return $this->handleBulkActions($request);
     }
+
+    public function updateSubmissionUser(Request $request)
+    {
+        // Controller now uses entry_id from route as the mutation target,
+        // so authorization and mutation are always the same record.
+        $formId = $this->resolveFormId($request);
+        return Acl::hasPermission('fluentform_manage_entries', $formId);
+    }
+
+    /**
+     * Resolve the form_id for authorization.
+     * For entry-scoped routes, always derive from the entry record to prevent
+     * attackers from passing an allowed form_id while targeting another form's entry.
+     */
+    private function resolveFormId(Request $request)
+    {
+        $entryId = $request->get('entry_id');
+        if ($entryId) {
+            $submission = Submission::select('form_id')->find(intval($entryId));
+            if ($submission) {
+                return $submission->form_id;
+            }
+        }
+
+        $formId = $request->get('form_id');
+        return $formId ? intval($formId) : null;
+    }
 }
--- a/fluentform/app/Http/Routes/api.php
+++ b/fluentform/app/Http/Routes/api.php
@@ -1,5 +1,7 @@
 <?php

+defined('ABSPATH') or die;
+
 /**
  * @var $router FluentFormFrameworkHttpRouter
  */
--- a/fluentform/app/Http/Routes/routes.php
+++ b/fluentform/app/Http/Routes/routes.php
@@ -0,0 +1,5 @@
+<?php
+
+defined('ABSPATH') or die;
+
+require_once __DIR__ . "/api.php";
--- a/fluentform/app/Models/Entry.php
+++ b/fluentform/app/Models/Entry.php
@@ -65,7 +65,8 @@
         $search = Arr::get($attributes, 'search');
         $wheres = Arr::get($attributes, 'wheres');

-        $query = $this->orderBy('id', $attributes['sort_by'])
+        $sortBy = FluentFormAppHelpersHelper::sanitizeOrderValue(Arr::get($attributes, 'sort_by', 'DESC'));
+        $query = $this->orderBy('id', $sortBy)
             ->when($formId, function ($q) use ($formId) {
                 return $q->where('form_id', $formId);
             })
@@ -89,11 +90,13 @@
                     ->where('created_at', '<=', $endDate);
             })
             ->when($search, function ($q) use ($search) {
-                return $q->where(function ($q) use ($search) {
-                    return $q->where('id', 'LIKE', "%{$search}%")
-                        ->orWhere('response', 'LIKE', "%{$search}%")
-                        ->orWhere('status', 'LIKE', "%{$search}%")
-                        ->orWhere('created_at', 'LIKE', "%{$search}%");
+                global $wpdb;
+                $escaped = $wpdb->esc_like($search);
+                return $q->where(function ($q) use ($escaped) {
+                    return $q->where('id', 'LIKE', "%{$escaped}%")
+                        ->orWhere('response', 'LIKE', "%{$escaped}%")
+                        ->orWhere('status', 'LIKE', "%{$escaped}%")
+                        ->orWhere('created_at', 'LIKE', "%{$escaped}%");
                 });
             })
             ->when($wheres, function ($q) use ($wheres) {
@@ -184,21 +187,11 @@

         EntryDetails::whereIn('submission_id', $entryIds)->delete();

-        //delete the pro models this way for now
-        // todo: update wpFluent to the framework model
         try {
             if (PaymentHelper::hasPaymentSettings()) {
-                wpFluent()->table('fluentform_order_items')
-                    ->whereIn('submission_id', $entryIds)
-                    ->delete();
-
-                wpFluent()->table('fluentform_transactions')
-                    ->whereIn('submission_id', $entryIds)
-                    ->delete();
-
-                wpFluent()->table('fluentform_subscriptions')
-                    ->whereIn('submission_id', $entryIds)
-                    ->delete();
+                OrderItem::whereIn('submission_id', $entryIds)->delete();
+                Transaction::whereIn('submission_id', $entryIds)->delete();
+                Subscription::whereIn('submission_id', $entryIds)->delete();
             }

             wpFluent()->table('ff_scheduled_actions')
--- a/fluentform/app/Models/Form.php
+++ b/fluentform/app/Models/Form.php
@@ -88,6 +88,26 @@
         return $this->hasMany(Log::class, 'parent_source_id', 'id');
     }

+    /**
+     * A form has many transactions.
+     *
+     * @return FluentFormFrameworkDatabaseOrmRelationsHasMany
+     */
+    public function transactions()
+    {
+        return $this->hasMany(Transaction::class, 'form_id', 'id');
+    }
+
+    /**
+     * A form has many order items.
+     *
+     * @return FluentFormFrameworkDatabaseOrmRelationsHasMany
+     */
+    public function orderItems()
+    {
+        return $this->hasMany(OrderItem::class, 'form_id', 'id');
+    }
+
     public static function prepare($attributes = [])
     {
         $now = current_time('mysql');
@@ -256,17 +276,9 @@

         if (PaymentHelper::hasPaymentSettings()) {
             try {
-                wpFluent()->table('fluentform_order_items')
-                    ->where('form_id', $formId)
-                    ->delete();
-
-                wpFluent()->table('fluentform_transactions')
-                    ->where('form_id', $formId)
-                    ->delete();
-
-                wpFluent()->table('fluentform_subscriptions')
-                    ->where('form_id', $formId)
-                    ->delete();
+                OrderItem::where('form_id', $formId)->delete();
+                Transaction::where('form_id', $formId)->delete();
+                Subscription::where('form_id', $formId)->delete();
             } catch (Exception $e) {
             }
         }
--- a/fluentform/app/Models/Model.php
+++ b/fluentform/app/Models/Model.php
@@ -2,6 +2,7 @@

 namespace FluentFormAppModels;

+use DateTimeInterface;
 use FluentFormFrameworkDatabaseOrmModel as BaseModel;

 class Model extends BaseModel
@@ -14,6 +15,20 @@
     protected $guarded = ['id', 'ID'];

     /**
+     * Serialize dates to Y-m-d H:i:s format for backward compatibility.
+     *
+     * The framework v2 defaults to ISO 8601 (e.g. 2026-03-03T08:54:40+00:00)
+     * but existing JS code expects the simple Y-m-d H:i:s format.
+     *
+     * @param DateTimeInterface $date
+     * @return string
+     */
+    protected function serializeDate(DateTimeInterface $date)
+    {
+        return $date->format('Y-m-d H:i:s');
+    }
+
+    /**
      * Get the number of models to return per page.
      *
      * @return int
--- a/fluentform/app/Models/OrderItem.php
+++ b/fluentform/app/Models/OrderItem.php
@@ -0,0 +1,48 @@
+<?php
+
+namespace FluentFormAppModels;
+
+class OrderItem extends Model
+{
+    /**
+     * The table associated with the model.
+     *
+     * @var string
+     */
+    protected $table = 'fluentform_order_items';
+
+    /**
+     * An order item belongs to a form.
+     *
+     * @return FluentFormFrameworkDatabaseOrmRelationsBelongsTo
+     */
+    public function form()
+    {
+        return $this->belongsTo(Form::class, 'form_id', 'id');
+    }
+
+    /**
+     * An order item belongs to a submission.
+     *
+     * @return FluentFormFrameworkDatabaseOrmRelationsBelongsTo
+     */
+    public function submission()
+    {
+        return $this->belongsTo(Submission::class, 'submission_id', 'id');
+    }
+
+    public function scopeBySubmission($query, $submissionId)
+    {
+        return $query->where('submission_id', $submissionId);
+    }
+
+    public function scopeProducts($query)
+    {
+        return $query->where('type', '!=', 'discount');
+    }
+
+    public function scopeDiscounts($query)
+    {
+        return $query->where('type', 'discount');
+    }
+}
--- a/fluentform/app/Models/Submission.php
+++ b/fluentform/app/Models/Submission.php
@@ -66,6 +66,36 @@
         return $this->hasMany(EntryDetails::class, 'submission_id', 'id');
     }

+    /**
+     * A submission has many transactions.
+     *
+     * @return FluentFormFrameworkDatabaseOrmRelationsHasMany
+     */
+    public function transactions()
+    {
+        return $this->hasMany(Transaction::class, 'submission_id', 'id');
+    }
+
+    /**
+     * A submission has many subscriptions.
+     *
+     * @return FluentFormFrameworkDatabaseOrmRelationsHasMany
+     */
+    public function subscriptions()
+    {
+        return $this->hasMany(Subscription::class, 'submission_id', 'id');
+    }
+
+    /**
+     * A submission has many order items.
+     *
+     * @return FluentFormFrameworkDatabaseOrmRelationsHasMany
+     */
+    public function orderItems()
+    {
+        return $this->hasMany(OrderItem::class, 'submission_id', 'id');
+    }
+
     public function customQuery($attributes = [])
     {
         $entryType = Arr::get($attributes, 'entry_type');
@@ -83,7 +113,7 @@
         $startDate = Arr::get($dateRange, 0);
         $endDate = Arr::get($dateRange, 1);
         $search = Arr::get($attributes, 'search');
-        $sortBy = Arr::get($attributes, 'sort_by', 'DESC');
+        $sortBy = FluentFormAppHelpersHelper::sanitizeOrderValue(Arr::get($attributes, 'sort_by', 'DESC'));

         $wheres = [];
         $paymentStatuses = Arr::get($attributes, 'payment_statuses');
@@ -116,11 +146,13 @@
                     ->where('fluentform_submissions.created_at', '<=', $endDate);
             })
             ->when($search, function ($q) use ($search) {
-                return $q->where(function ($q) use ($search) {
-                    return $q->where('fluentform_submissions.id', 'LIKE', "%{$search}%")
-                        ->orWhere('response', 'LIKE', "%{$search}%")
-                        ->orWhere('fluentform_submissions.status', 'LIKE', "%{$search}%")
-                        ->orWhere('fluentform_submissions.created_at', 'LIKE', "%{$search}%");
+                global $wpdb;
+                $escaped = $wpdb->esc_like($search);
+                return $q->where(function ($q) use ($escaped) {
+                    return $q->where('fluentform_submissions.id', 'LIKE', "%{$escaped}%")
+                        ->orWhere('response', 'LIKE', "%{$escaped}%")
+                        ->orWhere('fluentform_submissions.status', 'LIKE', "%{$escaped}%")
+                        ->orWhere('fluentform_submissions.created_at', 'LIKE', "%{$escaped}%");
                 });
             })
             ->when($wheres, function ($q) use ($wheres) {
@@ -247,21 +279,11 @@

         EntryDetails::whereIn('submission_id', $submissionIds)->delete();

-        //delete  models this way for now
-        // todo: update wpFluent to the framework model
         try {
             if (PaymentHelper::hasPaymentSettings()) {
-                wpFluent()->table('fluentform_order_items')
-                    ->whereIn('submission_id', $submissionIds)
-                    ->delete();
-
-                wpFluent()->table('fluentform_transactions')
-                    ->whereIn('submission_id', $submissionIds)
-                    ->delete();
-
-                wpFluent()->table('fluentform_subscriptions')
-                    ->whereIn('submission_id', $submissionIds)
-                    ->delete();
+                OrderItem::whereIn('submission_id', $submissionIds)->delete();
+                Transaction::whereIn('submission_id', $submissionIds)->delete();
+                Subscription::whereIn('submission_id', $submissionIds)->delete();
             }

             wpFluent()->table('ff_scheduled_actions')
@@ -290,17 +312,21 @@
                 return $q->whereIn('form_id', $allowFormIds);
             })
             ->when($search, function ($q) use ($search){
-                return $q->orWhereHas('form', function ($q) use ($search) {
-                    return $q->orWhere('title', 'LIKE', "%{$search}%");
+                global $wpdb;
+                $escaped = $wpdb->esc_like($search);
+                return $q->orWhereHas('form', function ($q) use ($escaped) {
+                    return $q->orWhere('title', 'LIKE', "%{$escaped}%");
                 });
             })
             ->paginate()
             ->toArray();

+        $useHumanDate = apply_filters('fluentform/entries_human_date', false);
+
         foreach ($result['data'] as &$entry) {
             $entry['entry_url'] = admin_url('admin.php?page=fluent_forms&route=entries&form_id=' . $entry['form_id'] . '#/entries/' . $entry['id']);

-            if (apply_filters('fluentform/entries_human_date', false)) {
+            if ($useHumanDate) {
                 $entry['human_date'] = human_time_diff(strtotime($entry['created_at']), strtotime(current_time('mysql')));
             }
         }
--- a/fluentform/app/Models/Subscription.php
+++ b/fluentform/app/Models/Subscription.php
@@ -0,0 +1,68 @@
+<?php
+
+namespace FluentFormAppModels;
+
+class Subscription extends Model
+{
+    /**
+     * The table associated with the model.
+     *
+     * @var string
+     */
+    protected $table = 'fluentform_subscriptions';
+
+    /**
+     * A subscription belongs to a form.
+     *
+     * @return FluentFormFrameworkDatabaseOrmRelationsBelongsTo
+     */
+    public function form()
+    {
+        return $this->belongsTo(Form::class, 'form_id', 'id');
+    }
+
+    /**
+     * A subscription belongs to a submission.
+     *
+     * @return FluentFormFrameworkDatabaseOrmRelationsBelongsTo
+     */
+    public function submission()
+    {
+        return $this->belongsTo(Submission::class, 'submission_id', 'id');
+    }
+
+    /**
+     * A subscription has many transactions.
+     *
+     * @return FluentFormFrameworkDatabaseOrmRelationsHasMany
+     */
+    public function transactions()
+    {
+        return $this->hasMany(Transaction::class, 'subscription_id', 'id');
+    }
+
+    public function getOriginalPlanAttribute($value)
+    {
+        return maybe_unserialize($value);
+    }
+
+    public function getVendorResponseAttribute($value)
+    {
+        return maybe_unserialize($value);
+    }
+
+    public function scopeBySubmission($query, $submissionId)
+    {
+        return $query->where('submission_id', $submissionId);
+    }
+
+    public function scopeByVendorSubscriptionId($query, $vendorId)
+    {
+        return $query->where('vendor_subscription_id', $vendorId);
+    }
+
+    public function scopeActive($query)
+    {
+        return $query->where('status', 'active');
+    }
+}
--- a/fluentform/app/Models/Traits/PredefinedForms.php
+++ b/fluentform/app/Models/Traits/PredefinedForms.php
@@ -69,7 +69,7 @@
         return [

             'blank_form' => [
-                'screenshot' => fluentformMix('img/forms/blank.png'),
+                'screenshot' => fluentFormMix('img/forms/blank.png'),
                 'createable' => true,
                 'title'      => 'Blank Form',
                 'brief'      => 'Create a blank form',
@@ -80,7 +80,7 @@
             ],

             // 'login_form' => array(
-            //     'screenshot' => fluentformMix('img/forms/login_form.png'),
+            //     'screenshot' => fluentFormMix('img/forms/login_form.png'),
             //     'createable' => true,
             //     'title'      => 'Login Form',
             //     'tag'        => ["login", 'sign-in', 'signin'],
@@ -91,7 +91,7 @@
             // ),

             'basic_contact_form' => [
-                'screenshot' => fluentformMix('img/forms/form-layout.png'),
+                'screenshot' => fluentFormMix('img/forms/form-layout.png'),
                 'createable' => true,
                 'title'      => 'Contact Form',
                 'is_pro'     => false,
@@ -103,7 +103,7 @@
             ],

             'conversational' => [
-                'screenshot' => fluentformMix('img/forms/conversational.png'),
+                'screenshot' => fluentFormMix('img/forms/conversational.png'),
                 'createable' => true,
                 'title'      => 'Conversational Form',
                 'brief'      => 'Create Smart form UI',
@@ -115,7 +115,7 @@

             //form number : 84
             'newsletter_form' => [
-                'screenshot' => fluentformMix('img/forms/newsletter_form.png'),
+                'screenshot' => fluentFormMix('img/forms/newsletter_form.png'),
                 'createable' => true,
                 'title'      => 'Newsletter Form',
                 'is_pro'     => false,
@@ -127,7 +127,7 @@
             ],

             'support_form' => [
-                'screenshot' => fluentformMix('img/forms/support_form.png'),
+                'screenshot' => fluentFormMix('img/forms/support_form.png'),
                 'createable' => true,
                 'title'      => 'Support Form',
                 'brief'      => 'Using this support form users can ask questions.',
@@ -138,7 +138,7 @@
             ],

             'inline_subscription' => [
-                'screenshot' => fluentformMix('img/forms/inline_subscription.png'),
+                'screenshot' => fluentFormMix('img/forms/inline_subscription.png'),
                 'createable' => true,
                 'title'      => 'Optin Form',
                 'brief'      => 'Create inline optin form.',
@@ -148,7 +148,7 @@
             ],

             'polling_form' => [
-                'screenshot' => fluentformMix('img/forms/polling_form.png'),
+                'screenshot' => fluentFormMix('img/forms/polling_form.png'),
                 'createable' => true,
                 'title'      => 'Polling Form',
                 'brief'      => 'A sample polling form to get user opinion from your scheduled time.',
@@ -160,7 +160,7 @@

             //form number : 55
             'product_order_form' => [
-                'screenshot' => fluentformMix('img/forms/product_order_form.png'),
+                'screenshot' => fluentFormMix('img/forms/product_order_form.png'),
                 'createable' => true,
                 'title'      => 'Product Order Form',
                 'is_pro'     => true,
@@ -172,7 +172,7 @@

             //form number : 58
             'online_service_order_form' => [
-                'screenshot' => fluentformMix('img/forms/online_service_order_form.png'),
+                'screenshot' => fluentFormMix('img/forms/online_service_order_form.png'),
                 'createable' => true,
                 'title'      => 'Online Service Order Form',
                 'is_pro'     => false,
@@ -184,7 +184,7 @@

             //form number : 60
             'payment_donation_form' => [
-                'screenshot' => fluentformMix('img/forms/payment_donation_form.png'),
+                'screenshot' => fluentFormMix('img/forms/payment_donation_form.png'),
                 'createable' => true,
                 'title'      => 'Online Donation Form',
                 'is_pro'     => true,
@@ -196,7 +196,7 @@

             //form number : 61
             'order_bump_form' => [
-                'screenshot' => fluentformMix('img/forms/order_bump_form.png'),
+                'screenshot' => fluentFormMix('img/forms/order_bump_form.png'),
                 'createable' => true,
                 'title'      => 'Order Bump Example Form',
                 'is_pro'     => true,
@@ -208,7 +208,7 @@

             //form number : 62
             'student_survey_form' => [
-                'screenshot' => fluentformMix('img/forms/student_survey_form.png'),
+                'screenshot' => fluentFormMix('img/forms/student_survey_form.png'),
                 'createable' => true,
                 'title'      => 'Student Survey Form',
                 'is_pro'     => true,
@@ -221,7 +221,7 @@

             //form number : 63
             'classroom_observation_form' => [
-                'screenshot' => fluentformMix('img/forms/classroom_observation_form.png'),
+                'screenshot' => fluentFormMix('img/forms/classroom_observation_form.png'),
                 'createable' => true,
                 'title'      => 'Classroom Observation Form',
                 'is_pro'     => true,
@@ -234,7 +234,7 @@

             //form number : 64
             'client_satisfaction_survey_form' => [
-                'screenshot' => fluentformMix('img/forms/client_satisfaction_survey_form.png'),
+                'screenshot' => fluentFormMix('img/forms/client_satisfaction_survey_form.png'),
                 'createable' => true,
                 'title'      => 'Client Satisfaction Survey Form',
                 'is_pro'     => false,
@@ -247,7 +247,7 @@

             //form number : 67
             'customer_complaint_form' => [
-                'screenshot' => fluentformMix('img/forms/customer_complaint_form.png'),
+                'screenshot' => fluentFormMix('img/forms/customer_complaint_form.png'),
                 'createable' => true,
                 'title'      => 'Customer Complaint Form',
                 'is_pro'     => false,
@@ -261,7 +261,7 @@

             //form number : 68
             'course_evaluation_survey_form' => [
-                'screenshot' => fluentformMix('img/forms/course_evaluation_survey_form.png'),
+                'screenshot' => fluentFormMix('img/forms/course_evaluation_survey_form.png'),
                 'createable' => true,
                 'title'      => 'Course Evaluation Survey form',
                 'is_pro'     => true,
@@ -275,7 +275,7 @@

             //form number : 70
             'market_research_survey_form' => [
-                'screenshot' => fluentformMix('img/forms/market_research_survey_form.png'),
+                'screenshot' => fluentFormMix('img/forms/market_research_survey_form.png'),
                 'createable' => true,
                 'title'      => 'Market Research Survey Form',
                 'is_pro'     => true,
@@ -288,7 +288,7 @@

             //form number : 71
             'database_management_help_request_from' => [
-                'screenshot' => fluentformMix('img/forms/database_management_help_request_from.png'),
+                'screenshot' => fluentFormMix('img/forms/database_management_help_request_from.png'),
                 'createable' => true,
                 'title'      => 'Database Management Help Request from',
                 'is_pro'     => true,
@@ -301,7 +301,7 @@

             //form number : 72
             'university_enrollment_form' => [
-                'screenshot' => fluentformMix('img/forms/university_enrollment_form.png'),
+                'screenshot' => fluentFormMix('img/forms/university_enrollment_form.png'),
                 'createable' => true,
                 'title'      => 'University Enrollment Form',
                 'is_pro'     => true,
@@ -315,7 +315,7 @@

             //form number : 74
             'volunteer_signup_form' => [
-                'screenshot' => fluentformMix('img/forms/volunteer_signup_form.png'),
+                'screenshot' => fluentFormMix('img/forms/volunteer_signup_form.png'),
                 'createable' => true,
                 'title'      => 'Volunteer sign up form',
                 'is_pro'     => false,
@@ -328,7 +328,7 @@

             //form number : 76
             'donation_form' => [
-                'screenshot' => fluentformMix('img/forms/donation_form.png'),
+                'screenshot' => fluentFormMix('img/forms/donation_form.png'),
                 'createable' => true,
                 'title'      => 'Donation Form',
                 'is_pro'     => false,
@@ -341,7 +341,7 @@

             //form number : 78
             'graphic_designer_contact_form' => [
-                'screenshot' => fluentformMix('img/forms/graphic_designer_contact_form.png'),
+                'screenshot' => fluentFormMix('img/forms/graphic_designer_contact_form.png'),
                 'createable' => true,
                 'title'      => 'Graphic Designer Contact Form',
                 'is_pro'     => false,
@@ -354,7 +354,7 @@

             //form number : 79
             'multi_file_upload_form' => [
-                'screenshot' => fluentformMix('img/forms/multi_file_upload_form.png'),
+                'screenshot' => fluentFormMix('img/forms/multi_file_upload_form.png'),
                 'createable' => true,
                 'title'      => 'Multi file upload form',
                 'is_pro'     => true,
@@ -367,7 +367,7 @@

             //form number : 81
             'highschool_transcript_request_from' => [
-                'screenshot' => fluentformMix('img/forms/highschool_transcript_request_from.png'),
+                'screenshot' => fluentFormMix('img/forms/highschool_transcript_request_from.png'),
                 'createable' => true,
                 'title'      => 'High School Transcript Request From',
                 'is_pro'     => true,
@@ -380,7 +380,7 @@

             //form number : 82
             'partnership_application_form' => [
-                'screenshot' => fluentformMix('img/forms/partnership_application_form.png'),
+                'screenshot' => fluentFormMix('img/forms/partnership_application_form.png'),
                 'createable' => true,
                 'title'      => 'Partnership application form',
                 'is_pro'     => true,
@@ -393,7 +393,7 @@

             //form number : 83
             'employee_evaluation_form' => [
-                'screenshot' => fluentformMix('img/forms/employee_evaluation_form.png'),
+                'screenshot' => fluentFormMix('img/forms/employee_evaluation_form.png'),
                 'createable' => true,
                 'title'      => 'Employee Evaluation Form',
                 'is_pro'     => true,
@@ -406,7 +406,7 @@

             //form number : 85
             'party_invite_form' => [
-                'screenshot' => fluentformMix('img/forms/party_invite_form.png'),
+                'screenshot' => fluentFormMix('img/forms/party_invite_form.png'),
                 'createable' =

ModSecurity Protection Against This CVE

Here you will find our ModSecurity compatible rule to protect against this particular CVE.

ModSecurity
SecRule REQUEST_URI "@contains /wp-admin/admin-ajax.php" "id:20261994,phase:2,deny,status:403,msg:'CVE-2026-5396 Fluent Forms Authorization Bypass Attempt',chain"
SecRule ARGS:action "@streq fluentform-change-entry-status" "chain"
SecRule ARGS:form_id "@unconditionalMatch" ""

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