VYPR
Medium severity4.4NVD Advisory· Published Jun 1, 2026

CVE-2026-45279

CVE-2026-45279

Description

Nextcloud Server allows non-admin users to copy arbitrary files via path traversal if {lang} is used in the template directory config.

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

Nextcloud Server allows non-admin users to copy arbitrary files via path traversal if `{lang}` is used in the template directory config.

Vulnerability

Nextcloud Server versions 31.0.0 to before 31.0.14 and 32.0.0 to before 32.0.4 are affected by a path traversal vulnerability. This issue occurs when the {lang} variable is used within the template directory configuration value. The vulnerability allows non-administrative users to potentially copy arbitrary files into their own Nextcloud directories, contingent on the underlying Unix permissions [2].

Exploitation

An attacker, who is a non-admin user, needs to have the {lang} variable configured in the template directory setting. The attacker can then exploit this by leveraging the template API to perform a path traversal, effectively copying files from the server's filesystem into their Nextcloud directory. The success and scope of the file copying depend on the file's read permissions and the Nextcloud user's write permissions within their directory [2].

Impact

Successful exploitation allows a non-admin user to copy arbitrary files from the server into their Nextcloud directory. This could lead to the disclosure of sensitive files or the overwriting of existing files within the user's accessible space, depending on the attacker's objective and the permissions of the target files [2].

Mitigation

Nextcloud Server should be upgraded to version 32.0.4 or 31.0.14. For Nextcloud Enterprise Server, upgrade to 32.0.4, 31.0.14, 30.0.17.7, 29.0.17.12, or 28.0.14.15. A potential workaround is to remove {lang} from the template directory configuration value. No other workarounds are available [2].

AI Insight generated on Jun 1, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.

Affected products

1

Patches

1
66c8f9c4dc88

Merge pull request #57414 from nextcloud/bugfix/noid/clean-language

