GHSA-JP3Q-WWP3-PWV9
Vulnerability from github – Published: 2026-01-22 21:41 – Updated: 2026-01-22 21:42Summary
An authenticated, low-privilege user (able to create/edit forms) can inject arbitrary HTML/JS into the Craft Control Panel (CP) builder and integrations views. User-controlled form labels and integration metadata are rendered with dangerouslySetInnerHTML without sanitization, leading to stored XSS that executes when any admin views the builder/integration screens.
Affected Product - Ecosystem: Packagist (Craft CMS plugin) - Package: solspace/craft-freeform - Version: <= 5.14.6 (latest observed). Likely all 5.x until patched.
Details
- Root cause: Multiple user-controlled strings (field labels, section labels, integration icons, short names, WYSIWYG previews) are injected into React components using dangerouslySetInnerHTML without sanitization.
- Evidence: dangerouslySetInnerHTML on user-controlled properties in bundled CP JS at packages/plugin/src/Resources/js/client/client.js.
PoCs
- Label-based XSS:
1. In Craft CP, create/edit a Freeform field and set its label to <img src=x onerror="alert('xss-label')">.
2. Open the form builder view containing the field.
3. Alert executes (stored XSS).
- Integration icon SVG:
1. Set an integration "icon SVG" to <svg><script>alert('xss-icon')</script></svg>.
2. Open the integrations CP view.
3. Script executes.
Impact Arbitrary JS in admin CP; session/CSRF token theft; potential full admin takeover via DOM-driven actions.
Remediation
- Sanitize/HTML-encode all user-controlled strings before passing to dangerouslySetInnerHTML, or avoid it for labels/titles/icons.
- Server-side: strip/escape disallowed tags on save for fields, integration metadata, WYSIWYG content.
- Add regression tests with <img onerror> payloads to ensure no execution in builder/integration views.
Workarounds - Restrict form-edit permissions to trusted admins only until patched. - Consider CSP that disallows inline scripts (defense-in-depth only).
Credits - Discovered by https://www.linkedin.com/in/praveenkavinda/ | Prav33N-Sec.
Disclosure / CVE Request - Request CVE for this confirmed stored XSS.
{
"affected": [
{
"database_specific": {
"last_known_affected_version_range": "\u003c= 5.14.6"
},
"package": {
"ecosystem": "Packagist",
"name": "solspace/craft-freeform"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "5.14.7"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [],
"database_specific": {
"cwe_ids": [
"CWE-79"
],
"github_reviewed": true,
"github_reviewed_at": "2026-01-22T21:41:14Z",
"nvd_published_at": null,
"severity": "LOW"
},
"details": "**Summary**\nAn authenticated, low-privilege user (able to create/edit forms) can inject arbitrary HTML/JS into the Craft Control Panel (CP) builder and integrations views. User-controlled form labels and integration metadata are rendered with `dangerouslySetInnerHTML` without sanitization, leading to stored XSS that executes when any admin views the builder/integration screens.\n\n**Affected Product**\n- Ecosystem: Packagist (Craft CMS plugin)\n- Package: solspace/craft-freeform\n- Version: \u003c= 5.14.6 (latest observed). Likely all 5.x until patched.\n\n**Details**\n- Root cause: Multiple user-controlled strings (field labels, section labels, integration icons, short names, WYSIWYG previews) are injected into React components using `dangerouslySetInnerHTML` without sanitization.\n- Evidence: `dangerouslySetInnerHTML` on user-controlled properties in bundled CP JS at [packages/plugin/src/Resources/js/client/client.js](packages/plugin/src/Resources/js/client/client.js#L1).\n\n**PoCs**\n- Label-based XSS:\n 1. In Craft CP, create/edit a Freeform field and set its label to `\u003cimg src=x onerror=\"alert(\u0027xss-label\u0027)\"\u003e`.\n 2. Open the form builder view containing the field.\n 3. Alert executes (stored XSS).\n- Integration icon SVG:\n 1. Set an integration \"icon SVG\" to `\u003csvg\u003e\u003cscript\u003ealert(\u0027xss-icon\u0027)\u003c/script\u003e\u003c/svg\u003e`.\n 2. Open the integrations CP view.\n 3. Script executes.\n\n**Impact**\nArbitrary JS in admin CP; session/CSRF token theft; potential full admin takeover via DOM-driven actions.\n\n**Remediation**\n- Sanitize/HTML-encode all user-controlled strings before passing to `dangerouslySetInnerHTML`, or avoid it for labels/titles/icons.\n- Server-side: strip/escape disallowed tags on save for fields, integration metadata, WYSIWYG content.\n- Add regression tests with `\u003cimg onerror\u003e` payloads to ensure no execution in builder/integration views.\n\n**Workarounds**\n- Restrict form-edit permissions to trusted admins only until patched.\n- Consider CSP that disallows inline scripts (defense-in-depth only).\n\n**Credits**\n- Discovered by https://www.linkedin.com/in/praveenkavinda/ | Prav33N-Sec.\n\n**Disclosure / CVE Request**\n- Request CVE for this confirmed stored XSS.",
"id": "GHSA-jp3q-wwp3-pwv9",
"modified": "2026-01-22T21:42:16Z",
"published": "2026-01-22T21:41:14Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/solspace/craft-freeform/security/advisories/GHSA-jp3q-wwp3-pwv9"
},
{
"type": "PACKAGE",
"url": "https://github.com/solspace/craft-freeform"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:P/VC:N/VI:N/VA:N/SC:L/SI:L/SA:N/E:P",
"type": "CVSS_V4"
}
],
"summary": "Freeform Craft Plugin CP UI (builder/integrations) has Stored Cross-Site Scripting (XSS) issue"
}
Sightings
| Author | Source | Type | Date |
|---|
Nomenclature
- Seen: The vulnerability was mentioned, discussed, or observed by the user.
- Confirmed: The vulnerability has been validated from an analyst's perspective.
- Published Proof of Concept: A public proof of concept is available for this vulnerability.
- Exploited: The vulnerability was observed as exploited by the user who reported the sighting.
- Patched: The vulnerability was observed as successfully patched by the user who reported the sighting.
- Not exploited: The vulnerability was not observed as exploited by the user who reported the sighting.
- Not confirmed: The user expressed doubt about the validity of the vulnerability.
- Not patched: The vulnerability was not observed as successfully patched by the user who reported the sighting.