mirror of
https://github.com/gosticks/plane.git
synced 2025-10-16 12:45:33 +00:00
[WEB-4943]: add url has allowed host or scheme for validating valid redirections (#7809)
* feat: enhance path validation and URL safety in path_validator.py * Added get_allowed_hosts function to retrieve allowed hosts from settings. * Updated get_safe_redirect_url to validate URLs against allowed hosts. * Improved URL construction logic for safer redirection handling. * feat: enhance URL validation in authentication views * Added url_has_allowed_host_and_scheme checks in SignUpAuthSpaceEndpoint and MagicSignInSpaceEndpoint for safer redirection. * Updated redirect logic to fallback to base host if the constructed URL is not allowed. * Improved overall URL safety and handling in authentication flows. * fix: improve host extraction in get_allowed_hosts function * Updated get_allowed_hosts to extract only the host from ADMIN_BASE_URL and SPACE_BASE_URL settings for better URL validation. * Enhanced overall safety and clarity in allowed hosts retrieval.
This commit is contained in:
parent
d521eab22f
commit
6d3d9e6df7
@ -3,6 +3,7 @@ from django.core.exceptions import ValidationError
|
||||
from django.core.validators import validate_email
|
||||
from django.http import HttpResponseRedirect
|
||||
from django.views import View
|
||||
from django.utils.http import url_has_allowed_host_and_scheme
|
||||
|
||||
# Module imports
|
||||
from plane.authentication.provider.credentials.email import EmailProvider
|
||||
@ -200,7 +201,10 @@ class SignUpAuthSpaceEndpoint(View):
|
||||
# redirect to referer path
|
||||
next_path = validate_next_path(next_path=next_path)
|
||||
url = f"{base_host(request=request, is_space=True).rstrip("/")}{next_path}"
|
||||
return HttpResponseRedirect(url)
|
||||
if url_has_allowed_host_and_scheme(url, allowed_hosts=get_allowed_hosts()):
|
||||
return HttpResponseRedirect(url)
|
||||
else:
|
||||
return HttpResponseRedirect(base_host(request=request, is_space=True))
|
||||
except AuthenticationException as e:
|
||||
params = e.get_error_dict()
|
||||
url = get_safe_redirect_url(
|
||||
|
||||
@ -2,6 +2,7 @@
|
||||
from django.core.validators import validate_email
|
||||
from django.http import HttpResponseRedirect
|
||||
from django.views import View
|
||||
from django.utils.http import url_has_allowed_host_and_scheme
|
||||
|
||||
# Third party imports
|
||||
from rest_framework import status
|
||||
@ -20,7 +21,7 @@ from plane.authentication.adapter.error import (
|
||||
AuthenticationException,
|
||||
AUTHENTICATION_ERROR_CODES,
|
||||
)
|
||||
from plane.utils.path_validator import get_safe_redirect_url, validate_next_path
|
||||
from plane.utils.path_validator import get_safe_redirect_url, validate_next_path, get_allowed_hosts
|
||||
|
||||
|
||||
class MagicGenerateSpaceEndpoint(APIView):
|
||||
@ -96,7 +97,10 @@ class MagicSignInSpaceEndpoint(View):
|
||||
# redirect to referer path
|
||||
next_path = validate_next_path(next_path=next_path)
|
||||
url = f"{base_host(request=request, is_space=True).rstrip("/")}{next_path}"
|
||||
return HttpResponseRedirect(url)
|
||||
if url_has_allowed_host_and_scheme(url, allowed_hosts=get_allowed_hosts()):
|
||||
return HttpResponseRedirect(url)
|
||||
else:
|
||||
return HttpResponseRedirect(base_host(request=request, is_space=True))
|
||||
|
||||
except AuthenticationException as e:
|
||||
params = e.get_error_dict()
|
||||
@ -155,7 +159,10 @@ class MagicSignUpSpaceEndpoint(View):
|
||||
# redirect to referer path
|
||||
next_path = validate_next_path(next_path=next_path)
|
||||
url = f"{base_host(request=request, is_space=True).rstrip("/")}{next_path}"
|
||||
return HttpResponseRedirect(url)
|
||||
if url_has_allowed_host_and_scheme(url, allowed_hosts=get_allowed_hosts()):
|
||||
return HttpResponseRedirect(url)
|
||||
else:
|
||||
return HttpResponseRedirect(base_host(request=request, is_space=True))
|
||||
|
||||
except AuthenticationException as e:
|
||||
params = e.get_error_dict()
|
||||
|
||||
@ -1,6 +1,11 @@
|
||||
# Django imports
|
||||
from django.utils.http import url_has_allowed_host_and_scheme
|
||||
from django.conf import settings
|
||||
|
||||
# Python imports
|
||||
from urllib.parse import urlparse
|
||||
|
||||
|
||||
def _contains_suspicious_patterns(path: str) -> bool:
|
||||
"""
|
||||
Check for suspicious patterns that might indicate malicious intent.
|
||||
@ -38,6 +43,21 @@ def _contains_suspicious_patterns(path: str) -> bool:
|
||||
return False
|
||||
|
||||
|
||||
def get_allowed_hosts() -> list[str]:
|
||||
"""Get the allowed hosts from the settings."""
|
||||
base_origin = settings.WEB_URL or settings.APP_BASE_URL
|
||||
allowed_hosts = [base_origin]
|
||||
if settings.ADMIN_BASE_URL:
|
||||
# Get only the host
|
||||
host = urlparse(settings.ADMIN_BASE_URL).netloc
|
||||
allowed_hosts.append(host)
|
||||
if settings.SPACE_BASE_URL:
|
||||
# Get only the host
|
||||
host = urlparse(settings.SPACE_BASE_URL).netloc
|
||||
allowed_hosts.append(host)
|
||||
return allowed_hosts
|
||||
|
||||
|
||||
def validate_next_path(next_path: str) -> str:
|
||||
"""Validates that next_path is a safe relative path for redirection."""
|
||||
# Browsers interpret backslashes as forward slashes. Remove all backslashes.
|
||||
@ -92,7 +112,14 @@ def get_safe_redirect_url(base_url: str, next_path: str = "", params: dict = {})
|
||||
base_url = base_url.rstrip('/')
|
||||
if params:
|
||||
encoded_params = urlencode(params)
|
||||
return f"{base_url}/?next_path={validated_path}&{encoded_params}"
|
||||
url = f"{base_url}/?next_path={validated_path}&{encoded_params}"
|
||||
else:
|
||||
url = f"{base_url}/?next_path={validated_path}"
|
||||
|
||||
return f"{base_url}/?next_path={validated_path}"
|
||||
# Check if the URL is allowed
|
||||
if url_has_allowed_host_and_scheme(url, allowed_hosts=get_allowed_hosts()):
|
||||
return url
|
||||
|
||||
# Return the base URL if the URL is not allowed
|
||||
return base_url
|
||||
|
||||
Loading…
Reference in New Issue
Block a user