From abbf859d531d99e1babbf7e63a089a6bc038c0eb Mon Sep 17 00:00:00 2001 From: Jb Audras Date: Tue, 16 May 2023 14:16:39 +0000 Subject: [PATCH] I18N: Introduce sanitization function for locale. Introduce the `sanitize_locale_name()` for sanitizing user input of locales. Props xknown, timothyblynjacobs, ocean90, peterwilsoncc. git-svn-id: https://develop.svn.wordpress.org/trunk@55760 602fd350-edb4-49c9-b593-d223f7449a82 --- src/wp-includes/formatting.php | 23 +++++++++ src/wp-includes/l10n.php | 4 +- .../tests/formatting/sanitizeLocaleName.php | 49 +++++++++++++++++++ 3 files changed, 74 insertions(+), 2 deletions(-) create mode 100644 tests/phpunit/tests/formatting/sanitizeLocaleName.php diff --git a/src/wp-includes/formatting.php b/src/wp-includes/formatting.php index 8240adcc82..01d1eebc4d 100644 --- a/src/wp-includes/formatting.php +++ b/src/wp-includes/formatting.php @@ -2433,6 +2433,29 @@ function sanitize_html_class( $classname, $fallback = '' ) { return apply_filters( 'sanitize_html_class', $sanitized, $classname, $fallback ); } +/** + * Strips out all characters not allowed in a locale name. + * + * @since 6.2.1 + * + * @param string $locale_name The locale name to be sanitized. + * @return string The sanitized value. + */ +function sanitize_locale_name( $locale_name ) { + // Limit to A-Z, a-z, 0-9, '_', '-'. + $sanitized = preg_replace( '/[^A-Za-z0-9_-]/', '', $locale_name ); + + /** + * Filters a sanitized locale name string. + * + * @since 6.2.1 + * + * @param string $sanitized The sanitized locale name. + * @param string $locale_name The locale name before sanitization. + */ + return apply_filters( 'sanitize_locale_name', $sanitized, $locale_name ); +} + /** * Converts lone & characters into `&` (a.k.a. `&`) * diff --git a/src/wp-includes/l10n.php b/src/wp-includes/l10n.php index 9d6135b470..448a233fa8 100644 --- a/src/wp-includes/l10n.php +++ b/src/wp-includes/l10n.php @@ -149,9 +149,9 @@ function determine_locale() { $wp_lang = ''; if ( ! empty( $_GET['wp_lang'] ) ) { - $wp_lang = sanitize_text_field( $_GET['wp_lang'] ); + $wp_lang = sanitize_locale_name( wp_unslash( $_GET['wp_lang'] ) ); } elseif ( ! empty( $_COOKIE['wp_lang'] ) ) { - $wp_lang = sanitize_text_field( $_COOKIE['wp_lang'] ); + $wp_lang = sanitize_locale_name( wp_unslash( $_COOKIE['wp_lang'] ) ); } if ( ! empty( $wp_lang ) && ! empty( $GLOBALS['pagenow'] ) && 'wp-login.php' === $GLOBALS['pagenow'] ) { diff --git a/tests/phpunit/tests/formatting/sanitizeLocaleName.php b/tests/phpunit/tests/formatting/sanitizeLocaleName.php new file mode 100644 index 0000000000..cd22acbf2c --- /dev/null +++ b/tests/phpunit/tests/formatting/sanitizeLocaleName.php @@ -0,0 +1,49 @@ +assertSame( $expected, sanitize_locale_name( $input ) ); + } + + public function data_sanitize_locale_name_returns_non_empty_string() { + return array( + // array( expected, input ) + array( 'en_US', 'en_US' ), + array( 'en', 'en' ), + array( 'fr_FR', 'fr_FR' ), + array( 'fr_FR', 'fr_FR' ), + array( 'fr_FR-e2791ba830489d23043be8650a22a22b', 'fr_FR-e2791ba830489d23043be8650a22a22b' ), + array( '-fr_FRmo', '-fr_FR.mo' ), + array( '12324', '$12324' ), + array( '4124FRRa', '/4124$$$%%FRRa' ), + array( 'FR', 'assertSame( '', sanitize_locale_name( $input ) ); + } + + public function data_sanitize_locale_name_returns_empty_string() { + return array( + // array( input ) + array( '$<>' ), + array( '/$$$%%\\)' ), + array( '....' ), + array( '@///' ), + ); + } +}