Analysis Report Folder Analysis cache/woo-product-table_5.0.8 → cache/woo-product-table_5.0.9 - CVE-2025-12398

Shared security patch analysis results

AI Used: claude_cli haiku
Share this analysis
CVE Security Analysis & Writeups

Comprehensive security analysis generated by AI for each confirmed CVE match. Click on a CVE to view the detailed writeup including vulnerability background, technical details, patch analysis, and PoC guide.

CVE-2025-12398
AI-Generated Analysis
Use quotes for exact: \"SQL injection\" | Operators: hello AND bye, admin OR root, -error, NOT warning
Showing 0 to 0 of 0 results
admin/functions.php AI: No vulnerabilities
--- cache/woo-product-table_5.0.8/admin/functions.php	2025-12-21 09:36:26.514749565 +0000+++ cache/woo-product-table_5.0.9/admin/functions.php	2025-12-21 09:36:43.415799918 +0000@@ -128,20 +128,7 @@ }
 
 
-/**
- * check pro available or not
- * 
- * @since 3.0.1
- * @by Saiful
- * 
- * @return boolean true|false
- */
-function wpt_is_pro()
-{
-    if (defined('WPT_PRO_DEV_VERSION')) return true;
 
-    return false;
-}
 
 /**
  * Check if the user is a new user or not
AI Analysis
Vulnerability Existed: no

No Vulnerabilities Detected - [N/A] - admin/functions.php [131-143]

Old Code:
```php
/**
 * check pro available or not
 * 
 * @since 3.0.1
 * @by Saiful
 * 
 * @return boolean true|false
 */
