diff --git a/netbox/netbox/views/generic/object_views.py b/netbox/netbox/views/generic/object_views.py index 316c3a1ee..a078f565c 100644 --- a/netbox/netbox/views/generic/object_views.py +++ b/netbox/netbox/views/generic/object_views.py @@ -9,7 +9,6 @@ from django.forms.widgets import HiddenInput from django.shortcuts import redirect, render from django.urls import reverse from django.utils.html import escape -from django.utils.http import is_safe_url from django.utils.safestring import mark_safe from extras.signals import clear_webhooks @@ -259,9 +258,7 @@ class ObjectImportView(GetReturnURLMixin, BaseObjectView): if '_addanother' in request.POST: return redirect(request.get_full_path()) - return_url = form.cleaned_data.get('return_url') - if return_url is not None and is_safe_url(url=return_url, allowed_hosts=request.get_host()): - return redirect(return_url) + self.get_return_url(request, obj) return redirect(self.get_return_url(request, obj)) else: @@ -507,10 +504,9 @@ class ObjectDeleteView(GetReturnURLMixin, BaseObjectView): messages.success(request, msg) return_url = form.cleaned_data.get('return_url') - if return_url is not None and is_safe_url(url=return_url, allowed_hosts=request.get_host()): + if return_url and return_url.startswith('/'): return redirect(return_url) - else: - return redirect(self.get_return_url(request, obj)) + return redirect(self.get_return_url(request, obj)) else: logger.debug("Form validation failed") diff --git a/netbox/users/views.py b/netbox/users/views.py index cd3c34aa9..43c65eada 100644 --- a/netbox/users/views.py +++ b/netbox/users/views.py @@ -10,7 +10,6 @@ from django.http import HttpResponseRedirect from django.shortcuts import get_object_or_404, redirect, render from django.urls import reverse from django.utils.decorators import method_decorator -from django.utils.http import is_safe_url from django.views.decorators.debug import sensitive_post_parameters from django.views.generic import View from social_core.backends.utils import load_backends @@ -78,17 +77,17 @@ class LoginView(View): }) def redirect_to_next(self, request, logger): - if request.method == "POST": - redirect_to = request.POST.get('next', settings.LOGIN_REDIRECT_URL) + data = request.POST if request.method == "POST" else request.GET + redirect_url = data.get('next', settings.LOGIN_REDIRECT_URL) + + if redirect_url and redirect_url.startswith('/'): + logger.debug(f"Redirecting user to {redirect_url}") else: - redirect_to = request.GET.get('next', settings.LOGIN_REDIRECT_URL) + if redirect_url: + logger.warning(f"Ignoring unsafe 'next' URL passed to login form: {redirect_url}") + redirect_url = reverse('home') - if redirect_to and not is_safe_url(url=redirect_to, allowed_hosts=request.get_host()): - logger.warning(f"Ignoring unsafe 'next' URL passed to login form: {redirect_to}") - redirect_to = reverse('home') - - logger.debug(f"Redirecting user to {redirect_to}") - return HttpResponseRedirect(redirect_to) + return HttpResponseRedirect(redirect_url) class LogoutView(View): diff --git a/netbox/utilities/views.py b/netbox/utilities/views.py index efea0b867..858e7b491 100644 --- a/netbox/utilities/views.py +++ b/netbox/utilities/views.py @@ -1,10 +1,7 @@ from django.contrib.auth.mixins import AccessMixin from django.core.exceptions import ImproperlyConfigured -from django.shortcuts import get_object_or_404, redirect from django.urls import reverse from django.urls.exceptions import NoReverseMatch -from django.utils.http import is_safe_url -from django.views.generic import View from .permissions import resolve_permission @@ -103,9 +100,9 @@ class GetReturnURLMixin: # First, see if `return_url` was specified as a query parameter or form data. Use this URL only if it's # considered safe. - query_param = request.GET.get('return_url') or request.POST.get('return_url') - if query_param and is_safe_url(url=query_param, allowed_hosts=request.get_host()): - return query_param + return_url = request.GET.get('return_url') or request.POST.get('return_url') + if return_url and return_url.startswith('/'): + return return_url # Next, check if the object being modified (if any) has an absolute URL. if obj is not None and obj.pk and hasattr(obj, 'get_absolute_url'):