Commit 5449ce65a3c for woocommerce

commit 5449ce65a3cedf6025156891ee67e5985878aa94
Author: Thomas Roberts <5656702+opr@users.noreply.github.com>
Date:   Tue Oct 20 15:30:08 2026 +0100

    Reject invalid Store API product price filters (#69998)

    * Fix Store API price filter validation

    * Add changelog entry for Store API price filter validation

    * Fix empty Store API price filters

diff --git a/plugins/woocommerce/changelog/fix-store-api-price-filter-validation b/plugins/woocommerce/changelog/fix-store-api-price-filter-validation
new file mode 100644
index 00000000000..481d1b1055d
--- /dev/null
+++ b/plugins/woocommerce/changelog/fix-store-api-price-filter-validation
@@ -0,0 +1,4 @@
+Significance: patch
+Type: fix
+
+Reject invalid minimum and maximum price filters instead of returning a server error.
diff --git a/plugins/woocommerce/src/StoreApi/Routes/V1/Products.php b/plugins/woocommerce/src/StoreApi/Routes/V1/Products.php
index 91fad8b3d7e..d20201f72bb 100644
--- a/plugins/woocommerce/src/StoreApi/Routes/V1/Products.php
+++ b/plugins/woocommerce/src/StoreApi/Routes/V1/Products.php
@@ -350,6 +350,7 @@ class Products extends AbstractRoute {
 		$params['min_price'] = array(
 			'description'       => __( 'Limit result set to products based on a minimum price, provided using the smallest unit of the currency.', 'woocommerce' ),
 			'type'              => 'string',
+			'pattern'           => '^[0-9]*$',
 			'sanitize_callback' => 'sanitize_text_field',
 			'validate_callback' => 'rest_validate_request_arg',
 		);
@@ -357,6 +358,7 @@ class Products extends AbstractRoute {
 		$params['max_price'] = array(
 			'description'       => __( 'Limit result set to products based on a maximum price, provided using the smallest unit of the currency.', 'woocommerce' ),
 			'type'              => 'string',
+			'pattern'           => '^[0-9]*$',
 			'sanitize_callback' => 'sanitize_text_field',
 			'validate_callback' => 'rest_validate_request_arg',
 		);
diff --git a/plugins/woocommerce/tests/php/src/Blocks/StoreApi/Routes/Products.php b/plugins/woocommerce/tests/php/src/Blocks/StoreApi/Routes/Products.php
index 9b073a566cc..98393fcfe7c 100644
--- a/plugins/woocommerce/tests/php/src/Blocks/StoreApi/Routes/Products.php
+++ b/plugins/woocommerce/tests/php/src/Blocks/StoreApi/Routes/Products.php
@@ -214,6 +214,39 @@ class Products extends ControllerTestCase {
 		$this->assertEquals( 'search-for-this-value', $data[0]['sku'] );
 	}

+	/**
+	 * @testdox Should return a bad request response for invalid price filter values.
+	 * @testWith ["min_price", "true"]
+	 *           ["max_price", "10.99"]
+	 *           ["min_price", "-1"]
+	 *
+	 * @param string $param Price filter parameter.
+	 * @param string $value Price filter value.
+	 */
+	public function test_rejects_invalid_price_filter_values( string $param, string $value ): void {
+		$request = new \WP_REST_Request( 'GET', '/wc/store/v1/products' );
+		$request->set_param( $param, $value );
+
+		$response = rest_get_server()->dispatch( $request );
+		$data     = $response->get_data();
+
+		$this->assertSame( 400, $response->get_status(), 'Invalid price filters should return HTTP 400.' );
+		$this->assertSame( 'rest_invalid_param', $data['code'], 'The response should identify an invalid request parameter.' );
+	}
+
+	/**
+	 * @testdox Should allow empty price filter values used by product filter blocks.
+	 */
+	public function test_allows_empty_price_filter_values(): void {
+		$request = new \WP_REST_Request( 'GET', '/wc/store/v1/products' );
+		$request->set_param( 'min_price', '' );
+		$request->set_param( 'max_price', '' );
+
+		$response = rest_get_server()->dispatch( $request );
+
+		$this->assertSame( 200, $response->get_status(), 'Empty price filters should be ignored.' );
+	}
+
 	/**
 	 * Test conversion of product to rest response.
 	 */