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().
*