From 887e7664d2823066cf14d91cc1ace661b11a4d7f Mon Sep 17 00:00:00 2001 From: Ashar Fuadi Date: Mon, 19 Oct 2026 18:04:02 +0700 Subject: [PATCH 1/2] Icons: Skip sanitizing the icons bundled with WordPress. The core icons ship with WordPress and are trusted, and sanitizing them with wp_kses() is the main cost of rendering them. --- src/wp-includes/class-wp-icons-registry.php | 26 ++++++++++++++++++- tests/phpunit/tests/icons/wpGetIcon.php | 4 +-- tests/phpunit/tests/icons/wpIconsRegistry.php | 16 +++++++++++- 3 files changed, 42 insertions(+), 4 deletions(-) diff --git a/src/wp-includes/class-wp-icons-registry.php b/src/wp-includes/class-wp-icons-registry.php index 6186a0b48907a..bb7faee29f60a 100644 --- a/src/wp-includes/class-wp-icons-registry.php +++ b/src/wp-includes/class-wp-icons-registry.php @@ -357,6 +357,7 @@ protected function sanitize_icon_content( $icon_content ) { * Retrieves the content of a registered icon. * * @since 7.0.0 + * @since 7.2.0 Core icons skip sanitization. * * @param string $icon_name Icon name including namespace. * @return string|null The content of the icon, if found. @@ -380,7 +381,12 @@ protected function get_content( $icon_name ) { return null; } - $content = $this->sanitize_icon_content( file_get_contents( $icon_path ) ); + $content = file_get_contents( $icon_path ); + + // Core icons are trusted, so skip the costly sanitization. + if ( ! $this->is_core_icon_file( $icon_path ) ) { + $content = $this->sanitize_icon_content( $content ); + } if ( empty( $content ) ) { wp_trigger_error( @@ -395,6 +401,24 @@ protected function get_content( $icon_name ) { return $this->registered_icons[ $icon_name ]['content']; } + /** + * Determines whether an icon file is a core icon. + * + * @since 7.2.0 + * + * @param string $icon_path Icon file path, resolved with `realpath()`. + * @return bool Whether the file is in the core icon library. + */ + private function is_core_icon_file( $icon_path ) { + static $library_directory = null; + + if ( null === $library_directory ) { + $library_directory = realpath( ABSPATH . WPINC . '/images/icon-library' ); + } + + return is_string( $library_directory ) && str_starts_with( $icon_path, $library_directory . DIRECTORY_SEPARATOR ); + } + /** * Retrieves an array containing the properties of a registered icon. * diff --git a/tests/phpunit/tests/icons/wpGetIcon.php b/tests/phpunit/tests/icons/wpGetIcon.php index 4b1b7eed6c1eb..51dcfd04d1fd0 100644 --- a/tests/phpunit/tests/icons/wpGetIcon.php +++ b/tests/phpunit/tests/icons/wpGetIcon.php @@ -47,11 +47,11 @@ public function test_wp_get_icon_returns_empty_string_for_unknown_icon() { /** * @ticket 64847 + * @ticket 66338 */ public function test_wp_get_icon_default_attributes() { $output = wp_get_icon( 'core/plus' ); - // WP_HTML_Tag_Processor lowercases attribute names. - $this->assertStringContainsString( 'viewbox="0 0 24 24"', $output ); + $this->assertStringContainsStringIgnoringCase( 'viewbox="0 0 24 24"', $output ); $this->assertStringContainsString( 'width="24"', $output ); $this->assertStringContainsString( 'height="24"', $output ); $this->assertStringContainsString( 'aria-hidden="true"', $output ); diff --git a/tests/phpunit/tests/icons/wpIconsRegistry.php b/tests/phpunit/tests/icons/wpIconsRegistry.php index b647a5543b5ec..a968d8e1d10be 100644 --- a/tests/phpunit/tests/icons/wpIconsRegistry.php +++ b/tests/phpunit/tests/icons/wpIconsRegistry.php @@ -481,11 +481,12 @@ public function test_unregister_unknown_icon() { /** * @ticket 64651 + * @ticket 66338 * * @covers ::get_content */ public function test_get_content_reads_from_valid_file_path() { - $path = $this->create_temp_icon_file( '' ); + $path = $this->create_temp_icon_file( '' ); $this->registry->register( 'test-collection/from-file', @@ -497,6 +498,19 @@ public function test_get_content_reads_from_valid_file_path() { $icon = $this->registry->get_registered_icon( 'test-collection/from-file' ); $this->assertStringContainsString( 'assertStringNotContainsString( 'original_registry->get_registered_icon( 'core/pencil' ); + $this->assertSame( file_get_contents( ABSPATH . WPINC . '/images/icon-library/pencil.svg' ), $icon['content'] ); } /** From fa2f8de982de7f4bad1381e70a4269e524e27803 Mon Sep 17 00:00:00 2001 From: Ashar Fuadi Date: Fri, 23 Oct 2026 11:14:54 +0700 Subject: [PATCH 2/2] Address comments --- src/wp-includes/class-wp-icons-registry.php | 16 +++++++++----- tests/phpunit/tests/icons/wpIconsRegistry.php | 21 ++++++++++++++++++- 2 files changed, 31 insertions(+), 6 deletions(-) diff --git a/src/wp-includes/class-wp-icons-registry.php b/src/wp-includes/class-wp-icons-registry.php index bb7faee29f60a..50b53fa7442f7 100644 --- a/src/wp-includes/class-wp-icons-registry.php +++ b/src/wp-includes/class-wp-icons-registry.php @@ -20,6 +20,14 @@ class WP_Icons_Registry { */ protected $registered_icons = array(); + /** + * Resolved path of the core icon library directory. + * + * @since 7.2.0 + * @var string|false|null + */ + private $core_icon_library_directory = null; + /** * Container for the main instance of the class. * @@ -410,13 +418,11 @@ protected function get_content( $icon_name ) { * @return bool Whether the file is in the core icon library. */ private function is_core_icon_file( $icon_path ) { - static $library_directory = null; - - if ( null === $library_directory ) { - $library_directory = realpath( ABSPATH . WPINC . '/images/icon-library' ); + if ( null === $this->core_icon_library_directory ) { + $this->core_icon_library_directory = realpath( ABSPATH . WPINC . '/images/icon-library' ); } - return is_string( $library_directory ) && str_starts_with( $icon_path, $library_directory . DIRECTORY_SEPARATOR ); + return is_string( $this->core_icon_library_directory ) && str_starts_with( $icon_path, $this->core_icon_library_directory . DIRECTORY_SEPARATOR ); } /** diff --git a/tests/phpunit/tests/icons/wpIconsRegistry.php b/tests/phpunit/tests/icons/wpIconsRegistry.php index a968d8e1d10be..31f11543adea0 100644 --- a/tests/phpunit/tests/icons/wpIconsRegistry.php +++ b/tests/phpunit/tests/icons/wpIconsRegistry.php @@ -481,11 +481,30 @@ public function test_unregister_unknown_icon() { /** * @ticket 64651 - * @ticket 66338 * * @covers ::get_content */ public function test_get_content_reads_from_valid_file_path() { + $path = $this->create_temp_icon_file( '' ); + + $this->registry->register( + 'test-collection/from-file', + array( + 'label' => 'From File', + 'file_path' => $path, + ) + ); + + $icon = $this->registry->get_registered_icon( 'test-collection/from-file' ); + $this->assertStringContainsString( 'create_temp_icon_file( '' ); $this->registry->register(