CVE-2026-55793: Craft CMS Cross-Site Scripting Vulnerability

MODERATE
Advisory Identifier: GHSA-xrqc-p465-2xvg
CWE-79
Published
Advisory Summary

CVE-2026-55793 is a MODERATE cross-site scripting vulnerability (CWE-79: Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')) affecting Craft CMS libraries in the Packagist ecosystem. The issue involves stored xss via structure entry title in table view. Remediation is available by upgrading affected packages to version 5.9.53.

CVE-2026-55793 at a Glance

CVE Identifier CVE-2026-55793
GHSA Identifier GHSA-xrqc-p465-2xvg
Severity MODERATE
CVSS v3.1 Base Score N/A
CWE Classification CWE-79: Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')
Affected Product Craft CMS
Package Ecosystem Packagist
Published Date
Last Updated

How to Fix CVE-2026-55793

To remediate this vulnerability, upgrade all affected Craft CMS dependencies to a patched release. Both direct and transitive dependencies should be checked, as frameworks or security libraries may pull in vulnerable versions transitively.

craftcms/cms fixed: 5.9.53
Upgrade craftcms/cms to 5.9.53
Verify Direct and Transitive Dependencies

Run the following command in your project directory to verify that all resolved dependencies use the patched release:

inspect dependencies for craftcms/cms

How to Check if You Are Affected

To determine whether your application is affected by CVE-2026-55793, inspect your project dependencies for Craft CMS:

inspect dependencies for craftcms/cms

Analyze the terminal output to locate any version of Craft CMS in your dependency tree. If any resolved version matches the vulnerable ranges listed in the table below (for example, versions earlier than 5.9.53), your application is affected.

Note: Verify declared build manifests and deployed runtime container environments.

Technical Details

Vulnerability Mechanism & Technical Analysis

This vulnerability is classified under CWE-79: Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting'), where the product does not neutralize or incorrectly neutralizes user-controllable input before it is placed in output that is used as a web page that is served to other users. In Craft CMS, software state validation or input boundaries are circumvented during processing. Review the official source advisory statement below for specific affected components, classes, and reproduction details.

Source Advisory Statement

# Stored XSS via Structure entry title in table view ## Summary An Author-level control panel user can store a 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. Execution was verified with `alert(document.domain)`. Impact was verified against an elevated admin session by changing the admin user’s email through `users/save-user`, then using the password-reset flow to take over the account. ## Preconditions - Attacker has `createEntries` + `saveEntries` on a Structure-type section (Author-style CP account; no admin permission required). - Victim has `saveEntries` on the same section. Craft only renders drag handles when `structureEditable` is true, which requires this permission, so any admin qualifies. - Section must be type Structure. Channel and Single sections are unaffected. - Poisoned entry must have no children at the time of the drag (fires on the 0-to-1 descendant transition). - For the email-change account-takeover path, the victim must currently have an elevated session. The stored XSS itself does not require elevation. ## Root cause `ElementTableSorter.js` lines 643-652: ```js const ancestorTitle = this._updateAncestors._$ancestor.data('title'); $( '<button … aria-label="' + Craft.t('app', 'Show {title} children', {title: ancestorTitle}) + '"></button>' ).insertAfter(…); ``` `Craft.t` with a `{title}` token calls `_parseToken`, reaches `case 'none': return arg` (`Craft.js:193-194`), and returns the title verbatim. The result is handed to jQuery's `$()` and parsed as HTML. The server-side template correctly encodes the entry title into `data-title`, but the browser decodes that attribute before jQuery returns it from `.data('title')`. At that point the value is attacker-controlled HTML, and it is inserted into the `aria-label` attribute without `Craft.escapeHtml()`. ## Steps to reproduce **Plant (attacker - Author account):** ```bash python3 poc.py --url http://target --cp admin \ --user author@example.com --pass secret --section mySection ``` **Trigger (victim - any control panel user with `saveEntries`, e.g. admin):** 1. Open the section index in Table view. 2. Drag the "Drag me" entry and drop it as the first child of the poisoned entry. `alert(document.domain)` fires in the victim’s session. **Impact payload tested in an elevated admin session (249 chars, within the 255-char title limit):** ```html "><img src=x onerror="fetch(Craft.actionUrl+'users/save-user',{method:'POST',body:Craft.csrfTokenName+'='+encodeURIComponent(Craft.csrfTokenValue)+'&userId=1&email=attacker%40evil.com',headers:{'Content-Type':'application/x-www-form-urlencoded'}})"> ``` When triggered during an elevated admin session, the admin’s email is changed to the attacker-controlled address. The attacker can then request a password reset and receive the reset link. ## Impact Stored XSS in the control panel from an Author-level account. The payload runs as the victim control panel user and can use `Craft.csrfTokenName` / `Craft.csrfTokenValue` to send same-origin action requests as that user. In my test environment, triggering the payload in an elevated admin session allowed Author-to-admin account takeover via admin email change and password reset. ## Mitigating factors - Requires an existing control panel account (Author role minimum). - Victim must perform a drag operation, not just visit the page. - The demonstrated email-change takeover requires the victim’s session to be elevated at trigger time. ## Resources https://github.com/craftcms/cms/commit/162321e899cc97517fb6f5a02b5528f549d0c6cc

CVE-2026-55793 Affected Craft CMS Versions

1 component(s)
Fixed Versions:
5.9.53
Product / Package Ecosystem Affected Versions Fixed Version
craftcms/cms
Packagist
≥ 5.0.0-RC1
< 5.9.53
5.9.53

References

Frequently Asked Questions

What is CVE-2026-55793?
CVE-2026-55793 is a security vulnerability in Craft CMS involving Stored XSS via Structure entry title in table view under CWE-79: Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting').
Which Craft CMS versions are affected?
Affected versions include releases prior to 5.9.53 across affected packages: craftcms/cms.
What version fixes CVE-2026-55793?
Upgrading to 5.9.53 (or the latest patched release for your respective branch) resolves the vulnerability.
Is craftcms/cms affected?
Yes, craftcms/cms is affected in releases prior to 5.9.53. Upgrade your dependency to 5.9.53 or later.
What CWE is associated with CVE-2026-55793?
CVE-2026-55793 is classified under CWE-79: Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting') (The product does not neutralize or incorrectly neutralizes user-controllable input before it is placed in output that is used as a web page that is served to other users.).
How can I check Packagist dependencies for CVE-2026-55793?
Run the dependency tree command: 'inspect dependencies for craftcms/cms'. This command lists direct and transitive dependencies in your build configuration so you can verify if a vulnerable version is resolved.
Machine-readable OSV JSON OSV v1.5 Schema
Normalized schema output
{
  "schema_version" : "1.4.0",
  "id" : "GHSA-xrqc-p465-2xvg",
  "modified" : "2026-07-06T21:35:02Z",
  "published" : "2026-07-06T21:35:02Z",
  "aliases" : [ "CVE-2026-55793" ],
  "summary" : "Craft CMS: Stored XSS via Structure entry title in table view",
  "details" : "# Stored XSS via Structure entry title in table view\n\n## Summary\n\nAn Author-level control panel user can store a 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.\n\nThe 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.\n\nExecution was verified with `alert(document.domain)`. Impact was verified against an elevated admin session by changing the admin user’s email through `users/save-user`, then using the password-reset flow to take over the account.\n\n## Preconditions\n\n- Attacker has `createEntries` + `saveEntries` on a Structure-type section (Author-style CP account; no admin permission required).\n- Victim has `saveEntries` on the same section. Craft only renders drag handles when `structureEditable` is true, which requires this permission, so any admin qualifies.\n- Section must be type Structure. Channel and Single sections are unaffected.\n- Poisoned entry must have no children at the time of the drag (fires on the 0-to-1 descendant transition).\n- For the email-change account-takeover path, the victim must currently have an elevated session. The stored XSS itself does not require elevation.\n\n## Root cause\n\n`ElementTableSorter.js` lines 643-652:\n\n```js\nconst ancestorTitle = this._updateAncestors._$ancestor.data('title');\n$(\n  '<button … aria-label=\"' +\n    Craft.t('app', 'Show {title} children', {title: ancestorTitle}) +\n  '\"></button>'\n).insertAfter(…);\n```\n\n`Craft.t` with a `{title}` token calls `_parseToken`, reaches `case 'none': return arg` (`Craft.js:193-194`), and returns the title verbatim. The result is handed to jQuery's `$()` and parsed as HTML.\n\nThe server-side template correctly encodes the entry title into `data-title`, but the browser decodes that attribute before jQuery returns it from `.data('title')`. At that point the value is attacker-controlled HTML, and it is inserted into the `aria-label` attribute without `Craft.escapeHtml()`.\n\n## Steps to reproduce\n\n**Plant (attacker - Author account):**\n\n```bash\npython3 poc.py --url http://target --cp admin \\\n  --user author@example.com --pass secret --section mySection\n```\n\n**Trigger (victim - any control panel user with `saveEntries`, e.g. admin):**\n\n1. Open the section index in Table view.\n2. Drag the \"Drag me\" entry and drop it as the first child of the poisoned entry.\n\n`alert(document.domain)` fires in the victim’s session.\n\n**Impact payload tested in an elevated admin session (249 chars, within the 255-char title limit):**\n\n```html\n\"><img src=x onerror=\"fetch(Craft.actionUrl+'users/save-user',{method:'POST',body:Craft.csrfTokenName+'='+encodeURIComponent(Craft.csrfTokenValue)+'&userId=1&email=attacker%40evil.com',headers:{'Content-Type':'application/x-www-form-urlencoded'}})\">\n```\n\nWhen triggered during an elevated admin session, the admin’s email is changed to the attacker-controlled address. The attacker can then request a password reset and receive the reset link.\n\n## Impact\n\nStored XSS in the control panel from an Author-level account. The payload runs as the victim control panel user and can use `Craft.csrfTokenName` / `Craft.csrfTokenValue` to send same-origin action requests as that user.\n\nIn my test environment, triggering the payload in an elevated admin session allowed Author-to-admin account takeover via admin email change and password reset.\n\n## Mitigating factors\n\n- Requires an existing control panel account (Author role minimum).\n- Victim must perform a drag operation, not just visit the page.\n- The demonstrated email-change takeover requires the victim’s session to be elevated at trigger time.\n\n## Resources\n\nhttps://github.com/craftcms/cms/commit/162321e899cc97517fb6f5a02b5528f549d0c6cc",
  "severity" : [ {
    "type" : "CVSS_V4",
    "score" : "CVSS:4.0/AV:N/AC:L/AT:P/PR:L/UI:P/VC:N/VI:H/VA:N/SC:N/SI:N/SA:N"
  } ],
  "affected" : [ {
    "package" : {
      "ecosystem" : "Packagist",
      "name" : "craftcms/cms"
    },
    "ranges" : [ {
      "type" : "ECOSYSTEM",
      "events" : [ {
        "introduced" : "5.0.0-RC1"
      }, {
        "fixed" : "5.9.53"
      } ]
    } ],
    "database_specific" : {
      "last_known_affected_version_range" : "< 5.9.22"
    }
  } ],
  "references" : [ {
    "type" : "WEB",
    "url" : "https://github.com/craftcms/cms/security/advisories/GHSA-xrqc-p465-2xvg"
  }, {
    "type" : "ADVISORY",
    "url" : "https://nvd.nist.gov/vuln/detail/CVE-2026-55793"
  }, {
    "type" : "WEB",
    "url" : "https://github.com/craftcms/cms/commit/162321e899cc97517fb6f5a02b5528f549d0c6cc"
  }, {
    "type" : "PACKAGE",
    "url" : "https://github.com/craftcms/cms"
  } ],
  "database_specific" : {
    "cwe_ids" : [ "CWE-79" ],
    "severity" : "MODERATE",
    "github_reviewed" : true,
    "github_reviewed_at" : "2026-07-06T21:35:02Z",
    "nvd_published_at" : "2026-07-01T22:16:50Z"
  }
}