From c3d331e93a8278c0941acaae165d5891b962c5ab Mon Sep 17 00:00:00 2001 From: Piyush <296399266+skikken@users.noreply.github.com> Date: Thu, 10 Sep 2026 21:29:37 +0530 Subject: [PATCH] fix(themes): escape current theme action links The action links for the active theme on themes.php were assembled as raw relative URL strings with unescaped labels, so a submenu label containing markup was emitted verbatim. The section also duplicated six near-identical anchors. Compute the URL and class list per item and emit a single escaped anchor. Keep the hide-if-no-customize class, which the rule in wp-admin/css/themes.css relies on. Refs https://core.trac.wordpress.org/ticket/31365 --- src/wp-admin/themes.php | 44 +++++++++++++++++++++++++++++++++-------- 1 file changed, 36 insertions(+), 8 deletions(-) diff --git a/src/wp-admin/themes.php b/src/wp-admin/themes.php index ac2cd4a9824cb..00c34d546b50e 100644 --- a/src/wp-admin/themes.php +++ b/src/wp-admin/themes.php @@ -366,7 +366,7 @@ ); foreach ( (array) $submenu['themes.php'] as $item ) { - $class = ''; + $classes = array( 'button', 'button-compact' ); if ( in_array( $item[2], $forbidden_paths, true ) || str_starts_with( $item[2], 'customize.php' ) ) { continue; @@ -376,7 +376,7 @@ if ( 0 === strcmp( $self, $item[2] ) && empty( $parent_file ) || $parent_file && $item[2] === $parent_file ) { - $class = ' current'; + $classes[] = 'current'; } if ( ! empty( $submenu[ $item[2] ] ) ) { @@ -384,18 +384,39 @@ $menu_hook = get_plugin_page_hook( $submenu[ $item[2] ][0][2], $item[2] ); if ( file_exists( WP_PLUGIN_DIR . "/{$submenu[$item[2]][0][2]}" ) || ! empty( $menu_hook ) ) { - $current_theme_actions[] = "{$item[0]}"; + $url = admin_url( "admin.php?page={$submenu[$item[2]][0][2]}" ); } else { - $current_theme_actions[] = "{$item[0]}"; + $url = admin_url( $submenu[ $item[2] ][0][2] ); } + + $current_theme_actions[] = sprintf( + '%s', + esc_attr( implode( ' ', $classes ) ), + esc_url( $url ), + esc_html( $item[0] ) + ); } elseif ( ! empty( $item[2] ) && current_user_can( $item[1] ) ) { $menu_file = $item[2]; if ( current_user_can( 'customize' ) ) { if ( 'custom-header' === $menu_file ) { - $current_theme_actions[] = "{$item[0]}"; + $customize_url = add_query_arg( array( 'autofocus' => array( 'control' => 'header_image' ) ), 'customize.php' ); + + $current_theme_actions[] = sprintf( + '%s', + esc_attr( implode( ' ', array_merge( $classes, array( 'hide-if-no-customize' ) ) ) ), + esc_url( admin_url( $customize_url ) ), + esc_html( $item[0] ) + ); } elseif ( 'custom-background' === $menu_file ) { - $current_theme_actions[] = "{$item[0]}"; + $customize_url = add_query_arg( array( 'autofocus' => array( 'control' => 'background_image' ) ), 'customize.php' ); + + $current_theme_actions[] = sprintf( + '%s', + esc_attr( implode( ' ', array_merge( $classes, array( 'hide-if-no-customize' ) ) ) ), + esc_url( admin_url( $customize_url ) ), + esc_html( $item[0] ) + ); } } @@ -405,10 +426,17 @@ } if ( file_exists( ABSPATH . "wp-admin/$menu_file" ) ) { - $current_theme_actions[] = "{$item[0]}"; + $url = admin_url( $item[2] ); } else { - $current_theme_actions[] = "{$item[0]}"; + $url = admin_url( "themes.php?page={$item[2]}" ); } + + $current_theme_actions[] = sprintf( + '%s', + esc_attr( implode( ' ', $classes ) ), + esc_url( $url ), + esc_html( $item[0] ) + ); } } }