VYPR
Low severityNVD Advisory· Published Jun 9, 2026· Updated Jun 9, 2026

CVE-2026-49738

CVE-2026-49738

Description

The path allowance check in GeneralUtility::isAllowedAbsPath() performed a plain string prefix comparison without requiring a directory separator boundary, causing a path like /var/www/html-other/secret.yaml to be incorrectly accepted as valid when the project root was /var/www/html. Administrator users with access to the File Abstraction Layer were able to create new file storage definitions pointing to directories outside the project root, bypassing this path check. This issue affects TYPO3 CMS versions before 10.4.57, 11.0.0-11.5.51, 12.0.0-12.4.46, 13.0.0-13.4.31 and 14.0.0-14.3.3.

Affected products

2
  • TYPO3/Typo3references2 versions
    (expand)+ 1 more
    • (no CPE)
    • (no CPE)range: <10.4.57, 11.0.0-11.5.51, 12.0.0-12.4.46, 13.0.0-13.4.31, 14.0.0-14.3.3

Patches

2
44c2fa980794

[SECURITY] Fix path prefix confusion in isAllowedAbsPath

https://github.com/TYPO3/typo3Oliver HaderJun 9, 2026via github-commit-search
4 files changed · +58 2
  • typo3/sysext/core/Classes/Utility/GeneralUtility.php+2 1 modified
    @@ -2071,9 +2071,10 @@ public static function validPathStr(string $theFile): bool
          */
         public static function isAllowedAbsPath(string $path): bool
         {
    +        $path = PathUtility::sanitizeTrailingSeparator($path);
             return PathUtility::isAbsolutePath($path) && static::validPathStr($path)
                 && (
    -                str_starts_with($path, Environment::getProjectPath())
    +                str_starts_with($path, Environment::getProjectPath() . '/')
                     || PathUtility::isAllowedAdditionalPath($path)
                 );
         }
    
  • typo3/sysext/core/Tests/Functional/Utility/GeneralUtilityTest.php+51 0 added
    @@ -0,0 +1,51 @@
    +<?php
    +
    +declare(strict_types=1);
    +
    +/*
    + * This file is part of the TYPO3 CMS project.
    + *
    + * It is free software; you can redistribute it and/or modify it under
    + * the terms of the GNU General Public License, either version 2
    + * of the License, or any later version.
    + *
    + * For the full copyright and license information, please read the
    + * LICENSE.txt file that was distributed with this source code.
    + *
    + * The TYPO3 project - inspiring people to share!
    + */
    +
    +namespace TYPO3\CMS\Core\Tests\Functional\Utility;
    +
    +use PHPUnit\Framework\Attributes\DataProvider;
    +use PHPUnit\Framework\Attributes\Test;
    +use TYPO3\CMS\Core\Core\Environment;
    +use TYPO3\CMS\Core\Utility\GeneralUtility;
    +use TYPO3\TestingFramework\Core\Functional\FunctionalTestCase;
    +
    +final class GeneralUtilityTest extends FunctionalTestCase
    +{
    +    protected bool $initializeDatabase = false;
    +
    +    public static function isAllowedAbsPathDataProvider(): iterable
    +    {
    +        yield '{{project-path}}' => ['{{project-path}}', true];
    +        yield '{{project-path}}/' => ['{{project-path}}/', true];
    +        yield '{{project-path}}/some-file.png' => ['{{project-path}}/', true];
    +        yield '{{project-path}}-other' => ['{{project-path}}-other', false];
    +        yield '{{project-path}}-other/' => ['{{project-path}}-other', false];
    +        yield '{{project-path}}-other/some-file.png' => ['{{project-path}}-other', false];
    +    }
    +
    +    /**
    +     * See `\TYPO3\CMS\Core\Tests\Unit\Utility\PathUtilityTest::allowedAdditionalPathsAreEvaluated`
    +     * for the evaluation of `$GLOBALS['TYPO3_CONF_VARS']['BE']['lockRootPath']`.
    +     */
    +    #[Test]
    +    #[DataProvider('isAllowedAbsPathDataProvider')]
    +    public function allowedAbsolutePathIsEvaluated(string $path, bool $expectation): void
    +    {
    +        $path = str_replace('{{project-path}}', Environment::getPublicPath(), $path);
    +        self::assertSame($expectation, GeneralUtility::isAllowedAbsPath($path));
    +    }
    +}
    
  • typo3/sysext/core/Tests/Unit/Utility/PathUtilityTest.php+4 0 modified
    @@ -516,15 +516,19 @@ public static function allowedAdditionalPathsAreEvaluatedDataProvider(): \Genera
             yield ['/var/shared/', '/var/shared', true];
             yield ['/var/shared', '/var/shared/', true];
             yield ['/var/shared/', '/var/shared/', true];
    +        yield ['/var/shared', '/var/shared/file.png', true];
             yield ['/var/shared/', '/var/shared/file.png', true];
    +        yield ['/var/shared', '/var/shared-secret', false];
             yield ['/var/shared/', '/var/shared-secret', false];
             yield ['/var/shared/', '/var', false];
             // array settings
             yield [['/var'], '/var/shared', true];
             yield [['/var/shared/'], '/var/shared', true];
             yield [['/var/shared'], '/var/shared/', true];
             yield [['/var/shared/'], '/var/shared/', true];
    +        yield [['/var/shared'], '/var/shared/file.png', true];
             yield [['/var/shared/'], '/var/shared/file.png', true];
    +        yield [['/var/shared'], '/var/shared-secret', false];
             yield [['/var/shared/'], '/var/shared-secret', false];
             yield [['/var/shared/'], '/var', false];
         }
    
  • typo3/sysext/scheduler/Tests/Functional/Controller/NewSchedulerTaskControllerTest.php+1 1 modified
    @@ -173,7 +173,7 @@ public function queryParamsAreProcessed(): void
                     'SCRIPT_NAME' => '/typo3/index.php',
                 ]));
             $request = $request->withQueryParams([
    -            'returnUrl' => Environment::getPublicPath() . 'typo3/scheduler/manage?token=123&test=value',
    +            'returnUrl' => Environment::getPublicPath() . '/typo3/scheduler/manage?token=123&test=value',
                 'defaultValues' => $defaultValues,
             ]);
     
    
