Protect against use of download links, where the directory is no longer approved.
This commit is contained in:
parent
004b007199
commit
99f85a55d5
|
@ -31,11 +31,21 @@ class WC_Download_Handler {
|
|||
* Check if we need to download a file and check validity.
|
||||
*/
|
||||
public static function download_product() {
|
||||
// phpcs:disable WordPress.Security.NonceVerification.Recommended
|
||||
$product_id = absint( $_GET['download_file'] ); // phpcs:ignore WordPress.VIP.SuperGlobalInputUsage.AccessDetected, WordPress.VIP.ValidatedSanitizedInput.InputNotValidated, WordPress.Security.ValidatedSanitizedInput.InputNotValidated
|
||||
$product = wc_get_product( $product_id );
|
||||
$downloads = $product ? $product->get_downloads() : array();
|
||||
$data_store = WC_Data_Store::load( 'customer-download' );
|
||||
|
||||
if ( ! $product || empty( $_GET['key'] ) || empty( $_GET['order'] ) ) { // WPCS: input var ok, CSRF ok.
|
||||
$key = empty( $_GET['key'] ) ? '' : sanitize_text_field( wp_unslash( $_GET['key'] ) );
|
||||
|
||||
if (
|
||||
! $product
|
||||
|| empty( $key )
|
||||
|| empty( $_GET['order'] )
|
||||
|| ! isset( $downloads[ $key ] )
|
||||
|| ! $downloads[ $key ]->get_enabled()
|
||||
) {
|
||||
self::download_error( __( 'Invalid download link.', 'woocommerce' ) );
|
||||
}
|
||||
|
||||
|
@ -43,6 +53,7 @@ class WC_Download_Handler {
|
|||
if ( empty( $_GET['email'] ) && empty( $_GET['uid'] ) ) { // WPCS: input var ok, CSRF ok.
|
||||
self::download_error( __( 'Invalid download link.', 'woocommerce' ) );
|
||||
}
|
||||
// phpcs:enable WordPress.Security.NonceVerification.Recommended
|
||||
|
||||
$order_id = wc_get_order_id_by_order_key( wc_clean( wp_unslash( $_GET['order'] ) ) ); // WPCS: input var ok, CSRF ok.
|
||||
$order = wc_get_order( $order_id );
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
<?php
|
||||
|
||||
use Automattic\WooCommerce\Internal\ProductDownloads\ApprovedDirectories\Register as Approved_Directories;
|
||||
|
||||
/**
|
||||
* Class WC_Download_Handler_Tests.
|
||||
*/
|
||||
|
@ -49,4 +51,97 @@ class WC_Download_Handler_Tests extends \WC_Unit_Test_Case {
|
|||
$parsed_file_path = WC_Download_Handler::parse_file_path( $remote_file_path );
|
||||
$this->assertTrue( $parsed_file_path['remote_file'] );
|
||||
}
|
||||
|
||||
/**
|
||||
* @testdox Customers may not use a direct download link to obtain a downloadable file that has been disabled.
|
||||
*/
|
||||
public function test_inactive_downloads_will_not_be_served() {
|
||||
self::remove_download_handlers();
|
||||
|
||||
$downloads_served = 0;
|
||||
$download_counter = function () use ( &$downloads_served ) {
|
||||
$downloads_served++;
|
||||
};
|
||||
|
||||
// Track downloads served.
|
||||
add_action( 'woocommerce_download_file_force', $download_counter );
|
||||
|
||||
/**
|
||||
* @var Approved_Directories $approved_directories
|
||||
*/
|
||||
$approved_directories = wc_get_container()->get( Approved_Directories::class );
|
||||
$approved_directories->set_mode( Approved_Directories::MODE_ENABLED );
|
||||
$approved_directories->add_approved_directory( 'https://always.trusted' );
|
||||
$approved_directory_rule_id = $approved_directories->add_approved_directory( 'https://new.supplier' );
|
||||
|
||||
$product = WC_Helper_Product::create_downloadable_product(
|
||||
array(
|
||||
array(
|
||||
'name' => 'Book 1',
|
||||
'file' => 'https://always.trusted/123.pdf',
|
||||
),
|
||||
array(
|
||||
'name' => 'Book 2',
|
||||
'file' => 'https://new.supplier/456.pdf',
|
||||
),
|
||||
)
|
||||
);
|
||||
|
||||
$customer = WC_Helper_Customer::create_customer();
|
||||
$email = 'admin@example.org';
|
||||
$order = WC_Helper_Order::create_order( $customer->get_id(), $product );
|
||||
$order->set_status( 'completed' );
|
||||
$order->save();
|
||||
|
||||
$product_id = $product->get_id();
|
||||
$downloads = $product->get_downloads();
|
||||
$download_keys = array_keys( $downloads );
|
||||
|
||||
// phpcs:disable WordPress.Security.NonceVerification.Recommended WordPress.Security.ValidatedSanitizedInput.InputNotValidated
|
||||
$_GET = array(
|
||||
'download_file' => $product_id,
|
||||
'order' => $order->get_order_key(),
|
||||
'email' => $email,
|
||||
'uid' => hash( 'sha256', $email ),
|
||||
'key' => $download_keys[0],
|
||||
);
|
||||
|
||||
WC_Download_Handler::download_product();
|
||||
$this->assertEquals( 1, $downloads_served, 'Valid download request (download key 1 - corresponding approved directory rule enabled) was successfully served.' );
|
||||
|
||||
$_GET['key'] = $download_keys[1];
|
||||
WC_Download_Handler::download_product();
|
||||
$this->assertEquals( 2, $downloads_served, 'Valid download request (download key 2 - corresponding approved directory rule enabled) was successfully served.' );
|
||||
|
||||
$approved_directories->disable_by_id( $approved_directory_rule_id );
|
||||
$this->expectException( WPDieException::class );
|
||||
WC_Download_Handler::download_product();
|
||||
$this->assertEquals( 2, $downloads_served, 'Invalid download request (download key 2 - corresponding approved directory rule disabled) was not served.' );
|
||||
|
||||
$_GET['key'] = $download_keys[0];
|
||||
WC_Download_Handler::download_product();
|
||||
$this->assertEquals( 1, $downloads_served, 'Valid download request (download key 1 - corresponding approved directory rule remained enabled) was still successfully served.' );
|
||||
|
||||
// Cleanup.
|
||||
add_action( 'woocommerce_download_file_force', $download_counter );
|
||||
self::restore_download_handlers();
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregister download handlers to prevent unwanted output and side-effects.
|
||||
*/
|
||||
private static function remove_download_handlers() {
|
||||
remove_action( 'woocommerce_download_file_xsendfile', array( WC_Download_Handler::class, 'download_file_xsendfile' ) );
|
||||
remove_action( 'woocommerce_download_file_redirect', array( WC_Download_Handler::class, 'download_file_redirect' ) );
|
||||
remove_action( 'woocommerce_download_file_force', array( WC_Download_Handler::class, 'download_file_force' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Restores download handlers in case needed by other tests.
|
||||
*/
|
||||
private static function restore_download_handlers() {
|
||||
add_action( 'woocommerce_download_file_redirect', array( WC_Download_Handler::class, 'download_file_redirect' ), 10, 2 );
|
||||
add_action( 'woocommerce_download_file_xsendfile', array( WC_Download_Handler::class, 'download_file_xsendfile' ), 10, 2 );
|
||||
add_action( 'woocommerce_download_file_force', array( WC_Download_Handler::class, 'download_file_force' ), 10, 2 );
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue