Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
5 changes: 5 additions & 0 deletions .changeset/apos-literal-content.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"apostrophe": minor
---

Added support for modules to declare *literal content* routes - URLs that serve non-page files such as `robots.txt`, `sitemap.xml`, or `llms.txt` rather than rendered pages. External front-end integrations (such as the Astro integration) can now read these routes and serve such files correctly instead of attempting to render them as pages. Custom modules can contribute their own routes by handling the new `@apostrophecms/url:getLiteralContentRoutes` event.
5 changes: 5 additions & 0 deletions .changeset/array-draggable-false-non-inline.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"apostrophe": minor
---

Added support for `draggable: false` on non-inline `array` schema fields. Previously this option was only respected when `inline: true`. When set on a standard (modal-based) array field, drag-and-drop reordering and keyboard reordering are now disabled in the array editor's slat list.
5 changes: 5 additions & 0 deletions .changeset/asset-lock-check-once-per-process.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"apostrophe": patch
---

The lock file dependency check that forces a full rebuild now runs once per process. Watcher-triggered rebuilds stay scoped to the detected changes instead of rebuilding everything on every file change when the lock file changed or is absent. This bug was in effect only for projects missing a lock file in their `npmRoot` (e.g. npm monorepos).
8 changes: 8 additions & 0 deletions .changeset/asset-lock-file-cache-invalidation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
"apostrophe": patch
"@apostrophecms/vite": patch
---

Fixed the admin UI sometimes serving a stale build after dependencies changed (for example after `npm install` or `npm update`). Apostrophe now detects dependency changes from the content of the lock file rather than its modified time, which could be misleading after a fresh checkout or a restored CI/Docker build cache.

For external build module authors: lock file change detection now happens in the core and is passed to the build module via the `lockChanged` build option. The `apos.asset.getSystemLastChangeMs()` helper is deprecated and the build manifest no longer includes a `ts` timestamp.
5 changes: 5 additions & 0 deletions .changeset/astro-literal-content.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@apostrophecms/apostrophe-astro": minor
---

In SSR mode, the integration now automatically serves *literal content* files declared by Apostrophe modules - such as `robots.txt`, `sitemap.xml`, and `llms.txt` - by proxying them directly to Apostrophe instead of rendering them as pages. These files no longer need to be listed individually in the `proxyRoutes` option, which continues to work as before for any additional routes you wish to proxy.
5 changes: 5 additions & 0 deletions .changeset/astro-trailing-slash-query.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@apostrophecms/apostrophe-astro": patch
---

Query string parameters are no longer lost when a URL with a trailing slash is normalized, so `/articles/?page=2` now renders the same content as `/articles?page=2`. Previously such URLs were redirected to the page URL alone (e.g. `/articles`), losing the query string and showing the first page. Redirects to a different origin are now always passed through to the browser.
5 changes: 5 additions & 0 deletions .changeset/boozy-manual-severaltoms.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"sanitize-html": patch
---

