GHSA-f66c-mpj2-3r8h: Infoleak
Summary The slugTaken endpoint returns a boolean indicating whether a slug is in use across all document types and visibility levels, with no permission or type filter. Any logged-in user (including editors) can enumerate the slug space of private pages, drafts, and archived content.
Details POST /api/v1/@apostrophecms/doc/slugTaken with body { "slug": "target-slug" } runs a raw existence check against the documents collection. It does not: - Check whether the requesting user has read permission for documents of that type. - Filter by visibility (public, loginRequired, private). - Filter by viewRole. - Filter by publication status (draft vs published).
A single authenticated session can iterate the slug space to enumerate the full content inventory of the CMS, including content the user is explicitly prohibited from viewing.
PoC bash Any authenticated session works — editor role sufficient curl -b "$EDITORJAR" -X POST \ -H "Content-Type: application/json" \ -d '{"slug":"known-private-page-slug"}' \ "http://TARGET/api/v1/@apostrophecms/doc/slugTaken" → { "taken": true } — confirms private page exists
curl -b "$EDITORJAR" -X POST \ -H "Content-Type: application/json" \ -d '{"slug":"zzz-nonexistent"}' \ "http://TARGET/api/v1/@apostrophecms/doc/slugTaken" → { "taken": false }
Impact Information disclosure. An attacker with any authenticated session can enumerate all slugs in the system, mapping the full content structure of the CMS including private and admin-only content.
Affected Software
Remediation
Recommended actions to resolve this vulnerability, in priority order.
- Upgrade
Upgrade
npm/apostropheto a version that resolves this vulnerability.Fixed in 4.34.0
Event History
Frequently Asked Questions
Who can exploit this issue?
Any authenticated user can exploit it; an editor-level session is sufficient. No higher document read permission is required.
What information can an attacker obtain?
The endpoint returns whether a submitted slug exists, including for private pages, drafts, archived content, and documents the requester cannot view. Repeated requests can be used to enumerate the CMS content inventory.
How can I determine whether my instance is affected?
Using an authenticated low-privilege account, submit a POST request to /api/v1/@apostrophecms/doc/slugTaken with the slug of content that account is not permitted to read. A response of { "taken": true } confirms that the endpoint exposes the existence of that content.