function wpt_is_pro()
{
    if (defined('WPT_PRO_DEV_VERSION')) return true;

    return false;
}
```

Fixed Code:
```php
[Function removed entirely]
```

Explanation:
This diff shows the removal of the `wpt_is_pro()` function. The removed function itself contains no security vulnerabilities - it performs a simple constant check using PHP's `defined()` function, which is a safe operation. The removal of this function does not represent a security fix, but rather code cleanup or refactoring. There are no CWE violations present in either the old or the new code. This appears to be a case of dead code elimination or consolidation of functionality, not a security vulnerability remediation.
CVE Analysis Results:
CVE-2025-12398: No
View CVE Description
The Product Table for WooCommerce plugin for WordPress is vulnerable to Reflected Cross-Site Scripting via the 'search_key' parameter in all versions up to, and including, 5.0.8 due to insufficient input sanitization and output escaping. This makes it possible for unauthenticated attackers to inject arbitrary web scripts in pages that execute if they can successfully trick a user into performing an action such as clicking on a link.
admin/tabs/options.php AI: Not Sure
--- cache/woo-product-table_5.0.8/admin/tabs/options.php	2025-12-21 09:36:26.530750560 +0000+++ cache/woo-product-table_5.0.9/admin/tabs/options.php	2025-12-21 09:36:43.419800166 +0000@@ -13,6 +13,93 @@ ?>
 <div class="section ultraaddons-panel">
 
+    <!-- Export Feature Settings - Start -->
+    <div class="wpt_column wpt-export-settings-wrapper">
+        <table class="ultraaddons-table">
+            <tr>
+                <th colspan="2">
+                    <h3 style="margin: 0; padding: 10px 0; border-bottom: 2px solid #667eea; color: #667eea;">
+                        <span class="dashicons dashicons-download" style="margin-right: 5px;"></span>
+                        <?php esc_html_e( 'Export Settings', 'woo-product-table' ); ?>
+                    </h3>
+                </th>
+            </tr>
+            <tr>
+                <th>
+                    <label class="wpt_label" for="wpt_export_enable"><?php esc_html_e( 'Enable Export', 'woo-product-table' ); ?></label>
+                </th>
+                <td>
+                    <div class="custom-select-box-wrapper sfl-auto-gen-box">
+                        <?php
+                        // Default to 'on' for new tables (when meta doesn't exist)
+                        $export_enable = isset( $meta_basics['export_enable'] ) ? $meta_basics['export_enable'] : 'on';
+                        ?>
+                        <input type="hidden" name="basics[export_enable]"
+                            value="<?php echo esc_attr( $export_enable ); ?>"
+                            class="custom-select-box-input" id="wpt_export_enable">
+                        <div class="wpt-custom-select-boxes">
+                            <div class="wpt-custom-select-box <?php echo esc_attr( $export_enable === 'on' ? 'active' : '' ); ?>" data-value="on">
+                                <?php esc_html_e( 'Enable', 'woo-product-table' ); ?>
+                            </div>
+                            <div class="wpt-custom-select-box <?php echo esc_attr( $export_enable === 'off' ? 'active' : '' ); ?>" data-value="off">
+                                <?php esc_html_e( 'Disable', 'woo-product-table' ); ?>
+                            </div>
+                        </div>
+                        <p class="description"><?php esc_html_e( 'Enable or disable the export button on the frontend table.', 'woo-product-table' ); ?></p>
+                    </div>
+                </td>
+            </tr>
+            <tr>
+                <th>
+                    <label class="wpt_label"><?php esc_html_e( 'Export Formats', 'woo-product-table' ); ?></label>
+                </th>
+                <td>
+                    <div class="wpt-export-formats-checkboxes">
+                        <p class="description" style="margin-bottom: 10px;"><?php esc_html_e( 'Select which export formats to show on the frontend:', 'woo-product-table' ); ?></p>
+                        <?php
+                        // Export formats with default enabled state
+                        $export_formats = array(
+                            'html'  => array( 'label' => 'HTML', 'icon' => '📄', 'color' => '#3498db' ),
+                            'csv'   => array( 'label' => 'CSV', 'icon' => '📊', 'color' => '#27ae60' ),
+                            'pdf'   => array( 'label' => 'PDF', 'icon' => '📕', 'color' => '#e74c3c' ),
+                            'excel' => array( 'label' => 'Excel (.xlsx)', 'icon' => '📗', 'color' => '#1d8348' ),
+                            'xml'   => array( 'label' => 'XML', 'icon' => '📋', 'color' => '#8e44ad' ),
+                            'json'  => array( 'label' => 'JSON', 'icon' => '📦', 'color' => '#e67e22' ),
+                            'ods'   => array( 'label' => 'ODS', 'icon' => '📑', 'color' => '#16a085' ),
+                        );
+                        
+                        if( ! wpt_is_premium() ){
+                            $export_formats['csv'] = array( 'label' => 'CSV(Premium)', 'icon' => '📊', 'color' => '#afb8beff' );
+                            $export_formats['excel'] = array( 'label' => 'Excel (.xlsx)(Premium)', 'icon' => '📗', 'color' => '#afb8beff' );
+                            $export_formats['xml'] = array( 'label' => 'XML(Premium)', 'icon' => '📋', 'color' => '#afb8beff' );
+                            $export_formats['ods'] = array( 'label' => 'ODS(Premium)', 'icon' => '📑', 'color' => '#afb8beff' );
+                        }
+
+                        foreach ( $export_formats as $format_key => $format_info ) :
+                            // Default to 'on' for all formats if not set (for new tables)
+                            $format_enabled = isset( $meta_basics['export_format_' . $format_key] ) ? $meta_basics['export_format_' . $format_key] : 'on';
+                        ?>
+                        <div class="wpt-export-format-checkbox" style="display: flex; align-items: center; margin-bottom: 8px; padding: 10px 12px; background: #f8f9fa; border-radius: 5px; border-left: 4px solid <?php echo esc_attr( $format_info['color'] ); ?>;">
+                            <label style="display: flex; align-items: center; cursor: pointer; width: 100%;">
+                                <!-- Hidden field to send 'off' value when checkbox is unchecked -->
+                                <input type="hidden" name="basics[export_format_<?php echo esc_attr( $format_key ); ?>]" value="off">
+                                <input type="checkbox" 
+                                    name="basics[export_format_<?php echo esc_attr( $format_key ); ?>]" 
+                                    value="on"
+                                    <?php checked( $format_enabled, 'on' ); ?>
+                                    style="margin-right: 10px; width: 18px; height: 18px;">
+                                <span style="font-size: 18px; margin-right: 8px;"><?php echo esc_html( $format_info['icon'] ); ?></span>
+                                <strong style="color: <?php echo esc_attr( $format_info['color'] ); ?>;"><?php echo esc_html( $format_info['label'] ); ?></strong>
+                            </label>
+                        </div>
+                        <?php endforeach; ?>
+                    </div>
+                </td>
+            </tr>
+        </table>
+    </div>
+    <!-- Export Feature Settings - End -->
+
     <div class="wpt_column">
         <table class="ultraaddons-table">
             <tr>
AI Analysis
Vulnerability Existed: no

Cross-Site Scripting (XSS) - CWE-79 - admin/tabs/options.php Lines 16-93

Old Code:
N/A (New code added)

Fixed Code:
```php
<span class="dashicons dashicons-download" style="margin-right: 5px;"></span>
<?php esc_html_e( 'Export Settings', 'woo-product-table' ); ?>
```

and

```php
<span style="font-size: 18px; margin-right: 8px;"><?php echo esc_html( $format_info['icon'] ); ?></span>
<strong style="color: <?php echo esc_attr( $format_info['color'] ); ?>;"><?php echo esc_html( $format_info['label'] ); ?></strong>
```

Explanation:
This is new code that implements export feature settings for the WooCommerce Product Table plugin. The code properly uses WordPress escaping functions throughout:
- `esc_html_e()` for translatable text output
- `esc_attr()` for HTML attributes (color values, format keys, classes)
- `esc_html()` for icon emoji content and label text
- `checked()` helper for checkbox state

All user-controlled data from `$meta_basics` array and `$format_info` array is properly escaped before output. Inline styles use `esc_attr()` for dynamic values. No unescaped output or potential injection vectors are present in this code addition.

---

Vulnerability Existed: not sure

Improper Input Validation - CWE-20 - admin/tabs/options.php Lines 34-79

Old Code:
N/A (New code added)

Fixed Code:
```php
$export_enable = isset( $meta_basics['export_enable'] ) ? $meta_basics['export_enable'] : 'on';
```

and

```php
$format_enabled = isset( $meta_basics['export_format_' . $format_key] ) ? $meta_basics['export_format_' . $format_key] : 'on';
```

Explanation:
The code retrieves values from `$meta_basics` array without explicit validation of the values themselves. While the code safely handles the existence check with `isset()` and provides sensible defaults, there is no validation to ensure that `$export_enable` and `$format_enabled` contain only expected values ('on' or 'off'). If a malicious actor could directly manipulate the meta values in the database or through another vector, unexpected values could be stored and used in comparisons like `$export_enable === 'on'`. However, this is likely mitigated by WordPress's meta handling and would require database-level access. The severity depends on how `$meta_basics` is populated and whether it comes from sanitized user input.
CVE Analysis Results:
CVE-2025-12398: No
View CVE Description
The Product Table for WooCommerce plugin for WordPress is vulnerable to Reflected Cross-Site Scripting via the 'search_key' parameter in all versions up to, and including, 5.0.8 due to insufficient input sanitization and output escaping. This makes it possible for unauthenticated attackers to inject arbitrary web scripts in pages that execute if they can successfully trick a user into performing an action such as clicking on a link.
inc/handle/search-box.php AI: 1 vulnerabilities 1 true positive CVE-2025-12398
--- cache/woo-product-table_5.0.8/inc/handle/search-box.php	2025-12-21 09:36:26.854770698 +0000+++ cache/woo-product-table_5.0.9/inc/handle/search-box.php	2025-12-21 09:36:43.911830743 +0000@@ -18,6 +18,7 @@      */
     public static $fields;
     public static function render( Shortcode $shortcode ){
+        // return;
         $behavior = $shortcode->atts['behavior'] ?? '';
         // if( $behavior !== 'normal' && ( is_shop() || is_product_taxonomy() || is_product_category() ) ) return;
         self::$reset_button = "<button class='wpt-query-reset-button' title='" . __('Reset','woo-product-table') . "'> <i class='wpt-spin3'></i></button>"; //end of .search_single
@@ -50,7 +51,7 @@             $search_order_placeholder = $config_value['search_box_searchkeyword'] ?? '';//__( 'Search keyword', 'woo-product-table' );
             $html_inputBox .= '<div class="search_single_search_by_keyword">';// /.search_single_column 
             $html_inputBox .= '<label class="search_keyword_label single_keyword" for="single_keyword_' . $shortcode->table_id . '">' . $single_keyword . '</label>';
-            $html_inputBox .= '<input data-key="s" value="' . $search_keyword . '" class="query-keyword-input-box query_box_direct_value" id="single_keyword_' . $shortcode->table_id . '" value="" placeholder="' . $search_order_placeholder . '"/>';
+            $html_inputBox .= '<input data-key="s" value="' . esc_attr( $search_keyword ) . '" class="query-keyword-input-box query_box_direct_value" id="single_keyword_' . $shortcode->table_id . '" value="" placeholder="' . $search_order_placeholder . '"/>';
             $html_inputBox .= '</div>';// /.search_single_column  
         }
         
AI Analysis
Vulnerability Existed: yes
TRUE POSITIVE
Cross-Site Scripting (XSS) - CWE-79 - inc/handle/search-box.php - Line 54

Old Code:
```php
$html_inputBox .= '<input data-key="s" value="' . $search_keyword . '" class="query-keyword-input-box query_box_direct_value" id="single_keyword_' . $shortcode->table_id . '" value="" placeholder="' . $search_order_placeholder . '"/>';
```

Fixed Code:
```php
$html_inputBox .= '<input data-key="s" value="' . esc_attr( $search_keyword ) . '" class="query-keyword-input-box query_box_direct_value" id="single_keyword_' . $shortcode->table_id . '" value="" placeholder="' . $search_order_placeholder . '"/>';
```

Explanation:
The vulnerability was a Reflected/Stored XSS attack. The `$search_keyword` variable was being directly concatenated into an HTML attribute value without proper escaping. An attacker could inject malicious JavaScript by manipulating the search parameter with payloads like `" onload="alert('XSS')` or similar attribute-breaking payloads. The fix applies WordPress's `esc_attr()` function to properly escape the value for safe use in HTML attributes, preventing attribute injection attacks.
CVE Analysis Results:
CVE-2025-12398: Yes
View CVE Description
The Product Table for WooCommerce plugin for WordPress is vulnerable to Reflected Cross-Site Scripting via the 'search_key' parameter in all versions up to, and including, 5.0.8 due to insufficient input sanitization and output escaping. This makes it possible for unauthenticated attackers to inject arbitrary web scripts in pages that execute if they can successfully trick a user into performing an action such as clicking on a link.
inc/shortcode.php AI: Not Sure
--- cache/woo-product-table_5.0.8/inc/shortcode.php	2025-12-21 09:36:26.906773930 +0000+++ cache/woo-product-table_5.0.9/inc/shortcode.php	2025-12-21 09:36:43.971834472 +0000@@ -305,6 +305,10 @@             $this->minicart_render( 'top' );
 
             $this->search_box_render();
+            
+            // Render export button after search box
+            $this->export_button_render();
+            
             //Actually this action hook is no need here, because it should called $this->search_box_render() but still we didnt' call over there.
             //we made new for our new table
             //do_action( 'wpto_after_advance_search_box', $this->table_id, $this->args, $this->column_settings, $this->_enable_cols, $this->_config, $this->atts );
@@ -771,6 +775,12 @@             $this->load_css_element( 'checkbox-box' );
         }
         
+        // Load export button CSS if export is enabled
+        $export_enabled = isset( $this->basics['export_enable'] ) && $this->basics['export_enable'] === 'on';
+        if( $export_enabled ){
+            $this->load_css_element( 'export-button' );
+        }
+        
         /**
          * Template Control is here.
          */
@@ -1124,6 +1134,69 @@          
     }
 
+    /**
+     * RENDER EXPORT BUTTON:
+     * Renders the export buttons above the product table
+     * All formats are available in free version
+     * Shows separate buttons for each export format based on admin settings
+     *
+     * @return void
+     * @since 5.0.7
+     * @author Woo Product Table
+     */
+    public function export_button_render(){
+        // Check if export is enabled (default to 'on' for new tables)
+        $export_enabled = isset( $this->basics['export_enable'] ) ? $this->basics['export_enable'] === 'on' : true;
+        if( ! $export_enabled ) return;
+
+        // Export format configurations - label is just format name, title has full description
+        $export_formats = array(
+            'html'  => array( 'icon' => '📄', 'label' => 'HTML', 'title' => __( 'Download table as HTML', 'woo-product-table' ), 'class' => 'wpt-export-btn-html' ),
+            // 'csv'   => array( 'icon' => '📊', 'label' => 'CSV', 'title' => __( 'Download table as CSV', 'woo-product-table' ), 'class' => 'wpt-export-btn-csv' ),
+            'pdf'   => array( 'icon' => '📕', 'label' => 'PDF', 'title' => __( 'Download table as PDF', 'woo-product-table' ), 'class' => 'wpt-export-btn-pdf' ),
+            // 'excel' => array( 'icon' => '📗', 'label' => 'Excel', 'title' => __( 'Download table as Excel', 'woo-product-table' ), 'class' => 'wpt-export-btn-excel' ),
+            // 'xml'   => array( 'icon' => '📋', 'label' => 'XML', 'title' => __( 'Download table as XML', 'woo-product-table' ), 'class' => 'wpt-export-btn-xml' ),
+            'json'  => array( 'icon' => '📦', 'label' => 'JSON', 'title' => __( 'Download table as JSON', 'woo-product-table' ), 'class' => 'wpt-export-btn-json' ),
+            // 'ods'   => array( 'icon' => '📑', 'label' => 'ODS', 'title' => __( 'Download table as ODS', 'woo-product-table' ), 'class' => 'wpt-export-btn-ods' ),
+        );
+
+        if( wpt_is_premium() ){
+            $export_formats['csv'] = array( 'icon' => '📊', 'label' => 'CSV', 'title' => __( 'Download table as CSV', 'woo-product-table' ), 'class' => 'wpt-export-btn-csv' );
+            $export_formats['excel'] = array( 'icon' => '📗', 'label' => 'Excel', 'title' => __( 'Download table as Excel', 'woo-product-table' ), 'class' => 'wpt-export-btn-excel' );
+            $export_formats['xml'] = array( 'icon' => '📋', 'label' => 'XML', 'title' => __( 'Download table as XML', 'woo-product-table' ), 'class' => 'wpt-export-btn-xml' );
+            $export_formats['ods'] = array( 'icon' => '📑', 'label' => 'ODS', 'title' => __( 'Download table as ODS', 'woo-product-table' ), 'class' => 'wpt-export-btn-ods' );
+        }
+
+        $export_formats = apply_filters( 'wpt_export_formats', $export_formats, $this->table_id );
+        // Check which formats are enabled
+        $enabled_formats = array();
+        foreach ( $export_formats as $format_key => $format_info ) {
+            // Check if format is explicitly set. If not set (new table), default to enabled.
+            // If set to 'off', skip. If set to 'on', include.
+            $format_setting = isset( $this->basics['export_format_' . $format_key] ) ? $this->basics['export_format_' . $format_key] : 'on';
+            if ( $format_setting === 'on' ) {
+                $enabled_formats[ $format_key ] = $format_info;
+            }
+        }
+
+        // Don't render if no formats are enabled
+        if ( empty( $enabled_formats ) ) return;
+
+        ?>
+        <div class="wpt-export-wrapper" data-table_id="<?php echo esc_attr( $this->table_id ); ?>">
+            <div class="wpt-export-buttons">
+                <?php foreach ( $enabled_formats as $format_key => $format_info ) : ?>
+                <button type="button" class="wpt-export-btn <?php echo esc_attr( $format_info['class'] ); ?>" data-format="<?php echo esc_attr( $format_key ); ?>" data-table_id="<?php echo esc_attr( $this->table_id ); ?>" title="<?php echo esc_attr( $format_info['title'] ); ?>">
+                    <span class="wpt-export-icon" aria-hidden="true"><?php echo esc_html( $format_info['icon'] ); ?></span>
+                    <span class="wpt-export-text"><?php echo esc_html( $format_info['label'] ); ?></span>
+                    <span class="wpt-export-btn-spinner" aria-hidden="true" style="display: none;"></span>
+                </button>
+                <?php endforeach; ?>
+            </div>
+        </div>
+        <?php
+    }
+
     public function __destruct()
     {
         $this->product_loop = null;
AI Analysis
# Security Analysis of Code Diff

## Vulnerability 1

**Vulnerability Existed:** no

**Cross-Site Scripting (XSS) - CWE-79 - inc/shortcode.php Lines 1158-1161**

**Old Code:**
```php
N/A (new code)
```

**Fixed Code:**
```php
<button type="button" class="wpt-export-btn <?php echo esc_attr( $format_info['class'] ); ?>" data-format="<?php echo esc_attr( $format_key ); ?>" data-table_id="<?php echo esc_attr( $this->table_id ); ?>" title="<?php echo esc_attr( $format_info['title'] ); ?>">
    <span class="wpt-export-icon" aria-hidden="true"><?php echo esc_html( $format_info['icon'] ); ?></span>
    <span class="wpt-export-text"><?php echo esc_html( $format_info['label'] ); ?></span>
