Skip to content

Commit f24baa6

Browse files
Merge pull request #57534 from nextcloud/backport/57419/stable30
[stable30] fix(l10n): Fix language selection
2 parents 742e8e3 + 26d7e32 commit f24baa6

File tree

2 files changed

+42
-5
lines changed

2 files changed

+42
-5
lines changed

lib/private/L10N/Factory.php

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -82,10 +82,7 @@ public function __construct(
8282
public function get($app, $lang = null, $locale = null) {
8383
return new LazyL10N(function () use ($app, $lang, $locale) {
8484
$app = \OC_App::cleanAppId($app);
85-
if ($lang !== null) {
86-
$lang = str_replace(['\0', '/', '\\', '..'], '', $lang);
87-
}
88-
85+
$lang = $this->cleanLanguage($lang);
8986
$forceLang = $this->config->getSystemValue('force_language', false);
9087
if (is_string($forceLang)) {
9188
$lang = $forceLang;
@@ -118,6 +115,29 @@ public function get($app, $lang = null, $locale = null) {
118115
});
119116
}
120117

118+
/**
119+
* Remove some invalid characters before using a string as a language
120+
*
121+
* @psalm-taint-escape callable
122+
* @psalm-taint-escape cookie
123+
* @psalm-taint-escape file
124+
* @psalm-taint-escape has_quotes
125+
* @psalm-taint-escape header
126+
* @psalm-taint-escape html
127+
* @psalm-taint-escape include
128+
* @psalm-taint-escape ldap
129+
* @psalm-taint-escape shell
130+
* @psalm-taint-escape sql
131+
* @psalm-taint-escape unserialize
132+
*/
133+
private function cleanLanguage(?string $lang): ?string {
134+
if ($lang === null) {
135+
return null;
136+
}
137+
$lang = preg_replace('/[^a-zA-Z0-9.;,=-]/', '', $lang);
138+
return str_replace('..', '', $lang);
139+
}
140+
121141
/**
122142
* Find the best language
123143
*
@@ -427,7 +447,7 @@ public function localeExists($locale) {
427447
* @throws LanguageNotFoundException
428448
*/
429449
private function getLanguageFromRequest(?string $app = null): string {
430-
$header = $this->request->getHeader('ACCEPT_LANGUAGE');
450+
$header = $this->cleanLanguage($this->request->getHeader('ACCEPT_LANGUAGE'));
431451
if ($header !== '') {
432452
$available = $this->findAvailableLanguages($app);
433453

tests/lib/L10N/FactoryTest.php

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,23 @@ protected function getFactory(array $methods = [], $mockRequestGetHeaderMethod =
9090
return new Factory($this->config, $this->request, $this->userSession, $this->cacheFactory, $this->serverRoot, $this->appManager);
9191
}
9292

93+
public static function dataCleanLanguage(): array {
94+
return [
95+
'null shortcut' => [null, null],
96+
'default language' => ['de', 'de'],
97+
'malicious language' => ['de/../fr', 'defr'],
98+
'request language' => ['kab;q=0.8,ka;q=0.7,de;q=0.6', 'kab;q=0.8,ka;q=0.7,de;q=0.6'],
99+
];
100+
}
101+
102+
/**
103+
* @dataProvider dataCleanLanguage
104+
*/
105+
public function testCleanLanguage(?string $lang, ?string $expected): void {
106+
$factory = $this->getFactory();
107+
$this->assertSame($expected, self::invokePrivate($factory, 'cleanLanguage', [$lang]));
108+
}
109+
93110
public function dataFindAvailableLanguages(): array {
94111
return [
95112
[null],

0 commit comments

Comments
 (0)