From 7dee44dc5f0e6efdbf249ac6c4ab87571cc5c425 Mon Sep 17 00:00:00 2001 From: Preston Davies Date: Thu, 26 Mar 2026 20:22:24 -0400 Subject: [PATCH 1/4] fix: allow dollar sign in custom blocks --- wp/headless-wp/includes/classes/Integrations/Gutenberg.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/wp/headless-wp/includes/classes/Integrations/Gutenberg.php b/wp/headless-wp/includes/classes/Integrations/Gutenberg.php index 6b3525967..9af37d68b 100644 --- a/wp/headless-wp/includes/classes/Integrations/Gutenberg.php +++ b/wp/headless-wp/includes/classes/Integrations/Gutenberg.php @@ -320,7 +320,8 @@ public function process_block_with_dom_document_api( $html, $block_name, $block_ public function set_block_attributes_tag_api( $placeholder, $html, $block_attrs_serialized ) { $search = sprintf( '/data-wp-block="%s"/', preg_quote( $placeholder, '/' ) ); $replace = sprintf( 'data-wp-block="%s"', htmlspecialchars( $block_attrs_serialized ) ); - + // Escape backslashes and dollar signs for the replacement string + $replace = str_replace( array( '\\', '$' ), array( '\\\\', '\\$' ), $replace ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped return preg_replace( $search, From 4ea9438e3188e9b0f0c9016e32ddc591ffb0f3e1 Mon Sep 17 00:00:00 2001 From: Preston Davies Date: Thu, 26 Mar 2026 20:27:02 -0400 Subject: [PATCH 2/4] fix: composer lint issue --- wp/headless-wp/includes/classes/Integrations/Gutenberg.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wp/headless-wp/includes/classes/Integrations/Gutenberg.php b/wp/headless-wp/includes/classes/Integrations/Gutenberg.php index 9af37d68b..a2454a2e0 100644 --- a/wp/headless-wp/includes/classes/Integrations/Gutenberg.php +++ b/wp/headless-wp/includes/classes/Integrations/Gutenberg.php @@ -321,7 +321,7 @@ public function set_block_attributes_tag_api( $placeholder, $html, $block_attrs_ $search = sprintf( '/data-wp-block="%s"/', preg_quote( $placeholder, '/' ) ); $replace = sprintf( 'data-wp-block="%s"', htmlspecialchars( $block_attrs_serialized ) ); // Escape backslashes and dollar signs for the replacement string - $replace = str_replace( array( '\\', '$' ), array( '\\\\', '\\$' ), $replace ); + $replace = str_replace( [ '\\', '$' ], [ '\\\\', '\\$' ], $replace ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped return preg_replace( $search, From 0dc499b87cbec5857d44e99a03a233ec7f312153 Mon Sep 17 00:00:00 2001 From: Preston Davies Date: Tue, 31 Mar 2026 01:44:34 -0400 Subject: [PATCH 3/4] fix: update changeset and add in tests --- .changeset/fix-gutenberg-dollar-sign.md | 6 ++++ .../php/tests/TestGutenbergIntegration.php | 34 +++++++++++++++++++ 2 files changed, 40 insertions(+) create mode 100644 .changeset/fix-gutenberg-dollar-sign.md diff --git a/.changeset/fix-gutenberg-dollar-sign.md b/.changeset/fix-gutenberg-dollar-sign.md new file mode 100644 index 000000000..d5e069e73 --- /dev/null +++ b/.changeset/fix-gutenberg-dollar-sign.md @@ -0,0 +1,6 @@ +--- +"@headstartwp/headstartwp": patch +--- + +Fix Gutenberg block attribute rendering when JSON attribute values contain dollar signs (for example, `$50 million`), preventing incorrect replacement escaping. + diff --git a/wp/headless-wp/tests/php/tests/TestGutenbergIntegration.php b/wp/headless-wp/tests/php/tests/TestGutenbergIntegration.php index ab39924ce..18efa7d33 100644 --- a/wp/headless-wp/tests/php/tests/TestGutenbergIntegration.php +++ b/wp/headless-wp/tests/php/tests/TestGutenbergIntegration.php @@ -152,6 +152,32 @@ public function test_render_classic_block() { ); } + /** + * Tests HTML_Tag_Processor attribute injection does not drop `$...` sequences + * in serialized JSON string values (e.g. "$50 million"). + * + * This specifically covers the preg_replace() replacement-string escaping logic + * used by set_block_attributes_tag_api(). + * + * @return void + */ + public function test_set_block_attributes_tag_api_preserves_dollar_signs() { + $placeholder = '___HEADSTARTWP_BLOCK_ATTRS___'; + $html = '

'; + $attrs_json = wp_json_encode( + [ + 'content' => '$50 million', + 'level' => 2, + ] + ); + + $enhanced = $this->parser->set_block_attributes_tag_api( $placeholder, $html, $attrs_json ); + + $this->assertStringContainsString( 'data-wp-block-name="', $enhanced, 'Sanity check: block-name attribute should exist.' ); + // The core assertion: `$50` should not be interpreted by preg_replace() replacement parsing. + $this->assertStringContainsString( '$50 million', $enhanced ); + } + /** * Test to ensure the parser handles both HTML and Multi-byte encodings properly * @@ -435,6 +461,14 @@ public function block_roundtrip_data_provider() { ], '

', ], + 'block value containing dollar sign in JSON string' => [ + 'core/heading', + [ + 'x' => '$50 million', + 'level' => 2, + ], + '

', + ], 'html_entities' => [ 'core/heading', [ From 1d9e7f807c0315021694bca3c287787ad08a90b5 Mon Sep 17 00:00:00 2001 From: Preston Davies Date: Tue, 31 Mar 2026 11:40:00 -0400 Subject: [PATCH 4/4] fix: test fixes from bugbot --- wp/headless-wp/tests/php/tests/TestGutenbergIntegration.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/wp/headless-wp/tests/php/tests/TestGutenbergIntegration.php b/wp/headless-wp/tests/php/tests/TestGutenbergIntegration.php index 18efa7d33..8df621af5 100644 --- a/wp/headless-wp/tests/php/tests/TestGutenbergIntegration.php +++ b/wp/headless-wp/tests/php/tests/TestGutenbergIntegration.php @@ -173,7 +173,9 @@ public function test_set_block_attributes_tag_api_preserves_dollar_signs() { $enhanced = $this->parser->set_block_attributes_tag_api( $placeholder, $html, $attrs_json ); - $this->assertStringContainsString( 'data-wp-block-name="', $enhanced, 'Sanity check: block-name attribute should exist.' ); + // set_block_attributes_tag_api() only replaces the `data-wp-block` placeholder value. + $this->assertStringNotContainsString( $placeholder, $enhanced ); + $this->assertStringContainsString( 'data-wp-block="', $enhanced ); // The core assertion: `$50` should not be interpreted by preg_replace() replacement parsing. $this->assertStringContainsString( '$50 million', $enhanced ); }