From 33ec179849c7f64d3ec5120184626231d860a1d7 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 18 Jul 2023 06:26:57 -0700 Subject: [PATCH] Release: 10.6.1 (#10229) * Empty commit for release pull request * Product Filters > Fix Performance issue and Fatal error on stores with a high volume of products (#10198) * Remove queries that fetch all products for manipulating the results returned by the Store API for certain use-cases. * Keep support for Product Collection block * Stop reading Product IDs from asset store in filter blocks (#10195) * Remove queries that fetch all products for manipulating the results returned by the Store API for certain use-cases. * Remove the code that's supposed to read product ids for filter context and logic around that in useCollectionData * Fix incorrect merge --------- Co-authored-by: Patricia Hillebrandt * Add testing instructions for 10.6.1 * Fix margin issue with the Proceed to checkout button on the site editor (#10182) * Fix margin issue with the Proceed to checkout button on the site editor * Remove unecessary selector * Merge pull request from GHSA-gxfx-93xq-pr6p * Add cors check * refactor logic * Refactor add_cors_headers to allow null and allowed hosts * Move remove_filter inline * Revert unrelated code style changes * Add explainer to docblock * Remove access for null origin * Move CORS handling to auth class so it applies API wide * Move only Authentication to priority 11 * Handle preflight requests so cart-tokens work --------- Co-authored-by: Mike Jolley * Add changelog to readme * Bump versions, readme, changelog, and testing notes * Add testing zip * fix migration (#10205) * New testing zip * Don't send headers early in Store API (#10241) * Replace sanitization functions to enforce string values (#10242) * New testing zip * Update changelog --------- Co-authored-by: github-actions Co-authored-by: Patricia Hillebrandt Co-authored-by: Karol Manijak <20098064+kmanijak@users.noreply.github.com> Co-authored-by: Thomas Roberts Co-authored-by: Alex Florisca Co-authored-by: Seghir Nadir Co-authored-by: Mike Jolley Co-authored-by: Luigi Teschio --- .../hooks/collections/use-collection-data.ts | 5 +- assets/js/blocks/attribute-filter/block.tsx | 5 - .../cart-express-payment-block/editor.scss | 5 + assets/js/blocks/price-filter/block.tsx | 5 - assets/js/blocks/stock-filter/block.tsx | 5 - composer.json | 2 +- .../testing/releases/1061.md | 46 ++++++++ .../testing/releases/README.md | 1 + package-lock.json | 4 +- package.json | 2 +- readme.txt | 13 ++- src/BlockTypes/ProductCollection.php | 13 --- src/BlockTypes/ProductQuery.php | 28 ----- src/Domain/Bootstrap.php | 9 +- src/Package.php | 2 +- src/StoreApi/Authentication.php | 104 +++++++++++++++++- .../Schemas/V1/AbstractAddressSchema.php | 20 ++-- .../Schemas/V1/BillingAddressSchema.php | 2 +- src/StoreApi/StoreApi.php | 9 +- tests/php/StoreApi/Routes/Checkout.php | 48 ++++++++ woocommerce-gutenberg-products-block.php | 2 +- 21 files changed, 240 insertions(+), 90 deletions(-) create mode 100644 docs/internal-developers/testing/releases/1061.md diff --git a/assets/js/base/context/hooks/collections/use-collection-data.ts b/assets/js/base/context/hooks/collections/use-collection-data.ts index 7fd25c8a0a6..07bb5afdbd2 100644 --- a/assets/js/base/context/hooks/collections/use-collection-data.ts +++ b/assets/js/base/context/hooks/collections/use-collection-data.ts @@ -3,7 +3,7 @@ */ import { useState, useEffect, useMemo } from '@wordpress/element'; import { useDebounce } from 'use-debounce'; -import { isEmpty, objectHasProp } from '@woocommerce/types'; +import { objectHasProp } from '@woocommerce/types'; import { sort } from 'fast-sort'; import { useShallowEqual } from '@woocommerce/base-hooks'; @@ -46,7 +46,6 @@ interface UseCollectionDataProps { queryStock?: boolean; queryRating?: boolean; queryState: Record< string, unknown >; - productIds?: number[]; isEditor?: boolean; } @@ -56,7 +55,6 @@ export const useCollectionData = ( { queryStock, queryRating, queryState, - productIds, isEditor = false, }: UseCollectionDataProps ) => { let context = useQueryStateContext(); @@ -166,7 +164,6 @@ export const useCollectionData = ( { per_page: undefined, orderby: undefined, order: undefined, - ...( ! isEmpty( productIds ) && { include: productIds } ), ...collectionDataQueryVars, }, shouldSelect: debouncedShouldSelect, diff --git a/assets/js/blocks/attribute-filter/block.tsx b/assets/js/blocks/attribute-filter/block.tsx index 23a64ff1430..eec2207d118 100644 --- a/assets/js/blocks/attribute-filter/block.tsx +++ b/assets/js/blocks/attribute-filter/block.tsx @@ -89,10 +89,6 @@ const AttributeFilterBlock = ( { isString ); - const productIds = isEditor - ? [] - : getSettingWithCoercion( 'product_ids', [], Array.isArray ); - const [ hasSetFilterDefaultsFromUrl, setHasSetFilterDefaultsFromUrl ] = useState( false ); @@ -144,7 +140,6 @@ const AttributeFilterBlock = ( { queryState: { ...queryState, }, - productIds, isEditor, } ); diff --git a/assets/js/blocks/cart/inner-blocks/cart-express-payment-block/editor.scss b/assets/js/blocks/cart/inner-blocks/cart-express-payment-block/editor.scss index f30f4936feb..24a209975d4 100644 --- a/assets/js/blocks/cart/inner-blocks/cart-express-payment-block/editor.scss +++ b/assets/js/blocks/cart/inner-blocks/cart-express-payment-block/editor.scss @@ -13,6 +13,11 @@ } } +.wp-block-woocommerce-proceed-to-checkout-block { + margin-bottom: 28px; + margin-top: 28px; +} + .wp-block-woocommerce-checkout-express-payment-block-placeholder { * { pointer-events: all; // Overrides parent disabled component in editor context diff --git a/assets/js/blocks/price-filter/block.tsx b/assets/js/blocks/price-filter/block.tsx index 24b89a34a14..4b6355efe1b 100644 --- a/assets/js/blocks/price-filter/block.tsx +++ b/assets/js/blocks/price-filter/block.tsx @@ -99,10 +99,6 @@ const PriceFilterBlock = ( { isBoolean ); - const productIds = isEditor - ? [] - : getSettingWithCoercion( 'product_ids', [], Array.isArray ); - const [ hasSetFilterDefaultsFromUrl, setHasSetFilterDefaultsFromUrl ] = useState( false ); @@ -112,7 +108,6 @@ const PriceFilterBlock = ( { const { results, isLoading } = useCollectionData( { queryPrices: true, queryState, - productIds, isEditor, } ); diff --git a/assets/js/blocks/stock-filter/block.tsx b/assets/js/blocks/stock-filter/block.tsx index 78541c959a6..9b55c03ef32 100644 --- a/assets/js/blocks/stock-filter/block.tsx +++ b/assets/js/blocks/stock-filter/block.tsx @@ -76,10 +76,6 @@ const StockStatusFilterBlock = ( { {} ); - const productIds = isEditor - ? [] - : getSettingWithCoercion( 'product_ids', [], Array.isArray ); - const STOCK_STATUS_OPTIONS: { current: Current } = useRef( getSetting( 'hideOutOfStockItems', false ) ? otherStockStatusOptions @@ -111,7 +107,6 @@ const StockStatusFilterBlock = ( { useCollectionData( { queryStock: true, queryState, - productIds, isEditor, } ); diff --git a/composer.json b/composer.json index 5003da8af52..350ae0eb539 100644 --- a/composer.json +++ b/composer.json @@ -3,7 +3,7 @@ "description": "WooCommerce blocks for the Gutenberg editor.", "homepage": "https://woocommerce.com/", "type": "wordpress-plugin", - "version": "10.6.0", + "version": "10.6.1", "keywords": [ "gutenberg", "woocommerce", diff --git a/docs/internal-developers/testing/releases/1061.md b/docs/internal-developers/testing/releases/1061.md new file mode 100644 index 00000000000..2a70a9d4dd5 --- /dev/null +++ b/docs/internal-developers/testing/releases/1061.md @@ -0,0 +1,46 @@ +# Testing notes and ZIP for release 10.6.1 + +Zip file for testing: [woocommerce-gutenberg-products-block.zip](https://github.com/woocommerce/woocommerce-blocks/files/12071467/woocommerce-gutenberg-products-block.zip) + + +## WooCommerce Core + +### Product Filters > Fix Performance issue and Fatal error on stores with a high volume of products [#10198](https://github.com/woocommerce/woocommerce-blocks/pull/10198) + +1. Make sure you have products in your store with attributes and stock status (You can do so by simply importing the sample products from the core of Woo). +2. Create a new post. +3. Insert the Products (Beta) block. +4. Insert the Filter by Attribute, Filter by Stock and Filter by Price +5. Make sure all filters have the "Display product count" setting enabled (it is disabled by default). + +Screenshot 2023-07-13 at 20 09 31 + +6. Save the post and go to the front-end +7. Make sure you can use all filters as expected and without any problems/errors: the product counts should match the number of existing products in the store with the filtered criteria, not just what is displayed on the page. +8. Repeat all steps from 2 to 7, but using the Products Collection block instead of Products (beta). Make sure all filters work as expected. + +### Stop reading Product IDs from asset store in filter blocks [#10195](https://github.com/woocommerce/woocommerce-blocks/pull/10195) + +1. Go to Editor +2. Add Filters pattern +3. Add Products (Beta) block +4. Save and go to frontend +5. Play around with filters + +Expected: Make sure you can use all filters as expected and without any problems/errors: the product counts should match the number of existing products in the store with the filtered criteria, not just what is displayed on the page. + +### Fix margin issue with the Proceed to checkout button on the site editor [#10182](https://github.com/woocommerce/woocommerce-blocks/pull/10182) + +1. Install and activate a block theme (e.g. Twenty Twenty Three) +2. Edit the Cart page +3. Check the Proceed to Checkout button has some padding around it +4. Switch to a non block theme page (e.g. Storefront) +5. Check that the Proceed to Checkout button has the same padding around it + +| Before | After | +|--------------------------------------------------------------------------------------------------------------------------------------------- |--------------------------------------------------------------------------------------------------------------------------------------------- | +| ![Screenshot 2023-07-12 at 09 59 20](https://github.com/woocommerce/woocommerce-blocks/assets/3966773/6130d5bc-9e9b-487d-a2a3-9d2a46ced417) | ![Screenshot 2023-07-12 at 12 57 32](https://github.com/woocommerce/woocommerce-blocks/assets/3966773/48681cef-6517-4c52-af71-7d78d0dbd02e) | + + + + diff --git a/docs/internal-developers/testing/releases/README.md b/docs/internal-developers/testing/releases/README.md index bccdc5828f8..be8dae9f598 100644 --- a/docs/internal-developers/testing/releases/README.md +++ b/docs/internal-developers/testing/releases/README.md @@ -158,6 +158,7 @@ Every release includes specific testing instructions for new features and bug fi - [10.4.5](./1045.md) - [10.5.0](./1050.md) - [10.6.0](./1060.md) + - [10.6.1](./1061.md) diff --git a/package-lock.json b/package-lock.json index 543a3cfc2d9..793b2c0a47f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@woocommerce/block-library", - "version": "10.6.0", + "version": "10.6.1", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@woocommerce/block-library", - "version": "10.6.0", + "version": "10.6.1", "hasInstallScript": true, "license": "GPL-3.0+", "dependencies": { diff --git a/package.json b/package.json index ef664e8c480..53c85444c42 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "@woocommerce/block-library", "title": "WooCommerce Blocks", "author": "Automattic", - "version": "10.6.0", + "version": "10.6.1", "description": "WooCommerce blocks for the Gutenberg editor.", "homepage": "https://github.com/woocommerce/woocommerce-gutenberg-products-block/", "keywords": [ diff --git a/readme.txt b/readme.txt index 5cf66cca757..0356038ba0d 100644 --- a/readme.txt +++ b/readme.txt @@ -4,7 +4,7 @@ Tags: gutenberg, woocommerce, woo commerce, products, blocks, woocommerce blocks Requires at least: 6.2 Tested up to: 6.2 Requires PHP: 7.3 -Stable tag: 10.6.0 +Stable tag: 10.6.1 License: GPLv3 License URI: https://www.gnu.org/licenses/gpl-3.0.html @@ -81,6 +81,17 @@ Release and roadmap notes available on the [WooCommerce Developers Blog](https:/ == Changelog == += 10.6.1 - 2021-07-17 = + +#### Bug Fixes + +- Improve performance and fix memory exhaustion errors that could be triggered for stores with a high volume of products using the Products (Beta) or Products Collection blocks. ([#10198](https://github.com/woocommerce/woocommerce-blocks/pull/10198)) +- Fix a visual bug with margins around the Proceed to Checkout button on the Cart block. ([#10182](https://github.com/woocommerce/woocommerce-blocks/pull/10182)) +- Fixed formatting for addresses sent to the Store API. ([#10242](https://github.com/woocommerce/woocommerce-blocks/pull/10242)) + +#### Security +- Update CORS handling in Store API + = 10.6.0 - 2023-07-07 = #### Enhancements diff --git a/src/BlockTypes/ProductCollection.php b/src/BlockTypes/ProductCollection.php index 615924ac29c..0f0bc42c9ee 100644 --- a/src/BlockTypes/ProductCollection.php +++ b/src/BlockTypes/ProductCollection.php @@ -132,19 +132,6 @@ public function add_support_for_filter_blocks( $pre_render, $parsed_block ) { * which is a server-side rendered (SSR) block, retrieves the products that match the filters. */ $this->asset_data_registry->add( 'is_rendering_php_template', true, true ); - - $frontend_query = $this->get_final_frontend_query( $parsed_block['attrs']['query'], null, true ); - // Override the query to get all products. - $fields_to_override = [ - 'posts_per_page' => -1, - 'paged' => null, - ]; - $new_array = array_merge( $frontend_query, $fields_to_override ); - - $products = new \WP_Query( $new_array ); - $product_ids = wp_list_pluck( $products->posts, 'ID' ); - // Add the product ids to the asset data registry, so that filter blocks can use it. - $this->asset_data_registry->add( 'product_ids', $product_ids, true ); } /** diff --git a/src/BlockTypes/ProductQuery.php b/src/BlockTypes/ProductQuery.php index 380f9a34224..fe5f858a531 100644 --- a/src/BlockTypes/ProductQuery.php +++ b/src/BlockTypes/ProductQuery.php @@ -135,7 +135,6 @@ public function update_query( $pre_render, $parsed_block ) { // Set this so that our product filters can detect if it's a PHP template. $this->asset_data_registry->add( 'has_filterable_products', true, true ); $this->asset_data_registry->add( 'is_rendering_php_template', true, true ); - $this->asset_data_registry->add( 'product_ids', $this->get_products_ids_by_attributes( $parsed_block ), true ); add_filter( 'query_loop_block_query_vars', array( $this, 'build_query' ), @@ -224,33 +223,6 @@ public function build_query( $query ) { return $this->filter_query_to_only_include_ids( $merged_query, $handpicked_products ); } - /** - * Return the product ids based on the attributes and global query. - * This is used to allow the filter blocks to render data that matches with variations. More details here: https://github.com/woocommerce/woocommerce-blocks/issues/7245 - * - * @param array $parsed_block The block being rendered. - * @return array - */ - private function get_products_ids_by_attributes( $parsed_block ) { - $query = $this->merge_queries( - array( - 'post_type' => 'product', - 'post__in' => array(), - 'post_status' => 'publish', - 'posts_per_page' => -1, - 'meta_query' => array(), - 'tax_query' => array(), - ), - $this->get_queries_by_custom_attributes( $parsed_block ), - $this->get_global_query( $parsed_block ) - ); - - $products = new \WP_Query( $query ); - $post_ids = wp_list_pluck( $products->posts, 'ID' ); - - return $post_ids; - } - /** * Merge in the first parameter the keys "post_in", "meta_query" and "tax_query" of the second parameter. * diff --git a/src/Domain/Bootstrap.php b/src/Domain/Bootstrap.php index 9e475e705f2..d554d4bfa1b 100644 --- a/src/Domain/Bootstrap.php +++ b/src/Domain/Bootstrap.php @@ -100,11 +100,10 @@ protected function init() { $this->register_dependencies(); $this->register_payment_methods(); - if ( is_admin() ) { - if ( $this->package->get_version() !== $this->package->get_version_stored_on_db() ) { - $this->migration->run_migrations(); - $this->package->set_version_stored_on_db(); - } + // This is just a temporary solution to make sure the migrations are run. We have to refactor this. More details: https://github.com/woocommerce/woocommerce-blocks/issues/10196. + if ( $this->package->get_version() !== $this->package->get_version_stored_on_db() ) { + $this->migration->run_migrations(); + $this->package->set_version_stored_on_db(); } add_action( diff --git a/src/Package.php b/src/Package.php index 42a2dbe5a67..5707af2e7b9 100644 --- a/src/Package.php +++ b/src/Package.php @@ -109,7 +109,7 @@ public static function container( $reset = false ) { NewPackage::class, function ( $container ) { // leave for automated version bumping. - $version = '10.6.0'; + $version = '10.6.1'; return new NewPackage( $version, dirname( __DIR__ ), diff --git a/src/StoreApi/Authentication.php b/src/StoreApi/Authentication.php index 04cb63b5fb2..463a82d23bb 100644 --- a/src/StoreApi/Authentication.php +++ b/src/StoreApi/Authentication.php @@ -2,17 +2,113 @@ namespace Automattic\WooCommerce\StoreApi; use Automattic\WooCommerce\StoreApi\Utilities\RateLimits; +use Automattic\WooCommerce\StoreApi\Utilities\JsonWebToken; /** * Authentication class. */ class Authentication { /** - * Hook into WP lifecycle events. + * Hook into WP lifecycle events. This is hooked by the StoreAPI class on `rest_api_init`. */ public function init() { + if ( ! $this->is_request_to_store_api() ) { + return; + } add_filter( 'rest_authentication_errors', array( $this, 'check_authentication' ) ); add_action( 'set_logged_in_cookie', array( $this, 'set_logged_in_cookie' ) ); + add_filter( 'rest_pre_serve_request', array( $this, 'send_cors_headers' ), 10, 3 ); + add_filter( 'rest_allowed_cors_headers', array( $this, 'allowed_cors_headers' ) ); + + // Remove the default CORS headers--we will add our own. + remove_filter( 'rest_pre_serve_request', 'rest_send_cors_headers' ); + } + + /** + * Add allowed cors headers for store API headers. + * + * @param array $allowed_headers Allowed headers. + * @return array + */ + public function allowed_cors_headers( $allowed_headers ) { + $allowed_headers[] = 'Cart-Token'; + $allowed_headers[] = 'Nonce'; + $allowed_headers[] = 'X-WC-Store-API-Nonce'; + return $allowed_headers; + } + + /** + * Add CORS headers to a response object. + * + * These checks prevent access to the Store API from non-allowed origins. By default, the WordPress REST API allows + * access from any origin. Because some Store API routes return PII, we need to add our own CORS headers. + * + * Allowed origins can be changed using the WordPress `allowed_http_origins` or `allowed_http_origin` filters if + * access needs to be granted to other domains. + * + * Users of valid Cart Tokens are also allowed access from any origin. + * + * @param bool $value Whether the request has already been served. + * @param \WP_HTTP_Response $result Result to send to the client. Usually a `WP_REST_Response`. + * @param \WP_REST_Request $request Request used to generate the response. + * @return bool + */ + public function send_cors_headers( $value, $result, $request ) { + $origin = get_http_origin(); + + if ( 'null' !== $origin ) { + $origin = esc_url_raw( $origin ); + } + + // Send standard CORS headers. + $server = rest_get_server(); + $server->send_header( 'Access-Control-Allow-Methods', 'OPTIONS, GET, POST, PUT, PATCH, DELETE' ); + $server->send_header( 'Access-Control-Allow-Credentials', 'true' ); + $server->send_header( 'Vary', 'Origin', false ); + + // Allow preflight requests, certain http origins, and any origin if a cart token is present. Preflight requests + // are allowed because we'll be unable to validate cart token headers at that point. + if ( $this->is_preflight() || $this->has_valid_cart_token( $request ) || is_allowed_http_origin( $origin ) ) { + $server->send_header( 'Access-Control-Allow-Origin', $origin ); + } + + // Exit early during preflight requests. This is so someone cannot access API data by sending an OPTIONS request + // with preflight headers and a _GET property to override the method. + if ( $this->is_preflight() ) { + exit; + } + + return $value; + } + + /** + * Is the request a preflight request? Checks the request method + * + * @return boolean + */ + protected function is_preflight() { + return isset( $_SERVER['REQUEST_METHOD'], $_SERVER['HTTP_ACCESS_CONTROL_REQUEST_METHOD'], $_SERVER['HTTP_ACCESS_CONTROL_REQUEST_HEADERS'], $_SERVER['HTTP_ORIGIN'] ) && 'OPTIONS' === $_SERVER['REQUEST_METHOD']; + } + + /** + * Checks if we're using a cart token to access the Store API. + * + * @param \WP_REST_Request $request Request object. + * @return boolean + */ + protected function has_valid_cart_token( \WP_REST_Request $request ) { + $cart_token = $request->get_header( 'Cart-Token' ); + + return $cart_token && JsonWebToken::validate( $cart_token, $this->get_cart_token_secret() ); + } + + /** + * Gets the secret for the cart token using wp_salt. + * + * @return string + */ + protected function get_cart_token_secret() { + return '@' . wp_salt(); } /** @@ -22,10 +118,6 @@ public function init() { * @return \WP_Error|null|bool */ public function check_authentication( $result ) { - if ( ! $this->is_request_to_store_api() ) { - return $result; - } - // Enable Rate Limiting for logged-in users without 'edit posts' capability. if ( ! current_user_can( 'edit_posts' ) ) { $result = $this->apply_rate_limiting( $result ); @@ -42,7 +134,7 @@ public function check_authentication( $result ) { * @param string $logged_in_cookie The value for the logged in cookie. */ public function set_logged_in_cookie( $logged_in_cookie ) { - if ( ! defined( 'LOGGED_IN_COOKIE' ) || ! $this->is_request_to_store_api() ) { + if ( ! defined( 'LOGGED_IN_COOKIE' ) ) { return; } $_COOKIE[ LOGGED_IN_COOKIE ] = $logged_in_cookie; diff --git a/src/StoreApi/Schemas/V1/AbstractAddressSchema.php b/src/StoreApi/Schemas/V1/AbstractAddressSchema.php index 3d1e313b4a8..77b2198cbba 100644 --- a/src/StoreApi/Schemas/V1/AbstractAddressSchema.php +++ b/src/StoreApi/Schemas/V1/AbstractAddressSchema.php @@ -92,16 +92,16 @@ public function sanitize_callback( $address, $request, $param ) { $validation_util = new ValidationUtils(); $address = array_merge( array_fill_keys( array_keys( $this->get_properties() ), '' ), (array) $address ); - $address['country'] = wc_strtoupper( wc_clean( wp_unslash( $address['country'] ) ) ); - $address['first_name'] = wc_clean( wp_unslash( $address['first_name'] ) ); - $address['last_name'] = wc_clean( wp_unslash( $address['last_name'] ) ); - $address['company'] = wc_clean( wp_unslash( $address['company'] ) ); - $address['address_1'] = wc_clean( wp_unslash( $address['address_1'] ) ); - $address['address_2'] = wc_clean( wp_unslash( $address['address_2'] ) ); - $address['city'] = wc_clean( wp_unslash( $address['city'] ) ); - $address['state'] = $validation_util->format_state( wc_clean( wp_unslash( $address['state'] ) ), $address['country'] ); - $address['postcode'] = $address['postcode'] ? wc_format_postcode( wc_clean( wp_unslash( $address['postcode'] ) ), $address['country'] ) : ''; - $address['phone'] = wc_clean( wp_unslash( $address['phone'] ) ); + $address['country'] = wc_strtoupper( sanitize_text_field( wp_unslash( $address['country'] ) ) ); + $address['first_name'] = sanitize_text_field( wp_unslash( $address['first_name'] ) ); + $address['last_name'] = sanitize_text_field( wp_unslash( $address['last_name'] ) ); + $address['company'] = sanitize_text_field( wp_unslash( $address['company'] ) ); + $address['address_1'] = sanitize_text_field( wp_unslash( $address['address_1'] ) ); + $address['address_2'] = sanitize_text_field( wp_unslash( $address['address_2'] ) ); + $address['city'] = sanitize_text_field( wp_unslash( $address['city'] ) ); + $address['state'] = $validation_util->format_state( sanitize_text_field( wp_unslash( $address['state'] ) ), $address['country'] ); + $address['postcode'] = $address['postcode'] ? wc_format_postcode( sanitize_text_field( wp_unslash( $address['postcode'] ) ), $address['country'] ) : ''; + $address['phone'] = sanitize_text_field( wp_unslash( $address['phone'] ) ); return $address; } diff --git a/src/StoreApi/Schemas/V1/BillingAddressSchema.php b/src/StoreApi/Schemas/V1/BillingAddressSchema.php index 0ae1ca68e56..80fb83a8b78 100644 --- a/src/StoreApi/Schemas/V1/BillingAddressSchema.php +++ b/src/StoreApi/Schemas/V1/BillingAddressSchema.php @@ -54,7 +54,7 @@ public function get_properties() { */ public function sanitize_callback( $address, $request, $param ) { $address = parent::sanitize_callback( $address, $request, $param ); - $address['email'] = wc_clean( wp_unslash( $address['email'] ) ); + $address['email'] = sanitize_text_field( wp_unslash( $address['email'] ) ); return $address; } diff --git a/src/StoreApi/StoreApi.php b/src/StoreApi/StoreApi.php index 58b9fdafbb4..7b740e37a2e 100644 --- a/src/StoreApi/StoreApi.php +++ b/src/StoreApi/StoreApi.php @@ -23,11 +23,18 @@ public function init() { add_action( 'rest_api_init', function() { - self::container()->get( Authentication::class )->init(); self::container()->get( Legacy::class )->init(); self::container()->get( RoutesController::class )->register_all_routes(); } ); + // Runs on priority 11 after rest_api_default_filters() which is hooked at 10. + add_action( + 'rest_api_init', + function() { + self::container()->get( Authentication::class )->init(); + }, + 11 + ); } /** diff --git a/tests/php/StoreApi/Routes/Checkout.php b/tests/php/StoreApi/Routes/Checkout.php index 5af95a114d0..1c43f21ed8b 100644 --- a/tests/php/StoreApi/Routes/Checkout.php +++ b/tests/php/StoreApi/Routes/Checkout.php @@ -19,6 +19,8 @@ /** * Checkout Controller Tests. + * + * phpcs:disable WordPress.PHP.DevelopmentFunctions.error_log_print_r, WooCommerce.Commenting.CommentHooks.MissingHookComment */ class Checkout extends MockeryTestCase { /** @@ -385,4 +387,50 @@ public function test_checkout_force_create_account() { $customer = get_user_by( 'id', $data['customer_id'] ); $this->assertEquals( $customer->user_email, 'testaccount@test.com' ); } + + /** + * Test account creation options. + */ + public function test_checkout_invalid_address_data() { + $request = new \WP_REST_Request( 'POST', '/wc/store/v1/checkout' ); + $request->set_header( 'Nonce', wp_create_nonce( 'wc_store_api' ) ); + $request->set_body_params( + array( + 'billing_address' => (object) array( + 'first_name' => 'test', + 'last_name' => array( + 'invalid' => 'invalid_data', + ), + 'company' => '', + 'address_1' => 'test', + 'address_2' => '', + 'city' => 'test', + 'state' => '', + 'postcode' => 'cb241ab', + 'country' => 'GB', + 'phone' => '', + 'email' => 'testaccount@test.com', + ), + 'shipping_address' => (object) array( + 'first_name' => 'test', + 'last_name' => 'test', + 'company' => '', + 'address_1' => 'test', + 'address_2' => '', + 'city' => 'test', + 'state' => '', + 'postcode' => 'cb241ab', + 'country' => 'GB', + 'phone' => '', + ), + 'payment_method' => 'bacs', + ) + ); + + $response = rest_get_server()->dispatch( $request ); + $status = $response->get_status(); + $data = $response->get_data(); + + $this->assertEquals( 400, $status, print_r( $data, true ) ); + } } diff --git a/woocommerce-gutenberg-products-block.php b/woocommerce-gutenberg-products-block.php index 148df4c703b..5b69a165e1b 100644 --- a/woocommerce-gutenberg-products-block.php +++ b/woocommerce-gutenberg-products-block.php @@ -3,7 +3,7 @@ * Plugin Name: WooCommerce Blocks * Plugin URI: https://github.com/woocommerce/woocommerce-gutenberg-products-block * Description: WooCommerce blocks for the Gutenberg editor. - * Version: 10.6.0 + * Version: 10.6.1 * Author: Automattic * Author URI: https://woocommerce.com * Text Domain: woo-gutenberg-products-block