🚨 Security
Server-Side Template Injection (SSTI) via double template resolution in option rendering
This 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
attacker in the group of authenticated Panel users or user interaction of another authenticated user.
This vulnerability is of high severity for affected sites.
Your 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.
- CVE ID: CVE-2026-34587
- Severity: high (CVSS score 7.6)
- Advisory: GHSA-jcjw-58rv-c452
Thanks to @offset for responsibly reporting the identified issue.
Page, file and user creation APIs bypass create permission check via unfiltered blueprint parameter
This 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.
This vulnerability is of high severity for affected sites.
Your 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.
- CVE ID: CVE-2026-41325
- Severity: high (CVSS score 7.1)
- Advisory: GHSA-6gqr-mx34-wh8r
Thanks to @offset for responsibly reporting the identified issue.
pages.access/list and files.access/list permissions are not consistently checked in the Panel and REST API
This 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.
This vulnerability is of high severity for affected sites.
Your 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.
Write actions are not affected by this vulnerability.
- CVE ID: pending
- Severity: high (CVSS score 7.1)
- Advisory: GHSA-85x2-r8xv-ww8c
Read access to site, user and role information is not gated by permissions
This vulnerability affects all Kirby sites that might have potential attackers in the group of authenticated Panel users.
This vulnerability is of high severity for affected sites.
Your 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.
Write actions are not affected by this vulnerability as they were gated by permissions before.
- CVE ID: pending
- Severity: high (CVSS score 7.1)
- Advisory: GHSA-2h7v-4372-f6x2
Thanks to @HuajiHD for responsibly reporting the identified issue.
XML Injection in the XML creator toolkit
This 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.
If 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.
- CVE ID: CVE-2026-32870
- Severity: medium (CVSS score 6.9)
- Advisory: GHSA-9wfj-c55w-j9qr
Thanks to Patrick Falb (@dapatrese) at FORMER 03 for responsibly reporting the identified issue.
User avatar creation, replacement and deletion are not gated by user update permissions
This 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.
Your 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.
- CVE ID: pending
- Severity: moderate (CVSS score 5.3)
- Advisory: GHSA-39cp-6679-8xv2
Page creation API bypasses changeStatus permission check via unfiltered isDraft parameter
This 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.
Your 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.
- CVE ID: CVE-2026-40099
- Severity: moderate (CVSS score 5.3)
- Advisory: GHSA-w942-j9r6-hr6r
Thanks to @offset for responsibly reporting the identified issue.
System API endpoint leaks installed version and license data to authenticated users
This vulnerability affects all Kirby sites that might have potential attackers in the group of authenticated Panel users.
- CVE ID: pending
- Severity: moderate (CVSS score 5.3)
- Advisory: GHSA-x68m-c7jf-2572
Thanks to @HuajiHD and @0x-bala for responsibly reporting the identified issue.
✨ Enhancements
- Site permissions
- New
accesspermission for thesite - New
Kirby\Cms\Site::isAccessible()method checking if the current user hasaccesspermission for the site - New static
Kirby\Cms\Find::site()method returning the site object or throwingKirby\Exception\NotFoundExceptionif the site is not accessible - New i18n string
error.site.notAccessibleadded toi18n/translations/en.json
- New
- User permissions
- New
accessandlistpermissions for users and the current user. - New
Kirby\Cms\User::isAccessible()method checking if the current user hasaccesspermission for a given user - New
Kirby\Cms\User::isListable()method checking if a user is both accessible and haslistpermission. Inaccessible users are implicitly not listable - New static
Kirby\Cms\Find::users()method returning only users filtered byisListable()
- New
- Role permissions
- New
Kirby\Cms\Role::isAccessible()method checking if the current user hasusers.accessoruser.accesspermission. If the role is the same role as the user's, theuser.accesspermissions are checked. Otherwiseusers.access. - New static
Kirby\Cms\Find::role()andKirby\Cms\Find::roles()methods returning only roles filtered byisAccessible() - Added a new
error.role.notFoundtranslation key.
- New
- Avatar permissions
- New avatar hooks
user.createAvatar,user.replaceAvatar,user.deleteAvatar(including:beforeand:after) - New User class methods:
User::createAvatar(),User::replaceAvatar(),User::deleteAvatar() - New User rules:
UserRules::createAvatar(),UserRules::replaceAvatar(),UserRules::deleteAvatar(),UserRules::validAvatar()
- New avatar hooks
🚨 Security fixes
- The
GET /systemAPI route now consistently filters the relevant set of information by current system state Kirby\Cms\Find::parent()now usesKirby\Cms\Find::site()instead of$kirby->site()for the site model lookupKirby\Cms\Api::site()now delegates toKirby\Cms\Find::site()instead of$this->kirby->site()config/api/models/System.phpthetitlefield now reads directly from$this->kirby()->site()to bypass thesite.accesspermission check, ensuring the title is always available- The
page.movedialog now usesKirby\Cms\Find::site()in the submit handler, when a page gets moved to the top-level Kirby\Cms\Find::user()now enforcesisAccessible()on the resolved user, throwingKirby\Exception\NotFoundExceptionif the user exists but is inaccessible- API routes (
config/api/routes/users.php): all$this->user()/$this->users()calls replaced withKirby\Cms\Find::user()/Kirby\Cms\Find::users()to apply access control at the API layer. Also cleans up the/rolesroute and the change-password logic to useKirby\Cms\Find Kirby\Cms\Api::users()delegates toKirby\Cms\Find::users()instead of$this->kirby->users()Kirby\Cms\UserPickerfilters out non-listable users before search/sortKirby\Panel\Collector\UsersCollectorfilters out non-listable users before role filtering and paginationKirby\Panel\Controller\Search::users()filters byisListablebefore running the search query. Also movesfilter('isListable')beforesearch()in the pages search for consistencyKirby\Panel\User::prevNext()prev/next navigation now filters siblings byisListableso navigation skips hidden users- The API User model now filters siblings by
isListablefor prev and next user. - The model methods in the
Kirby\Panel\ChangesDialogclass (::files(),::users(),::pages()) will now filter by unlistable models to make sure that they don't accidentally appear in the dialog. Kirby\Cms\UserPickernow filters inaccessible users for the picker dialog.- Updated
Kirby\Cms\Roles::canBeCreated()andKirby\Cms\Roles::canBeChanged()to runfilter('isAccessible', true)before the existing filter so inaccessible roles are
excluded even if they would otherwise pass the create or changeRole permission checks - Updated
Kirby\Cms\User::roles()to applyfilter('isAccessible', true)to the full roles collection before any further permission checks so unauthenticated users and users without user access permissions get no roles. config/api/routes/roles.php: Replaced direct$kirby->roles()calls withFind::roles()so all roles API endpoints enforce access filtering. Replaced$kirby->roles()->find($name)withFind::role($name)for the single-role endpoint. Replaced$kirby->request()->get('canBe')with$this->requestQuery('canBe')for consistencyconfig/api/models/User.php: Added an extrafilterBy('isAccessible', true)call on therolesfield of the user API model to ensure the API response never exposes inaccessible rolesuser.updatepermissions are now required to create, replace or delete a user avatar.
🚨 Breaking changes
- The
pages.changeStatuspermission 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 thechangeStatuspermission and block changes to existing pages in apage.changeStatus:beforehook.
♻️ Refactored
- 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. - Replace deprecated calls to
$form->values() - Remove dead code (
next/prev) in the page API model