CVE-2026-112954
ApostropheCMS: Unauthenticated publicApiProjection bypass via search and sort query builders in the public REST API
ApostropheCMS is an open-source Node.js content management system. Prior to 4.34.0, a public REST API type that uses publicApiProjection can expose excluded searchable or sortable field information through search membership and sort ordering oracles. Returned fields remain filtered by the projection, so the issue permits targeted inference about hidden values rather than an unrestricted field dump. This issue is fixed in version 4.34.0.
Attack profile
How easy it is to exploit
- Attack vector
- Network
- Complexity
- Low
- Privileges needed
- None
- User interaction
- None
What an attacker can affect
- Confidentiality
- Low
- Integrity
- None
- Availability
- None
Exploit likelihood
What each source reports
CVE List Score used
5.3
ApostropheCMS: Unauthenticated publicApiProjection bypass via search and sort query builders in the public REST API
ApostropheCMS is an open-source Node.js content management system. Prior to 4.34.0, a public REST API type that uses publicApiProjection can expose excluded searchable or sortable field information through search membership and sort ordering oracles. Returned fields remain filtered by the projection, so the issue permits targeted inference about hidden values rather than an unrestricted field dump. This issue is fixed in version 4.34.0.
- Record
CVE-2026-112954- CVSS 3.1
- 5.3 Medium
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N - Weakness
- CWE-200
- Updated
- 2026-11-02
EUVDEUVD-2026-105104
5.3
ApostropheCMS is an open-source Node.js content management system. Prior to 4.34.0, a public REST API type that uses publicApiProjection can expose excluded searchable or sortable field information through search membership and sort ordering oracles. Returned fields remain filtered by the projection, so the issue permits targeted inference about hidden values rather than an unrestricted field dump. This issue is fixed in version 4.34.0.
- Record
EUVD-2026-105104- CVSS 3.1
- 5.3 Medium
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N - Updated
- 2026-11-02
GitHub AdvisoriesGHSA-rqgm-cfrp-4hx8
5.3
ApostropheCMS: Unauthenticated publicApiProjection bypass via search and sort query builders in the public REST API
Summary
ApostropheCMS lets a module expose an anonymous read API by setting publicApiProjection, which whitelists the document fields unauthenticated callers may see. The public REST GET handler applies every launderable query builder from the query string before forcing that projection. Two of those builders, search and sort, operate on the underlying documents rather than the projected output, and neither is checked against the projection. An unauthenticated attacker can therefore:
- use
?search=<word>as a value oracle to confirm and locate documents by the content of a field the projection was meant to hide, and - use
?sort[hiddenField]=1as an ordering oracle to rank documents by a hidden field's value.
This is the same confidentiality-bypass class already patched for the choices/counts builders (gated by choicesFieldAllowedByProjection). That gate was never applied to search or sort.
Details
For an anonymous caller, getRestQuery (piece types: modules/@apostrophecms/piece-type/index.js:1142; pages: modules/@apostrophecms/page/index.js) does:
const query = self.find(req).attachments(true);
query.applyBuildersSafely(req.query); // (1) apply ALL launderable builders from the query string
...
query.project({ ...self.options.publicApiProjection, cacheInvalidatedAt: 1 }); // (2) force read projection
query.set('publicApiProjection', self.options.publicApiProjection); // (3) used only by the choices/counts gate
applyBuildersSafely (modules/@apostrophecms/doc-type/index.js:3327) runs any builder that has a launder method. search and sort both have one (doc-type/index.js:2288 and 2127), so ?search= and ?sort[...]= are honored for anonymous requests.
Search vector. The search builder adds { $text: { $search: <user input> } } (doc-type/index.js:2282). The $text index covers highSearchText / lowSearchText / title / searchBoost (modules/@apostrophecms/doc/index.js:612). highSearchText / lowSearchText are built at save time by @apostrophecms/search:getSearchTexts → apos.schema.indexFields(schema, doc, texts) (modules/@apostrophecms/search/index.js:377), which indexes every schema field where searchable !== false (the default for string/text fields). publicApiProjection does not imply searchable: false. Step (2) narrows the output but not the $text filter, so the response returns the projected {title, slug, _id} of exactly the documents whose hidden field contains the searched word.
Sort vector. finalizeSort (doc-type/index.js:3526) forwards raw user sort keys to MongoDB (it only rewrites fields flagged sortify: true). ?sort[hiddenField]=1 orders results by a projection-excluded field. Combined with perPage/page, an attacker can page through the fully ordered set.
Neither builder consults publicApiProjection, whereas choices/counts are gated at doc-type/index.js:2807 via choicesFieldAllowedByProjection.
PoC
Preconditions: the piece/page type sets publicApiProjection, and its schema has at least one searchable (default) or sortable field not included in the projection.
Test type: article exposes only title, slug, _id, but has hidden secret (integer) and secretNote (searchable string):
export default {
extend: '@apostrophecms/piece-type',
options: { publicApiProjection: { title: 1, slug: 1, _id: 1 } },
fields: { add: {
secret: { type: 'integer' },
secretNote: { type: 'string' } // searchable by default; NOT in the projection
} }
};
Five articles were inserted and published via the authenticated REST API:
A.secret=50, secretNote="zzsecretalpha"; B=10/zzsecretbravo; C=90/zzsecretcharlie; D=30/zzsecretdelta; E=70/zzsecretecho.
The insert responses confirm the hidden secretNote value is written into the search index, e.g. for A: "highSearchText": "... zzsecretalpha", `"lowSearchText": "...…
- Record
GHSA-rqgm-cfrp-4hx8- CVSS 3.1
- 5.3 Medium
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N - Weakness
- CWE-200
- Updated
- 2026-11-02
NVD
5.3
ApostropheCMS is an open-source Node.js content management system. Prior to 4.34.0, a public REST API type that uses publicApiProjection can expose excluded searchable or sortable field information through search membership and sort ordering oracles. Returned fields remain filtered by the projection, so the issue permits targeted inference about hidden values rather than an unrestricted field dump. This issue is fixed in version 4.34.0.
- Record
CVE-2026-112954- CVSS 3.1
- 5.3 Medium
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N - Weakness
- CWE-200
- Updated
- 2026-11-02
OSV
5.3
ApostropheCMS: Unauthenticated publicApiProjection bypass via search and sort query builders in the public REST API
ApostropheCMS is an open-source Node.js content management system. Prior to 4.34.0, a public REST API type that uses publicApiProjection can expose excluded searchable or sortable field information through search membership and sort ordering oracles. Returned fields remain filtered by the projection, so the issue permits targeted inference about hidden values rather than an unrestricted field dump. This issue is fixed in version 4.34.0.
- Record
CVE-2026-112954- CVSS 3.1
- 5.3 Medium
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N - Weakness
- CWE-200
- Updated
- 2026-11-02
OSVGHSA-rqgm-cfrp-4hx8
5.3
ApostropheCMS: Unauthenticated publicApiProjection bypass via search and sort query builders in the public REST API
Summary
ApostropheCMS lets a module expose an anonymous read API by setting publicApiProjection, which whitelists the document fields unauthenticated callers may see. The public REST GET handler applies every launderable query builder from the query string before forcing that projection. Two of those builders, search and sort, operate on the underlying documents rather than the projected output, and neither is checked against the projection. An unauthenticated attacker can therefore:
- use
?search=<word>as a value oracle to confirm and locate documents by the content of a field the projection was meant to hide, and - use
?sort[hiddenField]=1as an ordering oracle to rank documents by a hidden field's value.
This is the same confidentiality-bypass class already patched for the choices/counts builders (gated by choicesFieldAllowedByProjection). That gate was never applied to search or sort.
Details
For an anonymous caller, getRestQuery (piece types: modules/@apostrophecms/piece-type/index.js:1142; pages: modules/@apostrophecms/page/index.js) does:
const query = self.find(req).attachments(true);
query.applyBuildersSafely(req.query); // (1) apply ALL launderable builders from the query string
...
query.project({ ...self.options.publicApiProjection, cacheInvalidatedAt: 1 }); // (2) force read projection
query.set('publicApiProjection', self.options.publicApiProjection); // (3) used only by the choices/counts gate
applyBuildersSafely (modules/@apostrophecms/doc-type/index.js:3327) runs any builder that has a launder method. search and sort both have one (doc-type/index.js:2288 and 2127), so ?search= and ?sort[...]= are honored for anonymous requests.
Search vector. The search builder adds { $text: { $search: <user input> } } (doc-type/index.js:2282). The $text index covers highSearchText / lowSearchText / title / searchBoost (modules/@apostrophecms/doc/index.js:612). highSearchText / lowSearchText are built at save time by @apostrophecms/search:getSearchTexts → apos.schema.indexFields(schema, doc, texts) (modules/@apostrophecms/search/index.js:377), which indexes every schema field where searchable !== false (the default for string/text fields). publicApiProjection does not imply searchable: false. Step (2) narrows the output but not the $text filter, so the response returns the projected {title, slug, _id} of exactly the documents whose hidden field contains the searched word.
Sort vector. finalizeSort (doc-type/index.js:3526) forwards raw user sort keys to MongoDB (it only rewrites fields flagged sortify: true). ?sort[hiddenField]=1 orders results by a projection-excluded field. Combined with perPage/page, an attacker can page through the fully ordered set.
Neither builder consults publicApiProjection, whereas choices/counts are gated at doc-type/index.js:2807 via choicesFieldAllowedByProjection.
PoC
Preconditions: the piece/page type sets publicApiProjection, and its schema has at least one searchable (default) or sortable field not included in the projection.
Test type: article exposes only title, slug, _id, but has hidden secret (integer) and secretNote (searchable string):
export default {
extend: '@apostrophecms/piece-type',
options: { publicApiProjection: { title: 1, slug: 1, _id: 1 } },
fields: { add: {
secret: { type: 'integer' },
secretNote: { type: 'string' } // searchable by default; NOT in the projection
} }
};
Five articles were inserted and published via the authenticated REST API:
A.secret=50, secretNote="zzsecretalpha"; B=10/zzsecretbravo; C=90/zzsecretcharlie; D=30/zzsecretdelta; E=70/zzsecretecho.
The insert responses confirm the hidden secretNote value is written into the search index, e.g. for A: "highSearchText": "... zzsecretalpha", `"lowSearchText": "...…
- Record
GHSA-rqgm-cfrp-4hx8- CVSS 3.1
- 5.3 Medium
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N - Weakness
- CWE-200
- Updated
- 2026-11-02
Affected products
Affected packages
| Package | Affected | Fixed |
|---|---|---|
apostrophenpm |
before 4.34.0 | 4.34.0 |
History
| Recorded | Changed | Sources |
|---|---|---|
| first indexed | CVE List, EUVD, GitHub Advisories, NVD, OSV |
Dates are when Every CVE recorded each change. Upstream dates may differ.
In short
CVE-2026-112954 is a medium vulnerability (CVSS 5.3) in Apostrophecms Apostrophe, published October 20, 2026. No source lists public exploit code. EPSS puts the chance of exploitation in the next 30 days at 0.44%, the 38th percentile. Fixed in apostrophe 4.34.0.
References
github.com/apostrophecms/apostrophe/security/advisories/GHSA-rqgm-cfrp-4hx8
github.com/apostrophecms/apostrophe/commit/06e4b377f3c360f9e89c32dccd7e505ad5ddf498
github.com/apostrophecms/apostrophe/commit/40b989b1a9dd23d30029b8ce7668634265cfe5ea
github.com/apostrophecms/apostrophe/releases/tag/[email protected]
nvd.nist.gov/vuln/detail/CVE-2026-112954
github.com/advisories/GHSA-rqgm-cfrp-4hx8
github.com/CVEProject/cvelistV5/tree/main/cves/2026/112xxx/CVE-2026-112954.json
github.com/apostrophecms/apostrophe
Left out here: the rest of 2 source descriptions. The JSON record has them.