Advisories for Npm/Apostrophe package

2026

ApostropheCMS: Unauthenticated resource exhaustion via pre-auth aposBigUpload processing in @apostrophecms/import-export

A potential unauthenticated denial-of-service vulnerability exists in the chunked import upload path used by @apostrophecms/import-export. The affected import API routes invoke: self.apos.http.bigUploadMiddleware() before the actual import handler performs its authentication check. Because no authorize callback is supplied to bigUploadMiddleware(), unauthenticated requests can reach the aposBigUpload start, chunk, and end operations before the later req.user check rejects the import. As a result, an unauthenticated remote attacker can create server-side upload state, …

ApostropheCMS: Unauthenticated publicApiProjection bypass via search and sort query builders in the public REST API

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> …

ApostropheCMS: Stored XSS via Unescaped Widget Style Attribute Serialization

ApostropheCMS widget styles support project-defined schema fields. If a project defines a free-form string style field with a CSS property and no selector, an editor-controlled value is rendered as an inline CSS declaration. The resulting declaration is then inserted into the widget wrapper's style="…" attribute without HTML-attribute escaping. An attacker who can edit the widget can include a double quote in the style value, terminate the style attribute, and inject …

ApostropheCMS: Import API bypasses publication roles for protected content

Review scope and evidence limits Package: @apostrophecms/import-export, version 3.7.0. Release tag reviewed: @apostrophecms/import-export@3.7.0, commit 9500b8b0e1d630d5e07fbb5fd2c25ed0943845ce (September 30, 2026). Current main reviewed: 5ce3732be0ebdebb4db0f8ef160c81e8671157e6. The importer, gzip extractor, import permission gate, and core permission implementation have no differences between that release tag and the reviewed main. This follow-up is a static remediation review. We have not repeated the original end-to-end reproductions against the latest release or tested a deployed site. Current exploitability …

ApostropheCMS: The general public can access @apostrophecms/user objects via a relationship (viewRole is not applied to the general public)

Even though it is correctly applied to guests, contributors, etc., viewRole is not applied to the general public. This means that a secured content type like @apostrophecms/user can in theory be accessed. This is greatly mitigated by the fact that there is no automatic public REST API for any content type. One has to expressly allow it. However, it does mean that developers can make the mistake of setting up …

ApostropheCMS: Same-parent page reorder in @apostrophecms/page move() re-ranks a restricted parent's children without create authorization

ApostropheCMS enforces per-type authorization on pages: a page type may declare editRole / publishRole (the core @apostrophecms/archive-page does), so a project can define restricted subtrees that only higher-privileged roles may create or edit within. The move() operation in the @apostrophecms/page module is supposed to enforce that a page may only be moved into a parent the actor has create rights over, the same boundary the page-insert route enforces. The destination-parent …

ApostropheCMS: Page REST API authorization bypass

Summary The core page REST API reads full page documents with .permission(false) in getOne and in the all=1 tree dump, bypassing page-type viewRole, visibility: private, and visibility: loginRequired checks. Unauthenticated visitors and low-privilege contributors can read admin-only and non-public page content by _id/slug. Details packages/apostrophe/modules/@apostrophecms/page/index.js: getOne (L456–465): await self.publicApiCheckAsync(req); … self.getRestQuery(req).permission(false).and(criteria) — no viewRole threshold, no visibility filtering. all handler (L318–395): getRestQuery(req).permission(false) (L384–385) walks every page type and returns the …

ApostropheCMS: No Attempt Throttling on Authenticated Password-Change Endpoint Enables Unlimited Current-Password Guessing

Summary The self-service password-change endpoint verifies the current password but does not use the login module’s failed-attempt counter, throttle, delay, or lockout mechanism. An attacker with a valid authenticated session can submit repeated guesses for the current password without receiving a lockout or throttle response. Exact source path packages/apostrophe/modules/@apostrophecms/settings/index.js:621-664 The password-change route is: PATCH /api/v1/@apostrophecms/settings/password The route calls the password verifier directly through: await self.handleProtectedSubform(req, subform, req.body ); The password …

ApostropheCMS: Cross-domain session token in URL confers session takeover

