{"id":"CVE-2026-31859","aliases":["CLSA-2026-1784682948","EUVD-2026-11261","GHSA-44px-qjjc-xrhq","GHSA-6j87-m5qx-9fqp","GHSA-fvwq-45qv-xvhv"],"status":"published","title":"Craft has Reflective XSS via incomplete return URL sanitization","description":"Craft is a content management system (CMS). The fix for CVE-2025-35939 in craftcms/cms introduced a strip_tags() call in src/web/User.php to sanitize return URLs before they are stored in the session. However, strip_tags() only removes HTML tags (angle brackets) -- it does not inspect or filter URL schemes. Payloads like javascript:alert(document.cookie) contain no HTML tags and pass through strip_tags() completely unmodified, enabling reflected XSS when the return URL is rendered in an href attribute. This vulnerability is fixed in  5.9.7 and 4.17.3.","severity":{"version":"4.0","score":6.9,"vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:N/SC:L/SI:L/SA:N","level":"medium","assessor":"cna","from":"cve"},"epss":{"day":"2026-11-12","score":0.00216,"percentile":0.12104,"model":"v2026.06.15"},"facts":{"in_kev":false,"known_exploited":false,"public_exploit":false,"fix_available":true},"cwes":["CWE-116","CWE-79"],"published":"2026-03-11T00:26:13Z","modified":"2026-10-28T02:30:49Z","fixes":[{"ecosystem":"Packagist","package":"craftcms/cms","fixed_in":["4.17.3","5.9.7"],"sources":["ghsa","osv"]}],"affected":[{"source":"ghsa","ecosystem":"Packagist","package":"craftcms/cms","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"4.15.3"},{"last_affected":"4.17.2"},{"fixed":"4.17.3"}]}]},{"source":"ghsa","ecosystem":"Packagist","package":"craftcms/cms","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"5.7.5"},{"last_affected":"5.9.6"},{"fixed":"5.9.7"}]}]},{"source":"osv","ecosystem":"TuxCare:Packagist","package":"craftcms/cms","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"3.9.15"},{"fixed":"3.9.15-p7+tuxcare"}]}],"versions":["3.9.15","3.9.15-p1+tuxcare","3.9.15-p2+tuxcare","3.9.15-p3+tuxcare","3.9.15-p4+tuxcare","3.9.15-p5+tuxcare","3.9.15-p6+tuxcare"],"bundled":12},{"source":"osv","ecosystem":"Packagist","package":"craftcms/cms","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"4.15.3"},{"fixed":"4.17.3"}]}],"versions":["4.15.3","4.15.4","4.15.5","4.15.6","4.15.6.1","4.15.6.2","4.15.7","4.16.0","4.16.1","4.16.10","4.16.11","4.16.12","4.16.13","4.16.14","4.16.15","4.16.16","4.16.17","4.16.18","4.16.19","4.16.2","4.16.3","4.16.4","4.16.5","4.16.6","4.16.6.1","4.16.7","4.16.8","4.16.9","4.16.9.1","4.17.0","4.17.0-beta.1","4.17.0-beta.2","4.17.1","4.17.2"]},{"source":"osv","ecosystem":"Packagist","package":"craftcms/cms","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"5.7.5"},{"fixed":"5.9.7"}]}],"versions":["5.7.10","5.7.11","5.7.5","5.7.6","5.7.7","5.7.8","5.7.8.1","5.7.8.2","5.7.9","5.8.0","5.8.1","5.8.10","5.8.11","5.8.12","5.8.13","5.8.13.1","5.8.13.2","5.8.14","5.8.15","5.8.16","5.8.17","5.8.18","5.8.19","5.8.2","5.8.20","5.8.21","5.8.22","5.8.23","5.8.3","5.8.4","5.8.5","5.8.6","5.8.7","5.8.8","5.8.9","5.9.0","5.9.0-beta.1","5.9.0-beta.2","5.9.1","5.9.2","5.9.3","5.9.4","5.9.5","5.9.6"]}],"products":[{"vendor":"craftcms","product":"cms"},{"vendor":"craftcms","product":"craft_cms"},{"vendor":"statamic","product":"cms"}],"references":[{"url":"https://github.com/craftcms/cms/security/advisories/GHSA-fvwq-45qv-xvhv","tags":["x_refsource_CONFIRM","Patch","Vendor Advisory","advisory","web"]},{"url":"https://github.com/craftcms/cms/commit/cc9921c14897ee2b592a431c2356af8a04ce4cfe","tags":["web"]},{"url":"https://nvd.nist.gov/vuln/detail/CVE-2026-31859","tags":["advisory"]},{"url":"https://github.com/advisories/GHSA-fvwq-45qv-xvhv"},{"url":"https://github.com/CVEProject/cvelistV5/tree/main/cves/2026/31xxx/CVE-2026-31859.json","tags":["advisory"]},{"url":"https://github.com/craftcms/cms","tags":["package"]}],"sources":{"cve":[{"id":"CVE-2026-31859","title":"Craft has Reflective XSS via incomplete return URL sanitization","description":"Craft is a content management system (CMS). The fix for CVE-2025-35939 in craftcms/cms introduced a strip_tags() call in src/web/User.php to sanitize return URLs before they are stored in the session. However, strip_tags() only removes HTML tags (angle brackets) -- it does not inspect or filter URL schemes. Payloads like javascript:alert(document.cookie) contain no HTML tags and pass through strip_tags() completely unmodified, enabling reflected XSS when the return URL is rendered in an href attribute. This vulnerability is fixed in  5.9.7 and 4.17.3.","status":"published","published":"2026-03-11T17:37:19.065Z","modified":"2026-03-12T14:00:23.631Z","cvss":[{"version":"4.0","score":6.9,"vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:N/SC:L/SI:L/SA:N","level":"medium","assessor":"cna"}],"cwes":["CWE-79","CWE-116"],"updated_at":"2026-11-02T14:19:50.92611Z"}],"euvd":[{"id":"EUVD-2026-11261","description":"Craft is a content management system (CMS). The fix for CVE-2025-35939 in craftcms/cms introduced a strip_tags() call in src/web/User.php to sanitize return URLs before they are stored in the session. However, strip_tags() only removes HTML tags (angle brackets) -- it does not inspect or filter URL schemes. Payloads like javascript:alert(document.cookie) contain no HTML tags and pass through strip_tags() completely unmodified, enabling reflected XSS when the return URL is rendered in an href attribute. This vulnerability is fixed in  5.9.7 and 4.17.3.","status":"published","published":"2026-03-11T17:37:19Z","modified":"2026-03-12T14:00:23Z","cvss":[{"version":"4.0","score":6.9,"vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:N/SC:L/SI:L/SA:N","level":"medium"}],"updated_at":"2026-11-02T19:25:36.272941Z"}],"ghsa":[{"id":"GHSA-fvwq-45qv-xvhv","title":"CraftCMS vulnerable to reflective XSS via incomplete return URL sanitization","description":"### Summary\n\nThe fix for CVE-2025-35939 in `craftcms/cms` introduced a `strip_tags()` call in `src/web/User.php` to sanitize return URLs before they are stored in the session. However, `strip_tags()` only removes HTML tags (angle brackets) -- it does not inspect or filter URL schemes. Payloads like `javascript:alert(document.cookie)` contain no HTML tags and pass through `strip_tags()` completely unmodified, enabling reflected XSS when the return URL is rendered in an `href` attribute.\n\n### Details\nThe patched code in is:\n\n```php\npublic function setReturnUrl($url): void\n{\n    parent::setReturnUrl(strip_tags($url));\n}\n```\n\n`strip_tags()` removes HTML tags (e.g., `\u003cscript\u003e`, `\u003cimg\u003e`) from a string, but it is **not** a URL sanitizer. When the sanitized return URL is subsequently rendered in an `href` attribute context (e.g., `\u003ca href=\"{{ returnUrl }}\"\u003e`), the following dangerous payloads survive `strip_tags()` completely unmodified:\n\n1. **`javascript:` protocol URLs** -- `javascript:alert(document.cookie)` contains no HTML tags, so `strip_tags()` returns it verbatim. When placed in an `href`, clicking the link executes the JavaScript.\n\n2. **`data:` URIs** -- `data:text/html;base64,PHNjcmlwdD5hbGVydCgxKTwvc2NyaXB0Pg==` uses Base64 encoding and contains no tags at all, bypassing `strip_tags()` entirely.\n\n3. **Protocol-relative URLs** -- `//evil.com/steal` contains no tags and is passed through unchanged. When rendered as an `href`, the browser resolves it relative to the current page’s protocol, redirecting the user to an attacker-controlled domain.\n\nThe core issue is that `strip_tags()` operates on HTML syntax (angle brackets) while the threat model here requires URL scheme validation. These are fundamentally different security concerns.\n\n### Impact\n\n**Reflected XSS via crafted return URL.** An attacker constructs a malicious link such as `https://target.example.com/craft/?returnUrl=javascript:alert(document.cookie)` and sends it to a victim. The attack flow is:\n\n1. Victim clicks the link, visiting the Craft CMS site.\n2. The application calls `setReturnUrl()` with the attacker-controlled value.\n3. `strip_tags()` processes the URL but finds no HTML tags -- it passes through unchanged.\n4. The URL is stored in the session and later rendered in an `href` attribute (e.g., a \"Return\" or \"Continue\" link).\n5. When the victim clicks that link, `javascript:alert(document.cookie)` executes in the context of the Craft CMS origin.\n\nThis enables:\n- **Session hijacking** via cookie theft (`document.cookie`)\n- **Data exfiltration** via `fetch()` to an attacker-controlled server\n- **Phishing** by redirecting to a lookalike domain (protocol-relative URL)\n- **CSRF** by performing actions on behalf of the authenticated user","status":"published","published":"2026-03-11T00:26:13Z","modified":"2026-05-25T06:15:36Z","cvss":[{"version":"4.0","score":6.9,"vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:N/SC:L/SI:L/SA:N","level":"medium"}],"cwes":["CWE-79","CWE-116"],"updated_at":"2026-11-02T14:40:13.628377Z"}],"nvd":[{"id":"CVE-2026-31859","description":"Craft is a content management system (CMS). The fix for CVE-2025-35939 in craftcms/cms introduced a strip_tags() call in src/web/User.php to sanitize return URLs before they are stored in the session. However, strip_tags() only removes HTML tags (angle brackets) -- it does not inspect or filter URL schemes. Payloads like javascript:alert(document.cookie) contain no HTML tags and pass through strip_tags() completely unmodified, enabling reflected XSS when the return URL is rendered in an href attribute. This vulnerability is fixed in  5.9.7 and 4.17.3.","status":"published","published":"2026-03-11T18:16:24.71Z","modified":"2026-06-17T10:34:38.86Z","cvss":[{"version":"4.0","score":6.9,"vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:N/SC:L/SI:L/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X","level":"medium","assessor":"secondary"},{"version":"3.1","score":6.1,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N","level":"medium","assessor":"primary"}],"cwes":["CWE-79","CWE-116"],"updated_at":"2026-11-02T14:12:08.98071Z"}],"osv":[{"id":"CVE-2026-31859","title":"Craft has Reflective XSS via incomplete return URL sanitization","description":"Craft is a content management system (CMS). The fix for CVE-2025-35939 in craftcms/cms introduced a strip_tags() call in src/web/User.php to sanitize return URLs before they are stored in the session. However, strip_tags() only removes HTML tags (angle brackets) -- it does not inspect or filter URL schemes. Payloads like javascript:alert(document.cookie) contain no HTML tags and pass through strip_tags() completely unmodified, enabling reflected XSS when the return URL is rendered in an href attribute. This vulnerability is fixed in  5.9.7 and 4.17.3.","status":"published","published":"2026-03-11T17:37:19.065Z","modified":"2026-10-28T02:30:49.346656957Z","cvss":[{"version":"4.0","score":6.9,"vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:N/SC:L/SI:L/SA:N","level":"medium"}],"cwes":["CWE-116","CWE-79"],"updated_at":"2026-11-02T15:45:01.218144Z"},{"id":"GHSA-fvwq-45qv-xvhv","title":"CraftCMS vulnerable to reflective XSS via incomplete return URL sanitization","description":"### Summary\n\nThe fix for CVE-2025-35939 in `craftcms/cms` introduced a `strip_tags()` call in `src/web/User.php` to sanitize return URLs before they are stored in the session. However, `strip_tags()` only removes HTML tags (angle brackets) -- it does not inspect or filter URL schemes. Payloads like `javascript:alert(document.cookie)` contain no HTML tags and pass through `strip_tags()` completely unmodified, enabling reflected XSS when the return URL is rendered in an `href` attribute.\n\n### Details\nThe patched code in is:\n\n```php\npublic function setReturnUrl($url): void\n{\n    parent::setReturnUrl(strip_tags($url));\n}\n```\n\n`strip_tags()` removes HTML tags (e.g., `\u003cscript\u003e`, `\u003cimg\u003e`) from a string, but it is **not** a URL sanitizer. When the sanitized return URL is subsequently rendered in an `href` attribute context (e.g., `\u003ca href=\"{{ returnUrl }}\"\u003e`), the following dangerous payloads survive `strip_tags()` completely unmodified:\n\n1. **`javascript:` protocol URLs** -- `javascript:alert(document.cookie)` contains no HTML tags, so `strip_tags()` returns it verbatim. When placed in an `href`, clicking the link executes the JavaScript.\n\n2. **`data:` URIs** -- `data:text/html;base64,PHNjcmlwdD5hbGVydCgxKTwvc2NyaXB0Pg==` uses Base64 encoding and contains no tags at all, bypassing `strip_tags()` entirely.\n\n3. **Protocol-relative URLs** -- `//evil.com/steal` contains no tags and is passed through unchanged. When rendered as an `href`, the browser resolves it relative to the current page’s protocol, redirecting the user to an attacker-controlled domain.\n\nThe core issue is that `strip_tags()` operates on HTML syntax (angle brackets) while the threat model here requires URL scheme validation. These are fundamentally different security concerns.\n\n### Impact\n\n**Reflected XSS via crafted return URL.** An attacker constructs a malicious link such as `https://target.example.com/craft/?returnUrl=javascript:alert(document.cookie)` and sends it to a victim. The attack flow is:\n\n1. Victim clicks the link, visiting the Craft CMS site.\n2. The application calls `setReturnUrl()` with the attacker-controlled value.\n3. `strip_tags()` processes the URL but finds no HTML tags -- it passes through unchanged.\n4. The URL is stored in the session and later rendered in an `href` attribute (e.g., a \"Return\" or \"Continue\" link).\n5. When the victim clicks that link, `javascript:alert(document.cookie)` executes in the context of the Craft CMS origin.\n\nThis enables:\n- **Session hijacking** via cookie theft (`document.cookie`)\n- **Data exfiltration** via `fetch()` to an attacker-controlled server\n- **Phishing** by redirecting to a lookalike domain (protocol-relative URL)\n- **CSRF** by performing actions on behalf of the authenticated user","status":"published","published":"2026-03-11T00:26:13Z","modified":"2026-03-13T10:56:24.729727Z","cvss":[{"version":"4.0","score":6.9,"vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:N/SC:L/SI:L/SA:N","level":"medium"}],"cwes":["CWE-116","CWE-79"],"updated_at":"2026-11-02T15:46:46.760785Z"},{"id":"CLSA-2026-1784682948","title":"TuxCare security update for craftcms/cms (14 CVEs)","description":"TuxCare rebuilt craftcms/cms to address 14 CVEs. Fixed in 3.9.15-p7+tuxcare.","status":"published","published":"2026-08-04T14:52:46Z","modified":"2026-10-15T09:47:59.497016132Z","updated_at":"2026-11-02T15:43:46.482572Z"}]}}
