Commit 7f1b9ec77a7 for woocommerce

commit 7f1b9ec77a7333e5bc2d6c80ed9629d6012b3a62
Author: malinajirka <malinajirka@gmail.com>
Date:   Thu Jul 23 13:45:04 2026 +0200

    Fix is_rest_api_request() missing ?rest_route= REST requests (#66816)

    * Detect rest_route query-string REST requests in is_rest_api_request()

    is_rest_api_request() only matched the pretty-permalink form of REST
    requests (/wp-json/...), so the equally valid ?rest_route=/... form
    (plain permalinks, Jetpack-signed requests) was wrongly treated as a
    non-REST request. Check the rest_route query parameter as well,
    mirroring the approach already used by is_store_api_request().

    * Add unit tests for rest_route detection in is_rest_api_request()

    * Add changelog entry

    * Explain why core REST detection functions cannot replace is_rest_api_request()

    Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

    ---------

    Co-authored-by: Claude Fable 5 <noreply@anthropic.com>

diff --git a/plugins/woocommerce/changelog/fix-wooplug-7135-rest-route-detection b/plugins/woocommerce/changelog/fix-wooplug-7135-rest-route-detection
new file mode 100644
index 00000000000..c4b36f22253
--- /dev/null
+++ b/plugins/woocommerce/changelog/fix-wooplug-7135-rest-route-detection
@@ -0,0 +1,4 @@
+Significance: patch
+Type: fix
+
+Detect REST API requests made via the ?rest_route= query parameter (plain permalinks, Jetpack-signed requests) in is_rest_api_request().
diff --git a/plugins/woocommerce/includes/class-woocommerce.php b/plugins/woocommerce/includes/class-woocommerce.php
index a7bed480c95..3ca44822882 100644
--- a/plugins/woocommerce/includes/class-woocommerce.php
+++ b/plugins/woocommerce/includes/class-woocommerce.php
@@ -612,7 +612,12 @@ final class WooCommerce {
 	 *
 	 * Legacy REST requests should still run some extra code for backwards compatibility.
 	 *
-	 * @todo: replace this function once core WP function is available: https://core.trac.wordpress.org/ticket/42061.
+	 * This method cannot be replaced with core's wp_is_serving_rest_request(): that function reads the
+	 * REST_REQUEST constant, which is only defined once rest_api_loaded() runs on parse_request — long
+	 * after this method is first called during plugin bootstrap (e.g. to decide whether to load the
+	 * frontend includes). Sniffing the request URI is the only signal available that early. Code that
+	 * runs after parse_request should prefer wp_is_serving_rest_request(), or wp_is_rest_endpoint(),
+	 * which also covers internal REST requests dispatched during a regular page load.
 	 *
 	 * @return bool
 	 */
@@ -621,8 +626,13 @@ final class WooCommerce {
 			return false;
 		}

+		// Pretty permalinks: the REST prefix is part of the path, e.g. /wp-json/wc/v3/products.
+		// Plain permalinks: the route is passed as a query parameter instead, e.g. ?rest_route=/wc/v3/products
+		// (also used by Jetpack-signed REST requests regardless of the permalink structure).
+		// phpcs:disable WordPress.Security.ValidatedSanitizedInput.MissingUnslash, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized, WordPress.Security.NonceVerification.Recommended
 		$rest_prefix         = trailingslashit( rest_get_url_prefix() );
-		$is_rest_api_request = ( false !== strpos( $_SERVER['REQUEST_URI'], $rest_prefix ) ); // phpcs:disable WordPress.Security.ValidatedSanitizedInput.MissingUnslash, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
+		$is_rest_api_request = ( false !== strpos( $_SERVER['REQUEST_URI'], $rest_prefix ) ) || ! empty( $_GET['rest_route'] );
+		// phpcs:enable

 		/**
 		 * Whether this is a REST API request.
diff --git a/plugins/woocommerce/tests/php/includes/class-woocommerce-test.php b/plugins/woocommerce/tests/php/includes/class-woocommerce-test.php
index 236b03c5ab0..b77c9a1de94 100644
--- a/plugins/woocommerce/tests/php/includes/class-woocommerce-test.php
+++ b/plugins/woocommerce/tests/php/includes/class-woocommerce-test.php
@@ -73,6 +73,26 @@ class WooCommerce_Test extends \WC_Unit_Test_Case {
 		$this->assertEquals( WC()->is_rest_api_request(), true );
 	}

+	/**
+	 * @testdox Should detect a REST API request that uses plain permalinks (?rest_route=).
+	 */
+	public function test_is_rest_api_request_returns_true_for_plain_permalinks(): void {
+		$_SERVER['REQUEST_URI'] = '/index.php?rest_route=/wc/v3/products';
+		$_GET['rest_route']     = '/wc/v3/products';
+
+		$this->assertTrue( WC()->is_rest_api_request(), 'A ?rest_route= request should be detected as a REST API request.' );
+	}
+
+	/**
+	 * @testdox Should not detect a request with an empty rest_route parameter as a REST API request.
+	 */
+	public function test_is_rest_api_request_returns_false_for_empty_rest_route(): void {
+		$_SERVER['REQUEST_URI'] = '/index.php?rest_route=';
+		$_GET['rest_route']     = '';
+
+		$this->assertFalse( WC()->is_rest_api_request(), 'An empty rest_route parameter should not be detected as a REST API request.' );
+	}
+
 	/**
 	 * Restore the request globals after each test.
 	 */