[v1.x] Apply the request body limit to the SSE and OAuth endpoints (#3344)

This commit is contained in:
Max 2026-08-21 17:02:59 +01:00 • committed by GitHub
commit 4dc224fab2
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -14,6 +14,7 @@ from starlette.applications import Starlette
from mcp.server.auth.provider import AuthorizeError, RegistrationError, TokenError
from mcp.server.auth.routes import create_auth_routes
from mcp.server.transport_security import DEFAULT_MAX_REQUEST_BODY_SIZE
# TODO(Marcelo): This TYPE_CHECKING shouldn't be here, but pytest doesn't seem to get the module correctly.
if TYPE_CHECKING:
@ -302,3 +303,59 @@ class TestTokenErrorHandling:
data = refresh_response.json()
assert data["error"] == "invalid_scope"
assert data["error_description"] == "The requested scope is invalid"
_FORM = "application/x-www-form-urlencoded"
@pytest.mark.anyio
@pytest.mark.parametrize(
("method", "path", "content_type"),
[
("POST", "/token", _FORM),
("POST", "/revoke", _FORM),
("POST", "/register", "application/json"),
("POST", "/authorize", _FORM),
# The other methods these routes accept reach the same body-reading handlers.
("OPTIONS", "/token", _FORM),
("OPTIONS", "/revoke", _FORM),
("OPTIONS", "/register", "application/json"),
("HEAD", "/authorize", _FORM),
],
)
async def test_oversized_request_body_returns_413(client: httpx.AsyncClient, method: str, path: str, content_type: str):
"""Each endpoint that reads a request body rejects one over 4 MiB before parsing it, whatever the method."""
response = await client.request(
method, path, content=b"x" * (DEFAULT_MAX_REQUEST_BODY_SIZE + 1), headers={"Content-Type": content_type}
)
assert response.status_code == 413
@pytest.mark.anyio
async def test_request_body_within_the_limit_is_still_parsed(client: httpx.AsyncClient):
"""A small body is passed through to the handler intact: the form is parsed and its fields validated."""
response = await client.post("/token", data={"grant_type": "authorization_code"})
assert response.status_code == 401
assert response.json() == {"error": "unauthorized_client", "error_description": "Missing client_id"}
@pytest.mark.anyio
async def test_cors_preflight_is_still_answered(client: httpx.AsyncClient):
"""A CORS preflight to a body-limited endpoint is answered by the CORS layer as before."""
response = await client.options(
"/token", headers={"Origin": "https://client.example.com", "Access-Control-Request-Method": "POST"}
)
assert response.status_code == 200
assert response.headers["access-control-allow-origin"] == "*"
@pytest.mark.anyio
async def test_oversized_cross_origin_request_gets_413_with_cors_headers(client: httpx.AsyncClient):
"""The 413 is produced inside the CORS layer, so a browser client can still read it."""
response = await client.post(
"/token",
content=b"x" * (DEFAULT_MAX_REQUEST_BODY_SIZE + 1),
headers={"Content-Type": _FORM, "Origin": "https://client.example.com"},
)
assert response.status_code == 413
assert response.headers["access-control-allow-origin"] == "*"