From a44e7c604aca515c460f2c530e585851fc9fe586 Mon Sep 17 00:00:00 2001 From: Alex Standiford Date: Thu, 11 Jun 2026 10:54:12 -0400 Subject: [PATCH] fix: validate Accept-Language header instead of trusting raw bytes HeaderLanguageProvider exploded the raw attacker-controlled HTTP_ACCEPT_LANGUAGE header. Validate the primary entry against the language-tag shape and return only the two-letter primary subtag; anything malformed resolves to null. Ref phpnomad/wordpress-integration#34 --- lib/Providers/HeaderLanguageProvider.php | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/lib/Providers/HeaderLanguageProvider.php b/lib/Providers/HeaderLanguageProvider.php index d2574b6..7f5f74a 100644 --- a/lib/Providers/HeaderLanguageProvider.php +++ b/lib/Providers/HeaderLanguageProvider.php @@ -11,14 +11,18 @@ class HeaderLanguageProvider */ public function getLanguage(): ?string { - if (isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) { - $languages = explode(',', $_SERVER['HTTP_ACCEPT_LANGUAGE']); + if (!isset($_SERVER['HTTP_ACCEPT_LANGUAGE']) || !is_string($_SERVER['HTTP_ACCEPT_LANGUAGE'])) { + return null; + } + + // The header is attacker-controlled: validate the primary entry + // against the language-tag shape instead of trusting raw bytes. + $primary = explode(',', $_SERVER['HTTP_ACCEPT_LANGUAGE'])[0]; - if (!empty($languages[0])) { - return strtolower(substr($languages[0], 0, 2)); - } + if (!preg_match('/^\s*([a-zA-Z]{2})(?:[-_][a-zA-Z0-9]{2,8})*\s*(?:;.*)?$/', $primary, $matches)) { + return null; } - return null; + return strtolower($matches[1]); } }