Commit a24559f5941 for woocommerce
commit a24559f59411fee0c41f1ad08c9eafa93e19df51
Author: Thomas Roberts <5656702+opr@users.noreply.github.com>
Date: Fri Mar 13 12:09:27 2026 +0000
Fix stored XSS on order notes in REST API v4 (#63661)
* Fix stored XSS on order notes in REST API v4
Add wp_kses_post sanitize_callback to the note field in the v4
OrderNoteSchema, matching the fix applied to v1/v2/v3 in #63616.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Add changelog entry for v4 order notes XSS fix
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Fix stored XSS on order notes in REST API v4
Sanitize note content with wp_kses_post() in the v4 OrderNotes
controller's create_item method, matching the fix applied to
v1/v2/v3 in #63616.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
diff --git a/plugins/woocommerce/changelog/fix-xss-order-notes-v4 b/plugins/woocommerce/changelog/fix-xss-order-notes-v4
new file mode 100644
index 00000000000..3251735454b
--- /dev/null
+++ b/plugins/woocommerce/changelog/fix-xss-order-notes-v4
@@ -0,0 +1,4 @@
+Significance: patch
+Type: fix
+
+Prevent stored XSS on order notes added via REST API v4
diff --git a/plugins/woocommerce/src/Internal/RestApi/Routes/V4/OrderNotes/Controller.php b/plugins/woocommerce/src/Internal/RestApi/Routes/V4/OrderNotes/Controller.php
index 8538225e5bd..af3f8c9c586 100644
--- a/plugins/woocommerce/src/Internal/RestApi/Routes/V4/OrderNotes/Controller.php
+++ b/plugins/woocommerce/src/Internal/RestApi/Routes/V4/OrderNotes/Controller.php
@@ -287,7 +287,7 @@ class Controller extends AbstractController {
}
$order = $this->get_order_by_id( (int) $request['order_id'] );
- $note_id = $order ? $order->add_order_note( $request['note'], $request['is_customer_note'], true ) : null;
+ $note_id = $order ? $order->add_order_note( wp_kses_post( $request['note'] ), $request['is_customer_note'], true ) : null;
if ( ! $note_id ) {
return $this->get_route_error_by_code( self::CANNOT_CREATE );
diff --git a/plugins/woocommerce/tests/php/includes/rest-api/Controllers/Version4/OrderNotes/class-wc-rest-order-notes-v4-controller-tests.php b/plugins/woocommerce/tests/php/includes/rest-api/Controllers/Version4/OrderNotes/class-wc-rest-order-notes-v4-controller-tests.php
index 3e15847e812..a06f730ebf3 100644
--- a/plugins/woocommerce/tests/php/includes/rest-api/Controllers/Version4/OrderNotes/class-wc-rest-order-notes-v4-controller-tests.php
+++ b/plugins/woocommerce/tests/php/includes/rest-api/Controllers/Version4/OrderNotes/class-wc-rest-order-notes-v4-controller-tests.php
@@ -387,6 +387,29 @@ class WC_REST_Order_Notes_V4_Controller_Tests extends WC_REST_Unit_Test_Case {
$this->assertEquals( 401, $response->get_status() );
}
+ /**
+ * Test that order note content is sanitized to prevent XSS.
+ */
+ public function test_create_item_sanitizes_note_content() {
+ $order = OrderHelper::create_order( $this->user );
+
+ $request = new WP_REST_Request( 'POST', '/wc/v4/order-notes' );
+ $request->set_body_params(
+ array(
+ 'order_id' => $order->get_id(),
+ 'note' => '<script>alert("xss")</script>Safe content<b>bold</b>',
+ )
+ );
+
+ $response = $this->server->dispatch( $request );
+ $data = $response->get_data();
+
+ $this->assertEquals( 201, $response->get_status() );
+ $this->assertStringNotContainsString( '<script>', $data['note'] );
+ $this->assertStringContainsString( 'Safe content', $data['note'] );
+ $this->assertStringContainsString( '<b>bold</b>', $data['note'] );
+ }
+
/**
* Test deleting order note without permission.
*/