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

Fixes #23583: Redirect after object deletion only to a safe return URL

ObjectDeleteView followed any return_url from the POST body that started with a slash, so protocol-relative values like //example.com sent the user off-site. Drop that branch and redirect through get_return_url(), which already reads the POST body and validates it with safe_for_redirect().
Jason Novinger 16 часов назад
Родитель
Сommit
baa1eeafb8
2 измененных файлов с 29 добавлено и 3 удалено
  1. 29 0
      netbox/netbox/tests/test_views.py
  2. 0 3
      netbox/netbox/views/generic/object_views.py

+ 29 - 0
netbox/netbox/tests/test_views.py

@@ -651,3 +651,32 @@ class ProtectedObjectsMessageTestCase(TestCase):
             '_confirm': True,
         })
         self.assertProtectedObjectsMessage(response)
+
+
+class ObjectDeleteReturnURLTestCase(TestCase):
+    """
+    After deleting an object, the delete view must redirect only to a local return URL. (Ref: #23583)
+    """
+
+    def setUp(self):
+        super().setUp()
+        self.add_permissions('dcim.view_site', 'dcim.delete_site')
+        self.site = Site.objects.create(name='Site 1', slug='site-1')
+        self.url = reverse('dcim:site_delete', kwargs={'pk': self.site.pk})
+
+    def test_unsafe_return_url_ignored(self):
+        for return_url in ('//example.com', '/\\example.com'):
+            with self.subTest(return_url=return_url):
+                site = Site.objects.create(name=f'Site {return_url}', slug='site-unsafe')
+                url = reverse('dcim:site_delete', kwargs={'pk': site.pk})
+                response = self.client.post(url, data={'confirm': True, 'return_url': return_url})
+                self.assertHttpStatus(response, 302)
+                self.assertEqual(response['Location'], reverse('dcim:site_list'))
+                self.assertFalse(Site.objects.filter(pk=site.pk).exists())
+
+    def test_local_return_url_followed(self):
+        return_url = reverse('dcim:region_list')
+        response = self.client.post(self.url, data={'confirm': True, 'return_url': return_url})
+        self.assertHttpStatus(response, 302)
+        self.assertEqual(response['Location'], return_url)
+        self.assertFalse(Site.objects.filter(pk=self.site.pk).exists())

+ 0 - 3
netbox/netbox/views/generic/object_views.py

@@ -510,9 +510,6 @@ class ObjectDeleteView(GetReturnURLMixin, BaseObjectView):
             logger.info(msg)
             messages.success(request, msg)
 
-            return_url = form.cleaned_data.get('return_url')
-            if return_url and return_url.startswith('/'):
-                return redirect(return_url)
             return redirect(self.get_return_url(request, obj))
 
         logger.debug("Form validation failed")