{"id":"CVE-2026-112952","kind":"vulnerability","aliases":["CVE-2026-112952","GHSA-f66c-mpj2-3r8h"],"related":[],"title":"ApostropheCMS: POST /api/v1/@apostrophecms/doc/slugTaken discloses existence of private/admin-only content to any authenticated user","description":"### Summary\nThe `slugTaken` endpoint returns a boolean indicating whether a slug is in use across\nall document types and visibility levels, with no permission or type filter. Any logged-in\nuser (including editors) can enumerate the slug space of private pages, drafts, and\narchived content.\n\n### Details\n`POST /api/v1/@apostrophecms/doc/slugTaken` with body `{ \"slug\": \"target-slug\" }` runs\na raw existence check against the documents collection. It does not:\n- Check whether the requesting user has read permission for documents of that type.\n- Filter by `visibility` (public, loginRequired, private).\n- Filter by `viewRole`.\n- Filter by publication status (draft vs published).\n\nA single authenticated session can iterate the slug space to enumerate the full content\ninventory of the CMS, including content the user is explicitly prohibited from viewing.\n\n### PoC\n```bash\n# Any authenticated session works — editor role sufficient\ncurl -b \"$EDITOR_JAR\" -X POST \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"slug\":\"known-private-page-slug\"}' \\\n  \"http://TARGET/api/v1/@apostrophecms/doc/slugTaken\"\n# → { \"taken\": true }  — confirms private page exists\n\ncurl -b \"$EDITOR_JAR\" -X POST \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"slug\":\"zzz-nonexistent\"}' \\\n  \"http://TARGET/api/v1/@apostrophecms/doc/slugTaken\"\n# → { \"taken\": false }\n```\n\n### Impact\nInformation disclosure. An attacker with any authenticated session can enumerate all\nslugs in the system, mapping the full content structure of the CMS including private and\nadmin-only content.","summary":"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.","severity":"medium","cvss":4.300000190734863,"epss":0.002730000065639615,"exploited":false,"exploited_at":null,"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"],"how_to_check":"Check the installed apostrophe version with `npm ls apostrophe`; versions below 4.34.0 are affected.\nAs 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.\nIf upgrading is not possible now, restrict who holds any login on the CMS, since any authenticated user can exploit this.","status":"open","published_at":"2026-10-20T16:17:06+00:00","first_seen_at":"2026-10-23T19:11:32+00:00","triaged_at":"2026-10-23T20:03:19+00:00","updated_at":"2026-10-23T21:57:20+00:00","category":"package","confidence":0.85,"triage":{"at":"2026-10-23T20:03:19Z","model":"claude-haiku-5-5","output":{"fix":"Upgrade apostrophe to 4.34.0 or later.\nIf upgrading is not possible now, restrict who holds any login on the CMS, since any authenticated user can exploit this.","summary":"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.","category":"package","who_cares":["apostrophecms","npm","self-hosted","node.js"],"confidence":0.85,"analyst_note":"","how_to_check":"Check the installed apostrophe version with `npm ls apostrophe`; versions below 4.34.0 are affected.\nAs 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.","needs_analyst":false,"not_affected_if":["managed-hosting","version-4.34.0-or-later","no-authenticated-users"],"severity_if_unknown":"medium"},"cost_usd":0.000448,"input_tokens":2587,"output_tokens":379,"severity_before":"medium"},"analyst_status":null,"analyst_note":null,"search":"'-112952':16A,24A '-200':28B '-2026':15A,23A '/api/v1':3A,118C '3r8h':21A 'across':45B,84C 'admin':277C 'admin-on':276C 'api':38B 'apostrophe':25A 'apostrophecm':1A,26B,35B 'apostrophecms/doc/slugtaken':4A,119C,222C,246C 'application/json':213C,239C 'archiv':67B,114C 'attack':253C 'authent':12A,169C,197C,256C 'b':204C,230C 'bash':195C 'bodi':121C 'boolean':76C 'check':54B,130C,138C 'cms':184C,272C 'collect':134C 'confirm':225C 'content':9A,115C,180C,186C,211C,237C,268C,279C 'content-typ':210C,236C 'curl':203C,229C 'cve':14A,22A 'cwe':27B 'd':214C,240C 'detail':116C 'disclos':5A 'disclosur':251C 'document':47B,86C,133C,147C 'draft':65B,112C,164C 'editor':61B,103C,200C,205C,231C 'endpoint':39B,73C 'enumer':63B,105C,177C,259C 'exist':6A,44B,129C,228C 'explicit':190C 'f66c':19A 'fals':248C 'filter':96C,151C,157C,160C 'full':179C,267C 'ghsa':18A 'ghsa-f66c-mpj2-3r8h':17A 'h':209C,235C 'host':34B 'impact':249C 'includ':60B,102C,185C,273C 'indic':77C 'inform':250C 'inventori':181C 'iter':172C 'jar':206C,232C 'known':217C 'known-private-page-slug':216C 'level':51B,90C 'log':57B,99C 'logged-in':56B,98C 'loginrequir':155C 'map':265C 'mpj2':20A 'node.js':29B 'nonexist':244C 'npm':30B 'packag':31B 'page':68B,111C,219C,227C 'permiss':53B,93C,145C 'poc':194C 'post':2A,117C,208C,234C 'privat':64B,110C,156C,218C,226C,274C 'private/admin-only':8A 'prohibit':191C 'public':154C,162C 'publish':166C 'raw':128C 'read':144C 'request':141C 'return':40B,74C 'role':201C 'run':126C 'self':33B 'self-host':32B 'session':170C,198C,257C 'singl':168C 'slug':43B,69B,80C,107C,122C,125C,174C,215C,220C,241C,261C 'slugtaken':37B,72C 'space':108C,175C 'status':163C 'structur':269C 'suffici':202C 'summari':70C 'system':264C 'taken':223C,247C 'target':124C 'target-slug':123C 'target/api/v1':221C,245C 'true':224C 'type':48B,87C,95C,150C,212C,238C 'use':83C 'user':13A,59B,101C,142C,188C 'view':193C 'viewrol':159C 'visibl':50B,89C,153C 'vs':165C 'whether':41B,78C,139C 'without':52B 'work':199C 'x':207C,233C 'zzz':243C 'zzz-nonexist':242C","nicknames":[],"mitigations":[],"who_affected":null,"tier":"C","verified":{},"analyst_revisit_at":null,"first_public_at":"2026-10-20T16:17:06+00:00","kev":null,"cvss_vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:N/A:N","cvss_by":"GitHub","poc_count":0,"sources":[{"source":"ghsa","external_id":"GHSA-f66c-mpj2-3r8h","url":"https://github.com/advisories/GHSA-f66c-mpj2-3r8h","severity":"medium","published_at":"2026-10-21T17:17:00+00:00","modified_at":"2026-10-21T17:17:02+00:00","first_seen_at":"2026-10-23T19:11:32+00:00","fetched_at":"2026-10-23T19:11:32+00:00","note":null},{"source":"nvd","external_id":"CVE-2026-112952","url":"https://nvd.nist.gov/vuln/detail/CVE-2026-112952","severity":null,"published_at":"2026-10-20T16:17:06+00:00","modified_at":"2026-10-21T16:17:15+00:00","first_seen_at":"2026-10-23T21:57:20+00:00","fetched_at":"2026-10-23T22:28:58+00:00","note":null}],"documents":[],"notice":"Free to read at a human pace. An API with keys and an MCP server are in preparation; early access: hello@infrastack.ai"}