From ac3ea37dd5bff978c7805cc7229ef43578e088fc Mon Sep 17 00:00:00 2001 From: Fabian Braun Date: Mon, 15 Dec 2025 12:45:51 +0100 Subject: [PATCH 1/4] fix: Validate component names --- .../templatetags/cms_component.py | 4 +++ tests/test_autocomponent.py | 28 +++++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/djangocms_frontend/templatetags/cms_component.py b/djangocms_frontend/templatetags/cms_component.py index 47c63cc4..a10dd545 100644 --- a/djangocms_frontend/templatetags/cms_component.py +++ b/djangocms_frontend/templatetags/cms_component.py @@ -30,6 +30,10 @@ class based on the implate it is part of. The component class is generated if "_cms_components" in context: if len(args) != 1: # pragma: no cover raise ValueError("The cms_component tag requires exactly one positional argument: the component name.") + if not isinstance(args[0], str): + raise ValueError("The component name must be a string.") + if not args[0].isidentifier(): + raise ValueError("The component name must be a valid Python identifier.") context["_cms_components"]["cms_component"].append((args, kwargs)) return "" diff --git a/tests/test_autocomponent.py b/tests/test_autocomponent.py index b9abf8c2..df68b574 100644 --- a/tests/test_autocomponent.py +++ b/tests/test_autocomponent.py @@ -107,6 +107,34 @@ def test_multiple_cms_component_tags_error(self): with self.assertRaises(TemplateSyntaxError): Template(invalid_template) + def test_cms_component_invalid_identifier(self): + # Test that cms_component tag raises ValueError for invalid identifiers + from django.template import Context + from djangocms_frontend.templatetags.cms_component import cms_component + + context = Context({"_cms_components": {"cms_component": []}}) + + # Valid identifier should work + cms_component(context, "valid_name") + self.assertEqual(len(context["_cms_components"]["cms_component"]), 1) + + # Invalid identifiers should raise ValueError + with self.assertRaises(ValueError) as cm: + cms_component(context, "invalid-name") + self.assertIn("valid Python identifier", str(cm.exception)) + + with self.assertRaises(ValueError) as cm: + cms_component(context, "123invalid") + self.assertIn("valid Python identifier", str(cm.exception)) + + with self.assertRaises(ValueError) as cm: + cms_component(context, "invalid name") + self.assertIn("valid Python identifier", str(cm.exception)) + + with self.assertRaises(ValueError) as cm: + cms_component(context, "") + self.assertIn("valid Python identifier", str(cm.exception)) + def test_component_folder_selection(self): from djangocms_frontend.component_pool import find_cms_component_templates From ac28efc009d26a979f0cd4400f7c93d2e2284ab6 Mon Sep 17 00:00:00 2001 From: Fabian Braun Date: Mon, 15 Dec 2025 13:04:51 +0100 Subject: [PATCH 2/4] docs: Update template component name validation --- docs/source/tutorial/template_components.rst | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/docs/source/tutorial/template_components.rst b/docs/source/tutorial/template_components.rst index 792d73fd..c72d00a2 100644 --- a/docs/source/tutorial/template_components.rst +++ b/docs/source/tutorial/template_components.rst @@ -10,9 +10,9 @@ Simplified Component Creation with Templates .. versionadded:: 2.1 -**Template components** are the easiest approach to creating or porting your own custom +**Template components** are the easiest approach to creating or porting your own custom frontend components, allowing you to define custom components **using django templates, -without needing to write any Python code**. +without needing to write any Python code**. Example Hero Template Component @@ -104,6 +104,11 @@ by django CMS to identify the plugin later. The ``name`` parameter is used to di component in the CMS admin interface. Internally the command declares a ``CMSFrontendComponent`` class. All named arguments are added to the component's Meta class. +.. note:: + The component name (the first argument to ``{% cms_component %}``) must be a valid Python identifier. + This means it should start with a letter or underscore, followed by letters, digits, or underscores, + and cannot contain spaces or special characters like hyphens. + Only one ``{% cms_component %}`` tag is allowed per template file. The first part is the declarative part of the template: From 7586f117395b52ca85bbcf8d32148043d227a7cd Mon Sep 17 00:00:00 2001 From: Fabian Braun Date: Mon, 15 Dec 2025 13:14:29 +0100 Subject: [PATCH 3/4] Apply suggestion from @fsbraun --- docs/source/tutorial/template_components.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/tutorial/template_components.rst b/docs/source/tutorial/template_components.rst index c72d00a2..d9472d84 100644 --- a/docs/source/tutorial/template_components.rst +++ b/docs/source/tutorial/template_components.rst @@ -11,7 +11,7 @@ Simplified Component Creation with Templates .. versionadded:: 2.1 **Template components** are the easiest approach to creating or porting your own custom -frontend components, allowing you to define custom components **using django templates, +frontend components, allowing you to define custom components **using Django templates, without needing to write any Python code**. From 57ca56b16bdf8dbf7cdb258646ee9d3cfa09614c Mon Sep 17 00:00:00 2001 From: Fabian Braun Date: Mon, 15 Dec 2025 13:18:38 +0100 Subject: [PATCH 4/4] add another test --- tests/test_autocomponent.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/test_autocomponent.py b/tests/test_autocomponent.py index df68b574..cc5e8d8c 100644 --- a/tests/test_autocomponent.py +++ b/tests/test_autocomponent.py @@ -118,6 +118,11 @@ def test_cms_component_invalid_identifier(self): cms_component(context, "valid_name") self.assertEqual(len(context["_cms_components"]["cms_component"]), 1) + # Non-string identifiers should raise ValueError + with self.assertRaises(ValueError) as cm: + cms_component(context, 123) + self.assertIn("component name must be a string.", str(cm.exception)) + # Invalid identifiers should raise ValueError with self.assertRaises(ValueError) as cm: cms_component(context, "invalid-name")