Apostrophe 4.32.0: Literal Content Routes, Apostrophe-Astro Updated, and a Modernized HTTP Client #5516
BoDonkey
announced in
Release Notes
Replies: 1 comment
|
Excited to test all these updates. |
0 replies
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Hello Apostrophe Community!
Apostrophe 4.32.0 is a maintenance release focused on smoother external front-end integration, an internal HTTP client modernization, and a batch of editorial and admin UI fixes. This is our second monthly maintenance and third overall release under the new cadence. And because last month was a quarterly feature release, our “stable” and “latest” tracks have significant differences. So this is a good moment to remember that "stable" customers get this release's fixes and security updates now, with 4.31.0's new features arriving on schedule next quarter.
While maintenance releases are focused primarily on necessary fixes, in
latestthey do sometimes include changes intended to achieve broader compatibility and promote future stability. You’ll see examples of that below. Forstablewe adhere more strictly to the “fixes only” rule in each maintenance release.Improved support for Non-HTML Pages
Files like
robots.txt,sitemap.xml, andllms.txtaren't pages — they're literal content that should be served as-is. Modules can now declare these and they are returned viagetLiteralContentRoutes, so external front-end integrations recognize and serve them correctly with no extra configuration, instead of mistakenly trying to render them as HTML-containing pages. This release applies the mechanism internally (@apostrophecms/sitemapand@apostrophecms/seoboth use it for their generated files), and custom modules can use the same approach for their own literal content routes. This reduces the need for redundant code and lowers the risk of omissions.Astro Integration: Now Supporting Astro v5–v7
The
@apostrophecms/apostrophe-astrointegration has been refactored to support Astro v5 through v7. This is a backward-compatible change but every project lead should read the migration guide before upgrading. Helpers can still be imported from the same locations as before, but those are now compatibility shims; projects should update their imports to the new locations described in the guide going forward. Since it is a feature, this change will not appear instableuntil next quarter.A Modernized HTTP Client
The server-side HTTP client (
apos.http) now uses Node's built-in, actively-developedfetchinstead of thenode-fetchpackage which is strictly in maintenance mode. For the vast majority of projects this is a drop-in change — commonapos.http.get()/apos.http.post()usage,form-datarequest bodies, cookie jars, and thetimeoutoption all continue to work as before. Projects using more advanced options (a customagent, aHostheader override, or readingoriginalResponseas a raw stream) should review the changelog for the small set of adjustments needed. The same modernization has been applied to theoembetterpackage. Since it is a feature, this change will not appear instableuntil next quarter.qAdditional Improvements
This release also includes a collection of fixes to admin UI stability and editorial workflows:
npm installornpm update)./, and the widget copy/cut/paste keyboard shortcuts no longer hijack native text copy during an active text selection.dateAndTimeschema fields no longer overflow their container in narrow layouts.create-apostropheinstaller's SQLite database setup now works correctly under npm v12 and when run from a global@apostrophecms/cliinstall. Since it is a Q2 feature, SQLite support will not appear instableuntil next quarter.Security
This release includes fixes for five reported vulnerabilities across core and several modules, along with a dependency update. In
apostrophecore, we've closed a gap that allowed relationship data excluded frompublicApiProjectionto be extracted through public REST API query builders, and restored a permission check that had regressed in the pagemove()operation. Insanitize-html, two separate issues allowed specially-crafted markup to bypass the tag allowlist under certain non-default configurations involvingsvg,math,textarea, orxmptags.@apostrophecms/import-exporthas been hardened against malicious archive metadata reaching database queries unlaundered, and@apostrophecms/formno longer stores file attachments before a submission passes validation. We're grateful to the researchers who responsibly reported these issues. Full technical detail and credit for each fix is in the changelog below.Separately,
nodemailerhas been bumped from 8.x to 9.x to pick up a fix for an upstream vulnerability where a message'srawoption could be used to bypass file- and URL-access controls. Apostrophe's own use ofnodemailerwas not exposed to this issue, but we recommend all users upgrade promptly. See the changelog for details, including one small behavior change in outbound HTTPS certificate validation that self-signed or non-standard certificate setups should be aware of.We recommend all projects update to 4.32.0 as soon as practical.
How to Update
Update your projects with
npm updateand let us know what you think on our roadmap.The “stable” option
Our Hosting, Pro and Assembly customers also have the option of following our “stable” releases. The difference is simple: The stable releases receive new features one full quarter later, but receive bug fixes and security fixes at the same time as “latest.” In order for this to be an effective technique to increase stability, customers still need to actively evaluate “latest” so that they have meaningful head start on what is coming in “stable.” Customers interested in following this path should reach out for complete information on how to point their dependencies to “stable.”
Everything we ship as “latest” is 100% ready for production. The “stable” series is an option for those with a large investment in training, automated testing, etc. It allows delayed implementation of new features without falling behind on fixes.
🚀 Happy coding!
Apostrophe 4.32.0
Adds
robots.txt,sitemap.xml, orllms.txtrather 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:getLiteralContentRoutesevent.Changes
The server-side HTTP client (
apos.http) now uses Node's built-infetchinstead ofnode-fetch.node-fetchis in maintenance mode, and Node's built-infetchis its standard, actively developed 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: commonapos.http.*usage is unchanged, and we deliberately preserved compatibility where it mattered -form-datarequest bodies, cookie jars, thetimeoutoption (now backed by anAbortSignal), and absolute redirectLocationheaders 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:agentoption is no longer supported (the built-infetchhas no equivalent). Pass an undicidispatcherinstead;apos.httpthrows ifagentis given.Hostrequest header can no longer be set (it is disallowed by the fetch standard and is silently ignored).originalResponse: truenow resolves with the built-infetchResponse. Itsbodyis a webReadableStream(userequire('node:stream').Readable.fromWeb()to read it as a Node stream), and node-fetch-only helpers such as.buffer()are no longer available.If-None-Match/If-Modified-Since) now also sendCache-Control: no-cache, as required by the fetch standard. An endpoint that returns304 Not Modifiedbased on those headers may return200to such a request.New capabilities:
timeoutoption (in milliseconds) and the standardsignal(AbortSignal) and undicidispatcheroptions are supported.bodymay be a nativeFormData, in addition to aform-datapackage instance.Bumped
globto^13(core) andrimrafto^6(uploadfs) to clear the deprecatedglob@10warning shown on every install. The oldglob@10arrived both directly from core and transitively throughuploadfs→rimraf@5; both now resolve to the current, supportedglob@13(rimraf@6depends onglob@13as well). No API or behavior changes.Fixes
Fixed the tag popover in the media library (used to apply tags to images in bulk) so it loads all image tags instead of only the first 50. Tags beyond the first 50 can now be found and applied, and creating a tag whose name already exists no longer produces a duplicate.
Fixed the admin UI sometimes serving a stale build after dependencies changed (for example after
npm installornpm 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
lockChangedbuild option. Theapos.asset.getSystemLastChangeMs()helper is deprecated and the build manifest no longer includes atstimestamp.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.Fix invalid HTML output for elements in sanitize-html (treat void elements correctly)
Fixed a layout issue where
dateAndTimeschema fields could overflow and trigger horizontal scrolling in narrow containers.fromRichText adds metatype to new widget
Fixed the widget copy shortcut (Ctrl+C / Cmd+C) hijacking native text copy in edit mode. With an active text selection, the cut, copy and remove (Backspace) widget shortcuts now defer to the browser. Pasting a widget with Ctrl+V / Cmd+V now checks that the widget copy is still the most recent thing in the system clipboard, so text copied elsewhere in the meantime is no longer shadowed by a stale widget paste. The widget clipboard storage remains backward compatible with entries written by previous releases.
Batch jobs now reliably record their total item count, so completion notifications no longer occasionally report a null total.
Security
.choices()/.counts()query builders (?choices=/?counts=on the public REST API) guarded against leaking distinct values of fields excluded frompublicApiProjectionby 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/authorAndfor a field named_author) and the_authorAndoperation 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 frompublicApiProjection. Relationship alias builders are now resolved back to their underlying schema field (matching the field name or itsidsStorage) before thepublicApiProjectionandviewPermissionchecks are applied, so the alias names are gated exactly like the field itself. Thanks to Ta Duc Thien (thientd) for reporting this issue.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 highereditRole/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.nodemailerdependency from 8.x to 9.x to pick up the fix for GHSA-p6gq-j5cr-w38f, where a message'srawoption could bypass nodemailer'sdisableFileAccess/disableUrlAccesscontrols and enable arbitrary file reads or SSRF. The real-world risk to Apostrophe is low: core only sends mail from trusted server-side code (such as password-reset emails), never sets those controls, and gives site visitors no way to control a message'srawfield. nodemailer 9 is a security-only major release with one behavior change worth noting for projects: outbound HTTPS used to fetch remote content (remote-URL attachments, OAuth2 token endpoints, HTTP/HTTPS proxies) now validates TLS certificates by default — if you depend on self-signed or otherwise invalid certificates, opt out per request withtls.rejectUnauthorized: false. As a precaution, make sure your own project code never forwards untrusted input into therawfield of a module'semail()call.Pro Modules
@apostrophecms-pro/automatic-translation1.5.1This bundle allows automatic translation of documents (pages and pieces) when localizing content. It comes with two translation providers: Google Cloud Translation and DeepL, and supports custom providers. Explore our documentation to learn how this extension can enhance your project. Once you're ready, obtain a license and install it through Apostrophe Workspaces. For further details or inquiries, feel free to contact us or visit our pricing page.
Changes
afterTranslateevent containing the translation input and output, the original and the translated document. The event fires before the localized document is saved in the database.Changes
@apostrophecms-pro/multisite4.6.0This module lets you have many ApostropheCMS websites running on a single codebase in a single Node.js process. Each has its own database, users, media uploads, etc. Sites can be created and managed via a dashboard site. Explore our documentation to discover how this extension can enhance your project. For further details or inquiries, feel free to contact us or visit our pricing page.
Fixes
dashboard/modules/@apostrophecms/user/index.jsat project level are now respected. Multisite no longer enhances the user module itself — those fields are provided by@apostrophecms-pro/multisite-dashboard— so project-level extensions merge as expected.@apostrophecms-pro/multisite-dashboard1.8.0This extension creates the new default multisite dashboard with infinite scroll, search functionality, and the ability to save templates. This extension requires that the project also have the
@apostrophecms-pro/multisiteextension installed and configured. Explore our documentation to discover how this extension can enhance your project. For further details or inquiries, feel free to contact us or visit our pricing page.Fixes
Free Modules
@apostrophecms/apostrophe-astro1.14.0This module integrates ApostropheCMS into your Astro application.
Adds
robots.txt,sitemap.xml, andllms.txt- by proxying them directly to Apostrophe instead of rendering them as pages. These files no longer need to be listed individually in theproxyRoutesoption, which continues to work as before for any additional routes you wish to proxy.vite/vite-plugin-apostrophe-generated-config.js, which writes real files to
node_modules/.apostrophe-astro-config/ (config.js, doctypes.js)
undicito ^7.x for Node.js 24+ compatibilitypeerDependenciesdeclaring Astro v5, v6, and v7 support.virtual:apostrophe-configimport inaposLiteralContentMiddleware.jsto use the generated-file module path./articles/?page=2now 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.Changes
undiciHTTP 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.@apostrophecms/form1.5.4Allow ApostropheCMS editors to build their own forms. They can then place any form in one or more content areas across the website.
Security
@apostrophecms/import-export3.6.2This module enables import and export of pages and pieces, with or without related documents such as files, images and other related types.
Security
aposDocs.json/aposAttachments.jsonfiles 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'saposLocale,parkedIdandtype— 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,nameandextensionmust 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._id,nameandextensionfields taken directly from the untrustedaposAttachments.jsoninside 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'sattachmentsdirectory. Projects using@apostrophecms/import-exportwith untrusted contributors should upgrade promptly. Thanks to kah-ja and luuhung1217 for responsibly reporting the vulnerability..tar.gzimport 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 emittedfinish, 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.@apostrophecms/import-export-xlsx1.1.0This module improves @apostrophecms/import-export by adding the
xlsxformat.Changes
xlsxtarball dependency with the registry-published@e965/xlsxmirror, 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.@apostrophecms/seo1.6.0Comprehensive SEO module providing meta field management and Schema.org structured data generation (JSON-LD) for all pages and pieces.
Adds
robots.txtandllms.txtfiles are now declared as literal content routes, so external front-end integrations (such as the Astro integration) serve them correctly in SSR mode automatically, with no per-route configuration. Requires a compatible version of Apostrophe core.@apostrophecms/sitemap1.5.0The Apostrophe Sitemap module generates XML sitemaps for websites powered by ApostropheCMS.
Adds
sitemap.xml(or per-locale sitemaps) is now declared as a literal content route, so external front-end integrations (such as the Astro integration) serve it correctly in SSR mode automatically, with no per-route configuration. Requires a compatible version of Apostrophe core.@apostrophecms/vite1.1.2This extension provides Vite integration for ApostropheCMS projects, enabling module bundling and hot module replacement (HMR) during development.
Fixes
Fixed the admin UI sometimes serving a stale build after dependencies changed (for example after
npm installornpm 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
lockChangedbuild option. Theapos.asset.getSystemLastChangeMs()helper is deprecated and the build manifest no longer includes atstimestamp.@apostrophecms/content-upgrader1.2.0This extension allows migration of existing content from Apostrophe 2.x projects to Apostrophe 4.x projects.
Adds
--only=type1,type2,...option to migrate only certain A2 doc types and the attachments they reference. Often used with--related-images.--related-imagesoption to migrate only the images actually referenced by the migrated content (one hop, resolved against the A4-form content), rather than every image. Works on its own or together with--only. Don’t addapostrophe-imageto--onlyif using this flag as that would migrate all of them.--mergeoption to add the upgraded content to an existing A4 database instead of insisting on-drop. Existing content is preserved and new content is inserted alongside it.-replaceoption, which upserts documents and attachments (overwriting any with matching ids) rather than inserting them, so re-running an upgrade does not fail with duplicate key errors.-replaceimplies-merge.-copy-mediaoption (which requires-a4-dir) to also copy the physical media files of the migrated attachments into the A4 project, respecting-onlyand-related-images. Assumes media is stored locally underpublic/uploads/attachments; it does not use uploadfs and so does not handle remote (e.g. S3) storage. If you are not using--onlyand--related, You may find it easier to justrsyncor otherwise copy theattachmentsfolder.Utilities
create-apostrophe1.1.0create-apostropheis the guided installer behindnpm create apostrophe. It clones a starter kit, wires up your database, installs dependencies, and creates an admin user — so a fresh Apostrophe project is running in minutes. It can be used standalone vianpm create apostrophe@latestor through the Apostrophe CLI'sapos createcommand.Fixes
npm create apostrophewith the SQLite database option under npm v12 (and when run from a global@apostrophecms/cliinstall). The installer now performs its post-install database work using the newly generated project's ownbetter-sqlite3, rather than the installer's bundled copy.oembetter1.2.1oembetteris a modern oembed client which allows you to add filters that provide or improve oembed support for services that either don't have it or don't do it very well.Changes
node-fetchdependency with Node's built-infetch. This is an internal change with no effect on the public API.sanitize-html2.17.6This module provides a simple HTML sanitizer with a clear API.
Security
textareaorxmp) nested inside ansvgormathroot were re-emitted without HTML-escaping.sanitize-htmltreated that content as inert raw text becausehtmlparser2classifies raw-text elements by tag name and ignores the namespace, but a real HTML5 parser treatstextarea/xmpas 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. Raw-text content is now HTML-escaped whenever it appears anywhere inside ansvgormathsubtree. The default configuration is not affected; the precondition is anallowedTagsthat includessvgormathtogether withtextareaorxmp. Thanks to khoadb175 for responsibly disclosing the vulnerability.allowedTagsbypass affecting configurations that allow thetextareaorxmpraw-text tags.htmlparser210.x did not recognize an end tag with a trailing solidus (e.g.</textarea/>) as closing the element, so it kept the following markup as raw text, but a spec-compliant browser treats</textarea/>as a valid close and parses that markup as a live element. Because raw-text content was re-emitted without escaping, a payload such as<textarea></textarea/><img src=x onerror=...>could smuggle non-allowlisted, executable markup through the sanitizer. The default configuration was not affected. This is now defended at two layers:htmlparser2was upgraded to 12.x, whose tokenizer closes these end tags correctly, and the raw text sanitize-html emits for these tags is always escaped so no<can reopen a tag when the output is re-parsed (textarea, an RCDATA element whose entitieshtmlparser2decodes, is escaped like normal text, whilexmp, a raw-text element, has only its angle brackets escaped to avoid double-encoding already-encoded entities). Becausehtmlparser2is ESM-only from version 11 onward,sanitize-htmlnow requires Node.js>=22.12.0(the first 22.x release in whichrequire()of an ES module is available unflagged). Thanks to bibu123456 for reporting the vulnerability and Kayiz-PT for coordinating the disclosure (GHSA-jxwj-j7wr-gfrw).uploadfs1.27.0uploadfscopies files to a web-accessible location and provides a consistent way to get the URLs that correspond to those files. uploadfs can also resize, crop and autorotate uploaded images. uploadfs includes S3-based, Azure-based, GCS-based and local filesystem-based backends and you may supply others.Changes
sharpimage-processing dependency from^0.32.6to^0.35.2. Since 0.33, sharp distributes its prebuilt binaries as platform-specific@img/sharp-*packages, and as of 0.35 it no longer runs an install script (the previousnode-gyp/prebuild-installstep). This removes a deprecation warning and prepares projects for npm v12, where dependency install scripts are disabled by default.globto^13(core) andrimrafto^6(uploadfs) to clear the deprecatedglob@10warning shown on every install. The oldglob@10arrived both directly from core and transitively throughuploadfs→rimraf@5; both now resolve to the current, supportedglob@13(rimraf@6depends onglob@13as well). No API or behavior changes.All reactions