diff --git a/frappe/auth.py b/frappe/auth.py index 03b28f0dc5..e631fa0c27 100644 --- a/frappe/auth.py +++ b/frappe/auth.py @@ -110,9 +110,12 @@ class HTTPRequest: ) # Check if the referrer or origin is in the allowed list - return (referrer and any(referrer.startswith(allowed) for allowed in allowed_referrers)) or ( - origin and any(origin == allowed for allowed in allowed_referrers) - ) + if referrer: + referrer_parsed = urlparse(referrer) + if any(referrer_parsed.netloc == urlparse(allowed).netloc for allowed in allowed_referrers): + return True + + return origin in allowed_referrers if origin else False class LoginManager: diff --git a/frappe/tests/test_auth.py b/frappe/tests/test_auth.py index 70399b0445..9ad7550baf 100644 --- a/frappe/tests/test_auth.py +++ b/frappe/tests/test_auth.py @@ -176,8 +176,10 @@ class TestAllowedReferrer(UnitTestCase): env = builder.get_environ() return Request(env) - # Test with valid referrer + # Set a single allowed referrer frappe.cache.set_value("allowed_referrers", ["https://example.com"]) + + # Test with valid referrer frappe.local.request = create_request({"Referer": "https://example.com/some/path"}) http_request = frappe.auth.HTTPRequest() self.assertTrue(http_request.is_allowed_referrer()) @@ -197,6 +199,16 @@ class TestAllowedReferrer(UnitTestCase): http_request = frappe.auth.HTTPRequest() self.assertFalse(http_request.is_allowed_referrer()) + # Test subdomain bypass prevention + frappe.local.request = create_request({"Referer": "https://example.com.evil.com"}) + http_request = frappe.auth.HTTPRequest() + self.assertFalse(http_request.is_allowed_referrer()) + + # Test exact domain match for referrer + frappe.local.request = create_request({"Referer": "https://example.com"}) + http_request = frappe.auth.HTTPRequest() + self.assertTrue(http_request.is_allowed_referrer()) + # Clean up frappe.cache.delete_value("allowed_referrers") frappe.local.request = None