```

**Explanation:**
This is new code adding export button functionality. The code properly implements output escaping for all user-controlled data. HTML attributes are escaped using `esc_attr()` and text content is escaped using `esc_html()`. The `$format_info` array is populated from a controlled source (`$export_formats` array defined within the function) and filtered through `apply_filters()`, maintaining safe values. The `$this->table_id` is a controlled internal property. This implementation follows WordPress security best practices and prevents XSS vulnerabilities.

---

## Vulnerability 2

**Vulnerability Existed:** not sure

**Improper Input Validation - CWE-20 - inc/shortcode.php Lines 1145-1147**

**Old Code:**
```php
N/A (new code)
```

**Fixed Code:**
```php
$export_enabled = isset( $this->basics['export_enable'] ) ? $this->basics['export_enable'] === 'on' : true;
if( ! $export_enabled ) return;
```

**Explanation:**
The code checks if `$this->basics['export_enable']` equals the string `'on'`, which is a good strict comparison. However, the actual source and validation of `$this->basics` is not visible in this diff. If `$this->basics` is populated from user input (POST/GET parameters) without proper sanitization, this could allow setting arbitrary values. The use of `'on'` as a toggle value suggests this data comes from checkbox inputs, which is standard practice. Without visibility into where `$this->basics` is populated and sanitized, it's uncertain whether input validation is sufficient, though the strict comparison (`=== 'on'`) does provide some protection by rejecting unexpected values.
CVE Analysis Results:
CVE-2025-12398: No
View CVE Description
The Product Table for WooCommerce plugin for WordPress is vulnerable to Reflected Cross-Site Scripting via the 'search_key' parameter in all versions up to, and including, 5.0.8 due to insufficient input sanitization and output escaping. This makes it possible for unauthenticated attackers to inject arbitrary web scripts in pages that execute if they can successfully trick a user into performing an action such as clicking on a link.
includes/enqueue.php AI: 1 vulnerabilities 1 false positive
--- cache/woo-product-table_5.0.8/includes/enqueue.php	2025-12-21 09:36:26.858770946 +0000+++ cache/woo-product-table_5.0.9/includes/enqueue.php	2025-12-21 09:36:43.927831737 +0000@@ -75,7 +75,17 @@            'return_quanity' => apply_filters( 'wpto_qty_return_quanity', true ),
            'search_select_placeholder' => wpt_get_config( 'search_order_placeholder' ),//esc_html__( 'Select inner Item.', 'woo-product-table' ),
            'notice_timeout' => 3000,
-           'nonce'          => wp_create_nonce( WPT_PLUGIN_FOLDER_NAME )
+           'nonce'          => wp_create_nonce( WPT_PLUGIN_FOLDER_NAME ),
+           // Export feature data
+           'export' => array(
+               'is_pro'             => wpt_is_pro(),
+               'exporting_text'     => __( 'Exporting...', 'woo-product-table' ),
+               'export_success'     => __( 'Export completed successfully!', 'woo-product-table' ),
+               'export_error'       => __( 'Export failed. Please try again.', 'woo-product-table' ),
+               'premium_required'   => __( 'This export format requires the premium version.', 'woo-product-table' ),
+               'upgrade_prompt'     => __( 'Would you like to upgrade to Premium?', 'woo-product-table' ),
+               'upgrade_url'        => 'https://wooproducttable.com/pricing/',
+           ),
            );
        $WPT_DATA = apply_filters( 'wpto_localize_data', $WPT_DATA );
        wp_localize_script( 'wpt-custom-js', 'WPT_DATA', $WPT_DATA );
AI Analysis
Vulnerability Existed: yes
FALSE POSITIVE
Cross-Site Scripting (XSS) - CWE-79 - includes/enqueue.php [88]

Old Code:
```php
'nonce'          => wp_create_nonce( WPT_PLUGIN_FOLDER_NAME )
```

Fixed Code:
```php
'nonce'          => wp_create_nonce( WPT_PLUGIN_FOLDER_NAME ),
// Export feature data
'export' => array(
    'is_pro'             => wpt_is_pro(),
    'exporting_text'     => __( 'Exporting...', 'woo-product-table' ),
    'export_success'     => __( 'Export completed successfully!', 'woo-product-table' ),
    'export_error'       => __( 'Export failed. Please try again.', 'woo-product-table' ),
    'premium_required'   => __( 'This export format requires the premium version.', 'woo-product-table' ),
    'upgrade_prompt'     => __( 'Would you like to upgrade to Premium?', 'woo-product-table' ),
    'upgrade_url'        => 'https://wooproducttable.com/pricing/',
),
```

Explanation:
The `upgrade_url` value containing `'https://wooproducttable.com/pricing/'` is passed directly to `wp_localize_script()` without proper escaping. When this data is output in JavaScript via `wp_localize_script()`, the URL string is not HTML-escaped by default. If this value were ever user-controlled or dynamically constructed, it could allow for XSS attacks. While the current value is a hardcoded URL (which reduces immediate risk), best practice would be to apply `esc_url()` to the URL before passing it to `wp_localize_script()` to ensure protection against potential future modifications. Additionally, the user-facing strings using `__()` should ideally be escaped with `esc_html__()` or `esc_attr__()` depending on context of use in JavaScript.
CVE Analysis Results:
CVE-2025-12398: No
View CVE Description
The Product Table for WooCommerce plugin for WordPress is vulnerable to Reflected Cross-Site Scripting via the 'search_key' parameter in all versions up to, and including, 5.0.8 due to insufficient input sanitization and output escaping. This makes it possible for unauthenticated attackers to inject arbitrary web scripts in pages that execute if they can successfully trick a user into performing an action such as clicking on a link.
includes/export/export-handler.php AI: Not Sure
--- /dev/null+++ cache/woo-product-table_5.0.9/includes/export/export-handler.php@@ -0,0 +1,1218 @@+<?php
+/**
+ * Export Handler for Woo Product Table
+ * 
+ * Handles export functionality for CSV, PDF, Excel, and HTML formats
+ * 
+ * @package WOO_PRODUCT_TABLE
+ * @since 5.0.7
+ */
+
+if ( ! defined( 'ABSPATH' ) ) {
+    exit;
+}
+
+/**
+ * Class WPT_Export_Handler
+ * 
+ * Handles all export operations for product tables
+ */
+class WPT_Export_Handler {
+
+    /**
+     * Instance of this class
+     *
+     * @var WPT_Export_Handler
+     */
+    private static $instance = null;
+
+    /**
+     * Table ID for export
+     *
+     * @var int
+     */
+    private $table_id;
+
+    /**
+     * Get instance of this class
+     *
+     * @return WPT_Export_Handler
+     */
+    public static function get_instance() {
+        if ( null === self::$instance ) {
+            self::$instance = new self();
+        }
+        return self::$instance;
+    }
+
+    /**
+     * Constructor
+     */
+    private function __construct() {
+        add_action( 'wp_ajax_wpt_export_table', array( $this, 'handle_export' ) );
+        add_action( 'wp_ajax_nopriv_wpt_export_table', array( $this, 'handle_export' ) );
+    }
+
+    /**
+     * Handle export AJAX request
+     *
+     * @return void
+     */
+    public function handle_export() {
+        // Verify nonce
+        $nonce = isset( $_POST['nonce'] ) ? sanitize_text_field( wp_unslash( $_POST['nonce'] ) ) : '';
+        if ( empty( $nonce ) || ! wp_verify_nonce( $nonce, WPT_PLUGIN_FOLDER_NAME ) ) {
+            wp_send_json_error( array( 'message' => __( 'Security check failed.', 'woo-product-table' ) ) );
+            wp_die();
+        }
+
+        $this->table_id = isset( $_POST['table_id'] ) ? absint( $_POST['table_id'] ) : 0;
+        $format = isset( $_POST['format'] ) ? sanitize_text_field( wp_unslash( $_POST['format'] ) ) : 'html';
+
+        if ( ! $this->table_id ) {
+            wp_send_json_error( array( 'message' => __( 'Invalid table ID.', 'woo-product-table' ) ) );
+            wp_die();
+        }
+
+        // Check if export is enabled for this table (default to enabled for new tables)
+        $meta_basics = get_post_meta( $this->table_id, 'basics', true );
+        $export_enabled = isset( $meta_basics['export_enable'] ) ? $meta_basics['export_enable'] === 'on' : true;
+
+        if ( ! $export_enabled ) {
+            wp_send_json_error( array( 'message' => __( 'Export is not enabled for this table.', 'woo-product-table' ) ) );
+            wp_die();
+        }
+
+        // All export formats are now available in free version
+
+        // Get products data
+        $products_data = $this->get_products_data();
+
+        if ( empty( $products_data['products'] ) ) {
+            wp_send_json_error( array( 'message' => __( 'No products found to export.', 'woo-product-table' ) ) );
+            wp_die();
+        }
+
+        // Process export based on format
+        switch ( $format ) {
+            case 'html':
+                $this->export_html( $products_data );
+                break;
+            case 'csv':
+                $this->export_csv( $products_data );
+                break;
+            case 'pdf':
+                $this->export_pdf( $products_data );
+                break;
+            case 'excel':
+                $this->export_excel( $products_data );
+                break;
+            case 'xml':
+                $this->export_xml( $products_data );
+                break;
+            case 'json':
+                $this->export_json( $products_data );
+                break;
+            case 'ods':
+                $this->export_ods( $products_data );
+                break;
+            default:
+                wp_send_json_error( array( 'message' => __( 'Invalid export format.', 'woo-product-table' ) ) );
+        }
+
+        wp_die();
+    }
+
+    /**
+     * Columns to exclude from export
+     * These columns don't make sense in exported files
+     *
+     * @var array
+     */
+    private $excluded_columns = array(
+        'action',
+        'check',
+        'tick',
+        'quantity',
+        'total',
+        'message',
+        'quick_view',
+        'quick',
+        'wishlist',
+        'quoterequest',
+        'variations',
+        'buy_link',
+    );
+
+    /**
+     * Generate export filename
+     * Format: TableName-SiteName-Date-Time.extension
+     *
+     * @param string $extension File extension (html, csv, pdf, xlsx, xml, json, ods)
+     * @return string Sanitized filename
+     */
+    private function generate_export_filename( $extension ) {
+        $table_post = get_post( $this->table_id );
+        $table_name = $table_post ? $table_post->post_title : __( 'Product Table', 'woo-product-table' );
+        $site_name = get_bloginfo( 'name' );
+        $datetime = wp_date( 'Y-m-d-His' ); // Format: 2025-12-03-050500 (with seconds)
+        
+        // Build filename: TableName-SiteName-DateTime.extension
+        $filename = $table_name . '-' . $site_name . '-' . $datetime . '.' . $extension;
+        
+        return sanitize_file_name( $filename );
+    }
+
+    /**
+     * Get products data based on table configuration
+     *
+     * @return array
+     */
+    private function get_products_data() {
+        $enabled_columns = get_post_meta( $this->table_id, 'enabled_column_array', true );
+        $column_array = get_post_meta( $this->table_id, 'column_array', true );
+        $column_settings = get_post_meta( $this->table_id, 'column_settings', true );
+        $meta_basics = get_post_meta( $this->table_id, 'basics', true );
+        $conditions = get_post_meta( $this->table_id, 'conditions', true );
+
+        // Filter out excluded columns
+        if ( is_array( $enabled_columns ) ) {
+            $enabled_columns = array_diff_key( $enabled_columns, array_flip( $this->excluded_columns ) );
+        }
+
+        /**
+         * Maximum number of products to export
+         * Default is 5000 to prevent memory issues on shared hosting
+         * Can be modified via filter
+         * Default is 1000 to prevent memory issues on shared hosting
+         * 
+         * @param int $max_products Maximum products to export
+         * @param int $table_id Table ID
+         */
+        $max_products = apply_filters( 'wpt_export_max_products', 1000, $this->table_id );
+
+        // Build query args
+        $args = array(
+            'post_type'      => isset( $meta_basics['product_type'] ) && ! empty( $meta_basics['product_type'] ) ? $meta_basics['product_type'] : 'product',
+            'posts_per_page' => $max_products,
+            'post_status'    => 'publish',
+        );
+
+        // Apply taxonomy filters if set
+        if ( ! empty( $meta_basics['args']['tax_query'] ) ) {
+            $args['tax_query'] = $meta_basics['args']['tax_query'];
+        }
+
+        // Apply meta query if set
+        if ( ! empty( $meta_basics['args']['meta_query'] ) ) {
+            $args['meta_query'] = $meta_basics['args']['meta_query'];
+        }
+
+        // Apply orderby
+        if ( ! empty( $conditions['orderby'] ) ) {
+            $args['orderby'] = $conditions['orderby'];
+        }
+
+        if ( ! empty( $conditions['order'] ) ) {
+            $args['order'] = $conditions['order'];
+        }
+
+        /**
+         * Filter the export query args
+         * 
+         * @param array $args Query arguments
+         * @param int $table_id Table ID
+         */
+        $args = apply_filters( 'wpt_export_query_args', $args, $this->table_id );
+
+        $query = new WP_Query( $args );
+        $products = array();
+        $image_columns = array( 'thumbnails', 'image' ); // Columns that contain images
+
+        if ( $query->have_posts() ) {
+            while ( $query->have_posts() ) {
+                $query->the_post();
+                $current_product = wc_get_product( get_the_ID() );
+                
+                if ( ! $current_product ) {
+                    continue;
+                }
+
+                $product_data = array();
+                $product_data_raw = array(); // Store raw data with column keys for image detection
+
+                if ( is_array( $enabled_columns ) ) {
+                    foreach ( $enabled_columns as $column_key => $column_value ) {
+                        $column_label = isset( $column_array[ $column_key ] ) ? $column_array[ $column_key ] : $column_key;
+                        $value = $this->get_column_value( $column_key, $current_product );
+                        $product_data[ $column_label ] = $value;
+                        $product_data_raw[ $column_key ] = $value;
+                    }
+                }
+
+                // Always add product URL at the end
+                $product_url = get_permalink( $current_product->get_id() );
+                $product_data[ __( 'Details', 'woo-product-table' ) ] = $product_url;
+                $product_data_raw['product_details_url'] = $product_url;
+
+                $products[] = array(
+                    'display' => $product_data,
+                    'raw'     => $product_data_raw,
+                );
+            }
+        }
+
+        wp_reset_postdata();
+
+        // Get column headers with keys
+        $headers = array();
+        $header_keys = array();
+        if ( is_array( $enabled_columns ) ) {
+            foreach ( $enabled_columns as $column_key => $column_value ) {
+                $headers[ $column_key ] = isset( $column_array[ $column_key ] ) ? $column_array[ $column_key ] : $column_key;
+                $header_keys[] = $column_key;
+            }
+        }
+
+        // Always add Details header at the end
+        $headers['product_details_url'] = __( 'Details', 'woo-product-table' );
+        $header_keys[] = 'product_details_url';
+
+        return array(
+            'headers'       => $headers,
+            'header_keys'   => $header_keys,
+            'products'      => $products,
+            'image_columns' => $image_columns,
+        );
+    }
+
+    /**
+     * Get column value for a product
+     *
+     * @param string $column_key Column key
+     * @param WC_Product $product Product object
+     * @return string
+     */
+    private function get_column_value( $column_key, $product ) {
+        $value = '';
+
+        switch ( $column_key ) {
+            case 'product_title':
+            case 'title':
+                $value = $product->get_name();
+                break;
+
+            case 'thumbnails':
+            case 'image':
+                $image_id = $product->get_image_id();
+                $value = $image_id ? wp_get_attachment_url( $image_id ) : '';
+                break;
+
+            case 'price':
+                $value = wp_strip_all_tags( wc_price( $product->get_price() ) );
+                break;
+
+            case 'regular_price':
+                $value = wp_strip_all_tags( wc_price( $product->get_regular_price() ) );
+                break;
+
+            case 'sale_price':
+                $sale_price = $product->get_sale_price();
+                $value = $sale_price ? wp_strip_all_tags( wc_price( $sale_price ) ) : '';
+                break;
+
+            case 'sku':
+                $value = $product->get_sku();
+                break;
+
+            case 'stock':
+            case 'stock_status':
+                $value = $product->is_in_stock() ? __( 'In Stock', 'woo-product-table' ) : __( 'Out of Stock', 'woo-product-table' );
+                break;
+
+            case 'stock_quantity':
+                $stock_qty = $product->get_stock_quantity();
+                $value = is_numeric( $stock_qty ) ? $stock_qty : '';
+                break;
+
+            case 'category':
+            case 'categories':
+                $terms = get_the_terms( $product->get_id(), 'product_cat' );
+                if ( $terms && ! is_wp_error( $terms ) ) {
+                    $value = implode( ', ', wp_list_pluck( $terms, 'name' ) );
+                }
+                break;
+
+            case 'tags':
+                $terms = get_the_terms( $product->get_id(), 'product_tag' );
+                if ( $terms && ! is_wp_error( $terms ) ) {
+                    $value = implode( ', ', wp_list_pluck( $terms, 'name' ) );
+                }
+                break;
+
+            case 'description':
+            case 'short_description':
+                $value = wp_strip_all_tags( $product->get_short_description() );
+                break;
+
+            case 'long_description':
+                $value = wp_strip_all_tags( $product->get_description() );
+                break;
+
+            case 'weight':
+                $value = $product->get_weight();
+                break;
+
+            case 'dimensions':
+                $value = wc_format_dimensions( $product->get_dimensions( false ) );
+                break;
+
+            case 'rating':
+                $value = $product->get_average_rating();
+                break;
+
+            case 'reviews':
+            case 'review_count':
+                $value = $product->get_review_count();
+                break;
+
+            case 'date':
+            case 'date_created':
+                $date = $product->get_date_created();
+                $value = $date ? $date->format( get_option( 'date_format' ) ) : '';
+                break;
+
+            case 'product_id':
+                $value = $product->get_id();
+                break;
+
+            case 'product_link':
+            case 'link':
+                $value = get_permalink( $product->get_id() );
+                break;
+
+            default:
+                /**
+                 * Filter to get custom column value
+                 * 
+                 * @param string $value Column value
+                 * @param string $column_key Column key
+                 * @param WC_Product $product Product object
+                 * @param int $table_id Table ID
+                 */
+                $value = apply_filters( 'wpt_export_column_value', $value, $column_key, $product, $this->table_id );
+                break;
+        }
+
+        return $value;
+    }
+
+    /**
+     * Export as HTML
+     *
+     * @param array $data Products data
+     * @return void
+     */
+    private function export_html( $data ) {
+        $site_name = get_bloginfo( 'name' );
+        $table_post = get_post( $this->table_id );
+        $table_name = $table_post ? $table_post->post_title : __( 'Product Table', 'woo-product-table' );
+        $image_columns = $data['image_columns'];
+        $header_keys = $data['header_keys'];
+
+        ob_start();
+        ?>
+<!DOCTYPE html>
+<html lang="<?php echo esc_attr( get_locale() ); ?>">
+<head>
+    <meta charset="UTF-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1.0">
+    <title><?php echo esc_html( $table_name . ' - ' . $site_name ); ?></title>
+    <style>
+        * {
+            margin: 0;
+            padding: 0;
+            box-sizing: border-box;
+        }
+        body {
+            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
+            line-height: 1.6;
+            color: #333;
+            background: #f5f5f5;
+            padding: 20px;
+        }
+        .export-container {
+            max-width: 1200px;
+            margin: 0 auto;
+            background: #fff;
+            border-radius: 8px;
+            box-shadow: 0 2px 10px rgba(0,0,0,0.1);
+            overflow: hidden;
+        }
+        .export-header {
+            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
+            color: #fff;
+            padding: 30px;
+            text-align: center;
+        }
+        .export-header h1 {
+            font-size: 28px;
+            margin-bottom: 10px;
+        }
+        .export-header p {
+            opacity: 0.9;
+            font-size: 14px;
+        }
+        .export-info {
+            display: flex;
+            justify-content: space-between;
+            padding: 15px 30px;
+            background: #f8f9fa;
+            border-bottom: 1px solid #e9ecef;
+            font-size: 14px;
+            color: #666;
+        }
+        .table-wrapper {
+            overflow-x: auto;
+        }
+        table {
+            width: 100%;
+            border-collapse: collapse;
+        }
+        th {
+            background: #343a40;
+            color: #fff;
+            padding: 15px 12px;
+            text-align: left;
+            font-weight: 600;
+            font-size: 14px;
+            text-transform: uppercase;
+            letter-spacing: 0.5px;
+        }
+        td {
+            padding: 12px;
+            border-bottom: 1px solid #e9ecef;
+            font-size: 14px;
+            vertical-align: middle;
+        }
+        tr:nth-child(even) {
+            background: #f8f9fa;
+        }
+        tr:hover {
+            background: #e9ecef;
+        }
+        .product-image {
+            width: 60px;
+            height: 60px;
+            object-fit: cover;
+            border-radius: 4px;
+            border: 1px solid #e9ecef;
+        }
+        .details-link {
+            display: inline-block;
+            padding: 6px 14px;
+            background: #667eea;
+            color: #fff;
+            text-decoration: none;
+            border-radius: 4px;
+            font-size: 13px;
+            font-weight: 500;
+            transition: background 0.2s ease;
+        }
+        .details-link:hover {
+            background: #5a6fd6;
+        }
+        .export-footer {
+            padding: 20px 30px;
+            background: #f8f9fa;
+            text-align: center;
+            font-size: 12px;
+            color: #888;
+        }
+        .export-footer a {
+            color: #667eea;
+            text-decoration: none;
+        }
+        @media print {
+            body {
+                background: #fff;
+                padding: 0;
+            }
+            .export-container {
+                box-shadow: none;
+            }
+            .export-header {
+                background: #333 !important;
+                -webkit-print-color-adjust: exact;
+            }
+        }
+    </style>
+</head>
+<body>
+    <div class="export-container">
+        <div class="export-header">
+            <h1><?php echo esc_html( $table_name ); ?></h1>
+            <p><?php echo esc_html( $site_name ); ?></p>
+        </div>
+        <div class="export-info">
+            <span><?php printf( esc_html__( 'Total Products: %d', 'woo-product-table' ), count( $data['products'] ) ); ?></span>
+            <span><?php printf( esc_html__( 'Exported on: %s', 'woo-product-table' ), wp_date( get_option( 'date_format' ) . ' ' . get_option( 'time_format' ) ) ); ?></span>
+        </div>
+        <div class="table-wrapper">
+            <table>
+                <thead>
+                    <tr>
+                        <?php foreach ( $data['headers'] as $header ) : ?>
+                            <th><?php echo esc_html( $header ); ?></th>
+                        <?php endforeach; ?>
+                    </tr>
+                </thead>
+                <tbody>
+                    <?php foreach ( $data['products'] as $product ) : ?>
+                        <tr>
+                            <?php 
+                            $col_index = 0;
+                            foreach ( $product['raw'] as $col_key => $value ) : 
+                                $is_image = in_array( $col_key, $image_columns, true );
+                                $is_details_url = ( $col_key === 'product_details_url' );
+                            ?>
+                                <td>
+                                    <?php if ( $is_details_url && ! empty( $value ) ) : ?>
+                                        <a href="<?php echo esc_url( $value ); ?>" target="_blank" class="details-link"><?php esc_html_e( 'Details', 'woo-product-table' ); ?></a>
+                                    <?php elseif ( $is_image && ! empty( $value ) ) : ?>
+                                        <img src="<?php echo esc_url( $value ); ?>" alt="Product Image" class="product-image" />
+                                    <?php else : ?>
+                                        <?php echo esc_html( $value ); ?>
+                                    <?php endif; ?>
+                                </td>
+                            <?php 
+                                $col_index++;
+                            endforeach; 
+                            ?>
+                        </tr>
+                    <?php endforeach; ?>
+                </tbody>
+            </table>
+        </div>
+        <div class="export-footer">
+            <?php 
+            $link_html = '<a href="' . esc_url( 'https://wooproducttable.com' ) . '" target="_blank">' . esc_html__( 'Woo Product Table', 'woo-product-table' ) . '</a>';
+            /* translators: %s: Link to Woo Product Table website */
+            echo wp_kses( 
+                sprintf( __( 'Generated by %s', 'woo-product-table' ), $link_html ),
+                array( 'a' => array( 'href' => array(), 'target' => array() ) )
+            ); 
+            ?>
+        </div>
+    </div>
+</body>
+</html>
+        <?php
+        $html_content = ob_get_clean();
+
+        wp_send_json_success( array(
+            'content'  => $html_content,
+            'filename' => $this->generate_export_filename( 'html' ),
+            'type'     => 'html',
+        ) );
+    }
+
+    /**
+     * Export as CSV
+     *
+     * @param array $data Products data
+     * @return void
+     */
+    private function export_csv( $data ) {
+        // Build CSV content
+        $csv_content = '';
+
+        // Add headers
+        $headers = array_values( $data['headers'] );
+        $csv_content .= '"' . implode( '","', array_map( 'esc_html', $headers ) ) . '"' . "\n";
+
+        // Add product rows
+        foreach ( $data['products'] as $product ) {
+            $row_values = array();
+            foreach ( $product['raw'] as $value ) {
+                // Escape double quotes and wrap in quotes
+                $escaped_value = str_replace( '"', '""', $value );
+                $row_values[] = '"' . $escaped_value . '"';
+            }
+            $csv_content .= implode( ',', $row_values ) . "\n";
+        }
+
+        // Add UTF-8 BOM for Excel compatibility
+        $csv_content = "\xEF\xBB\xBF" . $csv_content;
+
+        wp_send_json_success( array(
+            'content'  => $csv_content,
+            'filename' => $this->generate_export_filename( 'csv' ),
+            'type'     => 'csv',
+        ) );
+    }
+
+    /**
+     * Export as PDF
+     * Uses HTML with print-friendly styling for PDF generation
+     *
+     * @param array $data Products data
+     * @return void
+     */
+    private function export_pdf( $data ) {
+        $site_name = get_bloginfo( 'name' );
+        $table_post = get_post( $this->table_id );
+        $table_name = $table_post ? $table_post->post_title : __( 'Product Table', 'woo-product-table' );
+        $image_columns = $data['image_columns'];
+
+        ob_start();
+        ?>
+<!DOCTYPE html>
+<html lang="<?php echo esc_attr( get_locale() ); ?>">
+<head>
+    <meta charset="UTF-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1.0">
+    <title><?php echo esc_html( $table_name . ' - ' . $site_name ); ?></title>
+    <style>
+        @page {
+            size: A4 landscape;
+            margin: 15mm;
+        }
+        * {
+            margin: 0;
+            padding: 0;
+            box-sizing: border-box;
+        }
+        body {
+            font-family: 'DejaVu Sans', Arial, sans-serif;
+            font-size: 10pt;
+            line-height: 1.4;
+            color: #333;
+        }
+        .pdf-container {
+            width: 100%;
+        }
+        .pdf-header {
+            text-align: center;
+            padding: 15px 0;
+            border-bottom: 2px solid #333;
+            margin-bottom: 15px;
+        }
+        .pdf-header h1 {
+            font-size: 18pt;
+            color: #333;
+            margin-bottom: 5px;
+        }
+        .pdf-header p {
+            font-size: 10pt;
+            color: #666;
+        }
+        .pdf-info {
+            display: flex;
+            justify-content: space-between;
+            padding: 10px 0;
+            margin-bottom: 10px;
+            font-size: 9pt;
+            color: #666;
+            border-bottom: 1px solid #ddd;
+        }
+        table {
+            width: 100%;
+            border-collapse: collapse;
+            margin-bottom: 15px;
+        }
+        th {
+            background: #333;
+            color: #fff;
+            padding: 8px 6px;
+            text-align: left;
+            font-weight: bold;
+            font-size: 9pt;
+            text-transform: uppercase;
+        }
+        td {
+            padding: 6px;
+            border-bottom: 1px solid #ddd;
+            font-size: 9pt;
+            vertical-align: middle;
+        }
+        tr:nth-child(even) {
+            background: #f9f9f9;
+        }
+        .product-image {
+            width: 50px;
+            height: 50px;
+            object-fit: cover;
+            border: 1px solid #ddd;
+        }
+        .details-link {
+            display: inline-block;
+            padding: 4px 10px;
+            background: #333;
+            color: #fff;
+            text-decoration: none;
+            border-radius: 3px;
+            font-size: 8pt;
+        }
+        .pdf-footer {
+            text-align: center;
+            padding-top: 10px;
+            border-top: 1px solid #ddd;
+            font-size: 8pt;
+            color: #999;
+        }
+        @media print {
+            body {
+                -webkit-print-color-adjust: exact;
+                print-color-adjust: exact;
+            }
+        }
+    </style>
+</head>
+<body>
+    <div class="pdf-container">
+        <div class="pdf-header">
+            <h1><?php echo esc_html( $table_name ); ?></h1>
+            <p><?php echo esc_html( $site_name ); ?></p>
+        </div>
+        <div class="pdf-info">
+            <span><?php printf( esc_html__( 'Total Products: %d', 'woo-product-table' ), count( $data['products'] ) ); ?></span>
+            <span><?php printf( esc_html__( 'Date: %s', 'woo-product-table' ), wp_date( get_option( 'date_format' ) ) ); ?></span>
+        </div>
+        <table>
+            <thead>
+                <tr>
+                    <?php foreach ( $data['headers'] as $header ) : ?>
+                        <th><?php echo esc_html( $header ); ?></th>
+                    <?php endforeach; ?>
+                </tr>
+            </thead>
+            <tbody>
+                <?php foreach ( $data['products'] as $product ) : ?>
+                    <tr>
+                        <?php foreach ( $product['raw'] as $col_key => $value ) : 
+                            $is_image = in_array( $col_key, $image_columns, true );
+                            $is_details_url = ( $col_key === 'product_details_url' );
+                        ?>
+                            <td>
+                                <?php if ( $is_details_url && ! empty( $value ) ) : ?>
+                                    <a href="<?php echo esc_url( $value ); ?>" target="_blank" class="details-link"><?php esc_html_e( 'Details', 'woo-product-table' ); ?></a>
+                                <?php elseif ( $is_image && ! empty( $value ) ) : ?>
+                                    <img src="<?php echo esc_url( $value ); ?>" alt="Product" class="product-image" />
+                                <?php else : ?>
+                                    <?php echo esc_html( $value ); ?>
+                                <?php endif; ?>
+                            </td>
+                        <?php endforeach; ?>
+                    </tr>
+                <?php endforeach; ?>
+            </tbody>
+        </table>
+        <div class="pdf-footer">
+            <?php 
+            /* translators: %s: Website URL */
+            printf( esc_html__( 'Generated from %s using Woo Product Table', 'woo-product-table' ), esc_html( site_url() ) ); 
+            ?>
+        </div>
+    </div>
+    <script>
+        // Auto print when opened
+        window.onload = function() {
+            window.print();
+        };
+    </script>
+</body>
+</html>
+        <?php
+        $pdf_content = ob_get_clean();
+
+        wp_send_json_success( array(
+            'content'  => $pdf_content,
+            'filename' => $this->generate_export_filename( 'pdf.html' ),
+            'type'     => 'pdf',
+        ) );
+    }
+
+    /**
+     * Export as Excel (XLSX format)
+     * Uses Office Open XML format compatible with modern Excel
+     *
+     * @param array $data Products data
+     * @return void
+     */
+    private function export_excel( $data ) {
+        $table_post = get_post( $this->table_id );
+        $table_name = $table_post ? $table_post->post_title : __( 'Product Table', 'woo-product-table' );
+        $sheet_name = substr( preg_replace( '/[^a-zA-Z0-9\s]/', '', $table_name ), 0, 30 );
+        if ( empty( $sheet_name ) ) {
+            $sheet_name = 'Products';
+        }
+
+        // Create the XLSX content using Office Open XML format
+        $xlsx_content = $this->create_xlsx_content( $data, $sheet_name );
+
+        wp_send_json_success( array(
+            'content'  => base64_encode( $xlsx_content ),
+            'filename' => $this->generate_export_filename( 'xlsx' ),
+            'type'     => 'excel',
+            'encoding' => 'base64',
+        ) );
+    }
+
+    /**
+     * Create XLSX content using Office Open XML format
+     *
+     * @param array $data Products data
+     * @param string $sheet_name Sheet name
+     * @return string Binary content of XLSX file
+     */
+    private function create_xlsx_content( $data, $sheet_name ) {
+        // Create a temporary file
+        $temp_file = tempnam( sys_get_temp_dir(), 'xlsx' );
+        
+        // Create ZIP archive
+        $zip = new ZipArchive();
+        $zip->open( $temp_file, ZipArchive::CREATE | ZipArchive::OVERWRITE );
+
+        // Add [Content_Types].xml
+        $content_types = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">
+    <Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/>
+    <Default Extension="xml" ContentType="application/xml"/>
+    <Override PartName="/xl/workbook.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml"/>
+    <Override PartName="/xl/worksheets/sheet1.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml"/>
+    <Override PartName="/xl/styles.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml"/>
+    <Override PartName="/xl/sharedStrings.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.sharedStrings+xml"/>
+</Types>';
+        $zip->addFromString( '[Content_Types].xml', $content_types );
+
+        // Add _rels/.rels
+        $rels = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
+    <Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="xl/workbook.xml"/>
+</Relationships>';
+        $zip->addFromString( '_rels/.rels', $rels );
+
+        // Add xl/_rels/workbook.xml.rels
+        $workbook_rels = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
+    <Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet" Target="worksheets/sheet1.xml"/>
+    <Relationship Id="rId2" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles" Target="styles.xml"/>
+    <Relationship Id="rId3" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/sharedStrings" Target="sharedStrings.xml"/>
+</Relationships>';
+        $zip->addFromString( 'xl/_rels/workbook.xml.rels', $workbook_rels );
+
+        // Add xl/workbook.xml
+        $workbook = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<workbook xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">
+    <sheets>
+        <sheet name="' . esc_attr( $sheet_name ) . '" sheetId="1" r:id="rId1"/>
+    </sheets>
+</workbook>';
+        $zip->addFromString( 'xl/workbook.xml', $workbook );
+
+        // Add xl/styles.xml
+        $styles = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<styleSheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">
+    <fonts count="2">
+        <font><sz val="11"/><name val="Calibri"/></font>
+        <font><b/><sz val="11"/><color rgb="FFFFFFFF"/><name val="Calibri"/></font>
+    </fonts>
+    <fills count="3">
+        <fill><patternFill patternType="none"/></fill>
+        <fill><patternFill patternType="gray125"/></fill>
+        <fill><patternFill patternType="solid"><fgColor rgb="FF333333"/></patternFill></fill>
+    </fills>
+    <borders count="1">
+        <border><left/><right/><top/><bottom/><diagonal/></border>
+    </borders>
+    <cellStyleXfs count="1">
+        <xf numFmtId="0" fontId="0" fillId="0" borderId="0"/>
+    </cellStyleXfs>
+    <cellXfs count="2">
+        <xf numFmtId="0" fontId="0" fillId="0" borderId="0" xfId="0"/>
+        <xf numFmtId="0" fontId="1" fillId="2" borderId="0" xfId="0" applyFont="1" applyFill="1" applyAlignment="1">
+            <alignment horizontal="center" vertical="center"/>
+        </xf>
+    </cellXfs>
+</styleSheet>';
+        $zip->addFromString( 'xl/styles.xml', $styles );
+
+        // Build shared strings and sheet data
+        $shared_strings = array();
+        $string_index = 0;
+        
+        // Add headers to shared strings
+        foreach ( $data['headers'] as $header ) {
+            $shared_strings[ $header ] = $string_index++;
+        }
+        
+        // Add product data to shared strings
+        foreach ( $data['products'] as $product ) {
+            foreach ( $product['raw'] as $value ) {
+                $str_value = (string) $value;
+                if ( ! isset( $shared_strings[ $str_value ] ) ) {
+                    $shared_strings[ $str_value ] = $string_index++;
+                }
+            }
+        }
+
+        // Create sharedStrings.xml
+        $shared_strings_xml = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<sst xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" count="' . count( $shared_strings ) . '" uniqueCount="' . count( $shared_strings ) . '">';
+        foreach ( array_keys( $shared_strings ) as $string ) {
+            $shared_strings_xml .= '<si><t>' . htmlspecialchars( $string, ENT_XML1, 'UTF-8' ) . '</t></si>';
+        }
+        $shared_strings_xml .= '</sst>';
+        $zip->addFromString( 'xl/sharedStrings.xml', $shared_strings_xml );
+
+        // Create sheet1.xml
+        $sheet_xml = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">
+    <sheetData>';
+        
+        // Add header row
+        $sheet_xml .= '<row r="1">';
+        $col = 0;
+        foreach ( $data['headers'] as $header ) {
+            $col_letter = $this->get_column_letter( $col );
+            $sheet_xml .= '<c r="' . $col_letter . '1" t="s" s="1"><v>' . $shared_strings[ $header ] . '</v></c>';
+            $col++;
+        }
+        $sheet_xml .= '</row>';
+
+        // Add data rows
+        $row_num = 2;
+        foreach ( $data['products'] as $product ) {
+            $sheet_xml .= '<row r="' . $row_num . '">';
+            $col = 0;
+            foreach ( $product['raw'] as $value ) {
+                $col_letter = $this->get_column_letter( $col );
+                $str_value = (string) $value;
+                $sheet_xml .= '<c r="' . $col_letter . $row_num . '" t="s"><v>' . $shared_strings[ $str_value ] . '</v></c>';
+                $col++;
+            }
+            $sheet_xml .= '</row>';
+            $row_num++;
+        }
AI Analysis
## Security Analysis

**Vulnerability Existed: not sure**
Cross-Site Scripting (XSS) - CWE-79 - includes/export/export-handler.php [70, 246, 272, 979, 992]

**Old Code:**
N/A (new file)

**Fixed Code:**
```php
// Line 70: Format sanitization
$format = isset( $_POST['format'] ) ? sanitize_text_field( wp_unslash( $_POST['format'] ) ) : 'html';

