Note
This release is part of our monthly security release series. Find out more about those releases and their background on our website: https://getkirby.com/buzz/security-update
🚨 Security
Patched security issues
Severity: High
- Cross-site scripting (XSS) from imported table and iframe HTML in the site frontend - CVSS Score 8.5
- Cross-user login through a stale authentication challenge - CVSS Score 7.6
- Cross-site scripting (XSS) from language variables that overwrite Kirby's own translations - CVSS Score 7.4
- Cross-site scripting (XSS) from incomplete SVG and XML sanitization in
Dom::sanitize()- CVSS Score 7.4 - Access permissions are not checked when the
files,pagesandusersfields resolve their stored value - CVSS Score 7.1 - Page creation API writes content to an arbitrary path via unfiltered
rootanddirnameparameters - CVSS Score 7.1 - Path traversal in the template name during page creation - CVSS Score 7.1
- Path traversal during user creation allows writing account files outside the accounts directory - CVSS Score 7.0
Severity: Moderate
- Incomplete URL normalization in the Sane sanitizer for SVG files - CVSS Score 5.1
- Incomplete URL detection in SVG sanitization allows loading of external stylesheets - CVSS Score 5.1
Reporters
Thanks to the following reporters for responsibly reporting the identified issues
- Bruno Meilick (@bnomei)
- Markus Magnuson (@alimony)
- Eurico Nicacio (@euriconicacio)
- Smit Verma (@smitverma)
- @manus-use
- @iRevivalx
Security fixes in detail
- Validate ID on user creation
- Clearing session data on auth session creation to ensure never inheriting (partial) state from a previous challenge
- Prevent content path escape via injected
rootanddirnameon page creation - Sanitize table HTML in Parsley blocks
- Drop non-video iframes in Parsley blocks
- Detect
@importstring form inDom::extractUrls() - Prevent path traversal in template name
k-linkno longer accept dangerous URL schemes (javascript:etc.).- Renaming a file now validates the new extension and the resulting filename against the same rules that apply to uploads.
- Language variables can no longer overwrite Kirby's own translation strings. Several of those are rendered as HTML in the Panel, so a user with the
languages.updatepermission could inject HTML markup into other users' Panels. To override a core string, use thetranslationsextension in your config as before. - Comments and CDATA sections in SVG and XML files are now sanitized against mutation XSS
- Files, pages and users fields no longer disclose models the current user is not allowed to list. Such entries are shown as
–and keep their stored value. - Panel breadcrumbs redact parent pages the current user is not allowed to list.
- The change position and change status dialogs no longer disclose siblings the current user is not allowed to list.
- The sanitizer for uploaded SVG files could be tricked into accepting documents that reference external origins
- Guard license registration by system area access
✨ Enhancements
- The
(date:)KirbyTag keeps cached pages up to date and supports a new optionalexpiryattribute #7746
(date: year) // cache resets at the start of next year
(date: Y-m-d) // no expiry unless set
(date: Y-m-d expiry: tomorrow) // cache resets at midnight- Constrain dialogs and expanded link browsers to the viewport #6012
- Keep model picker actions visible while scrolling the item list #8160
- Search engines/robots no longer index the Panel. Panel responses now send an
X-Robots-Tag: noindex, nofollowheader and the Panel document includes a matchingrobotsmeta tag. #8446 - new
Kirby\Template\Template::sanitizeName()method - Exceptions thrown when creating an invalid language variable are now translatable.
- New
Url::normalize()method that brings a URL into the same form a browser parses.
🐛 Bug fixes
- Writer field: respect the order marks are listed in the blueprint as their relative rank, e.g. italics inside a link don't split link into multiple
<a>tags anymore #5481 - Fix a browser crash when opening the link or email dialog in the Writer field on Chromium browsers #8288
k-textarea-input: (pre)-selecting works again$helper.string.unescapeHTML()does not unescape entities twice anymore #8345- Unlocking a changes version when switching translation language in the Panel #8353
- Show clearer Panel dialog message when discarding changes of another user #8065
- Fixed a permanent "unsaved changes" state in the Panel for pages with an untouched
objectortoggle#8355 - Copying a page no longer breaks the cached UUIDs of the original #8368
- Renaming or moving a page now also updates the UUID cache entries of its drafts #8368
- A UUID cache entry that points at a model which no longer holds that UUID is discarded #8368
- Removes old media folder when a page is moved #8370
- Tags are always focusable when removable (and Backspace/Delete delete the tag when focused) #8372
- The session cookie is now removed when logging out of the Panel, so the pages cache doesn't stay blocked #8375
Page::create(),File::create()andUser::create()use the fields'savehandlers again, just like::update()does. This restores the v4 behavior. Values a field can't handle are normalized or dropped. #8440- Rules and permissions for
Page::create(),File::create()andUser::create()are checked before the given content is processed, so unauthorized requests are rejected upfront. #8440 Sql::joins()returns the bindings collected from its joins instead of discarding them. #8460Sql::insert()no longer fails with aTypeErrorwhen called without explicit bindings. #8461Database::connect()throws a clearInvalidArgumentExceptionfor incomplete connection options instead of aTypeError. #8463- A failed
Database::connect()no longer leaves the object with a partially updated configuration; the previous connection stays intact. #8463 Sql::unquoteIdentifier()no longer unwraps identifiers whose opening and closing quotes don't match, and no longer mangles the other quote character inside a quoted identifier. #8462- Fix copying (e.g. blocks) inside drawers and dialogs #8472
$page->changeTemplate()and$file->changeTemplate()no longer delete the existing content before the converted content has been written. If writing fails, the old content stays intact. #8473- Changing the template of a page with unsaved changes keeps the page listed as changed. #8473
- Changing the template no longer copies fields from the default language into partial translations. #8473
- Changing the template of content that is currently edited by another user fails before anything is modified. #8473
- Fix Panel page URLs when the server encodes
+as%2B#8485 - Creating a new default language now correctly throws when a default language already exists
- Restrict
Kirby\Toolkit\Html::youtube()domains. (thx to @bnomei and Danish Tariq of Laburity for their suggestion and responsible disclosure). - The
kirby.resetPasswordsession flag now gets cleared on logout - Escape attributes/tags in block snippets
- When renaming a file
a.jpgtobwith new extensionpngno longer overlooks an existingb.png.
♻️ Refactored
- New
Kirby\Cms\Inventoryclass used for page, site and user content dir inventories #8137 - The
Uuidclasses carry generic type templates, soPageUuid::model(),FileUuid::model()etc. are statically typed as the specific model #8368 Database::connect()now takesarray $params = []instead ofarray|null $params = null, matching the constructor. Passingnullwas never supported. #8463
☠️ Deprecated
Kirby\Filesystem\Dir::inventoryis deprecated. UseKirby\Cms\Inventory::forinstead. #8137$model->createDefaultContent()has been deprecated, use$model->createContent()instead. #8440
🚨 Breaking changes
- Iframes with unknown URLs are no longer imported as blocks in Parsley
- HTML tables are no longer kept as-is in Parsley. All elements are sanitized.
Kirby\Sane\Html::options()&Kirby\Sane\Svg::options()are now public