{
  "$type": "site.standard.document",
  "bskyPostRef": {
    "cid": "bafyreih5txo5w6yj2frjkvs5s5gffl2k5yg26mbpadnf5ypkyg6mdi4msm",
    "uri": "at://did:plc:uycvbmlz3vrjjgtyjzs4qtgh/app.bsky.feed.post/3mk5lgzwo6al2"
  },
  "coverImage": {
    "$type": "blob",
    "ref": {
      "$link": "bafkreifvz46teqgoaz7idbmotrpjfhxqlabsim4nn3xc4zksv3utloully"
    },
    "mimeType": "image/png",
    "size": 59283
  },
  "path": "/getkirby/kirby/releases/tag/5.4.0",
  "publishedAt": "2026-06-17T16:24:01.787Z",
  "site": "https://github.com",
  "tags": [
    "CVE-2026-34587",
    "GHSA-jcjw-58rv-c452",
    "@offset",
    "CVE-2026-41325",
    "GHSA-6gqr-mx34-wh8r",
    "CVE-2026-42137",
    "GHSA-85x2-r8xv-ww8c",
    "CVE-2026-42069",
    "GHSA-2h7v-4372-f6x2",
    "@HuajiHD",
    "CVE-2026-32870",
    "GHSA-9wfj-c55w-j9qr",
    "@dapatrese",
    "FORMER 03",
    "CVE-2026-42174",
    "GHSA-39cp-6679-8xv2",
    "CVE-2026-40099",
    "GHSA-w942-j9r6-hr6r",
    "CVE-2026-42051",
    "GHSA-x68m-c7jf-2572",
    "@0x-bala"
  ],
  "textContent": "## 🚨 Security\n\n### Server-Side Template Injection (SSTI) via double template resolution in option rendering\n\nThis vulnerability affects all Kirby sites that use option fields (`checkboxes`, `color`, `multiselect`, `select`, `radio`, `tags` or `toggles`) with options from a query or API whose values may not be fully trusted. It also affects direct uses of the `OptionsApi` or `OptionsQuery` classes of Kirby's `Options` package from plugin or site code. The attack requires either an\nattacker in the group of authenticated Panel users or user interaction of another authenticated user.\n\n**This vulnerability is of high severity for affected sites.**\n\nYour Kirby sites are _not_ affected if you are not using any of the mentioned fields or the `Options` package, if all options are defined statically in the blueprints or if all dynamically gathered options are to be trusted.\n\n  * **CVE ID:** CVE-2026-34587\n  * **Severity:** high (CVSS score 7.6)\n  * **Advisory:** GHSA-jcjw-58rv-c452\n\n\n\nThanks to @offset for responsibly reporting the identified issue.\n\n### Page, file and user creation APIs bypass `create` permission check via unfiltered `blueprint` parameter\n\nThis vulnerability affects all Kirby sites where users of a particular role have no permission to create pages, files or users (`pages.create`, `files.create` or `users.create` permission is disabled). This can be due to configuration in the user blueprint(s), via `options` in the model blueprint(s) or via a combination of both settings.\n\n**This vulnerability is of high severity for affected sites.**\n\nYour Kirby sites are _not_ affected if you intend all users of your site to be able to create pages, files and users. The vulnerability can only be exploited by authenticated users.\n\n  * **CVE ID:** CVE-2026-41325\n  * **Severity:** high (CVSS score 7.1)\n  * **Advisory:** GHSA-6gqr-mx34-wh8r\n\n\n\nThanks to @offset for responsibly reporting the identified issue.\n\n### `pages.access/list` and `files.access/list` permissions are not consistently checked in the Panel and REST API\n\nThis vulnerability affects all Kirby sites where users of a particular role have no permission to access or list pages or files (`pages.access`, `pages.list`, `files.access` or `files.list` permission is disabled). This can be due to configuration in the user blueprint(s), via `options` in the model blueprint(s) or via a combination of both settings.\n\n**This vulnerability is of high severity for affected sites.**\n\nYour Kirby sites are _not_ affected if you intend all users of your site to be able to access all pages and files of the site. The vulnerability can only be exploited by authenticated users.\nWrite actions are _not_ affected by this vulnerability.\n\n  * **CVE ID:** CVE-2026-42137\n  * **Severity:** high (CVSS score 7.1)\n  * **Advisory:** GHSA-85x2-r8xv-ww8c\n\n\n\n### Read access to site, user and role information is not gated by permissions\n\nThis vulnerability affects all Kirby sites that might have potential attackers in the group of authenticated Panel users.\n\n**This vulnerability is of high severity for affected sites.**\n\nYour Kirby sites are _not_ affected if you intend all users of your site to be able to list and access the site model and all users and roles, including the content stored within these models.\nWrite actions are _not_ affected by this vulnerability as they were gated by permissions before.\n\n  * **CVE ID:** CVE-2026-42069\n  * **Severity:** high (CVSS score 7.1)\n  * **Advisory:** GHSA-2h7v-4372-f6x2\n\n\n\nThanks to @HuajiHD for responsibly reporting the identified issue.\n\n### XML Injection in the XML creator toolkit\n\nThis vulnerability only affects Kirby sites that use the `Xml` data handler (e.g. `Data::encode($string, 'xml')`) or the `Xml::create()`, `Xml::tag()` or `Xml::value()` method(s) in site or plugin code. The Kirby core does not use any of the affected methods.\n\nIf you use an affected method and cannot rule out input to these methods controlled by an attacker, we strongly recommend to update to a patch release.\n\n  * **CVE ID:** CVE-2026-32870\n  * **Severity:** medium (CVSS score 6.9)\n  * **Advisory:** GHSA-9wfj-c55w-j9qr\n\n\n\nThanks to Patrick Falb (@dapatrese) at FORMER 03 for responsibly reporting the identified issue.\n\n### User avatar creation, replacement and deletion are not gated by user update permissions\n\nThis vulnerability affects all Kirby sites where users of a particular role have no permission to update user information (`user.update` or `users.update` permission is disabled). This can be due to configuration in the blueprint(s) of the acting users, via `options` in the blueprint(s) of the target users or via a combination of both settings.\n\nYour Kirby sites are _not_ affected if you intend all users of your site to be able to upload, replace or delete user avatars. The vulnerability can only be exploited by authenticated users.\n\n  * **CVE ID:** CVE-2026-42174\n  * **Severity:** moderate (CVSS score 5.3)\n  * **Advisory:** GHSA-39cp-6679-8xv2\n\n\n\n### Page creation API bypasses `changeStatus` permission check via unfiltered `isDraft` parameter\n\nThis vulnerability affects all Kirby sites where users have the permission to create pages (`pages.create` permission is enabled) but not the permission to change the status of pages (`pages.changeStatus` permission is disabled). This can be due to configuration in the user blueprint(s), via `options` in the page blueprint(s) or via a combination of both settings.\n\nYour Kirby sites are _not_ affected if your use case does not consider the creation of published pages a malicious action. The vulnerability can only be exploited by authenticated users.\n\n  * **CVE ID:** CVE-2026-40099\n  * **Severity:** moderate (CVSS score 5.3)\n  * **Advisory:** GHSA-w942-j9r6-hr6r\n\n\n\nThanks to @offset for responsibly reporting the identified issue.\n\n### System API endpoint leaks installed version and license data to authenticated users\n\nThis vulnerability affects all Kirby sites that might have potential attackers in the group of authenticated Panel users.\n\n  * **CVE ID:** CVE-2026-42051\n  * **Severity:** moderate (CVSS score 5.3)\n  * **Advisory:** GHSA-x68m-c7jf-2572\n\n\n\nThanks to @HuajiHD and @0x-bala for responsibly reporting the identified issue.\n\n## ✨ Enhancements\n\n  * Site permissions\n    * New `access` permission for the `site`\n    * New `Kirby\\Cms\\Site::isAccessible()` method checking if the current user has `access` permission for the site\n    * New static `Kirby\\Cms\\Find::site()` method returning the site object or throwing`Kirby\\Exception\\NotFoundException` if the site is not accessible\n    * New i18n string `error.site.notAccessible` added to `i18n/translations/en.json`\n  * User permissions\n    * New `access` and `list` permissions for users and the current user.\n    * New `Kirby\\Cms\\User::isAccessible()` method checking if the current user has `access` permission for a given user\n    * New `Kirby\\Cms\\User::isListable()` method checking if a user is both accessible and has `list` permission. Inaccessible users are implicitly not listable\n    * New static `Kirby\\Cms\\Find::users()` method returning only users filtered by `isListable()`\n  * Role permissions\n    * New `Kirby\\Cms\\Role::isAccessible()` method checking if the current user has `users.access` or `user.access` permission. If the role is the same role as the user's, the `user.access` permissions are checked. Otherwise `users.access`.\n    * New static `Kirby\\Cms\\Find::role()` and `Kirby\\Cms\\Find::roles()` methods returning only roles filtered by `isAccessible()`\n    * Added a new `error.role.notFound` translation key.\n  * Avatar permissions\n    * New avatar hooks `user.createAvatar`, `user.replaceAvatar`, `user.deleteAvatar` (including `:before` and `:after`)\n    * New User class methods: `User::createAvatar()`, `User::replaceAvatar()`, `User::deleteAvatar()`\n    * New User rules: `UserRules::createAvatar()`, `UserRules::replaceAvatar()`, `UserRules::deleteAvatar()`, `UserRules::validAvatar()`\n\n\n\n## 🚨 Security fixes\n\n  * The `GET /system` API route now consistently filters the relevant set of information by current system state\n  * `Kirby\\Cms\\Find::parent()` now uses `Kirby\\Cms\\Find::site()` instead of `$kirby->site()` for the site model lookup\n  * `Kirby\\Cms\\Api::site()` now delegates to `Kirby\\Cms\\Find::site()` instead of `$this->kirby->site()`\n  * `config/api/models/System.php` the `title` field now reads directly from `$this->kirby()->site()` to bypass the `site.access` permission check, ensuring the title is always available\n  * The `page.move` dialog now uses `Kirby\\Cms\\Find::site()` in the submit handler, when a page gets moved to the top-level\n  * `Kirby\\Cms\\Find::user()` now enforces `isAccessible()` on the resolved user, throwing `Kirby\\Exception\\NotFoundException` if the user exists but is inaccessible\n  * API routes (`config/api/routes/users.php`): all `$this->user()` / `$this->users()` calls replaced with `Kirby\\Cms\\Find::user()` / `Kirby\\Cms\\Find::users()` to apply access control at the API layer. Also cleans up the `/roles` route and the change-password logic to use `Kirby\\Cms\\Find`\n  * `Kirby\\Cms\\Api::users()` delegates to `Kirby\\Cms\\Find::users()` instead of `$this->kirby->users()`\n  * `Kirby\\Cms\\UserPicker` filters out non-listable users before search/sort\n  * `Kirby\\Panel\\Collector\\UsersCollector` filters out non-listable users before role filtering and pagination\n  * `Kirby\\Panel\\Controller\\Search::users()` filters by `isListable` before running the search query. Also moves `filter('isListable')` before `search()` in the pages search for consistency\n  * `Kirby\\Panel\\User::prevNext()` prev/next navigation now filters siblings by `isListable` so navigation skips hidden users\n  * The API User model now filters siblings by `isListable` for prev and next user.\n  * The model methods in the `Kirby\\Panel\\ChangesDialog` class (`::files()`, `::users()`, `::pages()`) will now filter by unlistable models to make sure that they don't accidentally appear in the dialog.\n  * `Kirby\\Cms\\UserPicker` now filters inaccessible users for the picker dialog.\n  * Updated `Kirby\\Cms\\Roles::canBeCreated()` and `Kirby\\Cms\\Roles::canBeChanged()` to run `filter('isAccessible', true)` before the existing filter so inaccessible roles are\nexcluded even if they would otherwise pass the create or changeRole permission checks\n  * Updated `Kirby\\Cms\\User::roles()` to apply `filter('isAccessible', true)` to the full roles collection before any further permission checks so unauthenticated users and users without user access permissions get no roles.\n  * `config/api/routes/roles.php`: Replaced direct `$kirby->roles()` calls with `Find::roles()` so all roles API endpoints enforce access filtering. Replaced `$kirby->roles()->find($name)` with `Find::role($name)` for the single-role endpoint. Replaced `$kirby->request()->get('canBe')` with `$this->requestQuery('canBe')` for consistency\n  * `config/api/models/User.php`: Added an extra `filterBy('isAccessible', true)` call on the `roles` field of the user API model to ensure the API response never exposes inaccessible roles\n  * `user.update` permissions are now required to create, replace or delete a user avatar.\n\n\n\n## 🚨 Breaking changes\n\n  * The `pages.changeStatus` permission now also takes effect during the creation of pages (by preventing the creation of published pages instead of drafts). If you need the old behavior of allowing the creation of published pages but preventing users from changing the status of existing pages, please enable the `changeStatus` permission and block changes to existing pages in a `page.changeStatus:before` hook.\n\n\n\n## ♻️ Refactored\n\n  * The methods `$page->copy()` and `$page->changeNum()` have been marked as internal. They should only be called after all necessary checks (e.g. permissions) have been performed.\n  * Replace deprecated calls to `$form->values()`\n  * Remove dead code (`next`/`prev`) in the page API model\n\n",
  "title": "5.4.0",
  "updatedAt": "2026-04-25T12:38:44.000Z"
}