Security: added a number of new attributes to be protected against unsafe URLs, e.g. `javascript:` and similar. None of these are used in the default configuration of `sanitize-html` or `apostrophe` or likely to be used there, and some attributes, like an `action` for a `form`, are inherently unsafe to allow if XSS protection is your goal. Nevertheless it makes sense to block certain URL types where they are not appropriate. Some attributes are not supported at all by modern browsers but are included for completeness. Thanks to [crattack](https://github.com/crattack) for reporting the vulnerability.
5 changes: 5 additions & 0 deletions .changeset/choices-relationship-alias-projection-guard.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"apostrophe": patch
---

Security fix: completed the fix for CVE-2026-39857 (GHSA-xmpp-f9v3-r7qh). The `.choices()` / `.counts()` query builders (`?choices=` / `?counts=` on the public REST API) guarded against leaking distinct values of fields excluded from `publicApiProjection` by resolving the schema field with an exact-name match. A relationship field registers extra query builders whose names differ from the field name — the "slug" alias builders that drop the leading underscore (`author` / `authorAnd` for a field named `_author`) and the `_authorAnd` operation builder — so those aliases were not gated and could still be used by an unauthenticated caller to extract the relationship's distinct choices (the referenced, publicly visible related documents by title/slug, plus per-value counts via `?counts=`) for a relationship an operator intentionally excluded from `publicApiProjection`. Relationship alias builders are now resolved back to their underlying schema field (matching the field name or its `idsStorage`) before the `publicApiProjection` and `viewPermission` checks are applied, so the alias names are gated exactly like the field itself. Thanks to Ta Duc Thien ([thientd](https://github.com/thientd)) for reporting this issue.
5 changes: 5 additions & 0 deletions .changeset/common-beans-lie.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@apostrophecms/seo": minor
---

Removes unimplemented hreflang output; use @apostrophecms/sitemap for hreflang support
5 changes: 5 additions & 0 deletions .changeset/curvy-bobcats-peel.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"apostrophe": minor
---

Introduced support for postgres://, sqlite://, and multipostgres:// database URIs in addition to mongodb://. The new db-connect API supports all of the database operations currently used in our own core, pro and multisite modules. For more information see the documentation.
5 changes: 5 additions & 0 deletions .changeset/emulate-idempotent-patch.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@apostrophecms/emulate-mongo-3-driver": patch
---

Patching the `mongodb-legacy` classes is now idempotent. When two copies of this package are loaded against the same `mongodb-legacy` instance (for example, a version or source skew between a direct and a transitive dependency), the second copy no longer throws `TypeError: Cannot redefine property: Symbol(@@mdb.callbacks.toEmulate)`. The emulation method is defined only once and is now `configurable`, so any mix of patched and unpatched copies can load in either order without error.
5 changes: 5 additions & 0 deletions .changeset/env-secrets-support.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"apostrophe": minor
---

The session secret and the uploadfs `disabledFileKey` can now be supplied via the `APOS_SESSION_SECRET` and `APOS_UPLOADFS_DISABLED_FILE_KEY` environment variables. As with other Apostrophe environment variables, these take precedence over the corresponding `app.js` configuration.
5 changes: 5 additions & 0 deletions .changeset/file-pretty-url-host-header-ssrf.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"apostrophe": patch
---

Security: when `@apostrophecms/file` pretty URLs are enabled (`prettyUrls: true`), the upstream request used to serve the file is no longer built from the incoming `Host` header. The self-request is now resolved against the site's configured `baseUrl` (via `req.baseUrl`), falling back to the request host only when no `baseUrl` is configured. This closes a server-side request forgery (SSRF) vector in which the `Host` header could steer the proxied fetch at another host. The real-world risk was low: the path is constrained to an existing attachment's `/uploads/attachments/<cuid>-<slug>.<ext>`, and cuids are unique and immutable, so any reachable content was already public via the front door. Thanks to [EchoSkorJjj](https://github.com/EchoSkorJjj) for reporting the issue.
7 changes: 7 additions & 0 deletions .changeset/fresh-breads-burn.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"sanitize-html": patch
---

Allow transformTags to emit text when textFilter is set, even if the tag
is initially empty. This is consistent with the documentation. Thanks to
[spokodev](https://github.com/spokodev) for the fix.
5 changes: 5 additions & 0 deletions .changeset/grumpy-beers-beam.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@apostrophecms/import-export-xlsx": minor
---

Replaced the off-registry SheetJS `xlsx` tarball dependency with the registry-published `@e965/xlsx` mirror, which exposes the same SheetJS Community Edition API. The module now installs entirely from the npm registry, avoiding install failures on networks restricted to the registry and the npm v12 restriction on non-registry (remote tarball) sources.
5 changes: 5 additions & 0 deletions .changeset/import-export-archive-nosql-operator.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@apostrophecms/import-export": patch
---

Security: hardened the importer against MongoDB query-operator smuggling through archive metadata (CWE-943). The `aposDocs.json` / `aposAttachments.json` files inside an uploaded archive are parsed with EJSON, which revives objects such as `{ "$ne": null }` as live values. Several attacker-controlled fields — an attachment's `_id`, and a document's `aposLocale`, `parkedId` and `type` — flowed unlaundered into MongoDB selectors (`attachment.db.findOne({ _id })` and the singleton/parked-document lookups), where an object value could act as a query operator. Imported attachment `_id`, `name` and `extension` must now be plain strings, and the singleton/parked lookup selectors coerce their inputs to strings, so no operator can reach the database. The practical impact was limited (field-level operators only — no top-level `$where`/`$expr`, no authentication or privilege escalation, and the importer already owns the documents), but untrusted archive data should never reach a query unlaundered. Found during an internal security review.
5 changes: 5 additions & 0 deletions .changeset/import-export-attachment-path-traversal.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@apostrophecms/import-export": patch
---

Security: the import routine reconstructed each imported attachment's on-disk source path by concatenating the `_id`, `name` and `extension` fields taken directly from the untrusted `aposAttachments.json` inside an uploaded archive, with no traversal check. The archive's existing zip-slip guard only validates tar entry names during extraction and did not cover this second path, which is built afterward. A `../` sequence (or absolute path) in one of those fields could point the reconstructed path outside the extraction directory, causing an arbitrary host file whose name ends in an allow-listed extension (for example `.txt`, `.csv`, `.pdf`) to be read and copied into the public uploads directory, where it was then served without authentication (CWE-22, GHSA-79qf-vqgc-7xx3). Exploiting this required an authenticated account with the contributor role or higher. Apostrophe now rejects any imported attachment whose reconstructed source path resolves outside the archive's `attachments` directory. Projects using `@apostrophecms/import-export` with untrusted contributors should upgrade promptly. Thanks to [kah-ja](https://github.com/kah-ja) and [luuhung1217](https://github.com/luuhung1217) for responsibly reporting the vulnerability.
5 changes: 5 additions & 0 deletions .changeset/import-export-extract-dos-hang.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@apostrophecms/import-export": patch
---

Security: hardened archive extraction against a denial-of-service hang (CWE-835). A crafted `.tar.gz` import whose archive contained a *directory* entry with a `../` traversal sequence in its name was correctly rejected by the zip-slip guard, but the extractor never advanced to the next tar entry for directories, so extraction never emitted `finish`, the extraction promise never resolved, and the import request/job hung indefinitely — leaving the uploaded file and a partially-extracted directory on disk. Repeated imports could exhaust connections and disk. The extractor now always drains and advances past a rejected entry regardless of its type. Reaching this requires an authenticated account permitted to import. Found during an internal security review.
5 changes: 5 additions & 0 deletions .changeset/itchy-zoos-kick.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"apostrophe": patch
---

Fix more admin UI a11y issues
5 changes: 5 additions & 0 deletions .changeset/jolly-zoos-fry.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@apostrophecms/redirect": minor
---

New `caseInsensitive` option. When enabled, "Old URL" values are stored in lowercase and incoming request URLs are matched case-insensitively. A migration lowercases existing redirects when the option is enabled. See the README for details, including a note on the non-reversible nature of this change.
5 changes: 5 additions & 0 deletions .changeset/mighty-wasps-train.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"create-apostrophe": patch
---

Fixes broken link
7 changes: 7 additions & 0 deletions .changeset/moody-poems-speak.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"apostrophe": minor
---

Fixed an issue where using the Tab key to navigate within modals could incorrectly jump focus to a wrong element instead of the next input field.

Fixed Tab navigation escaping out of modals when the form contained hidden sections or elements that became disabled after editing.
5 changes: 5 additions & 0 deletions .changeset/moody-tips-build.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@apostrophecms/font-size": patch
---

Initial release.
5 changes: 5 additions & 0 deletions .changeset/new-doors-turn.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"sanitize-html": patch
---

Address a potential vulnerability when nonTextTags is configured in a nonstandard way. While it is never a good idea to remove known non-text tags from the standard list e.g. script, styles, etc., this change ensures that doing so does not result in nested tags being passed through without sanitization when they are not expressly allowed. (ApostropheCMS would never trigger this situation.) Thanks to [Dipanshu singh](https://github.com/Dipanshusinghh) for pointing out the issue and contributing the fix.
5 changes: 5 additions & 0 deletions .changeset/old-seals-accept.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@apostrophecms/apostrophe-astro": minor
---

Upgraded the `undici` HTTP client from v6 to v8, which requires Node.js 22.19 or newer, and fixed a connection leak in the Astro proxy where responses that are not streamed on to the browser — redirects (301/302/307/308) and bodyless responses (204/304) — now release their backend response body immediately instead of leaving it for garbage collection, which under load could hold connections open and exhaust the connection pool.
5 changes: 5 additions & 0 deletions .changeset/page-move-destination-authorization.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"apostrophe": patch
---

Security: restored destination-parent authorization in the page `move()` operation (GHSA-wr5r-wqp2-x4fh). A regression had gated the destination "create" permission check on the source page being restored out of the archive, which silently disabled that check for every ordinary move. As a result a low-privileged but content-editing user (for example an editor) who could edit at least one page could relocate that page under a parent of a restricted page type they have no create/edit rights over (such as one declaring a higher `editRole`/`publishRole`), and in doing so trigger an unchecked re-ranking of the restricted parent's existing children. A cross-parent move into a non-archive destination now again requires "create" permission on the destination, with the archive-restore path handled as an explicit exception. Thanks to 5ud0 / Tarmo Technologies for reporting the issue.
19 changes: 19 additions & 0 deletions .changeset/pro-9597-fetch-apostrophe.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
---
"apostrophe": minor
---

The server-side HTTP client (`apos.http`) now uses Node's built-in `fetch` instead of `node-fetch`.

`node-fetch` is no longer maintained, and Node's built-in `fetch` is its standard, actively maintained successor, available in every Node.js version Apostrophe supports - so this is the right time to adopt it. We do not consider this a breaking change: common `apos.http.*` usage is unchanged, and we deliberately preserved compatibility where it mattered - `form-data` request bodies, cookie jars, the `timeout` option (now backed by an `AbortSignal`), and absolute redirect `Location` headers all behave as before.

Most code that calls `apos.http.get()`, `apos.http.post()`, etc. needs no changes. A few things to be aware of if you use advanced options or read raw responses:

- The `agent` option is no longer supported (the built-in `fetch` has no equivalent). Pass an undici `dispatcher` instead; `apos.http` throws if `agent` is given.
- A `Host` request header can no longer be set (it is disallowed by the fetch standard and is silently ignored).
- `originalResponse: true` now resolves with the built-in `fetch` `Response`. Its `body` is a web `ReadableStream` (use `require('node:stream').Readable.fromWeb()` to read it as a Node stream), and node-fetch-only helpers such as `.buffer()` are no longer available.
- Requests that send a conditional header (`If-None-Match` / `If-Modified-Since`) now also send `Cache-Control: no-cache`, as required by the fetch standard. An endpoint that returns `304 Not Modified` based on those headers may return `200` to such a request.

New capabilities:

- The `timeout` option (in milliseconds) and the standard `signal` (`AbortSignal`) and undici `dispatcher` options are supported.
- A request `body` may be a native `FormData`, in addition to a `form-data` package instance.
5 changes: 5 additions & 0 deletions .changeset/pro-9597-fetch-oembetter.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"oembetter": patch
---

Replaced the `node-fetch` dependency with Node's built-in `fetch`. This is an internal change with no effect on the public API.
5 changes: 5 additions & 0 deletions .changeset/pro-9620-npm12-apos-create.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"create-apostrophe": minor
---

Fixed `npm create apostrophe` with the SQLite database option under npm v12 (and when run from a global `@apostrophecms/cli` install). The installer now performs its post-install database work using the newly generated project's own `better-sqlite3`, rather than the installer's bundled copy.
6 changes: 6 additions & 0 deletions .changeset/pro-9620-npm12.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"apostrophe": patch
"uploadfs": patch
---

Bumped `glob` to `^13` (core) and `rimraf` to `^6` (uploadfs) to clear the deprecated `glob@10` warning shown on every install. The old `glob@10` arrived both directly from core and transitively through `uploadfs` → `rimraf@5`; both now resolve to the current, supported `glob@13` (`rimraf@6` depends on `glob@13` as well). No API or behavior changes.
5 changes: 5 additions & 0 deletions .changeset/proud-moons-guard.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"apostrophe": patch
---

Security fix: server-side prototype pollution (CWE-1321) via dot-notation paths. `apos.util.set()` and `apos.util.get()` now refuse to traverse `__proto__`, `constructor` and `prototype` path segments. Previously an authenticated editor could send a PATCH REST API request whose patch operators (for example `$pullAll` with a key of `__proto__.publicApiProjection`) wrote to `Object.prototype`. A polluted `publicApiProjection` defeated the `publicApiCheck()` authorization gate on piece-type REST endpoints for subsequent unauthenticated requests, for the lifetime of the Node.js process. All users should update. Thanks to [tonghuaroot](https://github.com/tonghuaroot), [H3xV0rT3x](https://github.com/H3xV0rT3x), and [5h1kh4r](https://github.com/5h1kh4r) for reporting the vulnerability.
5 changes: 5 additions & 0 deletions .changeset/rich-text-insert-menu-backspace.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"apostrophe": patch
---

Fixed pressing Backspace right after typing `/` in a rich text widget deleting the entire widget. Backspace now removes the slash and closes the insert menu. Global command menu shortcuts also no longer fire for key events already handled and prevented by other UI components.
11 changes: 11 additions & 0 deletions .changeset/ripe-terms-happen.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
---
"@apostrophecms/apostrophe-astro": minor
"apostrophe": minor
---

Fixed adding or removing an area field from a schema breaking existing documents on an external front such as Astro.

- `AposArea` now renders only schema-backed areas. A missing area no longer throws, and an area orphaned by removing its field from the schema (while its content remains in the document) renders nothing instead of breaking sibling areas in edit mode. Logged-in editors get a diagnostic message in place of an orphaned area; anonymous visitors see nothing.
- Editable documents sent to an external front now materialize empty area objects for schema area fields added after the document was created, so they can be edited in context.
- `apos.util.getManagerOf` accepts a `{ log }` option to suppress its error log when probing objects that may not have a manager.

23 changes: 23 additions & 0 deletions .changeset/sanitize-html-svg-mathml-rawtext-xss.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
---
"sanitize-html": patch
---

Security: fixed an XSS/allowlist bypass in which the contents of a raw-text
element (`textarea` or `xmp`) nested inside an `svg` or `math` root were
re-emitted without HTML-escaping. `sanitize-html` treated that content as inert
raw text because `htmlparser2` 10.x classified raw-text elements by tag name and
ignored the namespace, but a real HTML5 parser treats `textarea`/`xmp` as
ordinary foreign elements inside SVG/MathML and re-parses their contents as live
markup. As a result, markup and event-handler attributes that the allowlist
never permitted (for example `<svg><textarea><img src=x onerror=alert(1)>`)
could survive sanitization and execute in the browser. This is now fixed on two
fronts: `htmlparser2` was upgraded to 12.x, which is namespace-aware and parses
`textarea`/`xmp` inside SVG/MathML as ordinary elements, so their
non-allowlisted children (such as the injected `img`) are dropped by the
allowlist instead of being preserved as raw text; and any raw-text content
`sanitize-html` still emits for these tags (at HTML integration points such as
`foreignObject`/`mtext`, or outside foreign content) is always HTML-escaped. The
default configuration is not affected; the precondition is an `allowedTags` that
includes `svg` or `math` together with `textarea` or `xmp`. Thanks to
[khoadb175](https://github.com/khoadb175) for responsibly disclosing the
vulnerability.
Loading
Loading