Skip to content

Commit 78223ce

Browse files
committed
Validate related object field is list
The related object fields are not covered by the form, so don't pass any validation before trying to iterate over them and accessing their elements. Instead of allowing a hard technical error to be raised, explicitly check that it is indeed a list, and raise a normal validation error if not. The error message is chosen to be similar in format and wording to the other existing validation errors. The used word "list" is quite universal, and conveys the wanted meaning in the context of python, json and yaml.
1 parent 8452222 commit 78223ce

File tree

2 files changed

+46
-1
lines changed

2 files changed

+46
-1
lines changed

netbox/dcim/tests/test_views.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1038,6 +1038,42 @@ def test_import_error_numbering(self):
10381038
self.assertHttpStatus(response, 200)
10391039
self.assertContains(response, "Record 2 module-bays[3].name: This field is required.")
10401040

1041+
@override_settings(EXEMPT_VIEW_PERMISSIONS=['*'])
1042+
def test_import_nolist(self):
1043+
# Add all required permissions to the test user
1044+
self.add_permissions(
1045+
'dcim.view_devicetype',
1046+
'dcim.add_devicetype',
1047+
'dcim.add_consoleporttemplate',
1048+
'dcim.add_consoleserverporttemplate',
1049+
'dcim.add_powerporttemplate',
1050+
'dcim.add_poweroutlettemplate',
1051+
'dcim.add_interfacetemplate',
1052+
'dcim.add_frontporttemplate',
1053+
'dcim.add_rearporttemplate',
1054+
'dcim.add_modulebaytemplate',
1055+
'dcim.add_devicebaytemplate',
1056+
'dcim.add_inventoryitemtemplate',
1057+
)
1058+
1059+
for value in ('', 'null', '3', '"My console port"', '{name: "My other console port"}'):
1060+
with self.subTest(value=value):
1061+
import_data = f'''
1062+
manufacturer: Manufacturer 1
1063+
model: TEST-3000
1064+
slug: test-3000
1065+
u_height: 1
1066+
console-ports: {value}
1067+
'''
1068+
form_data = {
1069+
'data': import_data,
1070+
'format': 'yaml'
1071+
}
1072+
1073+
response = self.client.post(reverse('dcim:devicetype_bulk_import'), data=form_data, follow=True)
1074+
self.assertHttpStatus(response, 200)
1075+
self.assertContains(response, "Record 1 console-ports: Must be a list.")
1076+
10411077
def test_export_objects(self):
10421078
url = reverse('dcim:devicetype_list')
10431079
self.add_permissions('dcim.view_devicetype')

netbox/netbox/views/generic/bulk_views.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -381,8 +381,17 @@ def _save_object(self, model_form, request, parent_idx):
381381
# Iterate through the related object forms (if any), validating and saving each instance.
382382
for field_name, related_object_form in self.related_object_forms.items():
383383

384+
related_objects = model_form.data.get(field_name, list())
385+
if not isinstance(related_objects, list):
386+
raise ValidationError(
387+
self._compile_form_errors(
388+
{field_name: [_("Must be a list.")]},
389+
index=parent_idx
390+
)
391+
)
392+
384393
related_obj_pks = []
385-
for i, rel_obj_data in enumerate(model_form.data.get(field_name, list()), start=1):
394+
for i, rel_obj_data in enumerate(related_objects, start=1):
386395
rel_obj_data = self.prep_related_object_data(obj, rel_obj_data)
387396
f = related_object_form(rel_obj_data)
388397

0 commit comments

Comments
 (0)