{"id":"CVE-2026-75526","aliases":["EUVD-2026-63571","GHSA-hvq6-2r72-p2x7","PYSEC-2026-3825"],"status":"published","title":"django CMS: Stored XSS in edit-mode plugin exception rendering","description":"django CMS is an easy-to-use and developer-friendly enterprise content management system powered by Django. From 5.0.8 until 5.0.9, ContentRenderer.render_placeholder in cms/plugin_rendering.py can pass stored, attacker-controlled values to ContentRenderer.render_exception when plugin rendering fails in edit mode. Values from get_short_description(), the exception message, the placeholder, or placeholder.source are interpolated into a cms-rendering-exception heading and later returned through mark_safe. Because the heading is not escaped, stored HTML executes in an editor’s browser, and settings.DEBUG does not prevent the custom heading from rendering. The fix uses format_html to escape the message before safe placeholder output is returned. This issue is fixed in versions 5.0.9.","severity":{"version":"3.1","score":4.4,"vector":"CVSS:3.1/AV:N/AC:H/PR:L/UI:R/S:C/C:L/I:L/A:N","level":"medium","assessor":"cna","from":"cve"},"epss":{"day":"2026-11-11","score":0.00261,"percentile":0.18029,"model":"v2026.06.15"},"facts":{"in_kev":false,"known_exploited":false,"public_exploit":false,"fix_available":true},"cwes":["CWE-79"],"published":"2026-08-20T18:11:10Z","modified":"2026-10-28T02:30:50Z","fixes":[{"ecosystem":"PyPI","package":"django-cms","fixed_in":["5.0.9"],"sources":["ghsa","osv"]}],"affected":[{"source":"ghsa","ecosystem":"PyPI","package":"django-cms","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"5.0.8"},{"fixed":"5.0.9"}]}]},{"source":"osv","ecosystem":"PyPI","package":"django-cms","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"5.0.8"},{"fixed":"5.0.9"}]}],"versions":["5.0.8"]},{"source":"osv","ecosystem":"PyPI","package":"django-cms","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"5.0.8"},{"fixed":"5.0.9"}]}],"versions":["5.0.8"]}],"products":[{"vendor":"django cms association","product":"django-cms"},{"vendor":"django-cms","product":"django-cms"}],"references":[{"url":"https://github.com/django-cms/django-cms/security/advisories/GHSA-hvq6-2r72-p2x7","tags":["x_refsource_CONFIRM","advisory","web"]},{"url":"https://github.com/django-cms/django-cms/pull/8711","tags":["x_refsource_MISC","fix","web"]},{"url":"https://github.com/django-cms/django-cms/commit/b56a568844ff3702495945f73a31d0868285bf88","tags":["x_refsource_MISC","fix","web"]},{"url":"https://github.com/django-cms/django-cms/releases/tag/5.0.9","tags":["x_refsource_MISC","web"]},{"url":"https://github.com/advisories/GHSA-hvq6-2r72-p2x7","tags":["advisory"]},{"url":"https://github.com/CVEProject/cvelistV5/tree/main/cves/2026/75xxx/CVE-2026-75526.json","tags":["advisory"]},{"url":"https://nvd.nist.gov/vuln/detail/CVE-2026-75526","tags":["advisory"]},{"url":"https://github.com/django-cms/django-cms","tags":["package"]},{"url":"https://pypi.org/project/django-cms","tags":["package"]}],"sources":{"cve":[{"id":"CVE-2026-75526","title":"django CMS: Stored XSS in edit-mode plugin exception rendering","description":"django CMS is an easy-to-use and developer-friendly enterprise content management system powered by Django. From 5.0.8 until 5.0.9, ContentRenderer.render_placeholder in cms/plugin_rendering.py can pass stored, attacker-controlled values to ContentRenderer.render_exception when plugin rendering fails in edit mode. Values from get_short_description(), the exception message, the placeholder, or placeholder.source are interpolated into a cms-rendering-exception heading and later returned through mark_safe. Because the heading is not escaped, stored HTML executes in an editor’s browser, and settings.DEBUG does not prevent the custom heading from rendering. The fix uses format_html to escape the message before safe placeholder output is returned. This issue is fixed in versions 5.0.9.","status":"published","published":"2026-08-20T18:11:10.22Z","modified":"2026-08-20T19:20:52.755Z","cvss":[{"version":"3.1","score":4.4,"vector":"CVSS:3.1/AV:N/AC:H/PR:L/UI:R/S:C/C:L/I:L/A:N","level":"medium","assessor":"cna"}],"cwes":["CWE-79"],"updated_at":"2026-11-02T14:20:07.260654Z"}],"euvd":[{"id":"EUVD-2026-63571","description":"django CMS is an easy-to-use and developer-friendly enterprise content management system powered by Django. From 5.0.8 until 5.0.9, ContentRenderer.render_placeholder in cms/plugin_rendering.py can pass stored, attacker-controlled values to ContentRenderer.render_exception when plugin rendering fails in edit mode. Values from get_short_description(), the exception message, the placeholder, or placeholder.source are interpolated into a cms-rendering-exception heading and later returned through mark_safe. Because the heading is not escaped, stored HTML executes in an editor’s browser, and settings.DEBUG does not prevent the custom heading from rendering. The fix uses format_html to escape the message before safe placeholder output is returned. This issue is fixed in versions 5.0.9.","status":"published","published":"2026-08-20T18:11:10Z","modified":"2026-08-20T19:20:52Z","cvss":[{"version":"3.1","score":4.4,"vector":"CVSS:3.1/AV:N/AC:H/PR:L/UI:R/S:C/C:L/I:L/A:N","level":"medium"}],"updated_at":"2026-11-02T18:01:38.846525Z"}],"ghsa":[{"id":"GHSA-hvq6-2r72-p2x7","title":"django CMS: Stored XSS in edit-mode plugin exception rendering","description":"## Summary\n\nWhen plugin rendering fails in edit mode, django CMS renders a `cms-rendering-exception` block so editors can see that a placeholder could not be rendered. Older code built that block's heading by interpolating the exception message, placeholder/source strings, and the failing plugin's short description directly into an HTML string, then returned the placeholder output as safe markup.\n\nIf an editor could store HTML in data used by a plugin's `get_short_description()` (or in other values interpolated into the exception message), and that plugin later raised during edit-mode rendering, the payload was parsed as HTML in the staff user's browser. This is a stored XSS condition in the CMS editing context.\n\n## Impact\n\nThe vulnerable path is only reached when placeholder rendering catches a plugin rendering exception:\n\n```python\ntry:\n    placeholder_content = \"\".join(plugin_content)\nexcept Exception as e:\n    context[\"exc_info\"] = sys.exc_info()\n    placeholder_content = self.render_exception(\"rendering placeholder\", context, placeholder, editable)\n```\n\n`render_exception()` constructs a `message` from values that can include stored content:\n\n- `value` - the exception message.\n- `placeholder` - the placeholder string representation.\n- `placeholder.source` - the source object string representation, such as page content.\n- `instance.get_short_description()` - plugin-provided summary text, often derived from plugin model fields.\n\nIn the vulnerable implementation, that message was embedded directly into an HTML heading. The final placeholder content was later returned through `mark_safe`, so Django template autoescaping did not protect the heading.\n\n`settings.DEBUG` does not mitigate the issue: it only controls whether Django's traceback HTML is appended. The custom heading is rendered in edit mode regardless of `DEBUG`.\n\n## Patch\n\nEscape the custom exception heading before returning it as safe placeholder markup. The current fixed code uses `format_html`, which escapes `message` before inserting it into the heading:\n\n```python\nheading = format_html('\u003ch2 class=\"cms-rendering-exception-title\"\u003e{}\u003c/h2\u003e', message)\n```\n\nThe traceback HTML from `ExceptionReporter.get_traceback_html()` should remain separate from django CMS's custom heading; Django's traceback escaping does not protect additional HTML assembled by django CMS.\n\n## Workarounds\n\nUntil patched, reduce exposure by ensuring only fully trusted staff can edit plugins whose stored fields are included in `get_short_description()`, and fix or disable plugins that can be made to raise during edit-mode rendering. This is only a partial mitigation because the escaping bug is in the shared exception-rendering path.\n\n## References\n\n- `cms/plugin_rendering.py` - `ContentRenderer.render_placeholder`\n- `cms/plugin_rendering.py` - `ContentRenderer.render_exception`\n- Fixed code: `heading = format_html('\u003ch2 class=\"cms-rendering-exception-title\"\u003e{}\u003c/h2\u003e', message)`\n- Regression tests:\n  `cms.tests.test_plugin_renderers.TestExceptionCatchers.test_exception_in_plugin_render_escapes_user_content_in_edit_mode`,\n  `cms.tests.test_plugin_renderers.TestLegacyRendererExceptionCatcher.test_exception_in_plugin_render_escapes_user_content_in_edit_mode`","status":"published","published":"2026-08-20T18:42:36Z","modified":"2026-08-20T18:42:39Z","cvss":[{"version":"3.1","score":4.4,"vector":"CVSS:3.1/AV:N/AC:H/PR:L/UI:R/S:C/C:L/I:L/A:N","level":"medium"}],"cwes":["CWE-79"],"updated_at":"2026-11-02T14:41:04.755504Z"}],"nvd":[{"id":"CVE-2026-75526","description":"django CMS is an easy-to-use and developer-friendly enterprise content management system powered by Django. From 5.0.8 until 5.0.9, ContentRenderer.render_placeholder in cms/plugin_rendering.py can pass stored, attacker-controlled values to ContentRenderer.render_exception when plugin rendering fails in edit mode. Values from get_short_description(), the exception message, the placeholder, or placeholder.source are interpolated into a cms-rendering-exception heading and later returned through mark_safe. Because the heading is not escaped, stored HTML executes in an editor’s browser, and settings.DEBUG does not prevent the custom heading from rendering. The fix uses format_html to escape the message before safe placeholder output is returned. This issue is fixed in versions 5.0.9.","status":"published","published":"2026-08-20T19:17:03.787Z","modified":"2026-10-26T10:10:00.14Z","cvss":[{"version":"3.1","score":4.4,"vector":"CVSS:3.1/AV:N/AC:H/PR:L/UI:R/S:C/C:L/I:L/A:N","level":"medium","assessor":"secondary"}],"cwes":["CWE-79"],"updated_at":"2026-11-02T14:12:40.302911Z"}],"osv":[{"id":"CVE-2026-75526","title":"django CMS: Stored XSS in edit-mode plugin exception rendering","description":"django CMS is an easy-to-use and developer-friendly enterprise content management system powered by Django. From 5.0.8 until 5.0.9, ContentRenderer.render_placeholder in cms/plugin_rendering.py can pass stored, attacker-controlled values to ContentRenderer.render_exception when plugin rendering fails in edit mode. Values from get_short_description(), the exception message, the placeholder, or placeholder.source are interpolated into a cms-rendering-exception heading and later returned through mark_safe. Because the heading is not escaped, stored HTML executes in an editor’s browser, and settings.DEBUG does not prevent the custom heading from rendering. The fix uses format_html to escape the message before safe placeholder output is returned. This issue is fixed in versions 5.0.9.","status":"published","published":"2026-08-20T18:11:10.22Z","modified":"2026-10-28T02:30:50.361137881Z","cvss":[{"version":"3.1","score":4.4,"vector":"CVSS:3.1/AV:N/AC:H/PR:L/UI:R/S:C/C:L/I:L/A:N","level":"medium"}],"cwes":["CWE-79"],"updated_at":"2026-11-02T15:45:17.102988Z"},{"id":"GHSA-hvq6-2r72-p2x7","title":"django CMS: Stored XSS in edit-mode plugin exception rendering","description":"## Summary\n\nWhen plugin rendering fails in edit mode, django CMS renders a `cms-rendering-exception` block so editors can see that a placeholder could not be rendered. Older code built that block's heading by interpolating the exception message, placeholder/source strings, and the failing plugin's short description directly into an HTML string, then returned the placeholder output as safe markup.\n\nIf an editor could store HTML in data used by a plugin's `get_short_description()` (or in other values interpolated into the exception message), and that plugin later raised during edit-mode rendering, the payload was parsed as HTML in the staff user's browser. This is a stored XSS condition in the CMS editing context.\n\n## Impact\n\nThe vulnerable path is only reached when placeholder rendering catches a plugin rendering exception:\n\n```python\ntry:\n    placeholder_content = \"\".join(plugin_content)\nexcept Exception as e:\n    context[\"exc_info\"] = sys.exc_info()\n    placeholder_content = self.render_exception(\"rendering placeholder\", context, placeholder, editable)\n```\n\n`render_exception()` constructs a `message` from values that can include stored content:\n\n- `value` - the exception message.\n- `placeholder` - the placeholder string representation.\n- `placeholder.source` - the source object string representation, such as page content.\n- `instance.get_short_description()` - plugin-provided summary text, often derived from plugin model fields.\n\nIn the vulnerable implementation, that message was embedded directly into an HTML heading. The final placeholder content was later returned through `mark_safe`, so Django template autoescaping did not protect the heading.\n\n`settings.DEBUG` does not mitigate the issue: it only controls whether Django's traceback HTML is appended. The custom heading is rendered in edit mode regardless of `DEBUG`.\n\n## Patch\n\nEscape the custom exception heading before returning it as safe placeholder markup. The current fixed code uses `format_html`, which escapes `message` before inserting it into the heading:\n\n```python\nheading = format_html('\u003ch2 class=\"cms-rendering-exception-title\"\u003e{}\u003c/h2\u003e', message)\n```\n\nThe traceback HTML from `ExceptionReporter.get_traceback_html()` should remain separate from django CMS's custom heading; Django's traceback escaping does not protect additional HTML assembled by django CMS.\n\n## Workarounds\n\nUntil patched, reduce exposure by ensuring only fully trusted staff can edit plugins whose stored fields are included in `get_short_description()`, and fix or disable plugins that can be made to raise during edit-mode rendering. This is only a partial mitigation because the escaping bug is in the shared exception-rendering path.\n\n## References\n\n- `cms/plugin_rendering.py` - `ContentRenderer.render_placeholder`\n- `cms/plugin_rendering.py` - `ContentRenderer.render_exception`\n- Fixed code: `heading = format_html('\u003ch2 class=\"cms-rendering-exception-title\"\u003e{}\u003c/h2\u003e', message)`\n- Regression tests:\n  `cms.tests.test_plugin_renderers.TestExceptionCatchers.test_exception_in_plugin_render_escapes_user_content_in_edit_mode`,\n  `cms.tests.test_plugin_renderers.TestLegacyRendererExceptionCatcher.test_exception_in_plugin_render_escapes_user_content_in_edit_mode`","status":"published","published":"2026-08-20T18:42:36Z","modified":"2026-09-10T12:26:01.118431606Z","cvss":[{"version":"3.1","score":4.4,"vector":"CVSS:3.1/AV:N/AC:H/PR:L/UI:R/S:C/C:L/I:L/A:N","level":"medium"}],"cwes":["CWE-79"],"updated_at":"2026-11-02T15:46:49.621585Z"},{"id":"PYSEC-2026-3825","title":"django CMS: Stored XSS in edit-mode plugin exception rendering","description":"## Summary\n\nWhen plugin rendering fails in edit mode, django CMS renders a `cms-rendering-exception` block so editors can see that a placeholder could not be rendered. Older code built that block's heading by interpolating the exception message, placeholder/source strings, and the failing plugin's short description directly into an HTML string, then returned the placeholder output as safe markup.\n\nIf an editor could store HTML in data used by a plugin's `get_short_description()` (or in other values interpolated into the exception message), and that plugin later raised during edit-mode rendering, the payload was parsed as HTML in the staff user's browser. This is a stored XSS condition in the CMS editing context.\n\n## Impact\n\nThe vulnerable path is only reached when placeholder rendering catches a plugin rendering exception:\n\n```python\ntry:\n    placeholder_content = \"\".join(plugin_content)\nexcept Exception as e:\n    context[\"exc_info\"] = sys.exc_info()\n    placeholder_content = self.render_exception(\"rendering placeholder\", context, placeholder, editable)\n```\n\n`render_exception()` constructs a `message` from values that can include stored content:\n\n- `value` - the exception message.\n- `placeholder` - the placeholder string representation.\n- `placeholder.source` - the source object string representation, such as page content.\n- `instance.get_short_description()` - plugin-provided summary text, often derived from plugin model fields.\n\nIn the vulnerable implementation, that message was embedded directly into an HTML heading. The final placeholder content was later returned through `mark_safe`, so Django template autoescaping did not protect the heading.\n\n`settings.DEBUG` does not mitigate the issue: it only controls whether Django's traceback HTML is appended. The custom heading is rendered in edit mode regardless of `DEBUG`.\n\n## Patch\n\nEscape the custom exception heading before returning it as safe placeholder markup. The current fixed code uses `format_html`, which escapes `message` before inserting it into the heading:\n\n```python\nheading = format_html('\u003ch2 class=\"cms-rendering-exception-title\"\u003e{}\u003c/h2\u003e', message)\n```\n\nThe traceback HTML from `ExceptionReporter.get_traceback_html()` should remain separate from django CMS's custom heading; Django's traceback escaping does not protect additional HTML assembled by django CMS.\n\n## Workarounds\n\nUntil patched, reduce exposure by ensuring only fully trusted staff can edit plugins whose stored fields are included in `get_short_description()`, and fix or disable plugins that can be made to raise during edit-mode rendering. This is only a partial mitigation because the escaping bug is in the shared exception-rendering path.\n\n## References\n\n- `cms/plugin_rendering.py` - `ContentRenderer.render_placeholder`\n- `cms/plugin_rendering.py` - `ContentRenderer.render_exception`\n- Fixed code: `heading = format_html('\u003ch2 class=\"cms-rendering-exception-title\"\u003e{}\u003c/h2\u003e', message)`\n- Regression tests:\n  `cms.tests.test_plugin_renderers.TestExceptionCatchers.test_exception_in_plugin_render_escapes_user_content_in_edit_mode`,\n  `cms.tests.test_plugin_renderers.TestLegacyRendererExceptionCatcher.test_exception_in_plugin_render_escapes_user_content_in_edit_mode`","status":"published","published":"2026-09-10T09:44:52.636615Z","modified":"2026-09-10T12:15:03.035763796Z","cvss":[{"version":"3.1","score":4.4,"vector":"CVSS:3.1/AV:N/AC:H/PR:L/UI:R/S:C/C:L/I:L/A:N","level":"medium"}],"updated_at":"2026-11-02T15:52:57.255153Z"}]}}