https://github.com/nextcloud/serverJoas SchillingJan 7, 2026via nvd-ref
2 files changed · +47 10
  • lib/private/L10N/Factory.php+31 10 modified
    @@ -94,11 +94,8 @@ public function __construct(
     	public function get($app, $lang = null, $locale = null) {
     		return new LazyL10N(function () use ($app, $lang, $locale) {
     			$app = $this->appManager->cleanAppId($app);
    -			if ($lang !== null) {
    -				$lang = str_replace(['\0', '/', '\\', '..'], '', $lang);
    -			}
    -
    -			$forceLang = $this->request->getParam('forceLanguage') ?? $this->config->getSystemValue('force_language', false);
    +			$lang = $this->cleanLanguage($lang);
    +			$forceLang = $this->cleanLanguage($this->request->getParam('forceLanguage')) ?? $this->config->getSystemValue('force_language', false);
     			if (is_string($forceLang)) {
     				$lang = $forceLang;
     			}
    @@ -128,6 +125,29 @@ public function get($app, $lang = null, $locale = null) {
     		});
     	}
     
    +	/**
    +	 * Remove some invalid characters before using a string as a language
    +	 *
    +	 * @psalm-taint-escape callable
    +	 * @psalm-taint-escape cookie
    +	 * @psalm-taint-escape file
    +	 * @psalm-taint-escape has_quotes
    +	 * @psalm-taint-escape header
    +	 * @psalm-taint-escape html
    +	 * @psalm-taint-escape include
    +	 * @psalm-taint-escape ldap
    +	 * @psalm-taint-escape shell
    +	 * @psalm-taint-escape sql
    +	 * @psalm-taint-escape unserialize
    +	 */
    +	private function cleanLanguage(?string $lang): ?string {
    +		if ($lang === null) {
    +			return null;
    +		}
    +		$lang = preg_replace('/[^a-zA-Z0-9.;,=-]/', '', $lang);
    +		return str_replace('..', '', $lang);
    +	}
    +
     	/**
     	 * Check that $lang is an existing language and not null, otherwise return the language to use instead
     	 *
    @@ -160,7 +180,7 @@ private function validateLanguage(string $app, ?string $lang): string {
     	 */
     	public function findLanguage(?string $appId = null): string {
     		// Step 1: Forced language always has precedence over anything else
    -		$forceLang = $this->request->getParam('forceLanguage') ?? $this->config->getSystemValue('force_language', false);
    +		$forceLang = $this->cleanLanguage($this->request->getParam('forceLanguage')) ?? $this->config->getSystemValue('force_language', false);
     		if (is_string($forceLang)) {
     			$this->requestLanguage = $forceLang;
     		}
    @@ -217,7 +237,7 @@ public function findLanguage(?string $appId = null): string {
     
     	public function findGenericLanguage(?string $appId = null): string {
     		// Step 1: Forced language always has precedence over anything else
    -		$forcedLanguage = $this->request->getParam('forceLanguage') ?? $this->config->getSystemValue('force_language', false);
    +		$forcedLanguage = $this->cleanLanguage($this->request->getParam('forceLanguage')) ?? $this->config->getSystemValue('force_language', false);
     		if ($forcedLanguage !== false) {
     			return $forcedLanguage;
     		}
    @@ -412,7 +432,8 @@ public function getUserLanguage(?IUser $user = null): string {
     				return $language;
     			}
     
    -			if (($forcedLanguage = $this->request->getParam('forceLanguage')) !== null) {
    +			$forcedLanguage = $this->cleanLanguage($this->request->getParam('forceLanguage'));
    +			if ($forcedLanguage !== null) {
     				return $forcedLanguage;
     			}
     
    @@ -426,7 +447,7 @@ public function getUserLanguage(?IUser $user = null): string {
     			}
     		}
     
    -		return $this->request->getParam('forceLanguage') ?? $this->config->getSystemValueString('default_language', 'en');
    +		return $this->cleanLanguage($this->request->getParam('forceLanguage')) ?? $this->config->getSystemValueString('default_language', 'en');
     	}
     
     	/**
    @@ -452,7 +473,7 @@ public function localeExists($locale) {
     	 * @throws LanguageNotFoundException
     	 */
     	private function getLanguageFromRequest(?string $app = null): string {
    -		$header = $this->request->getHeader('ACCEPT_LANGUAGE');
    +		$header = $this->cleanLanguage($this->request->getHeader('ACCEPT_LANGUAGE'));
     		if ($header !== '') {
     			$available = $this->findAvailableLanguages($app);
     
    
  • tests/lib/L10N/FactoryTest.php+16 0 modified
    @@ -20,6 +20,7 @@
     use OCP\IUser;
     use OCP\IUserSession;
     use OCP\L10N\ILanguageIterator;
    +use PHPUnit\Framework\Attributes\DataProvider;
     use PHPUnit\Framework\MockObject\MockObject;
     use Test\TestCase;
     
    @@ -90,6 +91,21 @@ protected function getFactory(array $methods = [], $mockRequestGetHeaderMethod =
     		return new Factory($this->config, $this->request, $this->userSession, $this->cacheFactory, $this->serverRoot, $this->appManager);
     	}
     
    +	public static function dataCleanLanguage(): array {
    +		return [
    +			'null shortcut' => [null, null],
    +			'default language' => ['de', 'de'],
    +			'malicious language' => ['de/../fr', 'defr'],
    +			'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'],
    +		];
    +	}
    +
    +	#[DataProvider('dataCleanLanguage')]
    +	public function testCleanLanguage(?string $lang, ?string $expected): void {
    +		$factory = $this->getFactory();
    +		$this->assertSame($expected, self::invokePrivate($factory, 'cleanLanguage', [$lang]));
    +	}
    +
     	public static function dataFindAvailableLanguages(): array {
     		return [
     			[null],
    

Vulnerability mechanics

Root cause

"The application does not properly sanitize the language parameter, allowing path traversal."

Attack vector

A non-admin user can exploit this vulnerability by providing a crafted language string that includes path traversal sequences (e.g., `..`). This allows the user to copy arbitrary files, subject to Unix permissions, into their own Nextcloud directory. The vulnerability is triggered when the `{lang}` variable is used in the template directory configuration [ref_id=1].

Affected code

The vulnerability resides in the language handling logic, specifically within the `get` method of the `LazyL10N` class and related functions like `cleanLanguage` and `findLanguage`. The patch modifies the `cleanLanguage` method to sanitize the input language strings [ref_id=1].

What the fix does

The patch modifies the `cleanLanguage` function to remove null bytes, forward slashes, backslashes, and double dots from the language string. This sanitization prevents path traversal by ensuring that the language parameter cannot be used to navigate outside the intended directory structure [patch_id=4383036].

Preconditions

  • authThe attacker must be a non-admin user.
  • configThe `{lang}` variable must be used in the template directory configuration.

Generated on Jun 1, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

3

News mentions

0

No linked articles in our index yet.