@@ -17985,6 +17985,62 @@ async def test_enforce_admin_csrf_allowed_origin_port_normalization(self, monkey
1798517985 )
1798617986 await admin_mod.enforce_admin_csrf(request)
1798717987
17988+ @pytest.mark.asyncio
17989+ async def test_enforce_admin_csrf_skips_schemeless_allowed_origin(self, monkeypatch):
17990+ """Allowed-origins entry without scheme/netloc (e.g. bare path) is silently skipped."""
17991+ # First-Party
17992+ from mcpgateway import admin as admin_mod
17993+
17994+ # "just-a-path" is parsed by urlparse as a path component with no scheme/netloc,
17995+ # so the guard on line 1139-1140 must skip it and the request must be rejected.
17996+ monkeypatch.setattr("mcpgateway.admin.settings.allowed_origins", {"just-a-path"})
17997+
17998+ request = self._make_request(
17999+ method="POST",
18000+ headers={
18001+ "origin": "https://evil.com",
18002+ "host": "example.com",
18003+ "content-type": "application/x-www-form-urlencoded",
18004+ },
18005+ cookies={"jwt_token": "jwt", admin_mod.ADMIN_CSRF_COOKIE_NAME: "expected"},
18006+ form_data={admin_mod.ADMIN_CSRF_FORM_FIELD: "expected"},
18007+ )
18008+ with pytest.raises(HTTPException, match="CSRF origin validation failed"):
18009+ await admin_mod.enforce_admin_csrf(request)
18010+
18011+ @pytest.mark.asyncio
18012+ async def test_enforce_admin_csrf_survives_malformed_allowed_origin(self, monkeypatch):
18013+ """A malformed allowed_origins entry that triggers an exception must not crash."""
18014+ # First-Party
18015+ from unittest.mock import patch
18016+
18017+ from mcpgateway import admin as admin_mod
18018+
18019+ # Only the malformed entry — forces the except branch to run, then the
18020+ # function falls through to return False and CSRF validation fails.
18021+ monkeypatch.setattr("mcpgateway.admin.settings.allowed_origins", {"will-explode://bad"})
18022+
18023+ real_normalize = admin_mod._normalize_origin_parts
18024+
18025+ def _boom_on_bad(scheme, netloc):
18026+ if netloc == "bad":
18027+ raise ValueError("intentional test boom")
18028+ return real_normalize(scheme, netloc)
18029+
18030+ request = self._make_request(
18031+ method="POST",
18032+ headers={
18033+ "origin": "https://attacker.com",
18034+ "host": "internal:4444",
18035+ "content-type": "application/x-www-form-urlencoded",
18036+ },
18037+ cookies={"jwt_token": "jwt", admin_mod.ADMIN_CSRF_COOKIE_NAME: "expected"},
18038+ form_data={admin_mod.ADMIN_CSRF_FORM_FIELD: "expected"},
18039+ )
18040+ with patch.object(admin_mod, "_normalize_origin_parts", side_effect=_boom_on_bad):
18041+ with pytest.raises(HTTPException, match="CSRF origin validation failed"):
18042+ await admin_mod.enforce_admin_csrf(request)
18043+
1798818044 @pytest.mark.asyncio
1798918045 async def test_enforce_admin_csrf_empty_allowed_origins(self, monkeypatch):
1799018046 """Empty allowed_origins set must not bypass CSRF origin check."""
0 commit comments