Skip to content

fix: safe mode fatals with an undefined wp_get_current_user() - #484

Merged
imantsk merged 2 commits into
release/v3.10.2/corefrom
fix/safe-mode-early-cap-check/core
Aug 31, 2026
Merged

fix: safe mode fatals with an undefined wp_get_current_user()#484
imantsk merged 2 commits into
release/v3.10.2/corefrom
fix/safe-mode-early-cap-check/core

Conversation

@TallblokeUK

Copy link
Copy Markdown
Contributor

Safe mode is currently unusable. Any request carrying ?snippets-safe-mode=1 dies with a fatal, on 3.10.0 and on released 3.10.1. Reported by hannab in this forum thread — they were told to use safe mode to recover, and hit this instead.

The bug

Fatal error: Uncaught Error: Call to undefined function wp_get_current_user()
in wp-includes/capabilities.php:914
#0 .../php/Plugin.php(282): current_user_can('manage_options')
#1 .../php/Integration/Evaluate_Functions.php(51): Code_Snippets\Plugin->current_user_can()

Evaluate_Functions is constructed from Plugin::load_plugin(), which Core/load.php:88 calls at include time. The order in wp-settings.php is:

line what happens
574 active plugins are included — our constructor runs here
604 require pluggable.phpwp_get_current_user() defined here
622 plugins_loaded fires

So the constructor runs 30 lines before the function it depends on exists. The only reason this is not fatal on every request is the empty() short-circuit on the query var:

return ! empty( $_REQUEST['snippets-safe-mode'] ) && code_snippets()->current_user_can();

Set the query var and the second operand is evaluated, and the request is over.

This matters more than a normal edge case: safe mode is what we tell people to use when a snippet has broken their site, so it fails exactly when it is needed.

The fix

Split the check in two. The constructor tests only for the query var, which reads nothing but the request and is safe at any point. The capability is checked inside the callback, which cannot run before URLs are being generated, long after pluggable functions are available.

Behaviour is unchanged for a user without the capability — the query var is still never added to URLs.

Also included

The two filter callbacks in this same file declared non-nullable scalar parameters:

public function add_safe_mode_query_var( string $url ): string
public function disable_snippet_execution( bool $execute_snippets ): bool

Neither can trust its input, because an earlier callback in the chain may return anything, and on PHP 8 passing null to a non-nullable scalar is a TypeError regardless of strict_types. This is the same defect Sam reported on the forum for screen_settings, fixed in #481, and they suggested auditing the rest. disable_snippet_execution is hooked to code_snippets/execute_snippets, a filter we publish for third parties, and a null there takes down the front end rather than one admin screen.

One remains after this: Snippet_Files::add_settings_fields( array $fields ) on code_snippets_settings_fields. Left out to keep this diff to a single file — happy to follow up.

Testing

4 new tests in Evaluate_Functions_Safe_Mode_Test. All 4 fail on core today: two with Call to undefined method ...::is_safe_mode_query_var_set() and two with the exact TypeErrors above.

Full suite: 161 tests, 0 failures. phpcs clean.

Verified end to end against released 3.10.1 on WP 7.1 / PHP 8.5:

before after
/?snippets-safe-mode=1 fatal, 1.3 KB clean, 69 KB, closes </html>
/ 200 200
/wp-admin/ 302 302

(Worth noting when testing by hand: opcache will happily serve the old bytecode after you swap the file in, which cost me a confusing minute.)

@imantsk
imantsk changed the base branch from core to release/v3.10.2/core August 31, 2026 17:21
@imantsk
imantsk force-pushed the fix/safe-mode-early-cap-check/core branch from 64d066c to c87e537 Compare August 31, 2026 17:28
Evaluate_Functions is constructed from Plugin::load_plugin(), which
Core/load.php calls at include time. WordPress includes active plugins
at wp-settings.php:574 but does not load pluggable.php until line 604,
so wp_get_current_user() does not exist yet.

The constructor called is_safe_mode_requested(), which checks a
capability, so any request carrying the snippets-safe-mode query var
died with:

  Call to undefined function wp_get_current_user()
  in wp-includes/capabilities.php:914

Only the empty() short-circuit on the query var kept this from firing on
every request. Safe mode was unusable as a result, which matters because
it is the recovery route offered when a snippet breaks a site.

Split the check in two: the constructor now tests only for the query
var, and the capability is checked inside the callback, which never runs
before URLs are being generated. Behaviour is unchanged for a user
without the capability, as the query var is still never added.

Also loosen the parameter types on this file's two filter callbacks.
Neither can trust its input, since an earlier callback in the chain may
return anything, and a non-nullable scalar turns that into a TypeError.
Reported for screen_settings by Sam on the support forum.
@imantsk
imantsk force-pushed the fix/safe-mode-early-cap-check/core branch from c87e537 to 4d7d60d Compare August 31, 2026 17:40
@imantsk
imantsk merged commit 61bb492 into release/v3.10.2/core Aug 31, 2026
7 checks passed
@imantsk
imantsk deleted the fix/safe-mode-early-cap-check/core branch August 31, 2026 17:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants