From 2cb6213d2b270f0c96e6494fa095998135cefb54 Mon Sep 17 00:00:00 2001 From: Dennis Snell Date: Mon, 19 Jan 2026 11:44:35 -0700 Subject: [PATCH] HTML API: Fix missing null-check in wp_kses_hair() refactor. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When no attributes are present, `wp_kses_hair()` should return an empty array, but when the refactor was merged, the code assumed there would be attributes. An alternative fix is to use null-coalescing to iterate over an empty array. This would produce a marginally smaller function and read slightly more cleanly, but there’s no need to enter the `foreach` loop when it’s known in advance that there’s nothing over which to iterate. Developed in: https://github.com/WordPress/wordpress-develop/pull/10758 Discussed in: https://core.trac.wordpress.org/ticket/63724 Follow-up to: [61467] Props: dd32, dmsnell, jonsurrell. See: Trac-63724 Co-authored-by: Dion Hulse Co-authored-by: Jon Surrell Github-PR: 10758 Github-PR-URL: https://github.com/WordPress/wordpress-develop/pull/10758 Branch-Name: html-api/fix-wp-kses-hair-refactor Trac-Ticket: 63724 Trac-Ticket-URL: https://core.trac.wordpress.org/ticket/63724 --- src/wp-includes/kses.php | 7 ++++++- tests/phpunit/tests/kses/wpKsesHair.php | 22 ++++++++++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/src/wp-includes/kses.php b/src/wp-includes/kses.php index fd489c06c71f2..872034621f28f 100644 --- a/src/wp-includes/kses.php +++ b/src/wp-includes/kses.php @@ -1623,6 +1623,11 @@ function wp_kses_hair( $attr, $allowed_protocols ) { $processor = new WP_HTML_Tag_Processor( "" ); $processor->next_token(); + $attribute_names = $processor->get_attribute_names_with_prefix( '' ); + if ( ! isset( $attribute_names ) ) { + return $attributes; + } + $syntax_characters = array( '&' => '&', '<' => '<', @@ -1631,7 +1636,7 @@ function wp_kses_hair( $attr, $allowed_protocols ) { '"' => '"', ); - foreach ( $processor->get_attribute_names_with_prefix( '' ) as $name ) { + foreach ( $attribute_names as $name ) { $value = $processor->get_attribute( $name ); $is_bool = true === $value; if ( is_string( $value ) && in_array( $name, $uris, true ) ) { diff --git a/tests/phpunit/tests/kses/wpKsesHair.php b/tests/phpunit/tests/kses/wpKsesHair.php index 05d573bc070bc..0ef834880cd5c 100644 --- a/tests/phpunit/tests/kses/wpKsesHair.php +++ b/tests/phpunit/tests/kses/wpKsesHair.php @@ -39,6 +39,28 @@ public function test_attribute_parsing( string $input, array $expected ) { * @return Generator */ public function data_attribute_parsing() { + yield 'empty attributes' => array( + '', + array(), + ); + + yield 'prematurely-terminated attributes' => array( + '>', + array(), + ); + + yield 'prematurely-terminated malformed attributes' => array( + 'foo>bar="baz"', + array( + 'foo' => array( + 'name' => 'foo', + 'value' => '', + 'whole' => 'foo', + 'vless' => 'y', + ), + ), + ); + yield 'single attribute with double quotes' => array( 'class="test-class"', array(