|
1 | 1 | import pytest |
2 | 2 | from pydantic import AnyHttpUrl |
3 | 3 |
|
4 | | -from mcp.server.auth.routes import build_metadata, validate_issuer_url |
| 4 | +from mcp.server.auth.routes import build_metadata, validate_issuer_url, validate_redirect_uri |
5 | 5 | from mcp.server.auth.settings import AuthSettings, ClientRegistrationOptions, RevocationOptions |
6 | 6 |
|
7 | 7 |
|
@@ -70,3 +70,43 @@ def test_build_metadata_serves_issuer_without_trailing_slash(): |
70 | 70 | assert served["issuer"] == "https://as.example.com" |
71 | 71 | assert served["authorization_endpoint"] == "https://as.example.com/authorize" |
72 | 72 | assert served["token_endpoint"] == "https://as.example.com/token" |
| 73 | + |
| 74 | +def test_validate_redirect_uri_https_allowed(): |
| 75 | + validate_redirect_uri(AnyHttpUrl('https://example.com/cb')) |
| 76 | + |
| 77 | + |
| 78 | +def test_validate_redirect_uri_http_localhost_allowed(): |
| 79 | + validate_redirect_uri(AnyHttpUrl('http://localhost:3000/cb')) |
| 80 | + |
| 81 | + |
| 82 | +def test_validate_redirect_uri_http_127_0_0_1_allowed(): |
| 83 | + validate_redirect_uri(AnyHttpUrl('http://127.0.0.1:8080/cb')) |
| 84 | + |
| 85 | + |
| 86 | +def test_validate_redirect_uri_http_ipv6_loopback_allowed(): |
| 87 | + validate_redirect_uri(AnyHttpUrl('http://[::1]:9090/cb')) |
| 88 | + |
| 89 | + |
| 90 | +def test_validate_redirect_uri_javascript_scheme_rejected(): |
| 91 | + with pytest.raises(ValueError, match='Redirect URI must use HTTPS'): |
| 92 | + validate_redirect_uri(AnyHttpUrl('javascript:alert(1)')) |
| 93 | + |
| 94 | + |
| 95 | +def test_validate_redirect_uri_file_scheme_rejected(): |
| 96 | + with pytest.raises(ValueError, match='Redirect URI must use HTTPS'): |
| 97 | + validate_redirect_uri(AnyHttpUrl('file:///etc/passwd')) |
| 98 | + |
| 99 | + |
| 100 | +def test_validate_redirect_uri_http_non_loopback_rejected(): |
| 101 | + with pytest.raises(ValueError, match='Redirect URI must use HTTPS'): |
| 102 | + validate_redirect_uri(AnyHttpUrl('http://evil.com/cb')) |
| 103 | + |
| 104 | + |
| 105 | +def test_validate_redirect_uri_fragment_rejected(): |
| 106 | + with pytest.raises(ValueError, match='Redirect URI must not contain a fragment'): |
| 107 | + validate_redirect_uri(AnyHttpUrl('https://example.com/cb#frag')) |
| 108 | + |
| 109 | + |
| 110 | +def test_validate_redirect_uri_empty_fragment_rejected(): |
| 111 | + with pytest.raises(ValueError, match='Redirect URI must not contain a fragment'): |
| 112 | + validate_redirect_uri(AnyHttpUrl('https://example.com/cb#')) |
0 commit comments