Skip to content

Commit 1245a9f

Browse files
committed
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.
1 parent 78223ce commit 1245a9f

File tree

2 files changed

+45
-0
lines changed

2 files changed

+45
-0
lines changed

netbox/dcim/tests/test_views.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1074,6 +1074,43 @@ def test_import_nolist(self):
10741074
self.assertHttpStatus(response, 200)
10751075
self.assertContains(response, "Record 1 console-ports: Must be a list.")
10761076

1077+
@override_settings(EXEMPT_VIEW_PERMISSIONS=['*'])
1078+
def test_import_nodict(self):
1079+
# Add all required permissions to the test user
1080+
self.add_permissions(
1081+
'dcim.view_devicetype',
1082+
'dcim.add_devicetype',
1083+
'dcim.add_consoleporttemplate',
1084+
'dcim.add_consoleserverporttemplate',
1085+
'dcim.add_powerporttemplate',
1086+
'dcim.add_poweroutlettemplate',
1087+
'dcim.add_interfacetemplate',
1088+
'dcim.add_frontporttemplate',
1089+
'dcim.add_rearporttemplate',
1090+
'dcim.add_modulebaytemplate',
1091+
'dcim.add_devicebaytemplate',
1092+
'dcim.add_inventoryitemtemplate',
1093+
)
1094+
1095+
for value in ('', 'null', '3', '"My console port"', '["My other console port"]'):
1096+
with self.subTest(value=value):
1097+
import_data = f'''
1098+
manufacturer: Manufacturer 1
1099+
model: TEST-4000
1100+
slug: test-4000
1101+
u_height: 1
1102+
console-ports:
1103+
- {value}
1104+
'''
1105+
form_data = {
1106+
'data': import_data,
1107+
'format': 'yaml'
1108+
}
1109+
1110+
response = self.client.post(reverse('dcim:devicetype_bulk_import'), data=form_data, follow=True)
1111+
self.assertHttpStatus(response, 200)
1112+
self.assertContains(response, "Record 1 console-ports[1]: Must be a dictionary.")
1113+
10771114
def test_export_objects(self):
10781115
url = reverse('dcim:devicetype_list')
10791116
self.add_permissions('dcim.view_devicetype')

netbox/netbox/views/generic/bulk_views.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -392,6 +392,14 @@ def _save_object(self, model_form, request, parent_idx):
392392

393393
related_obj_pks = []
394394
for i, rel_obj_data in enumerate(related_objects, start=1):
395+
if not isinstance(rel_obj_data, dict):
396+
raise ValidationError(
397+
self._compile_form_errors(
398+
{f'{field_name}[{i}]': [_("Must be a dictionary.")]},
399+
index=parent_idx,
400+
)
401+
)
402+
395403
rel_obj_data = self.prep_related_object_data(obj, rel_obj_data)
396404
f = related_object_form(rel_obj_data)
397405

0 commit comments

Comments
 (0)