Make WordPress Core

Opened 3 weeks ago

Closed 2 days ago

#66106 closed defect (bug) (fixed)

get_locale(), get_user_locale() and determine_locale() can return a non-string

Reported by: jonsurrell Owned by: jonsurrell
Priority: normal Milestone: 7.2
Component: I18N Version:
Severity: normal Keywords: has-patch has-unit-tests commit
Cc: Focuses:

Description (last modified by jonsurrell)

get_locale(), get_user_locale() and determine_locale() can return a non-string values. All three functions are documented @return string, but they read from untyped data sources that may be invalid.

Once an unexpected value gets through, the first just-in-time translation of an unloaded text domain reaches WP_Textdomain_Registry::set(), which uses the locale as an array key: TypeError: Cannot access offset of type array on array.

This is not theoretical, it was discovered from fatal errors observed in production environments.

Change History (9)

#1 @jonsurrell
3 weeks ago

  • Description modified (diff)

This ticket was mentioned in ​PR #13335 on ​WordPress/wordpress-develop by ​@jonsurrell.


3 weeks ago
#2

  • Keywords has-patch has-unit-tests added

Prevent non-string locale values from causing a TypeError during just-in-time translation. Arrays can come from stored options, user meta, globals, filters, or requests such as wp-login.php?wp_lang[]=de_DE; the existing truthiness checks accept them.

Add is_string() checks to get_locale(), get_user_locale() and determine_locale(). Invalid values use the existing fallbacks: en_US for the site locale, get_locale() for the user or request locale, and the unfiltered locale for invalid filter results. Empty globals and empty locale or determine_locale filter results now use these fallbacks too. Locale string syntax is unchanged.

Writers are unchanged: wp_insert_user(), meta_input and direct metadata writes can still store non-strings, so the getters validate on read.

Adds regression tests for stored values, globals, filters and request parameters, including translation calls with array locales.

Trac ticket: https://core.trac.wordpress.org/ticket/66106

## Use of AI Tools

AI assistance: Yes
Tools: Claude Code (Claude Opus 5, Claude Fable 5.1), Codex
Used for: Investigation, implementation, tests, review and description. Codex revised the description.

​@jonsurrell commented on ​PR #13335:


3 weeks ago
#3

@josephscott This is ready for review if you'd like to take a look.

#4 @westonruter
3 weeks ago

This is not theoretical, it was discovered from fatal errors observed in production environments.

I'm curious as to the themes/plugins responsible for these errors. Clearly they are doing it wrong. Is there a better way to let them know they're doing it wrong besides what static analysis would have told them?

#5 @jonsurrell
3 weeks ago

I agree, I'd like to know how the bad data enters and address it there. Unfortunately, it's hard to know since this data is often stored or cached and the error only manifests in these fatals.

​@josephscott commented on ​PR #13335:


3 weeks ago
#6

I think the changes look good. One thing that this PR changes is that some filters will no longer fire on the invalid values. From my point of view, that is the right move - there are likely many filters out there that don't check the values first. I bring it up as something that might be worth noting.

Now that I've written that out, I expect that type of change to happen in various places. Perhaps it is worth a broader single callout instead of adding it to code comments every where.

#7 @jonsurrell
3 weeks ago

  • Keywords commit added

#8 @westonruter
3 weeks ago

  • Owner set to jonsurrell

#9 @jonsurrell
2 days ago

  • Resolution → fixed
  • Status assigned → closed

In 64122:

I18N: Ignore non-string values in the locale getters.

get_locale(), get_user_locale() and determine_locale() read untyped values from options, user meta, globals, filters and request parameters. A non-string value can cause a TypeError during just-in-time translation. The getters now ignore non-string and empty values and use their existing fallbacks.

Developed in: ​https://github.com/WordPress/wordpress-develop/pull/13335

Props jonsurrell, westonruter, josephscott.
Fixes #66106.

Note: See TracTickets for help on using tickets.