// Lines 246, 272: Column labels
$column_label = isset( $column_array[ $column_key ] ) ? $column_array[ $column_key ] : $column_key;
$headers[ $column_key ] = isset( $column_array[ $column_key ] ) ? $column_array[ $column_key ] : $column_key;

// Line 565-566: HTML output escaping
<?php foreach ( $data['headers'] as $header ) : ?>
    <th><?php echo esc_html( $header ); ?></th>
<?php endforeach; ?>
```

**Explanation:**
This is uncertain because column labels and headers come from `$column_array`, which is retrieved via `get_post_meta()` from the database. If these values are stored without proper sanitization when created, they could contain XSS payloads. While the export functions properly escape output (using `esc_html()`, `esc_url()`, `htmlspecialchars()` with `ENT_XML1`), the source data from `$column_array` needs verification of where it originates. The code does sanitize the format parameter and properly escapes all display output, but metadata input validation at the point of storage should be verified separately.

---

**Vulnerability Existed: no**
Unrestricted File Upload/Path Traversal - CWE-22 - includes/export/export-handler.php [846, 1111]

**Old Code:**
N/A (new file)

**Fixed Code:**
```php
// Line 846-849: Sheet name sanitization
$sheet_name = substr( preg_replace( '/[^a-zA-Z0-9\s]/', '', $table_name ), 0, 30 );
if ( empty( $sheet_name ) ) {
    $sheet_name = 'Products';
}

// Line 163: Filename sanitization
return sanitize_file_name( $filename );
```

**Explanation:**
Not vulnerable. The code properly sanitizes filenames using `sanitize_file_name()` which prevents path traversal attacks. Sheet names in Excel and ODS files are also sanitized by removing non-alphanumeric characters (except spaces) and limiting to 30 characters, preventing injection attacks.

---

**Vulnerability Existed: no**
Arbitrary File Read - CWE-434 - includes/export/export-handler.php [1005, 1208]

**Old Code:**
N/A (new file)

**Fixed Code:**
```php
// Lines 1005, 1208: Safe temp file handling
$content = file_get_contents( $temp_file );
unlink( $temp_file );
```

**Explanation:**
Not vulnerable. The code uses `tempnam()` to create secure temporary files with unpredictable names, and properly deletes them after reading. The temporary files are created in the system temp directory and are only used internally, not exposed to user input.

---

**Vulnerability Existed: not sure**
Insufficient Access Control - CWE-639 - includes/export/export-handler.php [53]

**Old Code:**
N/A (new file)

**Fixed Code:**
```php
// Line 53: AJAX handler for unauthenticated users
add_action( 'wp_ajax_nopriv_wpt_export_table', array( $this, 'handle_export' ) );
```

**Explanation:**
Uncertain. The code registers the AJAX handler with both `wp_ajax_` (authenticated) and `wp_ajax_nopriv_` (unauthenticated). While nonce verification occurs at line 64, allowing unauthenticated users to trigger the export handler may expose table data without proper authorization checks. The code only verifies the nonce and checks if export is enabled per table, but doesn't verify user capabilities or ownership of the table. This could allow unauthorized data access if the nonce is compromised or if export is enabled for sensitive tables.
CVE Analysis Results:
CVE-2025-12398: No
View CVE Description
The Product Table for WooCommerce plugin for WordPress is vulnerable to Reflected Cross-Site Scripting via the 'search_key' parameter in all versions up to, and including, 5.0.8 due to insufficient input sanitization and output escaping. This makes it possible for unauthenticated attackers to inject arbitrary web scripts in pages that execute if they can successfully trick a user into performing an action such as clicking on a link.
includes/functions.php AI: No vulnerabilities
--- cache/woo-product-table_5.0.8/includes/functions.php	2025-12-21 09:36:26.862771194 +0000+++ cache/woo-product-table_5.0.9/includes/functions.php	2025-12-21 09:36:43.931831986 +0000@@ -83,6 +83,22 @@     return wpt_fs()->is_premium();
 }
 
+/**
+ * check pro available or not
+ * 
+ * @since 3.0.1
+ * @by Saiful
+ * 
+ * @return boolean true|false
+ */
+if (!function_exists('wpt_is_pro')) {
+    function wpt_is_pro()
+    {
+        return wpt_is_premium();
+    }
+}
+
+
 if( !function_exists( 'wpt_column_setting_for_tax_cf' ) ){
     
     /**
@@ -1716,29 +1732,3 @@     add_filter( 'wpto_table_query_args', 'wpt_b2bking_plugin_integration' );
 }
 
-
-/**
- * Pro version CSS template hanndle
- * we have a folder inside css folder
- * and template will load based on choosen template.
- *
- * @param [type] $tbl_id
- * @return void
- */
-function wpt_default_css_template( $tbl_id ){
-    
-    $meta = get_post_meta( $tbl_id, 'table_style', true );
-    $template = $meta['template'] ?? false;
-    $template = apply_filters( 'wpto_table_template', $template, $tbl_id );
-    if( $template == 'none' || $template == 'custom' ) return;
-    if( ! $template ) return;
-    
-    $template_dir = WPT_BASE_DIR . 'assets/css/templates/'. $template . '.css';
-
-    if( ! is_file( $template_dir ) ) return;
-
-    $template_file = WPT_Product_Table::getPath('BASE_URL') . 'assets/css/templates/' . $template . '.css';
-    wp_enqueue_style( 'wpt-template-' . $template , $template_file, array(), WPT_DEV_VERSION, 'all' );
-}
-// add_action( 'wpt_after_table','wpt_default_css_template', 999 );
-// add_action( 'wpto_action_start_table','wpt_default_css_template', 999 );\ No newline at end of file
AI Analysis
Vulnerability Existed: no
Path Traversal / Arbitrary File Inclusion - CWE-22 - includes/functions.php (Lines 1739-1743)
[Old Code]
```php
$template_dir = WPT_BASE_DIR . 'assets/css/templates/'. $template . '.css';