Summary Apostrophe's cross-domain session migration emits a single-use session token as a URL query parameter and stores it in the Mongo cache. Any client that presents the token silently adopts the target user's full session (including passport.user) with no credentials. Details packages/apostrophe/modules/@apostrophecms/i18n/index.js: Token generation on cross-host/locale flows (L479–487) and passport-bridge cross-locale redirect (passport-bridge/index.js L557–593) places the token in the URL. acceptCrossDomainSessionToken (L258–298) reads ?aposCrossDomainSessionToken, Object.assigns the stored session (cookie + …

ApostropheCMS: Client-side Prototype Pollution in apos.http.parseQuery leading to DOM-based XSS against authenticated users

The client-side helper apos.http.parseQuery() in ApostropheCMS builds a nested object from a URL query string without sanitizing the keys proto, constructor, or prototype. Parsing an attacker-controlled query string therefore writes arbitrary properties onto Object.prototype (client-side prototype pollution). ApostropheCMS's admin UI calls this parser with window.location.search on every authenticated page load (the admin context bar's data() initializer). Because the admin UI is built on Vue, a polluted, enumerable property on Object.prototype …

ApostropheCMS: Authenticated projection-exclusion prototype-member deletion in @apostrophecms/db-connect causes process-wide denial of service

@apostrophecms/db-connect ships an in-memory MongoDB emulation used by its SQLite, PostgreSQL, and multipostgres adapters. Its unsetNestedField helper walks an attacker-controlled dotted field path with no segment validation and calls delete on the resolved property. A low-privileged authenticated user (any role holding view-draft, which every editor and contributor has; reduced to any logged-in user when guestApiAccess is enabled) can send a piece-type REST GET request whose project query builder contains an …

ApostropheCMS: Authenticated Object-Level Authorization Bypass in exist-in-locale Reveals Restricted Localized Draft Identifiers

Summary ApostropheCMS’s exist-in-locale API allows an authenticated low-privilege user to determine whether restricted documents exist in a specified locale and mode, including draft mode. The endpoint returns localized document identifiers without applying document-level view or edit authorization criteria. The issue was reproduced against a real local ApostropheCMS application running the reviewed source code. The test used a synthetic guest account and synthetic restricted draft records. Details Exact vulnerable source path: …

ApostropheCMS: :_id/locales REST route omits the public-API authorization gate its sibling read routes enforce (unauthenticated doc-existence + locale metadata disclosure)

The REST route GET /api/v1/<piece-type>/:_id/locales (and the equivalent page route) does not apply the publicApiCheckAsync gate that ApostropheCMS's other read routes enforce. An unauthenticated caller can therefore query this route for any document _id and learn whether the document exists and which locales it is published in — a document-existence and locale-metadata disclosure that the read-API is otherwise supposed to gate.

ApostropheCMS: Missing destination-parent authorization in page `move()` allows a low-privileged editor to move and re-rank pages inside a restricted subtree

ApostropheCMS enforces per-type authorization on pages: a page type may declare editRole / publishRole (and the core @apostrophecms/archive-page does), so a project can have page-type subtrees that only higher-privileged roles are allowed to create or edit within. The move() operation is supposed to enforce that a page may only be moved into a parent the actor has create rights over — this is the same boundary the page-insert route enforces …

Apostrophe has Server-Side Prototype Pollution in apos.util.set via patch operators that leads to process-wide authorization bypass

apos.util.set() traverses dot-notation paths without sanitizing proto, allowing an authenticated editor to write arbitrary values to Object.prototype via the $pullAll patch operator. A confirmed gadget in publicApiCheck() causes this to bypass authorization on all piece-type REST API endpoints for every subsequent unauthenticated request, for the lifetime of the Node.js process.

@apostrophecms/file pretty-URL Vulnerable to Unauthenticated SSRF via Host header

When prettyUrls: true is enabled on @apostrophecms/file (a documented SEO feature for serving uploaded files at clean URLs), the public pretty-URL handler builds the upstream URL using the raw Host HTTP request header: proxyUrl = ${req.protocol}://${req.get(&#39;host&#39;)}${uglyUrl} That URL is then fetch'ed and the response body + headers are streamed straight back to the requester. Because Host is fully attacker-controlled, an unauthenticated remote attacker can pivot the apostrophe process to issue …

Apostrophe has stored XSS via javascript: URL in Image Widget Link

A stored cross-site scripting vulnerability was identified in the image widget functionality. A user with the Editor role can configure an image widget link to use a javascript: URL payload. Because editors have permission to publish pages, the malicious widget can be published to the live site. When another user, including an administrator or public visitor, clicks the affected image/link, arbitrary JavaScript executes in the victim’s browser.

