Ver Fonte

Closes #22652: Disable autoescaping for Config Templates (#22653)

Force autoescape=False in ConfigTemplate.get_environment_params() after
merging user-supplied environment parameters. Config templates produce
plain-text network configurations and scripts, so HTML autoescaping is
not applicable.

Keep the override out of the shared render_jinja2() helper so export
templates can continue to use autoescape=True for HTML output. Add
regression coverage for both behaviors.
bctiemann há 1 semana atrás
pai
commit
ca7caecac5
2 ficheiros alterados com 34 adições e 3 exclusões
  1. 10 0
      netbox/extras/models/configs.py
  2. 24 3
      netbox/extras/tests/test_models.py

+ 10 - 0
netbox/extras/models/configs.py

@@ -316,6 +316,16 @@ class ConfigTemplate(
         self.template_code = self.data_file.data_as_string
     sync_data.alters_data = True
 
+    def get_environment_params(self):
+        """
+        Config templates render plain text (network configs, scripts), not HTML. Force
+        autoescape off so environment_params cannot enable it and create a latent XSS sink
+        if output is ever rendered in an HTML context.
+        """
+        params = super().get_environment_params()
+        params['autoescape'] = False
+        return params
+
     def format_render_error(self, exc):
         """
         Return a formatted error string for a rendering exception. When debug is enabled, the full

+ 24 - 3
netbox/extras/tests/test_models.py

@@ -1310,6 +1310,24 @@ class RenderTemplateMixinRenderTestCase(TestCase):
         self.assertNotEqual(plain.render(ctx), trimmed.render(ctx))
         self.assertEqual(trimmed.render(ctx).strip(), 'VALUE')
 
+    def test_configtemplate_autoescape_always_disabled(self):
+        """
+        ConfigTemplate renders plain text (network configs, scripts); autoescape must stay off
+        even if environment_params explicitly requests it (#22652).
+        """
+        t = ConfigTemplate(name='autoescape', template_code='{{ value }}', environment_params={'autoescape': True})
+        self.assertEqual(t.render({'value': '<script>'}), '<script>')
+
+    def test_exporttemplate_autoescape_is_configurable(self):
+        """
+        Unlike ConfigTemplate, ExportTemplate output may legitimately be HTML, so an explicit
+        autoescape=True in environment_params must be honored rather than forced off.
+        """
+        et = ExportTemplate(
+            name='autoescape', template_code='{{ value }}', environment_params={'autoescape': True}
+        )
+        self.assertEqual(et.render({'value': '<script>'}), '&lt;script&gt;')
+
     def test_environment_params_undefined_path_import(self):
         # Default Undefined renders nothing for a missing variable.
         default = ConfigTemplate(name='default', template_code='{{ missing }}')
@@ -1339,8 +1357,9 @@ class RenderTemplateMixinRenderTestCase(TestCase):
 
     def test_get_environment_params_handles_none(self):
         # The environment_params field may be cleared; ensure the mixin returns a dict (not None).
+        # ConfigTemplate always forces autoescape off (#22652).
         t = ConfigTemplate(name='empty', template_code='ok', environment_params=None)
-        self.assertEqual(t.get_environment_params(), {})
+        self.assertEqual(t.get_environment_params(), {'autoescape': False})
 
     def test_get_environment_params_resolves_path_imports(self):
         t = ConfigTemplate(
@@ -1666,9 +1685,11 @@ class JinjaEnvironmentParamsIntegrationTestCase(TestCase):
         self.assertEqual(template.environment_params['undefined'], 'jinja2.StrictUndefined')
 
     def test_none_environment_params(self):
+        # ConfigTemplate always forces autoescape off (#22652).
         template = self._make_template(None)
-        self.assertEqual(template.get_environment_params(), {})
+        self.assertEqual(template.get_environment_params(), {'autoescape': False})
 
     def test_empty_environment_params(self):
+        # ConfigTemplate always forces autoescape off (#22652).
         template = self._make_template({})
-        self.assertEqual(template.get_environment_params(), {})
+        self.assertEqual(template.get_environment_params(), {'autoescape': False})