Apply the request body limit to the SSE and OAuth endpoints (#3336)
This commit is contained in:
parent
0cee6249ba
commit
57394b0548
1 changed files with 420 additions and 189 deletions
|
|
@ -16,6 +16,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.auth.settings import ClientRegistrationOptions, RevocationOptions
|
||||
from mcp.server.transport_security import DEFAULT_MAX_REQUEST_BODY_SIZE
|
||||
from tests.server.mcpserver.auth.test_auth_integration import MockOAuthProvider
|
||||
|
||||
|
||||
|
|
@ -288,3 +289,61 @@ async def test_token_error_handling_refresh_token(
|
|||
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: httpx2.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: httpx2.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": "invalid_client", "error_description": "Missing client_id"}
|
||||
|
||||
|
||||
@pytest.mark.anyio
|
||||
async def test_cors_preflight_is_still_answered(client: httpx2.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: httpx2.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"] == "*"
|
||||
|
|
|
|||
Loading…
Reference in a new issue