Skip to content

Commit 8fb458f

Browse files
authored
Fix invalid extensions parameters were not handled (#3943)
* Fix handling of invalid extensions parameters * Add release file
1 parent 74c2cef commit 8fb458f

File tree

6 files changed

+50
-6
lines changed

6 files changed

+50
-6
lines changed

RELEASE.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
Release type: patch
2+
3+
In this release, we updated Strawberry to gracefully handle requests containing
4+
an invalid `extensions` parameter. Previously, such requests could result in
5+
internal server errors. Now, Strawberry will return a 400 Bad Request response
6+
with a clear error message, conforming to the GraphQL over HTTP specification.

strawberry/http/async_base_view.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -541,11 +541,18 @@ async def parse_http_body(
541541
"The GraphQL operation's `variables` must be an object or null, if provided.",
542542
)
543543

544+
extensions = data.get("extensions")
545+
if not isinstance(extensions, (dict, type(None))):
546+
raise HTTPException(
547+
400,
548+
"The GraphQL operation's `extensions` must be an object or null, if provided.",
549+
)
550+
544551
return GraphQLRequestData(
545552
query=query,
546553
variables=variables,
547554
operation_name=data.get("operationName"),
548-
extensions=data.get("extensions"),
555+
extensions=extensions,
549556
protocol=protocol,
550557
)
551558

strawberry/http/sync_base_view.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -166,11 +166,18 @@ def parse_http_body(self, request: SyncHTTPRequestAdapter) -> GraphQLRequestData
166166
"The GraphQL operation's `variables` must be an object or null, if provided.",
167167
)
168168

169+
extensions = data.get("extensions")
170+
if not isinstance(extensions, (dict, type(None))):
171+
raise HTTPException(
172+
400,
173+
"The GraphQL operation's `extensions` must be an object or null, if provided.",
174+
)
175+
169176
return GraphQLRequestData(
170177
query=query,
171178
variables=variables,
172179
operation_name=data.get("operationName"),
173-
extensions=data.get("extensions"),
180+
extensions=extensions,
174181
)
175182

176183
def _handle_errors(

tests/http/clients/base.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -209,7 +209,7 @@ def _build_body(
209209
if variables is not None:
210210
body["variables"] = variables
211211

212-
if extensions:
212+
if extensions is not None:
213213
body["extensions"] = extensions
214214

215215
if files:

tests/http/test_graphql_over_http_spec.py

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -455,9 +455,6 @@ async def test_6a70(http_client):
455455
assert "errors" not in response.json
456456

457457

458-
@pytest.mark.xfail(
459-
reason="OPTIONAL - Currently not supported by Strawberry", raises=AssertionError
460-
)
461458
@pytest.mark.parametrize(
462459
"invalid",
463460
["string", 0, False, ["array"]],

tests/http/test_query.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -295,6 +295,33 @@ async def test_query_extensions(
295295
assert data["valueFromExtensions"] == "hello"
296296

297297

298+
@pytest.mark.parametrize(
299+
"not_an_object_or_null",
300+
["string", 0, False, ["array"]],
301+
)
302+
async def test_requests_with_invalid_extension_parameter_are_rejected(
303+
http_client: HttpClient, not_an_object_or_null
304+
):
305+
response = await http_client.query(
306+
query="{ __typename }",
307+
extensions=not_an_object_or_null,
308+
)
309+
310+
assert response.status_code == 400
311+
message = (
312+
"The GraphQL operation's `extensions` must be an object or null, if provided."
313+
)
314+
315+
if isinstance(http_client, ChaliceHttpClient):
316+
# Our Chalice integration purposely wraps error messages with a JSON object
317+
assert response.json == {
318+
"Code": "BadRequestError",
319+
"Message": message,
320+
}
321+
else:
322+
assert response.data == message.encode()
323+
324+
298325
@pytest.mark.parametrize("method", ["get", "post"])
299326
async def test_returning_status_code(
300327
method: Literal["get", "post"], http_client: HttpClient

0 commit comments

Comments
 (0)