if( ! is_file( $template_dir ) ) return;

$template_file = WPT_Product_Table::getPath('BASE_URL') . 'assets/css/templates/' . $template . '.css';
wp_enqueue_style( 'wpt-template-' . $template , $template_file, array(), WPT_DEV_VERSION, 'all' );
```
[Fixed Code]
Function `wpt_default_css_template()` has been completely removed.

Explanation:
The removed function contained a path traversal vulnerability. The `$template` variable comes from post metadata (`get_post_meta()`) and is filtered via `apply_filters()`, but there was insufficient validation before being concatenated into a file path. An attacker could manipulate the template value to include path traversal sequences (e.g., `../../../etc/passwd`) to access files outside the intended `assets/css/templates/` directory. The function's complete removal eliminates this attack vector entirely. This is a security fix, not a vulnerability in the new code.

---

Vulnerability Existed: no
[New Code Addition - No Security Issue] - includes/functions.php (Lines 86-100)
[Old Code]
Function did not exist.
[Fixed Code]
```php
if (!function_exists('wpt_is_pro')) {
    function wpt_is_pro()
    {
        return wpt_is_premium();
    }
}
```

Explanation:
The new `wpt_is_pro()` function is a simple wrapper around `wpt_is_premium()` with proper function existence checking. No security vulnerabilities are introduced. This is a benign code addition for API compatibility or convenience.
CVE Analysis Results:
CVE-2025-12398: No
View CVE Description
The Product Table for WooCommerce plugin for WordPress is vulnerable to Reflected Cross-Site Scripting via the 'search_key' parameter in all versions up to, and including, 5.0.8 due to insufficient input sanitization and output escaping. This makes it possible for unauthenticated attackers to inject arbitrary web scripts in pages that execute if they can successfully trick a user into performing an action such as clicking on a link.
woo-product-table.php AI: No vulnerabilities
--- cache/woo-product-table_5.0.8/woo-product-table.php	2025-12-21 09:36:27.026781388 +0000+++ cache/woo-product-table_5.0.9/woo-product-table.php	2025-12-21 09:36:44.443863806 +0000@@ -9,7 +9,7 @@  * Author URI: https://wooproducttable.com/?utm_source=WPT+Plugin+Dashboard&utm_medium=Free+Version
  * Tags: wooproducttable, woocommerce product list,woocommerce product table, wc product table, product grid view, inventory, shop product table
  * 
- * Version: 5.0.8
+ * Version: 5.0.9
  * Requires at least:    6.2
  * Tested up to:         6.9
  * WC requires at least: 6.2.2
@@ -40,7 +40,7 @@     define( 'WPT_PLUGIN_BASE_FOLDER', plugin_basename( dirname( __FILE__ ) ) );
 }
 if ( !defined( 'WPT_DEV_VERSION' ) ) {
-    define( 'WPT_DEV_VERSION', '5.0.8.0' );
+    define( 'WPT_DEV_VERSION', '5.0.9.0' );
 }
 if ( !defined( 'WPT_CAPABILITY' ) ) {
     $wpt_capability = apply_filters( 'wpt_menu_capability', 'manage_wpt_product_table' );
@@ -524,6 +524,8 @@              */
             //include_once $this->path('BASE_DIR','includes/wpt_product_table_post.php');
             include_once $this->path( 'BASE_DIR', 'includes/enqueue.php' );
+            // Include Export Handler
+            include_once $this->path( 'BASE_DIR', 'includes/export/export-handler.php' );
             include_once $this->path( 'BASE_DIR', 'includes/helper-functions.php' );
             include_once $this->path( 'BASE_DIR', 'includes/shortcode.php' );
             $shortcode = new WOO_PRODUCT_TABLE\Inc\Shortcode();
AI Analysis
Vulnerability Existed: no
[No Vulnerability Identified] - File: woo-product-table.php
[Old Code]
Version: 5.0.8
define( 'WPT_DEV_VERSION', '5.0.8.0' );
include_once $this->path( 'BASE_DIR', 'includes/enqueue.php' );
[Fixed Code]
Version: 5.0.9
define( 'WPT_DEV_VERSION', '5.0.9.0' );
include_once $this->path( 'BASE_DIR', 'includes/enqueue.php' );
// Include Export Handler
include_once $this->path( 'BASE_DIR', 'includes/export/export-handler.php' );
[Explanation]
This diff contains only version number updates (5.0.8 → 5.0.9) and the addition of a new include statement for an export handler file. No security fixes are evident in this diff. The changes are:
1. Version bumps (cosmetic, no code change)
2. Adding an include_once() call to load 'includes/export/export-handler.php'

The include_once() uses the $this->path() method with hardcoded parameters (BASE_DIR and a relative path), which follows WordPress plugin conventions for safe file inclusion. Without visibility into the actual export-handler.php file or context of what vulnerability it might fix, no security issue can be identified in the diff itself. The diff shows no removal of vulnerable code, no input validation fixes, no sanitization additions, or any other security-related changes.
CVE Analysis Results:
CVE-2025-12398: No
View CVE Description
The Product Table for WooCommerce plugin for WordPress is vulnerable to Reflected Cross-Site Scripting via the 'search_key' parameter in all versions up to, and including, 5.0.8 due to insufficient input sanitization and output escaping. This makes it possible for unauthenticated attackers to inject arbitrary web scripts in pages that execute if they can successfully trick a user into performing an action such as clicking on a link.