diff --git a/src/wp-includes/class-wp-admin-bar.php b/src/wp-includes/class-wp-admin-bar.php index dc6ea0993c555..9e758b468d91f 100644 --- a/src/wp-includes/class-wp-admin-bar.php +++ b/src/wp-includes/class-wp-admin-bar.php @@ -587,7 +587,7 @@ final protected function _render_item( $node ) { } } - echo ">{$arrow}{$node->title}"; + echo '>' . $arrow . wp_kses_normalize_entities( $node->title ); if ( $has_link ) { echo ''; diff --git a/tests/phpunit/tests/adminbar.php b/tests/phpunit/tests/adminbar.php index cec80d0cae027..e06e4f213cd0f 100644 --- a/tests/phpunit/tests/adminbar.php +++ b/tests/phpunit/tests/adminbar.php @@ -267,6 +267,25 @@ public function test_admin_bar_with_tabindex_meta( $node_data, $expected_html ) $this->assertStringContainsString( $expected_html, $admin_bar_html ); } + /** + * @ticket 62545 + */ + public function test_admin_bar_normalizes_title_entities_without_escaping_html() { + $admin_bar = new WP_Admin_Bar(); + $admin_bar->add_node( + array( + 'id' => 'test-node', + 'title' => 'This & that', + 'href' => 'https://example.org', + ) + ); + + $admin_bar_html = get_echo( array( $admin_bar, 'render' ) ); + + $this->assertStringContainsString( 'This & that', $admin_bar_html ); + $this->assertStringNotContainsString( '<span class="ab-icon"', $admin_bar_html ); + } + /** * Data provider for test_admin_bar_with_tabindex_meta(). *