This is a security release to address a range of vulnerabilities:
Users could update attachments on pages they did not have permission to update.
Request endpoints for drawings could be misused to create cross-site-scripting scenarios.
Draft page endpoints could be misused to alter non-draft pages without needing edit permission for those pages.
Draft pages created by other users could appear in certain search result lists.
ZIP imports with certain content could lead to a remote-code-execution scenario.
Page visibility rules may not be updated in certain recycle-bin-related scenarios.
Updating is generally advised, but strongly so where untrusted users have general access to the instance.
If untrusted users do have access, especially edit level access, it's advised to double check image upload folders to ensure there's no unexpected non-image content.
On a typical Linux-based system installation, this could be done using the find command from the BookStack installation folder like so:
# Check the general "public" server space
find ./public/uploads -type f !\( -iname "*.jpg" -o -iname "*.jpeg" -o -iname "*.png" -o -iname "*.gif" -o -iname "*.webp" -o -iname "*.avif" -o -iname ".gitignore" -o -iname ".htaccess"\)# Check the internal image storage space
find ./storage/uploads/images -type f !\( -iname "*.jpg" -o -iname "*.jpeg" -o -iname "*.png" -o -iname "*.gif" -o -iname "*.webp" -o -iname "*.avif"\)
A big thanks to the following researchers for responsibly reporting vulnerabilities which were addressed in this release:
Fixed storage connections leaking when asset requests were cancelled or a transformation failed, which eventually made every asset request return a permission error until Directus was restarted (#28076) exists() now throws when the lookup itself fails, for example on a timeout, a connection error or rejected credentials, instead of also reporting false. Callers that relied on a false result for any failure need to handle the error. Note that S3 answers 403 rather than 404 for a missing object when the credentials cannot list the bucket, so granting s3:ListBucket is needed to keep getting a clean "missing" answer.
Fixed "Update Items" and "Delete Items" operations affecting every item in a collection when given an empty or missing key or query (#27759) Nothing to target is a no-op
"Update Items" and "Delete Items" operations now return null instead of falling back to every item whenever the configuration doesn't target anything β that is, when key is empty or missing (e.g. [], "") and query is empty or missing (e.g. {}). "Update Items" additionally returns null when there is nothing to write, i.e. an empty or missing payload (e.g. {}, or [] for a batch payload). Flows that relied on the previous fallback to every item can use {"limit": -1}.
Contradictory options error
"Update Items" and "Delete Items" operations now throw an error when both key and query are defined. "Update Items" also throws when key or query is combined with a batch payload.
Updated ASSETS_TRANSFORM_IMAGE_MAX_OUTPUT_DIMENSION to match ASSETS_TRANSFORM_IMAGE_MAX_DIMENSION (6000 px) (#28111)
The default maximum output dimension is now 6000 px. Users who rely on the previous limit of 3000 px can explicitly configure ASSETS_TRANSFORM_IMAGE_MAX_OUTPUT_DIMENSION.
Used the pm2 bundled with @directus/api in the Docker images instead of installing a separate copy, so its dependencies follow the versions pinned by the workspace (#28120)
If you extend the Docker image: it now boots via CMD ["node", "docker-entrypoint.cjs"], which runs the same bootstrap then pm2-runtime sequence as before. pm2-runtime is no longer on the PATH, so a custom CMD that called it directly should hand off to docker-entrypoint.cjs instead. pm2 itself remains on the PATH for docker exec diagnostics.
@directus/api
Fixed "Update Items" and "Delete Items" operations affecting every item in a collection when given an empty or missing key or query (#27759 by @ComfortablyCoding)
@directus/storage-driver-cloudinary
Fixed storage connections leaking when asset requests were cancelled or a transformation failed, which eventually made every asset request return a permission error until Directus was restarted (#28076 by @dstockton)
@directus/storage-driver-s3
Fixed storage connections leaking when asset requests were cancelled or a transformation failed, which eventually made every asset request return a permission error until Directus was restarted (#28076 by @dstockton)
@directus/storage-driver-local
Fixed storage connections leaking when asset requests were cancelled or a transformation failed, which eventually made every asset request return a permission error until Directus was restarted (#28076 by @dstockton)
@directus/storage-driver-supabase
Fixed storage connections leaking when asset requests were cancelled or a transformation failed, which eventually made every asset request return a permission error until Directus was restarted (#28076 by @dstockton)
@directus/sdk
Updated outdated type definitions for directus_files, directus_collections, directus_deployments, directus_settings, and directus_users (#27945 by @kheiner)
β¨ New Features & Improvements
@directus/app
Added search-first AI tool discovery for chat and MCP tools, with schema pinned as a root chat tool. (#27797 by @bryantgillespie)
Added a caption field to the WYSIWYG image drawer, which wraps the image in a figure with a figcaption (#28026 by @alvarosabu)
Added the collection name appended to display template in item and drawer headers (#28078 by @AlexGaillard)
@directus/api
Added search-first AI tool discovery for chat and MCP tools, with schema pinned as a root chat tool. (#27797 by @bryantgillespie)
@directus/cli
Introduced @directus/cli (d6s / directus-cli) β a client-side CLI that syncs schema and configuration between Directus instances through committed JSON files, with sync pull, sync diff, sync push, and an interactive wizard (#27861 by @bryantgillespie)
@directus/types
Updated outdated type definitions for directus_files, directus_collections, directus_deployments, directus_settings, and directus_users (#27945 by @kheiner)
Removed dead βSave and Quitβ dropdown row outside the content item view (#28051 by @robluton)
Fixed relational items with unsaved nested values, such as newly added translated items in a content version, rendering as -- instead of their display template (#28010 by @alvarosabu)
Fixed the repeater interface options showing empty sub-fields, and dropping their key and type on save, when the sub-fields were created through the API without repeating the key and type inside their meta (#28041 by @lazerg)
Fixed relational fields showing stale values after a manual flow updated them (#28056 by @AlexGaillard)
Fixed the Markdown interface's Edit and Preview buttons not indicating which view is currently active (#28023 by @Aniket-a14)
Fixed silent failure of dragging & dropping files with an unrecognized extension into the file library (#28093 by @alvarosabu)
Fixed a request for a non-existent item when opening an item whose Many-to-One field references an unsaved parent (#27975 by @sourav-18)
Updated outdated type definitions for directus_files, directus_collections, directus_deployments, directus_settings, and directus_users (#27945 by @kheiner)
Fixed field configuration appearing to close when selecting related collection that switches interface (#28118 by @robluton)
Fixed the translations interface AI translation button only showing for admins (#28089 by @AlexGaillard)
Fixed SSO login redirecting to the last visited page instead of the originally requested page (#28080 by @AlexGaillard)
Stopped the policy creation modal from writing app access permission rows to the database, matching the policy detail page where app access permissions are applied at runtime instead of stored (#28101 by @alvarosabu)
Added block-level custom formats to the WYSIWYG interface, so block, selector and items entries in the Custom Formats option apply classes and attributes to paragraphs, headings and other block nodes from the Formats dropdown (#28044 by @alvarosabu)
Fixed WYSIWYG content the editor can't represent being hidden and unrestorable in the comparison modal (#28067 by @alvarosabu)
@directus/api
Fixed storage connections leaking when asset requests were cancelled or a transformation failed, which eventually made every asset request return a permission error until Directus was restarted (#28076 by @dstockton)
Added a batch-import regression test pinning that a negative temporary key maps like any other non-existent auto-increment key in merge mode (#27861 by @bryantgillespie)
Updated MCP tool descriptions and safety annotations for connector clients. (#28090 by @bryantgillespie)
Improved MS SQL Server reliability and performance by optimizing schema introspection and only enabling trigger compatibility when required (#27699 by @br41nslug)
Updated various dependencies to address CVEs (#28110 by @br41nslug)
Fixed slow extension sync from remote storage during startup. Added EXTENSIONS_STORAGE_MAX_CONCURRENCY to configure the maximum number of concurrent requests to the extensions storage location (#27989 by @dstockton)
Updated ASSETS_TRANSFORM_IMAGE_MAX_OUTPUT_DIMENSION to match ASSETS_TRANSFORM_IMAGE_MAX_DIMENSION (6000 px) (#28111 by @ComfortablyCoding)
Fixed TranslationsService.updateMany incorrectly rejecting single-row updates containing both key and language (#28001 by @suhailopensource)
Fixed collection names with surrounding whitespace being accepted on creation (#28038 by @lazerg)
Fixed WebSocket rate limiting breaking on shared Redis setups where keys must start with a per-project prefix. The WebSocket limiter now accepts RATE_LIMITER_WEBSOCKETS_* values as overrides, including RATE_LIMITER_WEBSOCKETS_KEY_PREFIX to override the Redis key prefix. (#28107 by @AlexGaillard)
@directus/storage-driver-azure
Fixed storage connections leaking when asset requests were cancelled or a transformation failed, which eventually made every asset request return a permission error until Directus was restarted (#28076 by @dstockton)
@directus/storage-driver-gcs
Fixed storage connections leaking when asset requests were cancelled or a transformation failed, which eventually made every asset request return a permission error until Directus was restarted (#28076 by @dstockton)
Improved MS SQL Server reliability and performance by optimizing schema introspection and only enabling trigger compatibility when required (#27699 by @br41nslug)
@directus/env
Fixed slow extension sync from remote storage during startup. Added EXTENSIONS_STORAGE_MAX_CONCURRENCY to configure the maximum number of concurrent requests to the extensions storage location (#27989 by @dstockton)
Updated ASSETS_TRANSFORM_IMAGE_MAX_OUTPUT_DIMENSION to match ASSETS_TRANSFORM_IMAGE_MAX_DIMENSION (6000 px) (#28111 by @ComfortablyCoding)
Updated ESLint dependencies eslint, @eslint/js, eslint-plugin-vue, and typescript-eslint. Replaced eslint-plugin-import with eslint-plugin-import-x (#28047 by @br41nslug)
up2k: client could waste a little bandwidth while recovering from a network glitch 18791c5
if PRTY_CONFIG is set to a config-file that is also autodetected, then explain the misconfiguration instead of crashing like before 14e2d79
wopi: fix session-timeout hint to clients (thx @kamaeff!) 92c3f32
js: fix chance of duplicate prologue on very first page visit eeb399e
js: fix panic on image dragdrop out of the browser window dcc0abd
π§ other changes
up2k: client now detects when server or reverseproxy is incorrectly configured with an impractically small request-body-size-limit, crashing the website with an explanation how to fix it f19ef03
also allows setting the chunksize all the way down to 1 megabyte when absolutely necessary (bad idea, slow)
shares: harden single-file shares some more 21c2c729ff6a71
just removing footguns (motivated by a bug-report that was a false-positive)
copyparty.exe: upgrade to python 3.14.7 from 3.13.14 8b6e897
larger and slightly faster (compensated for the size bloat by making the text-image-generator more shitty)
π fun facts
the thumbex example is also a cool example how relevant the "pseudo" in PRNG can be; with random.randrange instead of os.urandom, first run followed by restarting copyparty and another run...heh
After upgrade, you need to run php bin/console doctrine:migrations:migrate (or equivalent) as webserver user after upgrade.. If you are running a docker container, use sudo docker exec --user=www-data partdb php bin/console doctrine:migrations:migrate, or sudo -E inside the docker container, to ensure that the migrations are applied to the correct database.
Important
Since this version Part-DB can enable foreign key checks for SQLite: it is recommend to do this, but it requires some manual intervention. See this announcement on how to do it: #1486
Note
If you are using Part-DB it would be helpful if you fill out this short survey on your usage of Part-DB (Google Forms): https://forms.gle/Q15twx3YYq3qCNfe8
New Features
Added OAuth server capability, for easy and automatic API access to Part-DB without the need to generate an API token by hand. An application can redirect to an Part-DB page, where an user can authorise the access of the external application. This feauture is opt-in and requires some setup by hand.
With the OAuth server capability, Part-DB can now be used as an remote MCP connector for AI applications like claude.ai. Just add a custom connector with your Part-DB url, and you can authorize the access via one click. No need for fiddeling with API tokens or config files
Allow to enable foreign key checking for sqlite via an env variable. It is recommended to do this, however it requires some manual intervention.
Add Docker based development environment by @swdee in #1441
Link to update manager from homepage banner update notification
Bug fixes
Fixed that orphaned BOMEntries, APITokens and Orderdetails were possible
Fix TypeError when tree expand/collapse/search is used before the tree finished loading by @Sebbeben in #1482
Require full user authentication, when changing users or groups
A GitHub Actions incident left the 1.8.0 release build queued indefinitely, so no Docker image was ever published for that tag. 1.8.1 re-runs the release with the same code, plus one plugin slot that merged in the meantime.
Security: Manage S/MIME and PGP public keys and configure Stalwart encryption at rest from account security settings
Notifications: Background notification onboarding, sequenced after the PWA install prompt
Navigation: Deep links for mail, calendar, contacts, files, and settings, with screen-reader improvements
Settings: Always show the Unified Mailbox switch in Layout settings
i18n: Catalan translation
i18n: Localized editor toolbar across every locale
Plugins: Contact API β contact.get, contact.create, contact.update, contact.search
Plugins: contact-cryptokeys UI slot, behind the ui:contact-cryptokeys permission, so a plugin can render a contact's crypto keys in place of the built-in list
Plugins: user.getAccounts and user.getIdentities
Plugins: user.logout method and logout hook
Plugins: Crypto API β public-key management and encryption-at-rest control on the privileged tier
Plugins: onBeforeBlobUpload can offload an attachment to external storage
Plugins: Binary Blob/File bodies for api.http.post
Plugins: upfiles.get moved behind email:blob-read, off the privileged tier
Dev: Mock JMAP defaults now include nested tags
Changes
Mail: The "Reset to defaults" button is gone from tag settings β one stray click wiped a carefully built tag list, with no confirmation and no undo
Fixes
Send: Send through the identity's own account client so DKIM matches the From domain (#461)
Send: Split Name <addr> recipients into the JMAP name and email fields (#672)
Send: Time out stalled JMAP requests so a send can't hang forever (#702)
Mail: Keep inline images when replying to application/octet-stream cid parts (#543)
Mail: Reply on your own thread message no longer re-addresses the original recipients (#703)
Mail: Empty folder no longer stops after 500 emails (#711)
Mail: Move messages across accounts from the "Move to" context menu, preserving read state and deferring source removal to Stalwart
Mail: Stop resurrecting deleted rows in the mailbox refresh merge
Mail: Keep the message/rfc822 attachment visible after inline unwrapping
Mail: Strip sender and recipient names from forward-as-attachment filenames
Mail: Open mailto: links in the built-in composer
Mail: Spell out the full tag path in drag-and-drop toasts, so Personal/Receipts and Work/Receipts no longer read as the same tag
Mail: Act on current email state in the context menu's mark-as-read instead of a stale copy
Mail: Match the selected-row tint between dark and light mode
Mail: Restore lost animations after the Tailwind config move
Drafts: Restore the sender identity when reopening a draft
Calendar: Stop re-adding the organizer to the attendee list on every save (#731)
Calendar: Stop re-probing shared accounts that have no calendar access
Calendar: Route the parse dump through the debug logger