Merge pull request #27140 from woocommerce/fix/23790

Always sanitize coupon code to prevent inconsistent between admins and shop owners
This commit is contained in:
Néstor Soriano 2020-08-07 12:42:02 +02:00 committed by GitHub
commit 76cf1e4e93
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 134 additions and 5 deletions

View File

@ -1691,7 +1691,7 @@ class WC_Cart extends WC_Legacy_Cart {
*/ */
public function remove_coupon( $coupon_code ) { public function remove_coupon( $coupon_code ) {
$coupon_code = wc_format_coupon_code( $coupon_code ); $coupon_code = wc_format_coupon_code( $coupon_code );
$position = array_search( $coupon_code, $this->get_applied_coupons(), true ); $position = array_search( $coupon_code, array_map( 'wc_format_coupon_code', $this->get_applied_coupons() ), true );
if ( false !== $position ) { if ( false !== $position ) {
unset( $this->applied_coupons[ $position ] ); unset( $this->applied_coupons[ $position ] );

View File

@ -153,6 +153,10 @@ class WC_Install {
'wc_update_440_insert_attribute_terms_for_variable_products', 'wc_update_440_insert_attribute_terms_for_variable_products',
'wc_update_440_db_version', 'wc_update_440_db_version',
), ),
'4.5.0' => array(
'wc_update_450_sanitize_coupons_code',
'wc_update_450_db_version',
),
); );
/** /**

View File

@ -255,6 +255,9 @@ class WC_Post_Data {
} }
} elseif ( 'product' === $data['post_type'] && 'auto-draft' === $data['post_status'] ) { } elseif ( 'product' === $data['post_type'] && 'auto-draft' === $data['post_status'] ) {
$data['post_title'] = 'AUTO-DRAFT'; $data['post_title'] = 'AUTO-DRAFT';
} elseif ( 'shop_coupon' === $data['post_type'] ) {
// Coupons should never allow unfiltered HTML.
$data['post_title'] = wp_filter_kses( $data['post_title'] );
} }
return $data; return $data;

View File

@ -721,7 +721,7 @@ class WC_Coupon_Data_Store_CPT extends WC_Data_Store_WP implements WC_Coupon_Dat
return $wpdb->get_col( return $wpdb->get_col(
$wpdb->prepare( $wpdb->prepare(
"SELECT ID FROM $wpdb->posts WHERE post_title = %s AND post_type = 'shop_coupon' AND post_status = 'publish' ORDER BY post_date DESC", "SELECT ID FROM $wpdb->posts WHERE post_title = %s AND post_type = 'shop_coupon' AND post_status = 'publish' ORDER BY post_date DESC",
$code wc_sanitize_coupon_code( $code )
) )
); );
} }

View File

@ -375,7 +375,7 @@ function wc_format_coupon_code( $value ) {
* @return string * @return string
*/ */
function wc_sanitize_coupon_code( $value ) { function wc_sanitize_coupon_code( $value ) {
return sanitize_post_field( 'post_title', $value, 0, 'db' ); return wp_filter_kses( sanitize_post_field( 'post_title', $value, 0, 'db' ) );
} }
/** /**

View File

@ -2175,3 +2175,70 @@ function wc_update_440_insert_attribute_terms_for_variable_products() {
function wc_update_440_db_version() { function wc_update_440_db_version() {
WC_Install::update_db_version( '4.4.0' ); WC_Install::update_db_version( '4.4.0' );
} }
/**
* Update DB version to 4.5.0.
*/
function wc_update_450_db_version() {
WC_Install::update_db_version( '4.5.0' );
}
/**
* Sanitize all coupons code.
*
* @return bool True to run again, false if completed.
*/
function wc_update_450_sanitize_coupons_code() {
global $wpdb;
$coupon_id = 0;
$last_coupon_id = get_option( 'woocommerce_update_450_last_coupon_id', '0' );
$coupons = $wpdb->get_results(
$wpdb->prepare(
"SELECT ID, post_title FROM $wpdb->posts WHERE ID > %d AND post_type = 'shop_coupon' LIMIT 10",
$last_coupon_id
),
ARRAY_A
);
if ( empty( $coupons ) ) {
delete_option( 'woocommerce_update_450_last_coupon_id' );
return false;
}
foreach ( $coupons as $key => $data ) {
$coupon_id = intval( $data['ID'] );
$code = trim( wp_filter_kses( $data['post_title'] ) );
if ( ! empty( $code ) && $data['post_title'] !== $code ) {
$wpdb->update(
$wpdb->posts,
array(
'post_title' => $code,
),
array(
'ID' => $coupon_id,
),
array(
'%s',
),
array(
'%d',
)
);
// Clean cache.
clean_post_cache( $coupon_id );
wp_cache_delete( WC_Cache_Helper::get_cache_prefix( 'coupons' ) . 'coupon_id_from_code_' . $data['post_title'], 'coupons' );
}
}
// Start the run again.
if ( $coupon_id ) {
return update_option( 'woocommerce_update_450_last_coupon_id', $coupon_id );
}
delete_option( 'woocommerce_update_450_last_coupon_id' );
return false;
}

View File

@ -171,9 +171,9 @@ class WC_Unit_Test_Case extends WP_HTTP_TestCase {
*/ */
public function login_as_administrator() { public function login_as_administrator() {
return $this->login_as_role( 'administrator' ); return $this->login_as_role( 'administrator' );
} }
/** /**
* Get an instance of a class that has been registered in the dependency injection container. * Get an instance of a class that has been registered in the dependency injection container.
* To get an instance of a legacy class (such as the ones in the 'íncludes' directory) use * To get an instance of a legacy class (such as the ones in the 'íncludes' directory) use
* 'get_legacy_instance_of' instead. * 'get_legacy_instance_of' instead.

View File

@ -0,0 +1,35 @@
<?php
/**
* Post data tests
*
* @package WooCommerce\Tests\Post_Data.
*/
/**
* Class WC_Post_Data_Test
*/
class WC_Post_Data_Test extends \WC_Unit_Test_Case {
/**
* @testdox coupon code should be always sanitized.
*/
public function test_coupon_code_sanitization() {
$this->login_as_role( 'shop_manager' );
$coupon = WC_Helper_Coupon::create_coupon( 'a&a' );
$post_data = get_post( $coupon->get_id() );
$this->assertEquals( 'a&amp;a', $post_data->post_title );
$coupon->delete( true );
$this->login_as_administrator();
$coupon = WC_Helper_Coupon::create_coupon( 'b&b' );
$post_data = get_post( $coupon->get_id() );
$this->assertEquals( 'b&amp;b', $post_data->post_title );
$coupon->delete( true );
wp_set_current_user( 0 );
$coupon = WC_Helper_Coupon::create_coupon( 'c&c' );
$post_data = get_post( $coupon->get_id() );
$this->assertEquals( 'c&amp;c', $post_data->post_title );
$coupon->delete( true );
}
}

View File

@ -0,0 +1,20 @@
<?php
/**
* Formatting functions tests
*
* @package WooCommerce\Tests\Formatting.
*/
/**
* Class WC_Formatting_Functions_Test
*/
class WC_Formatting_Functions_Test extends \WC_Unit_Test_Case {
/**
* Test wc_sanitize_coupon_code() function.
*/
public function test_wc_sanitize_coupon_code() {
$this->assertEquals( 'DUMMYCOUPON', wc_sanitize_coupon_code( 'DUMMYCOUPON' ) );
$this->assertEquals( 'a&amp;a', wc_sanitize_coupon_code( 'a&a' ) );
}
}