Craft CMS is a content management system (CMS). Versions 4.0.0-RC1 and above, prior to 4.18.0 and 5.0.0-RC1, and above, prior to 5.10.0, are vulnerable to Server-Side Request Forgery (SSRF) and Arbitrary JavaScript Injection through the /actions/app/resource-js endpoint. By exploiting the default permissive trustedHosts configuration, an attacker can poison the Host or X-Forwarded-Host header to manipulate the application’s $baseUrl. This bypasses the endpoint’s internal URL validation, forcing the backend Guzzle client to fetch a malicious payload from an attacker-controlled server and reflect it to the client with a Content-Type: application/javascript header. The vulnerability manifests when assetManager.cacheSourcePaths is set to false. This issue has been fixed in versions 4.18.0 and 5.10.0.
Craft CMS is a content management system (CMS). In versions 5.0.0-RC1 through 5.9.22 and 4.0.0-RC1 through 4.17.15, an attacker with only a GitHub account can plant a JavaScript payload in a craftcms/cms issue title. When a Craft admin uses the CraftSupport widget’s "Give feedback" screen and types a search term that returns the poisoned issue, the payload executes in the admin’s control panel session. No control panel account or elevated privileges are required on the attacker’s side. This issue has been fixed in versions 4.17.16 and 5.9.23.
Craft CMS is a content management system (CMS). In versions 5.0.0-RC1 through 5.9.21 and 4.0.0-RC1 through 4.17.14, theAssetsController::actionDeleteFolder() only requires the deleteAssets:<volume-uid> permission for the target folder. It never enforces deletePeerAssets:<volume-uid>, even though Assets::deleteFoldersByIds() cascades deletion to every descendant folder and every asset inside, regardless of the uploader's assigned privileges. A low-privilege user who has been granted folder-management rights on a shared volume can therefore destroy assets uploaded by other users (peer assets), bypassing the per-asset peer-permission check that the sibling actionDeleteAsset endpoint correctly applies. This issue has been fixed in versions 4.17.15 and 5.9.22.
Craft CMS is a content management system (CMS). Versions 5.0.0-RC1 through 5.9.20, and 4.0.0-RC1 through 4.17.13 contain an authorization issue in the AssetsController::actionReplaceFile that can delete a source asset without source delete permission by supplying both assetId and sourceAssetId. AssetsController::actionReplaceFile() supports replacing a target asset file using another existing asset as the source. The action loads: assetId -> $assetToReplace and sourceAssetId -> $sourceAsset, then enforces replace permissions using ($assetToReplace ?: $sourceAsset). When both IDs are provided, this expression resolves to the target asset so no permission check is performed against the source asset volume. When both assets are present, Craft copies the source file into the target and then deletes the source asset. There is no deletion check for for the source asset. An authenticated user who can replace files in one volume can delete assets in another volume where they do not have delete permission, as long as they can obtain a sourceAssetId, leading to broken content references and data loss. This issue has been fixed in versions 4.17.14 and 5.9.21.
Craft CMS is a content management system (CMS). In versions 5.0.0-RC1 through 5.9.22, an author-level control panel user can store a malicious JavaScript payload in an entry title. When an admin, or any control panel user with saveEntries for the same Structure section, drags another entry under the poisoned entry in table view, the payload executes in the victim’s session. The issue is exploitable because the title is escaped into data-title by the server, decoded again by the browser, read with jQuery .data('title'), and then concatenated into a new HTML string without attribute escaping. To exploit, an attacker must have an existing control panel account (Author role minimum), the victim must perform a drag operation (not just visit the page), and the victim’s session needs to be elevated at trigger time. This issue has been fixed in version 5.9.23.
Craft CMS from 4.0.0-RC1 contains an authenticated path traversal vulnerability in the assets/icon endpoint where the extension parameter is not validated before file existence checks. Attackers can bypass extension validation by passing traversal sequences that resolve to existing SVG files, allowing local file read access.
## Overview of all XSS Reports
Multiple stored XSS vulnerabilities were found in Craft CMS. They were split into **4 reports** as follows:
| Report | What's Vulnerable | Why Separate |
|--------|-------------------|--------------|
| **This Report (1)** | Multiple settings names | Twig Template: `_includes/forms/checkbox.twig` |
| **Report 2** | Entry Types Name | Twig Template: `_includes/forms/editableTable.twig` |
| **Report 3** | Card Attributes in Field Layout | `helpers/Cp.php` |
| **Report 4 (Commerce)** | Product Type Name | Source in Commerce, sink in CMS - will report this one via Commerce GHSA |
Reports 2, 3, and 4 are clearly distinct locations. For this report (Report 1), it was not clear whether to split or consolidate these 7 bugs. The bug report was consolidated and the final categorization should be left to the judgement of the user.
**Note:** This overview is only in this Report. Other reports only reference this one.
---
## Summary
Stored XSS in multiple settings. Names/labels are rendered without sanitization via `checkbox.twig` template which uses `{{ label|raw }}`.
---
## Affected Sources
| # | Source (injection point) | Sink (where payload reflects) |
| --- | ------------------------------------------------------------------------ | --------------------------------------------- |
| 1 | Section Name (`/admin/settings/sections`) | Entries field -> Sources checklist |
| 2 | Volume Name (`/admin/settings/assets/volumes/{vol_id}`) | Assets field -> Sources checklist |
| 3 | User Group Name (`/admin/settings/users/groups`) | Users field -> Sources, User permissions page |
| 4 | Global Set Name (`/admin/settings/globals`) | User permissions page |
| 5 | Generated Fields Name (Volumes, Users, etc.) | Card Attributes checkboxes |
| 6 | Checkboxes & Radio Buttons Field Option Label (`/admin/settings/fields`) | User profile pages |
| 7 | Custom Sources Label (`/admin/users` -> Customize Sources) | Users field -> Sources checklist |
---
## Proof of Concept
### Required Permissions (Attacker)
- Admin access
- `allowAdminChanges` is enabled in production, which is against our [security recommendations](https://craftcms.com/knowledge-base/securing-craft).
### Bugs 1-3: Section, Volume, User Group Names
1. Log in as admin.
2. Inject payload in one of these:
- **Settings** -> **Sections** -> Create/edit section -> **Name**
- **Settings** -> **Assets** -> **Volumes** -> Create/edit volume -> **Name**
- **Settings** -> **Users** -> **User Groups** -> Create/edit group -> **Name**
3. Set **Name** to:
```html
<img src=x onerror="alert('XSS')">
```
4. Save.
5. Go to **Settings** -> **Fields** -> Create new field.
6. To trigger the XSS payload: Set **Field Type** to "Entries" (for Sections), "Assets" (for Volumes), or "Users" (for User Groups). The alert fires when the Sources checkbox list renders.
**Note:** User Group Name also reflects on **User permissions page** under **User Groups** section (`/admin/users/{id}/permissions`).
---
### Bug 4: Global Set Name
1. Go to **Settings** -> **Globals** (`/admin/settings/globals`).
2. Create/edit a Global Set, set **Name** to payload.
3. Save.
4. Go to **Users** -> Edit any user -> **Permissions** tab (`/admin/users/{id}/permissions`).
5. Alert fires because our payload got rendered in the "Global Sets" permissions section without encoding/sanitization.
---
### Bug 5: Generated Fields Name
1. Go to **Settings** -> **Assets** -> **Volumes** -> Create/Edit a volume.
2. Scroll to **Generated Fields** section.
3. Add a field, set **Name** to payload:
```html
<img src=x onerror="alert('XSS')">
```
4. Save & Notice the alert. The payload renders in the **Card Attributes** checkbox list below it.
---
### Bug 6: Checkboxes/Radio Buttons Option Label
1. Go to **Settings** -> **Fields** (`/admin/settings/fields`).
2. Create new field, set **Field Type** to "Checkboxes" or "Radio Buttons".
3. In **Field Options**, add an option with **Label** set to payload.
4. Save the field.
5. Go to **Settings** -> **Users** -> **User Profile Fields** (`/admin/settings/users/fields`).
6. Add the created field to the layout and save.
7. Alert fires on any user profile page (`/admin/users/{id}`).
---
### Bug 7: Custom Sources Label
1. Go to **Users** (`/admin/users`).
2. Click the three dots icon -> **Customize Sources**.
3. Create a new custom source, set **Label** to payload.
4. Save.
5. Go to **Settings** -> **Fields** -> Create new field.
6. Set **Field Type** to "Users".
7. Alert fires in the Sources checkbox list.
## Resources
https://github.com/craftcms/cms/commit/943152d2246b36f12adf161a03b8695b773d9276
https://github.com/cr
### Summary
An authenticated low-privileged user can call `assets/preview-file` for an asset they are not authorized to view and still receive preview response data (`previewHtml`) for that private asset.
The returned preview HTML included a private preview image route containing the target private `assetId`, even though `canView` was `false` for the attacker account.
### Details
1. `assets/preview-file` accepts a maliciously controlled `assetId` and renders preview output.
2. The action does not enforce per-asset view authorization prior to returning preview content.
3. As a result, an authenticated user without asset-view permission can still obtain private preview output.
This affects Craft installations with authenticated users of mixed privilege levels with private assets.
### Resources
- d30df3112220db1ffd6726a3ed11857014c7fb27
- b1cddf72c98a
Craft CMS contains a missing authorization vulnerability in the assets/preview-thumb endpoint. A Control Panel user without permission to view a target private asset can call the endpoint with an attacker-controlled assetId and receive preview HTML containing a signed fallback transform preview link for that private asset, because no asset-view permission check is performed before preview generation. This affects versions >= 4.0.0-RC1, <= 4.17.7 and >= 5.0.0-RC1, <= 5.9.13, and is fixed in 4.17.8 and 5.9.14.
A stored Cross-site Scripting (XSS) vulnerability exists in the `editableTable.twig` component when using the `Row Heading` column type. The application fails to sanitize input within row headings, allowing an attacker to execute arbitrary JavaScript when another user views a page with the malicious table field.
## Prerequisites
* An administrator account
* `allowAdminChanges` must be enabled in production, which is [against security recommendations](https://craftcms.com/knowledge-base/securing-craft#set-allowAdminChanges-to-false-in-production).
## Steps to Reproduce
1. Navigate to **Settings** → **Fields** and create a new field with Type: **Table**
1. Add a **Column Heading** and set **Column Type** to `Row Heading`
1. In **Default Values** section, add a row with the following payload:
```html
<img src=x onerror="alert('XSS')">
```
1. Enable `Static Rows`
1. Use the field in any object (e.g., user profile fields) → then visit any user’s profile
1. Notice the XSS execution
## Resources
https://github.com/craftcms/cms/commit/7b372de262b8d9d2ce859f32780c3715719b6f5a
Craft CMS (composer package craftcms/cms) versions >= 5.5.0 and <= 5.9.13 contain a remote code execution vulnerability in the FieldsController::actionRenderCardPreview() method, which passes the fieldLayoutConfig POST parameter directly to Fields::createLayout() without calling Component::cleanseConfig(). An authenticated admin user can inject Yii2 event handlers (e.g., 'on init' keys) via the fieldLayoutConfig parameter to execute arbitrary PHP code and disclose sensitive information (such as environment variables containing database credentials and CRAFT_SECURITY_KEY). The issue is fixed in version 5.9.14.
## Summary
A stored XSS vulnerability exists in the User Permissions page. The User Group name is rendered without proper HTML escaping in the permissions section, allowing an attacker to execute arbitrary JavaScript when another user views or edits a user's permissions.
> [!NOTE]
> This is a separate vulnerability from the previously reported "[Stored XSS via User Group Name in User Settings Page](https://github.com/craftcms/cms/security/advisories/GHSA-2423-8xxj-wc3g)" and "[Multiple Stored XSS in User Group Edit Page](https://github.com/craftcms/cms/security/advisories/GHSA-vx7g-xw92-g4xj)". This affects a different sink: the individual user's permissions page.
## Proof of Concept
### Required Permissions
- Admin access
- `allowAdminChanges` is enabled in production, which is against our [security recommendations](https://craftcms.com/knowledge-base/securing-craft).
### Steps to Reproduce
1. Log in to the control panel as an admin
2. Navigate to **Settings → Users → User Groups**
3. Create or edit a user group and set the **Name** field to:
```html
<img src=x onerror="alert('XSS')" hidden>
```
4. Save the user group
5. Navigate to **Users** and edit any user (`/admin/users/{id}`)
6. Click on the **Permissions** tab
7. XSS executes
## Mitigation
Sanitize user group names when rendering in the user permissions template.
## References
https://github.com/craftcms/cms-ghsa-4mgv-366x-qxvx/pull/2
Craft CMS is a content management system (CMS). From 4.0.0 to before 4.17.12 and 5.9.18, Craft CMS which contains an input-handling flaw in a Yii object creation path that let any authenticated user inject malicious configuration and execute arbitrary commands on the server. The request-controlled condition field layouts data is converted into a live FieldLayout object without a Component::cleanseConfig() boundary. Because Craft configures models before parent::__construct(), attacker-controlled special config keys can take effect during object creation, and FieldLayout initialization then triggers a same-request event. This vulnerability is fixed in 4.17.12 and 5.9.18.
Craft CMS is a content management system (CMS). From 5.0.0-RC1 to before 5.9.18, AssetsController::actionShowInFolder() fetches an asset by ID and returns its filename and complete folder hierarchy (including volume handle, volume UID, folder names, folder UIDs, and folder URI paths) without checking whether the requesting user has viewAssets or viewPeerAssets permission on the asset’s volume. Any authenticated CP user — even one with zero volume permissions — can enumerate asset filenames and the full folder structure of any volume by supplying arbitrary asset IDs. This vulnerability is fixed in 5.9.18.
Craft CMS is a content management system (CMS). From 4.0.0 to before 4.17.12 and 5.9.18, the GraphQL Address element resolver (src/gql/resolvers/elements/Address.php) performs no schema scope filtering on top-level queries. A GraphQL API token scoped to a single low-privilege user group can read every address in the system, including addresses belonging to users in groups the token has no authorization to access. This exposes PII, including full names, addresses, organizations, tax IDs, etc. This vulnerability is fixed in 4.17.12 and 5.9.18.
Craft CMS versions 4.x through 4.17.8 and 5.x through 5.9.14 contain a Server-Side Request Forgery (SSRF) vulnerability in the resource-js endpoint. This vulnerability arises when the trustedHosts configuration is not explicitly restricted, allowing the application to trust a client-supplied Host header. An attacker can exploit this to control the baseUrl used in prefix validation, causing the server to make arbitrary HTTP requests. Versions 4.
Craft CMS versions 4.x through 4.17.8 and 5.x through 5.9.14 contain a Server-Side Request Forgery (SSRF) vulnerability. Exploitation requires specific GraphQL schema permissions related to editing and creating assets in a volume. The vulnerability is patched in versions 4.17.
Craft CMS versions 5.6.0 through 5.9.14 contain a missing authorization vulnerability in the actionSavePermissions() endpoint. A user with only viewUsers permission can remove arbitrary users from all user groups because the system does not enforce authorization checks for group removals. This issue is patched in version 5.9.15.
An SQL injection vulnerability (CWE-89) exists in Craft Commerce versions 5.0.0 through 5.5.4. The issue arises because the hasVariant and hasProduct properties bypass input sanitization, allowing authenticated control panel users to perform boolean-based blind SQL injection. This can lead to extraction of arbitrary database contents, including security keys that could be used to forge admin sessions and escalate privileges. The vulnerability was fixed in version 5.6.0.
Craft Commerce is an ecommerce platform for Craft CMS. In versions 4.0.0 through 4.10.2 and 5.0.0 through 5.5.4, there is an SQL injection vulnerability in the Commerce TotalRevenue widget which allows any authenticated control panel user to achieve remote code execution through a four-step exploitation chain. The attack exploits unsanitized widget settings interpolated into SQL expressions, combined with PDO's default multi-statement query support, to inject a maliciously serialized PHP object into the queue table. When the queue consumer processes the injected job, the unrestricted unserialize() call in yii2-queue instantiates a GuzzleHttp FileCookieJar gadget chain whose __destruct() method writes a PHP webshell to the server's webroot. The complete chain requires only three HTTP requests, no administrative privileges, and results in arbitrary command execution as the PHP process user, with queue processing triggered via an unauthenticated endpoint. This issue has been fixed in versions 4.10.3 and 5.5.5.