Apostrophe has authenticated SSRF in rich-text widget import via @apostrophecms/area/validate-widget

ApostropheCMS contains an authenticated server-side request forgery (SSRF) in the rich-text widget import flow. An authenticated user who can submit/edit rich-text widget content can cause the server to fetch attacker-controlled URLs during widget validation. For image-compatible responses, the fetched content can be persisted and re-hosted by Apostrophe, allowing response exfiltration.

Apostrophe has a Weak Password Recovery Mechanism for Forgotten Password and Improper Input Validation

ApostropheCMS's password reset flow constructs the reset URL using req.hostname, which is derived directly from the attacker-controlled HTTP Host header when apos.baseUrl is not explicitly configured. An unauthenticated attacker who knows a victim's email address can send a crafted reset request that causes the application to email the victim a reset link pointing to the attacker's domain. When the victim clicks the link, the valid reset token is delivered to …

Stored XSS in SEO Fields Leads to Authenticated API Data Exposure in ApostropheCMS

A stored cross-site scripting (XSS) vulnerability exists in SEO-related fields (SEO Title and Meta Description) in ApostropheCMS. Improper neutralization of user-controlled input in SEO-related fields allows injection of arbitrary JavaScript into HTML contexts, resulting in stored cross-site scripting (XSS). This can be leveraged to perform authenticated API requests and exfiltrate sensitive data, resulting in a compromise of application confidentiality.

ApostropheCMS: User Enumeration via Timing Side Channel in Password Reset Endpoint

The password reset endpoint (/api/v1/@apostrophecms/login/reset-request) exhibits a measurable timing side channel that allows unauthenticated attackers to enumerate valid usernames and email addresses. When a user is not found, the handler returns after a fixed 2-second artificial delay, but when a valid user is found, it performs database writes and SMTP operations with no equivalent delay normalization, producing a distinguishable timing profile.

ApostropheCMS: Stored XSS via CSS Custom Property Injection in @apostrophecms/color-field Escaping Style Tag Context

The @apostrophecms/color-field module bypasses color validation for values prefixed with – (intended for CSS custom properties), but performs no HTML sanitization on these values. When styles containing attacker-controlled color values are rendered into <style> tags — both in the global stylesheet (editors only) and in per-widget style elements (all visitors) — the lack of escaping allows an editor to inject </style> followed by arbitrary HTML/JavaScript, achieving stored XSS against all …

ApostropheCMS: publicApiProjection Bypass via project Query Builder in Piece-Type REST API

The getRestQuery method in the @apostrophecms/piece-type module checks whether a MongoDB projection has already been set before applying the admin-configured publicApiProjection. An unauthenticated attacker can supply a project query parameter in the REST API request to pre-populate the projection state, causing the security-enforced publicApiProjection to be skipped entirely. This allows disclosure of fields that the site administrator explicitly restricted from public access.

ApostropheCMS: Information Disclosure via choices/counts Query Parameters Bypassing publicApiProjection Field Restrictions

The choices and counts query parameters in the Apostrophe CMS REST API allow unauthenticated users to extract distinct field values for any schema field that has a registered query builder, completely bypassing publicApiProjection restrictions that are intended to limit which fields are exposed publicly. Fields protected by viewPermission are similarly exposed.

ApostropheCMS MFA/TOTP Bypass via Incorrect MongoDB Query in Bearer Token Middleware

The bearer token authentication middleware in @apostrophecms/express/index.js (lines 386-389) contains an incorrect MongoDB query that allows incomplete login tokens — where the password was verified but TOTP/MFA requirements were NOT — to be used as fully authenticated bearer tokens. This completely bypasses multi-factor authentication for any ApostropheCMS deployment using @apostrophecms/login-totp or any custom afterPasswordVerified login requirement.

2022
2021
2020

Open Redirect in apostrophe

Versions of apostrophe prior to 2.92.0 are vulnerable to Open Redirect. The package redirected requests to third-party websites if escaped URLs followed by a trailing / were appended at the end. Recommendation Update to version 2.92.0 or later.

Denial of Service in apostrophe

Versions of apostrophe prior to 2.97.1 are vulnerable to Denial of Service. The apostrophe-jobs module sets a callback for incoming jobs and doesn't clear it regardless of its status. This causes the server to accumulate callbacks, allowing an attacker to start a large number of jobs and exhaust system memory. Recommendation Upgrade to version 2.97.1 or later.