Opened 6 weeks ago
Last modified 4 weeks ago
#65729 new defect (bug)
List tables: Strip escaped HTML from values used in HTML attributes
| Reported by: | afercia | Owned by: | |
|---|---|---|---|
| Priority: | normal | Milestone: | 7.2 |
| Component: | Formatting | Version: | |
| Severity: | normal | Keywords: | has-screenshots has-patch has-unit-tests |
| Cc: | Focuses: | accessibility, administration |
Description
Discovered while working on #32892 / https://github.com/WordPress/wordpress-develop/pull/12683
Some of the values that are used in the List Tables for HTML attributes like aria-labels and such may contain escaped HTML.
Since the HTML is escaped, it is ignored by esc_attr() and printed in the HTML attribute as escaped HTML.
This may happen depending on the actual value. At the vey least, these values can be saved to the database even if they contain HTML:
- Attachment title.
- Site title (blogname).
- Link name (the old Link Manager is disabled for new installs since WP 3.5).
There may be more cases worth exploring.
Depending on the method used to retrieve the value, the HTML inside the valaue may be returned escaped or not.
To make things more complicated, it appears that soem values are saved to the database with any HTML already escaped. Other aren't. For example:
- An attachment title with HTML is saved unescaped:
my<div>image - A site title (blogname) is saved escaped:
My <div>aite
Regardless, before using these values within HTML attributes, WordPress should make sure to first unescape any HTML so that it can be detected as actual HTML by wp_strip_all_tags().
A note that I observed a few occurrences in the REST API and in the Customizer that use the following pattern: call html_entity_decode() first and then wp_strip_all_tags(). Since this appears to be a pattern repeated several times in the codebase, maybe a new formatting helper function could be handy.
To reproduce:
- Upload a new image to the Media Library.
- Once uploaded, edit the image.
- Edit the image title and add some HTML, for example add a
<div>inside the title. Save. - Go back to the Media Library, in the List view.
- Inspect the DOM and observe the aria-label values for all the 'row-actions' links.
- Observe the aria labels contain the
<div>. - Note that your browser devtools inspector will display the
<div>as not-encoded but it is actually encoded. Select the link in the devtools, right click and choose 'Edit as HTML'. In edit mode, observe it is actually escaped e.g.<div>.
Attachments (1)
Change History (6)
This ticket was mentioned in PR #12710 on WordPress/wordpress-develop by @khokansardar.
6 weeks ago
#1
- Keywords has-patch has-unit-tests added
This ticket was mentioned in Slack in #accessibility by joedolson. View the logs.
5 weeks ago
This ticket was mentioned in Slack in #accessibility by joedolson. View the logs.
4 weeks ago
#5
@
4 weeks ago
Since this appears to be a pattern repeated several times in the codebase, maybe a new formatting helper function could be handy.
A helper function might belong on a separate ticket. However, if it is only used with wp_strip_all_tags(), I think the existing function could have a new parameter instead:
* @param string $text String containing HTML tags.
* @param bool $remove_breaks Optional. Whether to remove left over line breaks and white space characters. Default false.
* @param bool $html_decode Optional. Whether to decode HTML before stripping tags. Default false.
* @return string The processed string.
*/
function wp_strip_all_tags( $text, $remove_breaks = false, $html_decode = false ) {
and then it would decode before preg_replace() (if true):
if ( $html_decode ) {
$text = html_entity_decode( $text, ENT_QUOTES, get_bloginfo( 'charset' ) );
}
(Or it could allow $flags constants other than ENT_QUOTES instead of being Boolean.)
![(please configure the [header_logo] section in trac.ini)](/chrome/site/your_project_logo.png)
Some of the values used in list table
aria-labelattributes reach the attribute with their HTML already escaped, so screen readers announce the markup as literal text.What the problem was:
_draft_or_post_title(), and the link name is escaped byWP_Links_List_Table::display_rows(). Some of these values are also stored in the database with any HTML already escaped.esc_attr()does not double encode, so the escaped HTML survives into the attribute. The browser decodes it back and the accessible name becomesEdit "my<div>image"instead ofEdit "myimage".What the fix does:
html_entity_decode()before callingwp_strip_all_tags(), so the HTML can be detected and removed, inWP_Media_List_Table::handle_row_actions(),WP_Posts_List_Table::handle_row_actions(), andWP_Links_List_Table::column_name().aria-labelvalue is cleaned.Approach and why:
WP_REST_URL_Details_Controller::prepare_metadata_for_output(), rather than introducing a new formatting helper. The ticket raises a helper as a possibility ("maybe a new formatting helper function could be handy"), but that is a new public API worth deciding separately with committer input, so this patch keeps the change to the affected call sites only.blogname) case named in the ticket is not currently used in any trunk list table attribute, so nothing is changed for it here.sanitize_term()on creation and update.Trac ticket: https://core.trac.wordpress.org/ticket/65729
## Use of AI Tools
AI assistance: Yes
Tool(s): Claude Code
Model(s): Claude Opus 4.8
Used for: Ticket analysis, tests and writing PR description.