security(server): getPageBreadCrumbs не фильтрует предков по permission — утечка title ограниченного предка #471
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Проблема (безопасность, server, пред-существующая — вскрыто при ревью #470)
getPageBreadCrumbs(apps/server/src/core/page/services/page.service.ts:1072) — рекурсивный CTE, который поднимается по цепочке предков, фильтруя ТОЛЬКО поdeletedAt(+MAX_PAGE_TREE_DEPTH), без page-level permission-фильтрации предков. Контроллер (page.controller.ts:815) валидируетvalidateCanViewтолько на ЦЕЛЕВОЙ странице, затем возвращает всю цепочку.Утечка: в гранулярной модели (страница внутри member-пространства может быть индивидуально ограничена — механизм
filterAccessiblePageIdsWithPermissionsиспользуется в sidebar/tree, значит сценарий реален) пользователь, видящий страницу P, но НЕ её предка A, получает title предка A в breadcrumbs.Область: пред-существующее — тот же эндпоинт
/pages/breadcrumbsуже зовёт веб-UI (apps/client/.../page-service.ts:106,space-tree.tsx:128) с той же авторизацией, т.е. UI breadcrumb-бар уже показывает эти title. #470 (getPageContext) зовёт ТОТ ЖЕ эндпоинт с той же CASL-областью юзера — не вводит и не усугубляет (агент не может открыть ограниченного предка, только видит title). Поэтому это НЕ баг #470, а отдельная серверная задача.Фикс: прогнать цепочку предков через
filterAccessiblePageIds*(какgetSidebarPages/getSidebarPagesTree) перед возвратом — ИЛИ задокументировать, что space-membership даёт видимость всех страниц (тогда утечки нет). Затрагивает и веб-UI, и #470-агента одинаково.Найдено ревью-ботом при ревью PR #470 (#443 ч.3). Только чтение title; escalation невозможен (permission на getPage/getNode сохраняется).