fix(nav): strip toggle aria-label tags without kses - #4626
Alexia-Soare wants to merge 7 commits into
Conversation
The mobile dropdown toggle built its aria-label with wp_filter_nohtml_kses(), which runs through the pluggable wp_kses_allowed_html filter. A plugin that widens that filter for every context, such as Booster for WooCommerce, keeps Neve's own <span> wrapper in the label, so screen readers announce raw markup instead of the menu title. Use wp_strip_all_tags(), which does a plain string strip. Refs: #4623 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
There was a problem hiding this comment.
Note
Copilot was unable to run its full agentic suite in this review.
Pull request overview
Adds a regression safeguard to ensure Neve’s mobile menu dropdown toggle aria-label remains plain text even when plugins loosen KSES rules.
Changes:
- Added a WP unit test covering permissive
wp_kses_allowed_htmlbehavior for menu toggle labels. - Updated
Nav_Walkerto usewp_strip_all_tags()instead ofwp_filter_nohtml_kses()when building the togglearia-label.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| tests/test-neve-nav-walker-aria-label.php | Adds regression test reproducing the permissive-KSES scenario and asserting the resulting aria-label. |
| inc/views/nav_walker.php | Switches sanitization approach for the toggle aria-label to prevent tag leakage when KSES rules are widened. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
The sample-data environment now installs a must-use plugin that widens wp_kses_allowed_html for every context, the way Booster for WooCommerce does. A Playwright spec opens the mobile menu on a phone profile and checks that every dropdown toggle aria-label is plain text. Refs: #4623 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
CI installs Chromium only. The iPhone 13 profile defaults to WebKit, so the spec could not launch a browser there. Use the Pixel 5 profile, which the existing accessibility spec also ends up running on. Refs: #4623 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Address review: array_merge_recursive() turns an existing boolean attribute allowance into an array, which is not the shape wp_kses expects. Set the span/class allowance directly in both the unit test and the e2e must-use plugin. Refs: #4623 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
wp_strip_all_tags() keeps line breaks by default. A menu title with a newline puts that newline in the attribute, where a screen reader reads it as a pause. Remove the breaks and trim the result. Refs: #4623 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
🟡 Changes requested
The new wp_strip_all_tags() call drops the item name from the sidebar toggle label when a menu title contains < followed by a non-space character.
Validation details
- Files reviewed: 5/5 changed files.
- Unit test:
TestNeveNavWalkerAriaLabelrun with PHPUnit 9.6.36, PHP 8.3.33, WordPress 7.1.1 test library. It passes on HEAD and fails onpr-basewith the leaked<span>label. The test discriminates the fix. - Label comparison: old and new expressions run with
wp evalon WordPress 7.1.1 for eleven titles. Apostrophes,&, entities, newlines and icon markup produce the same or better output. Titles with<plus a non-space character lose text. - Producer: an administrator with
unfiltered_htmlstoresPrice <5 itemsunchanged.the_titlefilters keep it. This is source and sandbox evidence, not a browser check. - Security: the
esc_attr()sink is unchanged.bin/is excluded from the release zip by.distignore. - E2E environment: the mu-plugin path and the sample-data menu with children were confirmed from source only.
Untested areas
- The Playwright spec
dropdown-aria-label.spec.tswas not run. It needs the Docker sample-data environment and built theme assets. - Booster for WooCommerce was not installed. The unit test reproduces its filter.
🤖 Automated review · run code-review-agent_6aad0e92028285.98308151.
🤖 Review agent — review posted ✅ on 24ff7530 · changes requested · 1 finding · 25 min
Run code-review-agent_6aad0e92028285.98308151 · trail
strip_tags() reads a lone "<" as the start of a tag and drops the text up to the next ">". A menu title such as "Kids <12" lost its name from the aria-label. Encode the lone "<" first with wp_pre_kses_less_than(), which is a plain regex and does not consult wp_kses_allowed_html. Refs: #4623 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
wp_strip_all_tags() ends with trim(), so the outer call did nothing. Refs: #4623 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Summary
The mobile menu's dropdown toggle announced raw markup,
Toggle <span class="menu-item-title-wrap dd-title">Shop</span>, instead ofToggle Shopwhen a plugin widenedwp_kses_allowed_htmlfor every context. The label is now built withwp_strip_all_tags(), a plain string strip that does not consult that filter.Note
The kses call was added in 4a37a0e (version 3.6.6). The bug shows only with a plugin that hooks
wp_kses_allowed_htmlwithout checking for the reservedstripcontext. Booster for WooCommerce does this.Reported in #4623 from HelpScout conversation 3451611756.
What changed
Nav walker — builds the sidebar toggle aria-label with
wp_strip_all_tags()instead ofwp_filter_nohtml_kses(). The output was already escaped withesc_attr(), so this is an accessibility fix, not a security one.E2E sample-data environment — installs a must-use plugin that widens
wp_kses_allowed_htmlfor every context, the way Booster does. A Playwright spec inspecs/accessibilityopens the mobile menu on a phone profile and checks every dropdown toggle label is plain text. The customizer, admin and editor specs share this environment; the filter is inert for them.Note
wp_filter_nohtml_kses()expects slashed form input and runswp_kses()in thestripcontext. Neve used it to remove its own<span>wrapper from HTML it built itself, which made the label depend on every plugin behaving correctly in that filter.Will affect visual aspect of the product
NO
Test instructions
Go to
WP Admin → Plugins → Add New Plugin, install and activate Booster for WooCommerce.Expect: the plugin is listed as active.
Go to
WP Admin → Appearance → Menus. Create a menu with a Shop item that has at least one child item. Under Menu Settings, tick Primary Menu and save.Expect: the menu shows in the header.
Open the homepage. Open the browser console and run:
Expect:
Toggle Shop. Before the fix this printedToggle <span class=\"menu-item-title-wrap dd-title\">Shop</span>.Narrow the window to phone width, open the hamburger menu, select the arrow button next to Shop with the element picker and open the Accessibility pane.
Expect: the computed name is
Toggle Shop.Deactivate Booster for WooCommerce and repeat step 3.
Expect:
Toggle Shop.Check before Pull Request is ready:
🤖 Generated with Claude Code