Skip to content
Heist/ feed
Back to feed
MediumFix available

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

Type: advisorySources: OSV

### 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 source

Published 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"
}