Users: Guard against roles missing a capabilities array in WP_User_Query and WP_Roles - #12931
Users: Guard against roles missing a capabilities array in WP_User_Query and WP_Roles#12931melbos wants to merge 1 commit into
Conversation
…has no capabilities array A role stored without a 'capabilities' key, or with a non-array value for it, causes two separate problems: - A fatal TypeError in WP_User_Query::prepare_query() when querying by capability (e.g. wp_dropdown_users() on the classic Author meta box), since array_filter() is called directly on $role_data['capabilities']. - The same unguarded assumption in WP_Roles::init_roles(), which runs on effectively every request that initializes roles, not just capability-filtered queries. This can happen when a plugin registers a role without capabilities and is later deactivated, leaving the malformed role behind in the site's user_roles option. Both call sites now treat a missing or non-array 'capabilities' value as an empty array instead of assuming it is always present and always an array. Props geekofshire, umeshnevase for the initial investigation and patches in WordPress#8351 and WordPress#8823, which this builds on and extends to also cover WP_Roles::init_roles(). Includes unit tests covering both failure modes. Trac ticket: https://core.trac.wordpress.org/ticket/62600
|
The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the Core Committers: Use this line as a base for the props when committing in SVN: To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook. |
Test using WordPress PlaygroundThe changes in this pull request can previewed and tested using a WordPress Playground instance. WordPress Playground is an experimental project that creates a full WordPress instance entirely within the browser. Some things to be aware of
For more details about these limitations and more, check out the Limitations page in the WordPress Playground documentation. |
A role stored without a
capabilitieskey, or with a non-array value for it, causes two separate problems, not just one:TypeErrorinWP_User_Query::prepare_query()when querying by capability, sincearray_filter()is called directly on$role_data['capabilities']. This is the crash originally reported on the ticket, viawp_dropdown_users()on the classic Author meta box.WP_Roles::init_roles(), which runs on effectively every request that initializes roles — not just capability-filtered queries. Neither add check is capabilities key is set #8351 nor Added condition to check if is an array() for $role_data['capabilities'] #8823 addresses this second call site.This can happen when a plugin registers a role without capabilities and is later deactivated, leaving the malformed role behind in the site's
user_rolesoption.This PR builds on the investigation already done in #8351 (by @geekofshire) and #8823 (by @umeshnevase) — thank you both. It combines the
is_array()robustness from #8823 with the more readable guard-clause shape from #8351, extends the same fix toWP_Roles::init_roles(), and adds the unit test coverage that @johnbillion asked for and that both prior PRs were still missing.Reproduction
Reproduced against a real WordPress install (not just the unit tests) by creating a role with no
capabilitieskey and triggering the exact code path from the original report (wp_dropdown_users()with a capability filter):Before the fix (
wp-content/debug.log):After the fix, same broken role, same trigger: no warnings, no errors,
debug.logisn't even created, and the query returns results correctly instead of crashing.Testing
class-wp-user-query.php:485, matching the original report) without the fix, and pass with it.userandcapabilitiestest groups (1,343 tests, 4,530 assertions) to confirm no regressions.Trac ticket: https://core.trac.wordpress.org/ticket/62600
Use of AI Tools
AI assistance: Yes
Tool(s): Claude Code
Model(s): Claude Sonnet 5
Used for: Investigating the root cause (including finding the second, unreported crash site in
WP_Roles::init_roles()), implementing the fix in both files, writing and running the unit tests, and reproducing the bug against a live WordPress install to verify the before/after behavior shown above. All changes were reviewed and directed by me.This Pull Request is for code review only. Please keep all other discussion in the Trac ticket. Do not merge this Pull Request. See GitHub Pull Requests for Code Review in the Core Handbook for more details.