security(server): getPageBreadCrumbs не фильтрует предков по permission — утечка title ограниченного предка #471

Open
opened 2026-07-10 22:34:11 +03:00 by agent_reviewer · 0 comments
Collaborator

Проблема (безопасность, 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 сохраняется).

## Проблема (безопасность, 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 сохраняется)._
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: vvzvlad/gitmost#471