Skip to content

Commit c368f75

Browse files
committed
Enforce strict user capability checks when deleting test orders (#11122)
1 parent 2a9c605 commit c368f75

File tree

3 files changed

+76
-31
lines changed

3 files changed

+76
-31
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
Significance: patch
2+
Type: update
3+
Comment: Enforce strict capability checks when deleting test orders.
4+
5+

includes/class-wc-payments-status.php

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,11 @@ public function debug_tools( $tools ) {
106106
* @return string Success or error message.
107107
*/
108108
public function delete_test_orders() {
109+
// Add explicit capability check.
110+
if ( ! current_user_can( 'manage_woocommerce' ) ) {
111+
return __( 'You do not have permission to delete orders.', 'woocommerce-payments' );
112+
}
113+
109114
try {
110115
// Get all orders with test mode meta.
111116
$test_orders = wc_get_orders(
@@ -126,7 +131,7 @@ public function delete_test_orders() {
126131
$deleted_count = 0;
127132
foreach ( $test_orders as $order ) {
128133
// Permanently delete the order (skip trash).
129-
if ( $order->delete() ) {
134+
if ( $order->delete( true ) ) {
130135
++$deleted_count;
131136
}
132137
}

tests/unit/test-class-wc-payments-status.php

Lines changed: 65 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,9 @@ class WC_Payments_Status_Test extends WCPAY_UnitTestCase {
4343
public function set_up() {
4444
parent::set_up();
4545

46+
// Set up an admin user with proper capabilities.
47+
wp_set_current_user( 1 );
48+
4649
$this->mock_gateway = $this->createMock( WC_Payment_Gateway_WCPay::class );
4750
$this->mock_http = $this->createMock( WC_Payments_Http_Interface::class );
4851
$this->mock_account = $this->createMock( WC_Payments_Account::class );
@@ -108,21 +111,20 @@ public function test_delete_test_orders_tool_structure() {
108111
*/
109112
public function test_delete_test_orders_with_no_orders() {
110113
// Mock wc_get_orders to return empty array.
111-
add_filter(
112-
'woocommerce_order_data_store_cpt_get_orders_query',
113-
function ( $query, $query_vars ) {
114-
if ( isset( $query_vars['meta_key'] ) && '_wcpay_mode' === $query_vars['meta_key'] ) {
115-
$query['post__in'] = [ 0 ]; // Force no results.
116-
}
117-
return $query;
118-
},
119-
10,
120-
2
121-
);
114+
$filter_callback = function ( $query, $query_vars ) {
115+
if ( isset( $query_vars['meta_key'] ) && '_wcpay_mode' === $query_vars['meta_key'] ) {
116+
$query['post__in'] = [ 0 ]; // Force no results.
117+
}
118+
return $query;
119+
};
120+
add_filter( 'woocommerce_order_data_store_cpt_get_orders_query', $filter_callback, 10, 2 );
122121

123122
$result = $this->status->delete_test_orders();
124123

125124
$this->assertEquals( 'No test orders found.', $result );
125+
126+
// Clean up filter.
127+
remove_filter( 'woocommerce_order_data_store_cpt_get_orders_query', $filter_callback, 10 );
126128
}
127129

128130
/**
@@ -148,14 +150,12 @@ public function test_delete_test_orders_deletes_orders() {
148150
// Verify result message.
149151
$this->assertStringContainsString( '2 test orders have been permanently deleted.', $result );
150152

151-
// Verify test orders were moved to trash.
152-
$trashed_order1 = wc_get_order( $order1->get_id() );
153-
$this->assertInstanceOf( WC_Order::class, $trashed_order1 );
154-
$this->assertEquals( 'trash', $trashed_order1->get_status() );
153+
// Verify test orders were permanently deleted (no longer exist).
154+
$deleted_order1 = wc_get_order( $order1->get_id() );
155+
$this->assertFalse( $deleted_order1, 'Test order 1 should be permanently deleted' );
155156

156-
$trashed_order2 = wc_get_order( $order2->get_id() );
157-
$this->assertInstanceOf( WC_Order::class, $trashed_order2 );
158-
$this->assertEquals( 'trash', $trashed_order2->get_status() );
157+
$deleted_order2 = wc_get_order( $order2->get_id() );
158+
$this->assertFalse( $deleted_order2, 'Test order 2 should be permanently deleted' );
159159

160160
// Verify non-test order was not deleted.
161161
$order3_check = wc_get_order( $order3->get_id() );
@@ -176,29 +176,64 @@ public function test_delete_test_orders_singular_message() {
176176

177177
$this->assertStringContainsString( '1 test order has been permanently deleted.', $result );
178178

179-
// Verify order was moved to trash.
180-
$trashed_order = wc_get_order( $order->get_id() );
181-
$this->assertInstanceOf( WC_Order::class, $trashed_order );
182-
$this->assertEquals( 'trash', $trashed_order->get_status() );
179+
// Verify order was permanently deleted (no longer exists).
180+
$deleted_order = wc_get_order( $order->get_id() );
181+
$this->assertFalse( $deleted_order, 'Test order should be permanently deleted' );
183182
}
184183

185184
/**
186185
* Test delete_test_orders handles exceptions.
187186
*/
188187
public function test_delete_test_orders_handles_exception() {
189188
// Mock wc_get_orders to throw an exception.
190-
add_filter(
191-
'woocommerce_order_data_store_cpt_get_orders_query',
192-
function ( $query, $query_vars ) {
193-
throw new Exception( 'Database error' );
194-
},
195-
10,
196-
2
197-
);
189+
$filter_callback = function () {
190+
throw new Exception( 'Database error' );
191+
};
192+
add_filter( 'woocommerce_order_data_store_cpt_get_orders_query', $filter_callback, 10, 2 );
198193

199194
$result = $this->status->delete_test_orders();
200195

201196
$this->assertStringContainsString( 'Error deleting test orders:', $result );
202197
$this->assertStringContainsString( 'Database error', $result );
198+
199+
// Clean up filter.
200+
remove_filter( 'woocommerce_order_data_store_cpt_get_orders_query', $filter_callback, 10 );
201+
}
202+
203+
/**
204+
* Test delete_test_orders denies access for users without manage_woocommerce capability.
205+
*/
206+
public function test_delete_test_orders_requires_manage_woocommerce_capability() {
207+
// Create test orders with _wcpay_mode meta.
208+
$order1 = wc_create_order();
209+
$order1->update_meta_data( '_wcpay_mode', 'test' );
210+
$order1->save();
211+
212+
$order2 = wc_create_order();
213+
$order2->update_meta_data( '_wcpay_mode', 'test' );
214+
$order2->save();
215+
216+
// Mock that the current user is missing the manage_woocommerce capability.
217+
$filter_callback = function ( $allcaps ) {
218+
$allcaps['manage_woocommerce'] = false;
219+
220+
return $allcaps;
221+
};
222+
add_filter( 'user_has_cap', $filter_callback );
223+
224+
$result = $this->status->delete_test_orders();
225+
226+
// Verify permission denied message.
227+
$this->assertEquals( 'You do not have permission to delete orders.', $result );
228+
229+
// Verify orders were NOT deleted.
230+
$order1_check = wc_get_order( $order1->get_id() );
231+
$this->assertInstanceOf( WC_Order::class, $order1_check );
232+
233+
$order2_check = wc_get_order( $order2->get_id() );
234+
$this->assertInstanceOf( WC_Order::class, $order2_check );
235+
236+
// Clean up filter.
237+
remove_filter( 'user_has_cap', $filter_callback );
203238
}
204239
}

0 commit comments

Comments
 (0)