Commit b49d11b9e46 for woocommerce

commit b49d11b9e46e5b7e24298dc49c7cb6507d21debc
Author: Michal Iwanow <4765119+mcliwanow@users.noreply.github.com>
Date:   Wed Sep 30 13:08:10 2026 +0200

    Keep price separators as plain text after decoding (#69238)

    PriceSeparators decodes HTML entities in the stored decimal and thousand
    separators for the Store API, block settings and admin payloads. A stored
    value such as "&lt;b&gt;x&lt;/b&gt;" decoded into markup. Separators are
    plain text, so markup characters are now dropped after decoding. Values
    already saved are covered too, since the change is on the read side.

    Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

diff --git a/plugins/woocommerce/changelog/fix-price-separator-plain-text b/plugins/woocommerce/changelog/fix-price-separator-plain-text
new file mode 100644
index 00000000000..25a101a483a
--- /dev/null
+++ b/plugins/woocommerce/changelog/fix-price-separator-plain-text
@@ -0,0 +1,4 @@
+Significance: patch
+Type: fix
+
+Keep price separators as plain text in the Store API and block settings.
diff --git a/plugins/woocommerce/src/Internal/Utilities/PriceSeparators.php b/plugins/woocommerce/src/Internal/Utilities/PriceSeparators.php
index a0a3f5ff8c2..977d814bdcc 100644
--- a/plugins/woocommerce/src/Internal/Utilities/PriceSeparators.php
+++ b/plugins/woocommerce/src/Internal/Utilities/PriceSeparators.php
@@ -20,7 +20,7 @@ class PriceSeparators {
 	 * @return string
 	 */
 	public static function get_decimal(): string {
-		return html_entity_decode( wc_get_price_decimal_separator(), ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML5 );
+		return self::to_plain_text( wc_get_price_decimal_separator() );
 	}

 	/**
@@ -29,6 +29,16 @@ class PriceSeparators {
 	 * @return string
 	 */
 	public static function get_thousand(): string {
-		return html_entity_decode( wc_get_price_thousand_separator(), ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML5 );
+		return self::to_plain_text( wc_get_price_thousand_separator() );
+	}
+
+	/**
+	 * Decode HTML entities in a separator. Separators are plain text, so markup characters are dropped.
+	 *
+	 * @param string $separator The stored separator.
+	 * @return string
+	 */
+	private static function to_plain_text( string $separator ): string {
+		return str_replace( array( '<', '>' ), '', html_entity_decode( $separator, ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML5 ) );
 	}
 }
diff --git a/plugins/woocommerce/tests/php/src/Internal/Utilities/PriceSeparatorsTest.php b/plugins/woocommerce/tests/php/src/Internal/Utilities/PriceSeparatorsTest.php
index d62e85f884d..24e1f350ec1 100644
--- a/plugins/woocommerce/tests/php/src/Internal/Utilities/PriceSeparatorsTest.php
+++ b/plugins/woocommerce/tests/php/src/Internal/Utilities/PriceSeparatorsTest.php
@@ -12,7 +12,7 @@ use WC_Unit_Test_Case;
 class PriceSeparatorsTest extends WC_Unit_Test_Case {

 	/**
-	 * @testdox Should decode HTML entities stored as separators, including HTML5-only entities.
+	 * @testdox Should decode HTML entities stored as separators and drop markup characters.
 	 * @dataProvider separator_entity_data
 	 *
 	 * @param string $stored   The raw option value.
@@ -33,12 +33,17 @@ class PriceSeparatorsTest extends WC_Unit_Test_Case {
 	 */
 	public function separator_entity_data(): array {
 		return array(
+			'plain period'            => array( '.', '.' ),
 			'plain comma'             => array( ',', ',' ),
 			'plain space'             => array( ' ', ' ' ),
 			'non-breaking space char' => array( "\u{00A0}", "\u{00A0}" ),
 			'named entity nbsp'       => array( '&nbsp;', "\u{00A0}" ),
 			'numeric entity comma'    => array( '&#44;', ',' ),
 			'HTML5-only entity apos'  => array( '&apos;', "'" ),
+			'numeric entity apos'     => array( '&#39;', "'" ),
+			'padded numeric apos'     => array( '&#039;', "'" ),
+			'narrow nbsp entity'      => array( '&#8239;', "\u{202F}" ),
+			'encoded markup'          => array( '&lt;b&gt;x&lt;/b&gt;', 'bx/b' ),
 		);
 	}