From c2e914e5afa6f464b550e3e452a37455b0e13c9e Mon Sep 17 00:00:00 2001 From: pablohashescobar Date: Tue, 16 Sep 2025 19:53:36 +0530 Subject: [PATCH 1/3] 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. --- apps/api/plane/utils/path_validator.py | 27 ++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/apps/api/plane/utils/path_validator.py b/apps/api/plane/utils/path_validator.py index e5bf7aeb2a2..3c9b9a7da2e 100644 --- a/apps/api/plane/utils/path_validator.py +++ b/apps/api/plane/utils/path_validator.py @@ -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,17 @@ 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: + allowed_hosts.append(settings.ADMIN_BASE_URL) + if settings.SPACE_BASE_URL: + allowed_hosts.append(settings.SPACE_BASE_URL) + 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 +108,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 \ No newline at end of file From ed5593f2a3c5dc9752ed3a40c22efc0d44580914 Mon Sep 17 00:00:00 2001 From: pablohashescobar Date: Tue, 16 Sep 2025 19:59:43 +0530 Subject: [PATCH 2/3] 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. --- apps/api/plane/authentication/views/space/email.py | 6 +++++- apps/api/plane/authentication/views/space/magic.py | 13 ++++++++++--- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/apps/api/plane/authentication/views/space/email.py b/apps/api/plane/authentication/views/space/email.py index d247f6e9899..2fb1c2c5ea4 100644 --- a/apps/api/plane/authentication/views/space/email.py +++ b/apps/api/plane/authentication/views/space/email.py @@ -3,6 +3,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 # Module imports from plane.authentication.provider.credentials.email import EmailProvider @@ -200,7 +201,10 @@ def post(self, request): # 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( diff --git a/apps/api/plane/authentication/views/space/magic.py b/apps/api/plane/authentication/views/space/magic.py index f50274a4a23..85e3a185cf9 100644 --- a/apps/api/plane/authentication/views/space/magic.py +++ b/apps/api/plane/authentication/views/space/magic.py @@ -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 @@ 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 @@ def post(self, request): # 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 @@ def post(self, request): # 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() From 9fbceaa9c9602823787d19ded0c5e2332b8427b6 Mon Sep 17 00:00:00 2001 From: pablohashescobar Date: Tue, 16 Sep 2025 21:29:21 +0530 Subject: [PATCH 3/3] 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. --- apps/api/plane/utils/path_validator.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/apps/api/plane/utils/path_validator.py b/apps/api/plane/utils/path_validator.py index 3c9b9a7da2e..a89c8b9698c 100644 --- a/apps/api/plane/utils/path_validator.py +++ b/apps/api/plane/utils/path_validator.py @@ -48,9 +48,13 @@ def get_allowed_hosts() -> list[str]: base_origin = settings.WEB_URL or settings.APP_BASE_URL allowed_hosts = [base_origin] if settings.ADMIN_BASE_URL: - allowed_hosts.append(settings.ADMIN_BASE_URL) + # Get only the host + host = urlparse(settings.ADMIN_BASE_URL).netloc + allowed_hosts.append(host) if settings.SPACE_BASE_URL: - allowed_hosts.append(settings.SPACE_BASE_URL) + # Get only the host + host = urlparse(settings.SPACE_BASE_URL).netloc + allowed_hosts.append(host) return allowed_hosts