Просмотр исходного кода

Fixes #23635: Reject nested values in a custom field's related object filter (#23687)

* Fixes #23635: Reject nested values in a custom field's related object filter

* review fixes
Arthur Hanson 16 часов назад
Родитель
Сommit
e74c090ea6
2 измененных файлов с 47 добавлено и 0 удалено
  1. 10 0
      netbox/extras/models/customfields.py
  2. 37 0
      netbox/extras/tests/test_customfields.py

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

@@ -821,6 +821,16 @@ class CustomField(CloningMixin, ExportTemplatesMixin, OwnerMixin, ChangeLoggedMo
                 raise ValidationError({
                     'related_object_filter': _("Filter must be defined as a dictionary mapping attributes to values.")
                 })
+            # Each value must be a scalar or a list of scalars (rendered as query parameters)
+            for attr, value in self.related_object_filter.items():
+                values = value if isinstance(value, (list, tuple)) else [value]
+                if any(isinstance(v, (dict, list, tuple)) for v in values):
+                    raise ValidationError({
+                        'related_object_filter': _(
+                            "Invalid value for filter attribute '{attr}': values must be a scalar or a list of "
+                            "scalars."
+                        ).format(attr=attr)
+                    })
 
     def serialize(self, value):
         """

+ 37 - 0
netbox/extras/tests/test_customfields.py

@@ -1112,6 +1112,43 @@ class CustomFieldTestCase(TestCase):
                 validation_schema=schema, default={'age': 25}
             ).full_clean()
 
+    def test_related_object_filter_validation(self):
+        object_type = ObjectType.objects.get_for_model(Site)
+
+        valid_filters = (
+            {'status': 'active'},
+            {'status': ['active', 'planned']},
+            {'id': 1, 'is_active': True, 'tenant': None, 'region_id': '$region'},
+            {'vid': [1, 2.5, False, None]},
+        )
+        for related_object_filter in valid_filters:
+            with self.subTest(related_object_filter=related_object_filter):
+                CustomField(
+                    name='test',
+                    type=CustomFieldTypeChoices.TYPE_OBJECT,
+                    related_object_type=object_type,
+                    related_object_filter=related_object_filter,
+                ).full_clean()
+
+        # Nested dicts/lists cannot be rendered as query parameters (#23635)
+        invalid_filters = (
+            {'and': [{'attr': 'name', 'value': 'a'}, {'attr': 'rd', 'value': 'b'}]},
+            {'status': {'value': 'active'}},
+            {'status': [['active']]},
+            {'status': ('active', ['planned'])},
+            {'status': ['active', ('planned',)]},
+        )
+        for related_object_filter in invalid_filters:
+            with self.subTest(related_object_filter=related_object_filter):
+                with self.assertRaises(ValidationError) as cm:
+                    CustomField(
+                        name='test',
+                        type=CustomFieldTypeChoices.TYPE_MULTIOBJECT,
+                        related_object_type=object_type,
+                        related_object_filter=related_object_filter,
+                    ).full_clean()
+                self.assertIn('related_object_filter', cm.exception.message_dict)
+
 
 class CustomFieldManagerTestCase(TestCase):