150a983a5d68

[SECURITY] Fix path prefix confusion in isAllowedAbsPath

https://github.com/TYPO3/typo3Oliver HaderJun 9, 2026via nvd-ref
3 files changed · +58 2
  • typo3/sysext/core/Classes/Utility/GeneralUtility.php+3 2 modified
    @@ -2541,10 +2541,11 @@ public static function isAllowedAbsPath(string $path): bool
             if (substr($path, 0, 6) === 'vfs://') {
                 return true;
             }
    +        $path = PathUtility::sanitizeTrailingSeparator($path);
             return PathUtility::isAbsolutePath($path) && static::validPathStr($path)
                 && (
    -                str_starts_with($path, Environment::getProjectPath())
    -                || str_starts_with($path, Environment::getPublicPath())
    +                str_starts_with($path, Environment::getProjectPath() . '/')
    +                || str_starts_with($path, Environment::getPublicPath() . '/')
                     || PathUtility::isAllowedAdditionalPath($path)
                 );
         }
    
  • typo3/sysext/core/Tests/Functional/Utility/GeneralUtilityTest.php+51 0 added
    @@ -0,0 +1,51 @@
    +<?php
    +
    +declare(strict_types=1);
    +
    +/*
    + * This file is part of the TYPO3 CMS project.
    + *
    + * It is free software; you can redistribute it and/or modify it under
    + * the terms of the GNU General Public License, either version 2
    + * of the License, or any later version.
    + *
    + * For the full copyright and license information, please read the
    + * LICENSE.txt file that was distributed with this source code.
    + *
    + * The TYPO3 project - inspiring people to share!
    + */
    +
    +namespace TYPO3\CMS\Core\Tests\Functional\Utility;
    +
    +use PHPUnit\Framework\Attributes\DataProvider;
    +use PHPUnit\Framework\Attributes\Test;
    +use TYPO3\CMS\Core\Core\Environment;
    +use TYPO3\CMS\Core\Utility\GeneralUtility;
    +use TYPO3\TestingFramework\Core\Functional\FunctionalTestCase;
    +
    +final class GeneralUtilityTest extends FunctionalTestCase
    +{
    +    protected bool $initializeDatabase = false;
    +
    +    public static function isAllowedAbsPathDataProvider(): iterable
    +    {
    +        yield '{{project-path}}' => ['{{project-path}}', true];
    +        yield '{{project-path}}/' => ['{{project-path}}/', true];
    +        yield '{{project-path}}/some-file.png' => ['{{project-path}}/', true];
    +        yield '{{project-path}}-other' => ['{{project-path}}-other', false];
    +        yield '{{project-path}}-other/' => ['{{project-path}}-other', false];
    +        yield '{{project-path}}-other/some-file.png' => ['{{project-path}}-other', false];
    +    }
    +
    +    /**
    +     * See `\TYPO3\CMS\Core\Tests\Unit\Utility\PathUtilityTest::allowedAdditionalPathsAreEvaluated`
    +     * for the evaluation of `$GLOBALS['TYPO3_CONF_VARS']['BE']['lockRootPath']`.
    +     */
    +    #[Test]
    +    #[DataProvider('isAllowedAbsPathDataProvider')]
    +    public function allowedAbsolutePathIsEvaluated(string $path, bool $expectation): void
    +    {
    +        $path = str_replace('{{project-path}}', Environment::getPublicPath(), $path);
    +        self::assertSame($expectation, GeneralUtility::isAllowedAbsPath($path));
    +    }
    +}
    
  • typo3/sysext/core/Tests/Unit/Utility/PathUtilityTest.php+4 0 modified
    @@ -565,15 +565,19 @@ public static function allowedAdditionalPathsAreEvaluatedDataProvider(): \Genera
             yield ['/var/shared/', '/var/shared', true];
             yield ['/var/shared', '/var/shared/', true];
             yield ['/var/shared/', '/var/shared/', true];
    +        yield ['/var/shared', '/var/shared/file.png', true];
             yield ['/var/shared/', '/var/shared/file.png', true];
    +        yield ['/var/shared', '/var/shared-secret', false];
             yield ['/var/shared/', '/var/shared-secret', false];
             yield ['/var/shared/', '/var', false];
             // array settings
             yield [['/var'], '/var/shared', true];
             yield [['/var/shared/'], '/var/shared', true];
             yield [['/var/shared'], '/var/shared/', true];
             yield [['/var/shared/'], '/var/shared/', true];
    +        yield [['/var/shared'], '/var/shared/file.png', true];
             yield [['/var/shared/'], '/var/shared/file.png', true];
    +        yield [['/var/shared'], '/var/shared-secret', false];
             yield [['/var/shared/'], '/var/shared-secret', false];
             yield [['/var/shared/'], '/var', false];
         }
    

Vulnerability mechanics

Root cause

"The path allowance check in GeneralUtility::isAllowedAbsPath() performed a plain string prefix comparison without requiring a directory separator boundary."

Attack vector

Administrator users with access to the File Abstraction Layer could create new file storage definitions. By providing a path that superficially matched the project root but extended beyond it, such as `/var/www/html-other/secret.yaml` when the project root was `/var/www/html`, they could bypass the path check [ref_id=1]. This allowed them to point storage definitions to directories outside the intended project root, leading to broken access control [ref_id=1].

Affected code

The vulnerability resides in the `GeneralUtility::isAllowedAbsPath()` method within the TYPO3 CMS Core API. The issue stems from a string prefix comparison that did not account for directory boundaries, allowing paths like `/var/www/html-other/secret.yaml` to be considered valid when the project root was `/var/www/html` [ref_id=1].

What the fix does

The patch modifies the `isAllowedAbsPath()` function to correctly enforce directory separator boundaries during path prefix comparisons [patch_id=5349011, patch_id=5349010]. This prevents paths that merely share a prefix with the project root but extend beyond it from being incorrectly accepted. For example, it now correctly rejects `/var/shared-secret` when the allowed path is `/var/shared` [ref_id=2, ref_id=3].

Preconditions

  • authThe attacker must be an administrator user with access to the File Abstraction Layer.
  • configThe attacker must be able to create new file storage definitions.

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

References

3

News mentions

1