Information Disclosure in TYPO3 Page Tree
Description
TYPO3 is a free and open source Content Management Framework. Backend users could see items in the backend page tree without having access if the mounts pointed to pages restricted for their user/group, or if no mounts were configured but the pages allowed access to "everybody." However, affected users could not manipulate these pages. Users are advised to update to TYPO3 versions 10.4.46 ELTS, 11.5.40 LTS, 12.4.21 LTS, 13.3.1 that fix the problem described. There are no known workarounds for this vulnerability.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
In TYPO3, backend users could see restricted pages in the page tree if mounts pointed to restricted pages or no mounts were configured, leading to information disclosure.
Vulnerability
Overview
CVE-2024-47780 is an information disclosure vulnerability in the TYPO3 Content Management Framework that affects the backend page tree component. The root cause is that the page tree incorrectly displayed pages to backend users even when those users lacked explicit access rights. Specifically, if user or group mounts pointed to pages that were restricted for that user or group, the restricted pages still appeared as visible entries in the page tree. Additionally, if no mounts were configured for a user but certain pages had global access permissions set to "everybody," those pages also appeared in the page tree for users who should not have seen them [1].
Exploitation
Context
Exploitation of this vulnerability requires a valid backend user account, but no special privileges beyond that. The attack surface is the TYPO3 backend page tree interface, which is accessible to authenticated backend users. The vulnerability does not require any user interaction beyond logging into the backend; the unauthorized page listings are automatically displayed. The commit diffs show that the fix involves removing references to hardcoded page IDs with unrestricted symbolic references, ensuring that the page tree only shows pages that are explicitly configured for display through proper mountpoint settings [2][3][4].
Impact
An attacker with a backend user account could enumerate the structure and titles of pages that were meant to be hidden from them, potentially revealing sensitive content or internal site architecture. Importantly, the affected users could not manipulate these pages; the exposure was limited to read-only visibility of page tree entries [1]. This information leak could aid further attacks or expose confidential information.
Mitigation
TYPO3 has released patched versions that fix the issue: 10.4.46 ELTS, 11.5.40 LTS, 12.4.21 LTS, and 13.3.1. Users are strongly advised to update to these or later versions. There are no known workarounds for this vulnerability [1].
AI Insight generated on May 20, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
typo3/cms-backendPackagist | >= 13.0.0, < 13.3.1 | 13.3.1 |
typo3/cms-backendPackagist | >= 12.0.0, < 12.4.21 | 12.4.21 |
typo3/cms-backendPackagist | >= 11.0.0, < 11.5.40 | 11.5.40 |
typo3/cms-backendPackagist | >= 10.0.0, < 10.4.46 | 10.4.46 |
Affected products
2Patches
39ae1ef969b63[SECURITY] Show only explicitly configured page tree information
5 files changed · +70 −20
Classes/Controller/Page/TreeController.php+4 −4 modified@@ -407,11 +407,11 @@ protected function pagesToFlatArray(array $page, int $entryPoint, int $depth = 0 'workspaceId' => !empty($page['t3ver_oid']) ? $page['t3ver_oid'] : $pageId, ]; - if (!empty($page['_children']) || $this->pageTreeRepository->hasChildren($pageId)) { + if ($depth >= $this->levelsToFetch && $this->pageTreeRepository->hasChildren($pageId)) { + $page = $this->pageTreeRepository->getTreeLevels($page, 1); + } + if (!empty($page['_children'])) { $item['hasChildren'] = true; - if ($depth >= $this->levelsToFetch) { - $page = $this->pageTreeRepository->getTreeLevels($page, 1); - } } if (is_array($lockInfo)) { $item['locked'] = true;
Classes/Tree/Repository/PageTreeRepository.php+8 −9 modified@@ -172,29 +172,28 @@ protected function applyCallbackToChildren(array &$tree, callable $callback): vo * * @param array $pageTree The page record of the top level page you want to get the page tree of * @param int $depth Number of levels to fetch - * @param array $entryPointIds entryPointIds to include + * @param ?array $entryPointIds entryPointIds to include (null in case no entry-points were provided) * @return array An array with page records and their children */ - public function getTreeLevels(array $pageTree, int $depth, array $entryPointIds = []): array + public function getTreeLevels(array $pageTree, int $depth, ?array $entryPointIds = null): array { $groupedAndSortedPagesByPid = []; - - if (count($entryPointIds) > 0) { + // the method was called without any entry-point information + if ($entryPointIds === null) { + $parentPageIds = [$pageTree['uid']]; + // the method was called with entry-point information, that is not empty + } elseif ($entryPointIds !== []) { $pageRecords = $this->getPageRecords($entryPointIds); $groupedAndSortedPagesByPid[$pageTree['uid']] = $pageRecords; $parentPageIds = $entryPointIds; - } else { - $parentPageIds = [$pageTree['uid']]; } - for ($i = 0; $i < $depth; $i++) { + // stop in case the initial or recursive query did not have any pages if (empty($parentPageIds)) { break; } $pageRecords = $this->getChildPageRecords($parentPageIds); - $groupedAndSortedPagesByPid = $this->groupAndSortPages($pageRecords, $groupedAndSortedPagesByPid); - $parentPageIds = array_column($pageRecords, 'uid'); } $this->addChildrenToPage($pageTree, $groupedAndSortedPagesByPid);
Tests/Functional/Controller/Page/Fixtures/be_users.csv+2 −0 modified@@ -3,4 +3,6 @@ ,1,0,"admin",,0,1,0 ,2,0,"test1",,1,0,0 ,3,0,"test1",,0,0,0 +,7,0,"editor",7,0,0,3 +,8,0,"editor",8,0,0,3 ,9,0,"editor",9,0,0,3
Tests/Functional/Controller/Page/Fixtures/PagesWithBEPermissions.yaml+7 −4 modified@@ -4,8 +4,6 @@ __variables: - &pageMount 7 - &pageFolder 254 - &contentText 'text' - - &idAcmeRootPage 1000 - - &idAcmeFirstPage 1100 entitySettings: '*': @@ -45,11 +43,13 @@ entities: workspace: - self: {id: 1, title: 'Workspace'} beGroup: + - self: {id: 7, title: 'editors without DB mounts', db_mountpoints: '', tables_select: 'pages,tt_content', tables_modify: 'pages,tt_content', page_types_select: '1,4,7,254', groupMods: 'web_layout,web_list'} + - self: {id: 8, title: 'editors with DB mounts', db_mountpoints: '9200', tables_select: 'pages,tt_content', tables_modify: 'pages,tt_content', page_types_select: '1,4,7,254', groupMods: 'web_layout,web_list'} - self: {id: 9, title: 'editors', db_mountpoints: '1000,2000,8110', tables_select: 'pages,tt_content', tables_modify: 'pages,tt_content', page_types_select: '1,4,7,254', groupMods: 'web_layout,web_list'} page: - - self: {id: *idAcmeRootPage, title: 'ACME Inc', type: *pageShortcut, shortcut: 'first', root: true, slug: '/'} + - self: {id: 1000, title: 'ACME Inc', type: *pageShortcut, shortcut: 'first', root: true, slug: '/'} children: - - self: {id: *idAcmeFirstPage, title: 'EN: Welcome', slug: '/welcome', subtitle: 'hello-and-welcome'} + - self: {id: 1100, title: 'EN: Welcome', slug: '/welcome', subtitle: 'hello-and-welcome'} - self: {id: 1200, title: 'EN: Features', slug: '/features'} children: - self: {id: 1210, title: 'EN: Frontend Editing', slug: '/features/frontend-editing', perms_userid: 9, perms_groupid: 1, description: 'accessible for user, but not for group'} @@ -153,3 +153,6 @@ entities: - { action: 'move', type: 'toPage', target: 1700 } - self: {id: 8120, title: 'Asia', slug: '/divisions/asia', description: 'not visible as not mounted'} - self: {id: 8130, title: 'South America', slug: '/divisions/south-america'} + - self: {id: 9100, title: 'Page 9100', type: *pageStandard, root: true, slug: '/page-9100', perms_userid: 1, perms_groupid: 1, perms_everybody: 15} + - self: {id: 9200, title: 'Page 9200', type: *pageStandard, root: true, slug: '/page-9200', perms_userid: 1, perms_groupid: 1, perms_everybody: 0} + - self: {id: 9300, title: 'Page 9300', type: *pageStandard, root: true, slug: '/page-9300', perms_userid: 1, perms_groupid: 1, perms_everybody: 15}
Tests/Functional/Controller/Page/TreeControllerTest.php+49 −3 modified@@ -59,11 +59,11 @@ protected function setUp(): void $this->withDatabaseSnapshot(function () { $this->importCSVDataSet(__DIR__ . '/Fixtures/be_users.csv'); // Admin user for importing dataset - $this->backendUser = $this->setUpBackendUser(1); - $GLOBALS['LANG'] = $this->get(LanguageServiceFactory::class)->createFromUserPreferences($this->backendUser); + $backendUser = $this->setUpBackendUser(1); + $GLOBALS['LANG'] = $this->get(LanguageServiceFactory::class)->createFromUserPreferences($backendUser); $scenarioFile = __DIR__ . '/Fixtures/PagesWithBEPermissions.yaml'; $factory = DataHandlerFactory::fromYamlFile($scenarioFile); - $writer = DataHandlerWriter::withBackendUser($this->backendUser); + $writer = DataHandlerWriter::withBackendUser($backendUser); $writer->invokeFactory($factory); self::failIfArrayIsNotEmpty($writer->getErrors()); }, function () { @@ -287,6 +287,19 @@ public function getAllEntryPointPageTreesWithRootPageAsMountPoint(): void ], ], ], + [ + // 9100 is shown due to `perms_everybody=15` + 'uid' => 9100, + 'title' => 'Page 9100', + '_children' => [], + ], + // 9200 is omitted due to `perms_everybody=0` + [ + // 9300 is shown due to `perms_everybody=15` + 'uid' => 9300, + 'title' => 'Page 9300', + '_children' => [], + ], ], ], [ @@ -813,4 +826,37 @@ static function (AfterPageTreeItemsPreparedEvent $event) use (&$afterPageTreeIte self::assertEquals('1000', $afterPageTreeItemsPreparedEvent->getItems()[1]['identifier']); self::assertEquals('ACME Inc', $afterPageTreeItemsPreparedEvent->getItems()[1]['name']); } + + public static function fetchDataActionConsidersPermissionsDataProvider(): \Generator + { + yield 'admin user can see all root pages' => [ + 'backendUser' => 1, + 'expectation' => ['0', '1000', '2000', '7000', '8000', '9100', '9200', '9300'], + ]; + yield 'editor with DB mounts can only see accessible pages' => [ + 'backendUser' => 9, + 'expectation' => ['0', '1000', '8110'], + ]; + yield 'editor with DB mounts cannot see inaccessible pages' => [ + 'backendUser' => 8, + 'expectation' => ['0'], + ]; + yield 'editor without DB mounts cannot see any pages' => [ + 'backendUser' => 7, + 'expectation' => ['0'], + ]; + } + + #[Test] + #[DataProvider('fetchDataActionConsidersPermissionsDataProvider')] + public function fetchDataActionConsidersPermissions(int $backendUser, array $expectation): void + { + $this->backendUser = $this->setUpBackendUser($backendUser); + $request = (new ServerRequest(new Uri('https://example.com')))->withQueryParams(['depth' => 1]); + $response = $this->get(TreeController::class)->fetchDataAction($request); + $data = json_decode((string)$response->getBody(), true); + $items = array_filter($data, static fn(array $page): bool => $page['depth'] <= 1); + $items = array_map(static fn(array $page): string => $page['identifier'], $items); + self::assertSame($expectation, array_values($items)); + } }
8b024b08a2c7[SECURITY] Show only explicitly configured page tree information
5 files changed · +70 −19
Classes/Controller/Page/TreeController.php+4 −4 modified@@ -411,11 +411,11 @@ protected function pagesToFlatArray(array $page, int $entryPoint, int $depth = 0 'allowEdit' => $this->userHasAccessToModifyPagesAndToDefaultLanguage && $backendUser->doesUserHaveAccess($page, Permission::PAGE_EDIT), ]; - if (!empty($page['_children']) || $this->pageTreeRepository->hasChildren($pageId)) { + if ($depth >= $this->levelsToFetch && $this->pageTreeRepository->hasChildren($pageId)) { + $page = $this->pageTreeRepository->getTreeLevels($page, 1); + } + if (!empty($page['_children'])) { $item['hasChildren'] = true; - if ($depth >= $this->levelsToFetch) { - $page = $this->pageTreeRepository->getTreeLevels($page, 1); - } } if (!empty($prefix)) { $item['prefix'] = htmlspecialchars($prefix);
Classes/Tree/Repository/PageTreeRepository.php+8 −9 modified@@ -166,29 +166,28 @@ protected function applyCallbackToChildren(array &$tree, callable $callback): vo * * @param array $pageTree The page record of the top level page you want to get the page tree of * @param int $depth Number of levels to fetch - * @param array $entryPointIds entryPointIds to include + * @param ?array $entryPointIds entryPointIds to include (null in case no entry-points were provided) * @return array An array with page records and their children */ - public function getTreeLevels(array $pageTree, int $depth, array $entryPointIds = []): array + public function getTreeLevels(array $pageTree, int $depth, ?array $entryPointIds = null): array { $groupedAndSortedPagesByPid = []; - - if (count($entryPointIds) > 0) { + // the method was called without any entry-point information + if ($entryPointIds === null) { + $parentPageIds = [$pageTree['uid']]; + // the method was called with entry-point information, that is not empty + } elseif ($entryPointIds !== []) { $pageRecords = $this->getPageRecords($entryPointIds); $groupedAndSortedPagesByPid[$pageTree['uid']] = $pageRecords; $parentPageIds = $entryPointIds; - } else { - $parentPageIds = [$pageTree['uid']]; } - for ($i = 0; $i < $depth; $i++) { + // stop in case the initial or recursive query did not have any pages if (empty($parentPageIds)) { break; } $pageRecords = $this->getChildPageRecords($parentPageIds); - $groupedAndSortedPagesByPid = $this->groupAndSortPages($pageRecords, $groupedAndSortedPagesByPid); - $parentPageIds = array_column($pageRecords, 'uid'); } $this->addChildrenToPage($pageTree, $groupedAndSortedPagesByPid);
Tests/Functional/Controller/Page/Fixtures/be_users.csv+2 −0 modified@@ -3,4 +3,6 @@ ,1,0,"admin",,0,1,0 ,2,0,"test1",,1,0,0 ,3,0,"test1",,0,0,0 +,7,0,"editor",7,0,0,3 +,8,0,"editor",8,0,0,3 ,9,0,"editor",9,0,0,3
Tests/Functional/Controller/Page/Fixtures/PagesWithBEPermissions.yaml+7 −4 modified@@ -4,8 +4,6 @@ __variables: - &pageMount 7 - &pageFolder 254 - &contentText 'text' - - &idAcmeRootPage 1000 - - &idAcmeFirstPage 1100 entitySettings: '*': @@ -45,11 +43,13 @@ entities: workspace: - self: {id: 1, title: 'Workspace'} beGroup: + - self: {id: 7, title: 'editors without DB mounts', db_mountpoints: '', tables_select: 'pages,tt_content', tables_modify: 'pages,tt_content', page_types_select: '1,4,7,254', groupMods: 'web_layout,web_list'} + - self: {id: 8, title: 'editors with DB mounts', db_mountpoints: '9200', tables_select: 'pages,tt_content', tables_modify: 'pages,tt_content', page_types_select: '1,4,7,254', groupMods: 'web_layout,web_list'} - self: {id: 9, title: 'editors', db_mountpoints: '1000,2000,8110', tables_select: 'pages,tt_content', tables_modify: 'pages,tt_content', page_types_select: '1,4,7,254', groupMods: 'web_layout,web_list'} page: - - self: {id: *idAcmeRootPage, title: 'ACME Inc', type: *pageShortcut, shortcut: 'first', root: true, slug: '/'} + - self: {id: 1000, title: 'ACME Inc', type: *pageShortcut, shortcut: 'first', root: true, slug: '/'} children: - - self: {id: *idAcmeFirstPage, title: 'EN: Welcome', slug: '/welcome', subtitle: 'hello-and-welcome'} + - self: {id: 1100, title: 'EN: Welcome', slug: '/welcome', subtitle: 'hello-and-welcome'} - self: {id: 1200, title: 'EN: Features', slug: '/features'} children: - self: {id: 1210, title: 'EN: Frontend Editing', slug: '/features/frontend-editing', perms_userid: 9, perms_groupid: 1, description: 'accessible for user, but not for group'} @@ -153,3 +153,6 @@ entities: - { action: 'move', type: 'toPage', target: 1700 } - self: {id: 8120, title: 'Asia', slug: '/divisions/asia', description: 'not visible as not mounted'} - self: {id: 8130, title: 'South America', slug: '/divisions/south-america'} + - self: {id: 9100, title: 'Page 9100', type: *pageStandard, root: true, slug: '/page-9100', perms_userid: 1, perms_groupid: 1, perms_everybody: 15} + - self: {id: 9200, title: 'Page 9200', type: *pageStandard, root: true, slug: '/page-9200', perms_userid: 1, perms_groupid: 1, perms_everybody: 0} + - self: {id: 9300, title: 'Page 9300', type: *pageStandard, root: true, slug: '/page-9300', perms_userid: 1, perms_groupid: 1, perms_everybody: 15}
Tests/Functional/Controller/Page/TreeControllerTest.php+49 −2 modified@@ -65,17 +65,18 @@ protected function setUp(): void $this->withDatabaseSnapshot(function () { $this->importCSVDataSet(__DIR__ . '/Fixtures/be_users.csv'); // Admin user for importing dataset - $this->backendUser = $this->setUpBackendUser(1); + $backendUser = $this->setUpBackendUser(1); Bootstrap::initializeLanguageObject(); $scenarioFile = __DIR__ . '/Fixtures/PagesWithBEPermissions.yaml'; $factory = DataHandlerFactory::fromYamlFile($scenarioFile); - $writer = DataHandlerWriter::withBackendUser($this->backendUser); + $writer = DataHandlerWriter::withBackendUser($backendUser); $writer->invokeFactory($factory); static::failIfArrayIsNotEmpty($writer->getErrors()); }); // Regular editor, non admin $this->backendUser = $this->setUpBackendUser(9); + Bootstrap::initializeLanguageObject(); $this->context = GeneralUtility::makeInstance(Context::class); $this->subject = $this->getAccessibleMock(TreeController::class, null); } @@ -278,6 +279,19 @@ public function getAllEntryPointPageTreesWithRootPageAsMountPoint(): void ], ], ], + [ + // 9100 is shown due to `perms_everybody=15` + 'uid' => 9100, + 'title' => 'Page 9100', + '_children' => [], + ], + // 9200 is omitted due to `perms_everybody=0` + [ + // 9300 is shown due to `perms_everybody=15` + 'uid' => 9300, + 'title' => 'Page 9300', + '_children' => [], + ], ], ], [ @@ -721,6 +735,39 @@ static function (AfterPageTreeItemsPreparedEvent $event) use (&$afterPageTreeIte self::assertEquals('ACME Inc', $afterPageTreeItemsPreparedEvent->getItems()[1]['name']); } + public static function fetchDataActionConsidersPermissionsDataProvider(): \Generator + { + yield 'admin user can see all root pages' => [ + 'backendUser' => 1, + 'expectation' => ['0', '1000', '2000', '7000', '8000', '9100', '9200', '9300'], + ]; + yield 'editor with DB mounts can only see accessible pages' => [ + 'backendUser' => 9, + 'expectation' => ['0', '1000', '8110'], + ]; + yield 'editor with DB mounts cannot see inaccessible pages' => [ + 'backendUser' => 8, + 'expectation' => ['0'], + ]; + yield 'editor without DB mounts cannot see any pages' => [ + 'backendUser' => 7, + 'expectation' => ['0'], + ]; + } + + #[Test] + #[DataProvider('fetchDataActionConsidersPermissionsDataProvider')] + public function fetchDataActionConsidersPermissions(int $backendUser, array $expectation): void + { + $this->backendUser = $this->setUpBackendUser($backendUser); + $request = (new ServerRequest(new Uri('https://example.com')))->withQueryParams(['depth' => 1]); + $response = (new TreeController())->fetchDataAction($request); + $data = json_decode((string)$response->getBody(), true); + $items = array_filter($data, static fn(array $page): bool => $page['depth'] <= 1); + $items = array_map(static fn(array $page): string => $page['identifier'], $items); + self::assertSame($expectation, array_values($items)); + } + private function setWorkspace(int $workspaceId): void { $this->backendUser->workspace = $workspaceId;
a7b3c924014a[SECURITY] Show only explicitly configured page tree information
4 files changed · +69 −17
Classes/Controller/Page/TreeController.php+4 −4 modified@@ -420,11 +420,11 @@ protected function pagesToFlatArray(array $page, int $entryPoint, int $depth = 0 'allowEdit' => $this->userHasAccessToModifyPagesAndToDefaultLanguage && $backendUser->doesUserHaveAccess($page, Permission::PAGE_EDIT), ]; - if (!empty($page['_children']) || $this->pageTreeRepository->hasChildren($pageId)) { + if ($depth >= $this->levelsToFetch && $this->pageTreeRepository->hasChildren($pageId)) { + $page = $this->pageTreeRepository->getTreeLevels($page, 1); + } + if (!empty($page['_children'])) { $item['hasChildren'] = true; - if ($depth >= $this->levelsToFetch) { - $page = $this->pageTreeRepository->getTreeLevels($page, 1); - } } if (!empty($prefix)) { $item['prefix'] = htmlspecialchars($prefix);
Classes/Tree/Repository/PageTreeRepository.php+8 −9 modified@@ -168,29 +168,28 @@ protected function applyCallbackToChildren(array &$tree, callable $callback) * * @param array $pageTree The page record of the top level page you want to get the page tree of * @param int $depth Number of levels to fetch - * @param array $entryPointIds entryPointIds to include + * @param ?array $entryPointIds entryPointIds to include (null in case no entry-points were provided) * @return array An array with page records and their children */ - public function getTreeLevels(array $pageTree, int $depth, array $entryPointIds = []): array + public function getTreeLevels(array $pageTree, int $depth, ?array $entryPointIds = null): array { $groupedAndSortedPagesByPid = []; - - if (count($entryPointIds) > 0) { + // the method was called without any entry-point information + if ($entryPointIds === null) { + $parentPageIds = [$pageTree['uid']]; + // the method was called with entry-point information, that is not empty + } elseif ($entryPointIds !== []) { $pageRecords = $this->getPageRecords($entryPointIds); $groupedAndSortedPagesByPid[$pageTree['uid']] = $pageRecords; $parentPageIds = $entryPointIds; - } else { - $parentPageIds = [$pageTree['uid']]; } - for ($i = 0; $i < $depth; $i++) { + // stop in case the initial or recursive query did not have any pages if (empty($parentPageIds)) { break; } $pageRecords = $this->getChildPageRecords($parentPageIds); - $groupedAndSortedPagesByPid = $this->groupAndSortPages($pageRecords, $groupedAndSortedPagesByPid); - $parentPageIds = array_column($pageRecords, 'uid'); } $this->addChildrenToPage($pageTree, $groupedAndSortedPagesByPid);
Tests/Functional/Controller/Page/Fixtures/PagesWithBEPermissions.yaml+7 −4 modified@@ -4,8 +4,6 @@ __variables: - &pageMount 7 - &pageFolder 254 - &contentText 'text' - - &idAcmeRootPage 1000 - - &idAcmeFirstPage 1100 entitySettings: '*': @@ -52,11 +50,13 @@ entities: - self: {id: 2, title: 'Franco-Canadian', code: 'fr'} - self: {id: 3, title: 'Spanish', code: 'es'} beGroup: + - self: {id: 7, title: 'editors without DB mounts', db_mountpoints: '', tables_select: 'pages,tt_content', tables_modify: 'pages,tt_content', page_types_select: '1,4,7,254', groupMods: 'web_layout,web_list'} + - self: {id: 8, title: 'editors with DB mounts', db_mountpoints: '9200', tables_select: 'pages,tt_content', tables_modify: 'pages,tt_content', page_types_select: '1,4,7,254', groupMods: 'web_layout,web_list'} - self: {id: 9, title: 'editors', db_mountpoints: '1000,2000,8110', tables_select: 'pages,tt_content', tables_modify: 'pages,tt_content', page_types_select: '1,4,7,254', groupMods: 'web_layout,web_list'} page: - - self: {id: *idAcmeRootPage, title: 'ACME Inc', type: *pageShortcut, shortcut: 'first', root: true, slug: '/'} + - self: {id: 1000, title: 'ACME Inc', type: *pageShortcut, shortcut: 'first', root: true, slug: '/'} children: - - self: {id: *idAcmeFirstPage, title: 'EN: Welcome', slug: '/welcome', subtitle: 'hello-and-welcome'} + - self: {id: 1100, title: 'EN: Welcome', slug: '/welcome', subtitle: 'hello-and-welcome'} - self: {id: 1200, title: 'EN: Features', slug: '/features'} children: - self: {id: 1210, title: 'EN: Frontend Editing', slug: '/features/frontend-editing', perms_userid: 9, perms_groupid: 1, description: "accessible for user, but not for group"} @@ -160,3 +160,6 @@ entities: - { action: 'move', type: 'toPage', target: 1700 } - self: {id: 8120, title: 'Asia', slug: '/divisions/asia', description: 'not visible as not mounted'} - self: {id: 8130, title: 'South America', slug: '/divisions/south-america'} + - self: {id: 9100, title: 'Page 9100', type: *pageStandard, root: true, slug: '/page-9100', perms_userid: 1, perms_groupid: 1, perms_everybody: 15} + - self: {id: 9200, title: 'Page 9200', type: *pageStandard, root: true, slug: '/page-9200', perms_userid: 1, perms_groupid: 1, perms_everybody: 0} + - self: {id: 9300, title: 'Page 9300', type: *pageStandard, root: true, slug: '/page-9300', perms_userid: 1, perms_groupid: 1, perms_everybody: 15}
Tests/Functional/Controller/Page/TreeControllerTest.php+50 −0 modified@@ -23,6 +23,8 @@ use TYPO3\CMS\Core\Context\Context; use TYPO3\CMS\Core\Context\WorkspaceAspect; use TYPO3\CMS\Core\Core\Bootstrap; +use TYPO3\CMS\Core\Http\ServerRequest; +use TYPO3\CMS\Core\Http\Uri; use TYPO3\CMS\Core\Tests\Functional\SiteHandling\SiteBasedTestTrait; use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3\TestingFramework\Core\AccessibleObjectInterface; @@ -291,6 +293,19 @@ public function getAllEntryPointPageTreesWithRootPageAsMountPoint(): void ], ], ], + [ + // 9100 is shown due to `perms_everybody=15` + 'uid' => 9100, + 'title' => 'Page 9100', + '_children' => [], + ], + // 9200 is omitted due to `perms_everybody=0` + [ + // 9300 is shown due to `perms_everybody=15` + 'uid' => 9300, + 'title' => 'Page 9300', + '_children' => [], + ], ], ], [ @@ -723,6 +738,41 @@ public function getSubtreeForAccessiblePageInWorkspace(): void self::assertEquals($expected, $actual); } + public static function fetchDataActionConsidersPermissionsDataProvider(): \Generator + { + yield 'admin user can see all root pages' => [ + 'backendUser' => 1, + 'expectation' => ['0', '1000', '2000', '7000', '8000', '9100', '9200', '9300'], + ]; + yield 'editor with DB mounts can only see accessible pages' => [ + 'backendUser' => 9, + 'expectation' => ['0', '1000', '8110'], + ]; + yield 'editor with DB mounts cannot see inaccessible pages' => [ + 'backendUser' => 8, + 'expectation' => ['0'], + ]; + yield 'editor without DB mounts cannot see any pages' => [ + 'backendUser' => 7, + 'expectation' => ['0'], + ]; + } + + /** + * @test + * @dataProvider fetchDataActionConsidersPermissionsDataProvider + */ + public function fetchDataActionConsidersPermissions(int $backendUser, array $expectation): void + { + $this->backendUser = $this->setUpBackendUser($backendUser); + $request = (new ServerRequest(new Uri('https://example.com')))->withQueryParams(['depth' => 1]); + $response = (new TreeController())->fetchDataAction($request); + $data = json_decode((string)$response->getBody(), true); + $items = array_filter($data, static fn(array $page): bool => $page['depth'] <= 1); + $items = array_map(static fn(array $page): string => $page['identifier'], $items); + self::assertSame($expectation, array_values($items)); + } + /** * @param int $workspaceId */
Vulnerability mechanics
Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
7- github.com/advisories/GHSA-rf5m-h8q9-9w6qghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2024-47780ghsaADVISORY
- github.com/TYPO3-CMS/backend/commit/8b024b08a2c7071a2f2ff7c758766e4e9273f83cghsaWEB
- github.com/TYPO3-CMS/backend/commit/9ae1ef969b63292a13f80955a95713cabd45cc22ghsaWEB
- github.com/TYPO3-CMS/backend/commit/a7b3c924014ada61632cd5e3fb9825fcc86c5719ghsaWEB
- github.com/TYPO3/typo3/security/advisories/GHSA-rf5m-h8q9-9w6qghsax_refsource_CONFIRMWEB
- typo3.org/security/advisory/typo3-core-sa-2024-012ghsax_refsource_MISCWEB
News mentions
0No linked articles in our index yet.