ソースを参照

Validate related object is dictionary

Elements of the "related objects list" are passed to the
`prep_related_object_data` function before any validation takes place,
with the potential of failing with a hard error. Similar to the "related
objects not list" case explicitly validate the elements general type,
and raise a normal validation error if it isn't a dictionary.

The word "dictionary" is used here, since it is python terminology, and
is close enough to yaml's "mapping". While json calls them "objects",
their key-value syntax should make it obvious what "dictionary" means
here.
Marko Hauptvogel 3 ヶ月 前
コミット
1245a9f99d
2 ファイル変更45 行追加0 行削除
  1. 37 0
      netbox/dcim/tests/test_views.py
  2. 8 0
      netbox/netbox/views/generic/bulk_views.py

+ 37 - 0
netbox/dcim/tests/test_views.py

@@ -1074,6 +1074,43 @@ console-ports: {value}
                 self.assertHttpStatus(response, 200)
                 self.assertContains(response, "Record 1 console-ports: Must be a list.")
 
+    @override_settings(EXEMPT_VIEW_PERMISSIONS=['*'])
+    def test_import_nodict(self):
+        # Add all required permissions to the test user
+        self.add_permissions(
+            'dcim.view_devicetype',
+            'dcim.add_devicetype',
+            'dcim.add_consoleporttemplate',
+            'dcim.add_consoleserverporttemplate',
+            'dcim.add_powerporttemplate',
+            'dcim.add_poweroutlettemplate',
+            'dcim.add_interfacetemplate',
+            'dcim.add_frontporttemplate',
+            'dcim.add_rearporttemplate',
+            'dcim.add_modulebaytemplate',
+            'dcim.add_devicebaytemplate',
+            'dcim.add_inventoryitemtemplate',
+        )
+
+        for value in ('', 'null', '3', '"My console port"', '["My other console port"]'):
+            with self.subTest(value=value):
+                import_data = f'''
+manufacturer: Manufacturer 1
+model: TEST-4000
+slug: test-4000
+u_height: 1
+console-ports:
+  - {value}
+'''
+                form_data = {
+                    'data': import_data,
+                    'format': 'yaml'
+                }
+
+                response = self.client.post(reverse('dcim:devicetype_bulk_import'), data=form_data, follow=True)
+                self.assertHttpStatus(response, 200)
+                self.assertContains(response, "Record 1 console-ports[1]: Must be a dictionary.")
+
     def test_export_objects(self):
         url = reverse('dcim:devicetype_list')
         self.add_permissions('dcim.view_devicetype')

+ 8 - 0
netbox/netbox/views/generic/bulk_views.py

@@ -392,6 +392,14 @@ class BulkImportView(GetReturnURLMixin, BaseMultiObjectView):
 
             related_obj_pks = []
             for i, rel_obj_data in enumerate(related_objects, start=1):
+                if not isinstance(rel_obj_data, dict):
+                    raise ValidationError(
+                        self._compile_form_errors(
+                            {f'{field_name}[{i}]': [_("Must be a dictionary.")]},
+                            index=parent_idx,
+                        )
+                    )
+
                 rel_obj_data = self.prep_related_object_data(obj, rel_obj_data)
                 f = related_object_form(rel_obj_data)