Skip to content

fix: stop safe mode restarting current user resolution - #570

Open
TallblokeUK wants to merge 4 commits into
core-betafrom
fix/safe-mode-user-resolution/core
Open

TallblokeUK wants to merge 4 commits into
core-betafrom
fix/safe-mode-user-resolution/core

Conversation

@TallblokeUK

Copy link
Copy Markdown
Contributor

Safe mode's capability check could be re-entered while WordPress was still resolving the current user, on sites running a plugin that builds a URL from a determine_current_user callback.

Guards the check against re-entry, and leaves URLs untouched until the current user is known.

Verification

  • Safe-mode PHPUnit group: passes with the change, and fails in both directions without it.
  • Full PHPUnit suite: 348 tests, 795 assertions, no failures.
  • npm run lint:php: clean.
  • Checked against a WooCommerce install: safe mode engages for an administrator and stays out of anonymous requests.

@TallblokeUK TallblokeUK added the run-tests Trigger automated tests label Sep 29, 2026
$resolving = true;

try {
return code_snippets()->current_user_can();

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need to actually check the user here? I feel like we can just pass on the var whenever it's set, and then only check the current user when evaluating snippets.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right that it works, and it is simpler. I tried it: with the capability check taken out of this callback, an anonymous request carrying the query var comes back 200 and the recursion is gone without needing the guard at all. It also drops a current_user_can() call that currently runs on every home_url() in the page, which is worth having.

One consequence to weigh first. home_url() is a base that callers append to, so adding the query var to it corrupts anything built by concatenation. Passing the var on unconditionally does that for every visitor:

http://example.test?snippets-safe-mode=1%2Findex.php&rest_route=/

That is the REST URL on plain permalinks, and it is wrong.

Worth saying that this is not caused by either version — it already happens for a user who does hold the capability, on core-beta as it stands:

http://example.test?snippets-safe-mode=1%2Findex.php&rest_route=/code-snippets/v1/snippets

So safe mode breaks REST URLs for administrators today on plain permalinks, which matters given the manage screen needs the REST API and safe mode is the route in when something is broken. I'll raise that separately. Taking the capability check out here widens it from administrators to everyone, which is the only reason I'd hesitate.

A third option, if you like it: take the simplification and stop filtering home_url as well, keeping only admin_url. Safe mode navigation is an admin concern, and rest_url() derives from home_url(), so that removes the malformed URLs rather than extending them. No more code than your suggestion.

Happy to go whichever way you prefer — it's your integration.

…into fix/safe-mode-user-resolution/core

# Conflicts:
#	CHANGELOG.md
…into fix/safe-mode-user-resolution/core

# Conflicts:
#	CHANGELOG.md
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

run-tests Trigger automated tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants