CVE-2026-35526
Description
Strawberry GraphQL is a library for creating GraphQL APIs. Prior to 0.312.3, Strawberry GraphQL's WebSocket subscription handlers for both the graphql-transport-ws and legacy graphql-ws protocols allocate an asyncio.Task and associated Operation object for every incoming subscribe message without enforcing any limit on the number of active subscriptions per connection. An unauthenticated attacker can open a single WebSocket connection, send connection_init, and then flood subscribe messages with unique IDs. Each message unconditionally spawns a new asyncio.Task and async generator, causing linear memory growth and event loop saturation. This leads to server degradation or an OOM crash. This vulnerability is fixed in 0.312.3.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
strawberry-graphqlPyPI | < 0.312.3 | 0.312.3 |
Affected products
1- cpe:2.3:a:strawberry:strawberry_graphql:*:*:*:*:*:python:*:*Range: <0.312.3
Patches
10977a4e6b41bFix WebSocket connection_init bypass and add subscription limit per connection (#4344)
19 files changed · +397 −0
RELEASE.md+28 −0 added@@ -0,0 +1,28 @@ +Release type: patch + +This release fixes two security vulnerabilities in the WebSocket subscription +handlers (CVE-2026-35526, CVE-2026-35523). + +**CVE-2026-35526 - Authentication bypass in `graphql-ws`**: The legacy +`graphql-ws` protocol handler didn't verify that the `connection_init` +handshake was completed before accepting `start` messages, allowing clients +to bypass any authentication logic in `on_ws_connect`. The connection is now +closed with `4401 Unauthorized` if the handshake hasn't been completed. + +**CVE-2026-35523 - Unbounded subscriptions per connection**: Both WebSocket +protocol handlers allowed unlimited concurrent subscriptions on a single +connection, making it possible for a malicious client to exhaust server +resources. A new `max_subscriptions_per_connection` parameter has been added +to all views (default: `100`). Set it to `None` to disable the limit. + +Example: + +```python +import strawberry +from strawberry.fastapi import GraphQLRouter + +schema = strawberry.Schema(query=Query, subscription=Subscription) + +# default is 100, set to None to disable the limit +graphql_app = GraphQLRouter(schema, max_subscriptions_per_connection=50) +```
strawberry/aiohttp/views.py+2 −0 modified@@ -102,6 +102,7 @@ def __init__( ), connection_init_wait_timeout: timedelta = timedelta(minutes=1), multipart_uploads_enabled: bool = False, + max_subscriptions_per_connection: int | None = 100, ) -> None: self.schema = schema self.allow_queries_via_get = allow_queries_via_get @@ -110,6 +111,7 @@ def __init__( self.subscription_protocols = subscription_protocols self.connection_init_wait_timeout = connection_init_wait_timeout self.multipart_uploads_enabled = multipart_uploads_enabled + self.max_subscriptions_per_connection = max_subscriptions_per_connection self.graphql_ide = graphql_ide async def render_graphql_ide(self, request: web.Request) -> web.Response:
strawberry/asgi/__init__.py+2 −0 modified@@ -114,6 +114,7 @@ def __init__( ), connection_init_wait_timeout: timedelta = timedelta(minutes=1), multipart_uploads_enabled: bool = False, + max_subscriptions_per_connection: int | None = 100, ) -> None: self.schema = schema self.allow_queries_via_get = allow_queries_via_get @@ -122,6 +123,7 @@ def __init__( self.protocols = subscription_protocols self.connection_init_wait_timeout = connection_init_wait_timeout self.multipart_uploads_enabled = multipart_uploads_enabled + self.max_subscriptions_per_connection = max_subscriptions_per_connection self.graphql_ide = graphql_ide async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None:
strawberry/channels/handlers/ws_handler.py+2 −0 modified@@ -112,13 +112,15 @@ def __init__( GRAPHQL_WS_PROTOCOL, ), connection_init_wait_timeout: datetime.timedelta | None = None, + max_subscriptions_per_connection: int | None = 100, ) -> None: if connection_init_wait_timeout is None: connection_init_wait_timeout = datetime.timedelta(minutes=1) self.connection_init_wait_timeout = connection_init_wait_timeout self.schema = schema self.keep_alive = keep_alive self.keep_alive_interval = keep_alive_interval + self.max_subscriptions_per_connection = max_subscriptions_per_connection self.protocols = subscription_protocols self.message_queue: asyncio.Queue[MessageQueueData] = asyncio.Queue() self.run_task: asyncio.Task | None = None
strawberry/fastapi/router.py+2 −0 modified@@ -131,6 +131,7 @@ def __init__( GRAPHQL_WS_PROTOCOL, ), connection_init_wait_timeout: timedelta = timedelta(minutes=1), + max_subscriptions_per_connection: int | None = 100, prefix: str = "", tags: list[str | Enum] | None = None, dependencies: Sequence[params.Depends] | None = None, @@ -184,6 +185,7 @@ def __init__( ) self.protocols = subscription_protocols self.connection_init_wait_timeout = connection_init_wait_timeout + self.max_subscriptions_per_connection = max_subscriptions_per_connection self.multipart_uploads_enabled = multipart_uploads_enabled self.graphql_ide = graphql_ide
strawberry/http/async_base_view.py+3 −0 modified@@ -88,6 +88,7 @@ class AsyncBaseHTTPView( keep_alive = False keep_alive_interval: float | None = None connection_init_wait_timeout: timedelta = timedelta(minutes=1) + max_subscriptions_per_connection: int | None = 100 request_adapter_class: Callable[[Request], AsyncHTTPRequestAdapter] websocket_adapter_class: Callable[ [ @@ -322,6 +323,7 @@ async def run( root_value=root_value, schema=self.schema, connection_init_wait_timeout=self.connection_init_wait_timeout, + max_subscriptions_per_connection=self.max_subscriptions_per_connection, ).handle() elif websocket_subprotocol == GRAPHQL_WS_PROTOCOL: await self.graphql_ws_handler_class( @@ -332,6 +334,7 @@ async def run( schema=self.schema, keep_alive=self.keep_alive, keep_alive_interval=self.keep_alive_interval, + max_subscriptions_per_connection=self.max_subscriptions_per_connection, ).handle() else: await websocket.close(4406, "Subprotocol not acceptable")
strawberry/litestar/controller.py+5 −0 modified@@ -224,6 +224,7 @@ class GraphQLController( ) keep_alive: bool = False keep_alive_interval: float = 1 + max_subscriptions_per_connection: int | None = 100 def is_websocket_request( self, request: Request | WebSocket @@ -385,6 +386,7 @@ def make_graphql_controller( ), connection_init_wait_timeout: timedelta = timedelta(minutes=1), multipart_uploads_enabled: bool = False, + max_subscriptions_per_connection: int | None = 100, ) -> type[GraphQLController]: # sourcery skip: move-assign if context_getter is None: custom_context_getter_ = _none_custom_context_getter @@ -421,6 +423,9 @@ class _GraphQLController(GraphQLController): _GraphQLController.allow_queries_via_get = allow_queries_via_get_ _GraphQLController.graphql_ide = graphql_ide_ _GraphQLController.multipart_uploads_enabled = multipart_uploads_enabled + _GraphQLController.max_subscriptions_per_connection = ( + max_subscriptions_per_connection + ) return _GraphQLController
strawberry/quart/views.py+2 −0 modified@@ -93,6 +93,7 @@ def __init__( ), connection_init_wait_timeout: timedelta = timedelta(minutes=1), multipart_uploads_enabled: bool = False, + max_subscriptions_per_connection: int | None = 100, ) -> None: self.schema = schema self.allow_queries_via_get = allow_queries_via_get @@ -101,6 +102,7 @@ def __init__( self.subscription_protocols = subscription_protocols self.connection_init_wait_timeout = connection_init_wait_timeout self.multipart_uploads_enabled = multipart_uploads_enabled + self.max_subscriptions_per_connection = max_subscriptions_per_connection self.graphql_ide = graphql_ide async def render_graphql_ide(self, request: Request) -> Response:
strawberry/subscriptions/protocols/graphql_transport_ws/handlers.py+18 −0 modified@@ -54,13 +54,15 @@ def __init__( root_value: RootValue | None, schema: BaseSchema, connection_init_wait_timeout: timedelta, + max_subscriptions_per_connection: int | None = None, ) -> None: self.view = view self.websocket = websocket self.context = context self.root_value = root_value self.schema = schema self.connection_init_wait_timeout = connection_init_wait_timeout + self.max_subscriptions_per_connection = max_subscriptions_per_connection self.connection_init_timeout_task: asyncio.Task | None = None self.connection_init_received = False self.connection_acknowledged = False @@ -233,6 +235,22 @@ async def handle_subscribe(self, message: SubscribeMessage) -> None: await self.websocket.close(code=4409, reason=reason) return + # NOTE: this applies to all in-flight operations (queries and mutations + # executed over WebSocket included), not only subscriptions. + if ( + self.max_subscriptions_per_connection is not None + and len(self.operations) >= self.max_subscriptions_per_connection + ): + error = GraphQLError("Subscription limit reached") + await self.send_message( + { + "id": message["id"], + "type": "error", + "payload": [error.formatted], + } + ) + return + operation = Operation( self, message["id"],
strawberry/subscriptions/protocols/graphql_ws/handlers.py+27 −0 modified@@ -44,6 +44,7 @@ def __init__( schema: BaseSchema, keep_alive: bool, keep_alive_interval: float | None, + max_subscriptions_per_connection: int | None = None, ) -> None: self.view = view self.websocket = websocket @@ -52,9 +53,11 @@ def __init__( self.schema = schema self.keep_alive = keep_alive self.keep_alive_interval = keep_alive_interval + self.max_subscriptions_per_connection = max_subscriptions_per_connection self.keep_alive_task: asyncio.Task | None = None self.subscriptions: dict[str, AsyncGenerator] = {} self.tasks: dict[str, asyncio.Task] = {} + self.connection_acknowledged: bool = False async def handle(self) -> None: try: @@ -119,6 +122,8 @@ async def handle_connection_init(self, message: ConnectionInitMessage) -> None: {"type": "connection_ack", "payload": connection_ack_payload} ) + self.connection_acknowledged = True + if self.keep_alive: keep_alive_handler = self.handle_keep_alive() self.keep_alive_task = asyncio.create_task(keep_alive_handler) @@ -129,7 +134,29 @@ async def handle_connection_terminate( await self.websocket.close(code=1000, reason="") async def handle_start(self, message: StartMessage) -> None: + if not self.connection_acknowledged: + await self.websocket.close(code=4401, reason="Unauthorized") + return + operation_id = message["id"] + + # Clean up existing operation with same ID to prevent task leaks + if operation_id in self.tasks: + await self.cleanup_operation(operation_id) + + if ( + self.max_subscriptions_per_connection is not None + and len(self.tasks) >= self.max_subscriptions_per_connection + ): + await self.send_message( + ErrorMessage( + type="error", + id=operation_id, + payload={"message": "Subscription limit reached"}, + ) + ) + return + payload = message["payload"] query = payload["query"] operation_name = payload.get("operationName")
tests/http/clients/aiohttp.py+2 −0 modified@@ -74,6 +74,7 @@ def __init__( connection_init_wait_timeout: timedelta = timedelta(minutes=1), result_override: ResultOverrideFunction = None, multipart_uploads_enabled: bool = False, + max_subscriptions_per_connection: int | None = 100, ): view = GraphQLView( schema=schema, @@ -84,6 +85,7 @@ def __init__( subscription_protocols=subscription_protocols, connection_init_wait_timeout=connection_init_wait_timeout, multipart_uploads_enabled=multipart_uploads_enabled, + max_subscriptions_per_connection=max_subscriptions_per_connection, ) view.result_override = result_override
tests/http/clients/asgi.py+2 −0 modified@@ -78,6 +78,7 @@ def __init__( connection_init_wait_timeout: timedelta = timedelta(minutes=1), result_override: ResultOverrideFunction = None, multipart_uploads_enabled: bool = False, + max_subscriptions_per_connection: int | None = 100, ): view = GraphQLView( schema, @@ -88,6 +89,7 @@ def __init__( subscription_protocols=subscription_protocols, connection_init_wait_timeout=connection_init_wait_timeout, multipart_uploads_enabled=multipart_uploads_enabled, + max_subscriptions_per_connection=max_subscriptions_per_connection, ) view.result_override = result_override
tests/http/clients/base.py+1 −0 modified@@ -108,6 +108,7 @@ def __init__( connection_init_wait_timeout: timedelta = timedelta(minutes=1), result_override: ResultOverrideFunction = None, multipart_uploads_enabled: bool = False, + max_subscriptions_per_connection: int | None = 100, ): ... @abc.abstractmethod
tests/http/clients/channels.py+2 −0 modified@@ -159,13 +159,15 @@ def __init__( connection_init_wait_timeout: timedelta = timedelta(minutes=1), result_override: ResultOverrideFunction = None, multipart_uploads_enabled: bool = False, + max_subscriptions_per_connection: int | None = 100, ): self.ws_app = DebuggableGraphQLWSConsumer.as_asgi( schema=schema, keep_alive=keep_alive, keep_alive_interval=keep_alive_interval, subscription_protocols=subscription_protocols, connection_init_wait_timeout=connection_init_wait_timeout, + max_subscriptions_per_connection=max_subscriptions_per_connection, ) self.http_app = DebuggableGraphQLHTTPConsumer.as_asgi(
tests/http/clients/fastapi.py+2 −0 modified@@ -89,6 +89,7 @@ def __init__( connection_init_wait_timeout: timedelta = timedelta(minutes=1), result_override: ResultOverrideFunction = None, multipart_uploads_enabled: bool = False, + max_subscriptions_per_connection: int | None = 100, ): self.app = FastAPI() @@ -101,6 +102,7 @@ def __init__( subscription_protocols=subscription_protocols, connection_init_wait_timeout=connection_init_wait_timeout, multipart_uploads_enabled=multipart_uploads_enabled, + max_subscriptions_per_connection=max_subscriptions_per_connection, context_getter=fastapi_get_context, root_value_getter=get_root_value, )
tests/http/clients/litestar.py+2 −0 modified@@ -65,6 +65,7 @@ def __init__( connection_init_wait_timeout: timedelta = timedelta(minutes=1), result_override: ResultOverrideFunction = None, multipart_uploads_enabled: bool = False, + max_subscriptions_per_connection: int | None = 100, ): BaseGraphQLController = make_graphql_controller( schema=schema, @@ -75,6 +76,7 @@ def __init__( subscription_protocols=subscription_protocols, connection_init_wait_timeout=connection_init_wait_timeout, multipart_uploads_enabled=multipart_uploads_enabled, + max_subscriptions_per_connection=max_subscriptions_per_connection, path="/graphql", context_getter=litestar_get_context, root_value_getter=get_root_value,
tests/http/clients/quart.py+2 −0 modified@@ -99,6 +99,7 @@ def __init__( connection_init_wait_timeout: timedelta = timedelta(minutes=1), result_override: ResultOverrideFunction = None, multipart_uploads_enabled: bool = False, + max_subscriptions_per_connection: int | None = 100, ): self.app = Quart(__name__) self.app.debug = True @@ -114,6 +115,7 @@ def __init__( subscription_protocols=subscription_protocols, connection_init_wait_timeout=connection_init_wait_timeout, multipart_uploads_enabled=multipart_uploads_enabled, + max_subscriptions_per_connection=max_subscriptions_per_connection, ) self.app.add_url_rule(
tests/websockets/test_graphql_transport_ws.py+97 −0 modified@@ -1216,3 +1216,100 @@ async def test_unexpected_client_disconnects_are_gracefully_handled( assert not process_errors.called assert Subscription.active_infinity_subscriptions == 0 + + +async def test_max_subscriptions_per_connection(http_client_class: type[HttpClient]): + """Test that subscriptions beyond the limit are rejected with an error.""" + test_client = http_client_class(schema, max_subscriptions_per_connection=2) + + async with test_client.ws_connect( + "/graphql", protocols=[GRAPHQL_TRANSPORT_WS_PROTOCOL] + ) as ws: + await ws.send_message({"type": "connection_init"}) + connection_ack_message: ConnectionAckMessage = await ws.receive_json() + assert connection_ack_message == {"type": "connection_ack"} + + # First two subscriptions should succeed + await ws.send_message( + { + "id": "sub1", + "type": "subscribe", + "payload": {"query": 'subscription { infinity(message: "Hi") }'}, + } + ) + next_message: NextMessage = await ws.receive_json() + assert next_message["type"] == "next" + assert next_message["id"] == "sub1" + + await ws.send_message( + { + "id": "sub2", + "type": "subscribe", + "payload": {"query": 'subscription { infinity(message: "Hi") }'}, + } + ) + next_message = await ws.receive_json() + assert next_message["type"] == "next" + assert next_message["id"] == "sub2" + + # Third subscription should be rejected + await ws.send_message( + { + "id": "sub3", + "type": "subscribe", + "payload": {"query": 'subscription { infinity(message: "Hi") }'}, + } + ) + error_message: ErrorMessage = await ws.receive_json() + assert error_message["type"] == "error" + assert error_message["id"] == "sub3" + assert error_message["payload"] == [{"message": "Subscription limit reached"}] + + # Completing one should free a slot + await ws.send_message({"id": "sub1", "type": "complete"}) + + await ws.send_message( + { + "id": "sub4", + "type": "subscribe", + "payload": {"query": 'subscription { infinity(message: "Hi") }'}, + } + ) + next_message = await ws.receive_json() + assert next_message["type"] == "next" + assert next_message["id"] == "sub4" + + await ws.send_message({"id": "sub2", "type": "complete"}) + await ws.send_message({"id": "sub4", "type": "complete"}) + await ws.close() + + +async def test_max_subscriptions_per_connection_disabled( + http_client_class: type[HttpClient], +): + """Test that setting max_subscriptions_per_connection to None disables the limit.""" + test_client = http_client_class(schema, max_subscriptions_per_connection=None) + + async with test_client.ws_connect( + "/graphql", protocols=[GRAPHQL_TRANSPORT_WS_PROTOCOL] + ) as ws: + await ws.send_message({"type": "connection_init"}) + connection_ack_message: ConnectionAckMessage = await ws.receive_json() + assert connection_ack_message == {"type": "connection_ack"} + + # Should be able to create many subscriptions without limit + for i in range(5): + await ws.send_message( + { + "id": f"sub{i}", + "type": "subscribe", + "payload": {"query": 'subscription { infinity(message: "Hi") }'}, + } + ) + next_message: NextMessage = await ws.receive_json() + assert next_message["type"] == "next" + assert next_message["id"] == f"sub{i}" + + for i in range(5): + await ws.send_message({"id": f"sub{i}", "type": "complete"}) + await ws.close()
tests/websockets/test_graphql_ws.py+196 −0 modified@@ -255,6 +255,30 @@ async def test_rejecting_connection_with_custom_connection_error_payload( assert not ws_raw.close_reason +async def test_start_without_connection_init_is_rejected( + ws_raw: WebSocketClient, +): + """Sending a 'start' message without a prior 'connection_init' must be rejected. + + This ensures that the on_ws_connect authentication hook cannot be bypassed + by skipping the connection_init handshake. + """ + await ws_raw.send_legacy_message( + { + "type": "start", + "id": "1", + "payload": { + "query": "subscription { listener }", + }, + } + ) + + await ws_raw.receive(timeout=2) + assert ws_raw.closed + assert ws_raw.close_code == 4401 + assert ws_raw.close_reason == "Unauthorized" + + async def test_context_can_be_modified_from_within_on_ws_connect( ws_raw: WebSocketClient, ): @@ -865,3 +889,175 @@ async def test_unexpected_client_disconnects_are_gracefully_handled( assert not process_errors.called assert Subscription.active_infinity_subscriptions == 0 + + +async def test_max_subscriptions_per_connection(http_client_class: type[HttpClient]): + """Test that subscriptions beyond the limit are rejected with an error.""" + test_client = http_client_class(schema, max_subscriptions_per_connection=2) + + async with test_client.ws_connect( + "/graphql", protocols=[GRAPHQL_WS_PROTOCOL] + ) as ws: + await ws.send_legacy_message({"type": "connection_init"}) + response: ConnectionAckMessage = await ws.receive_json() + assert response["type"] == "connection_ack" + + # First two subscriptions should succeed + await ws.send_legacy_message( + { + "type": "start", + "id": "sub1", + "payload": { + "query": 'subscription { infinity(message: "Hi") }', + }, + } + ) + data_message: DataMessage = await ws.receive_json() + assert data_message["type"] == "data" + assert data_message["id"] == "sub1" + + await ws.send_legacy_message( + { + "type": "start", + "id": "sub2", + "payload": { + "query": 'subscription { infinity(message: "Hi") }', + }, + } + ) + data_message = await ws.receive_json() + assert data_message["type"] == "data" + assert data_message["id"] == "sub2" + + # Third subscription should be rejected + await ws.send_legacy_message( + { + "type": "start", + "id": "sub3", + "payload": { + "query": 'subscription { infinity(message: "Hi") }', + }, + } + ) + error_message: ErrorMessage = await ws.receive_json() + assert error_message["type"] == "error" + assert error_message["id"] == "sub3" + assert error_message["payload"] == {"message": "Subscription limit reached"} + + # Completing one should free a slot + await ws.send_legacy_message({"type": "stop", "id": "sub1"}) + + await ws.send_legacy_message( + { + "type": "start", + "id": "sub4", + "payload": { + "query": 'subscription { infinity(message: "Hi") }', + }, + } + ) + # The 'complete' for sub1 is always sent before sub4's first data message + # (cleanup_operation awaits the cancelled task, which sends the complete). + msg = await ws.receive_json() + while msg.get("id") == "sub1": + msg = await ws.receive_json() + assert msg["type"] == "data" + assert msg["id"] == "sub4" + + await ws.close() + + +async def test_reusing_operation_id_does_not_count_against_limit( + http_client_class: type[HttpClient], +): + """Reusing an existing operation ID should clean up the old subscription + and not count against the subscription limit.""" + test_client = http_client_class(schema, max_subscriptions_per_connection=2) + + async with test_client.ws_connect( + "/graphql", protocols=[GRAPHQL_WS_PROTOCOL] + ) as ws: + await ws.send_legacy_message({"type": "connection_init"}) + response: ConnectionAckMessage = await ws.receive_json() + assert response["type"] == "connection_ack" + + # Fill up to the limit + await ws.send_legacy_message( + { + "type": "start", + "id": "sub1", + "payload": { + "query": 'subscription { infinity(message: "Hi") }', + }, + } + ) + data_message: DataMessage = await ws.receive_json() + assert data_message["type"] == "data" + assert data_message["id"] == "sub1" + + await ws.send_legacy_message( + { + "type": "start", + "id": "sub2", + "payload": { + "query": 'subscription { infinity(message: "Hi") }', + }, + } + ) + data_message = await ws.receive_json() + assert data_message["type"] == "data" + assert data_message["id"] == "sub2" + + # Reuse "sub1" — should clean up the old one, not hit the limit + await ws.send_legacy_message( + { + "type": "start", + "id": "sub1", + "payload": { + "query": 'subscription { infinity(message: "Hello") }', + }, + } + ) + + msg = await ws.receive_json() + # skip any leftover messages from the old sub1 + while msg.get("id") == "sub1" and msg.get("type") == "complete": + msg = await ws.receive_json() + + assert msg["type"] == "data" + assert msg["id"] == "sub1" + + await ws.close() + + +async def test_max_subscriptions_per_connection_disabled( + http_client_class: type[HttpClient], +): + """Test that setting max_subscriptions_per_connection to None disables the limit.""" + test_client = http_client_class(schema, max_subscriptions_per_connection=None) + + async with test_client.ws_connect( + "/graphql", protocols=[GRAPHQL_WS_PROTOCOL] + ) as ws: + await ws.send_legacy_message({"type": "connection_init"}) + response: ConnectionAckMessage = await ws.receive_json() + assert response["type"] == "connection_ack" + + # Should be able to create many subscriptions without limit + for i in range(5): + await ws.send_legacy_message( + { + "type": "start", + "id": f"sub{i}", + "payload": { + "query": 'subscription { infinity(message: "Hi") }', + }, + } + ) + data_message: DataMessage = await ws.receive_json() + assert data_message["type"] == "data" + assert data_message["id"] == f"sub{i}" + + for i in range(5): + await ws.send_legacy_message({"type": "stop", "id": f"sub{i}"}) + await ws.close()
Vulnerability mechanics
Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
5- github.com/advisories/GHSA-hv3w-m4g2-5x77ghsaADVISORY
- github.com/strawberry-graphql/strawberry/security/advisories/GHSA-hv3w-m4g2-5x77nvdVendor AdvisoryWEB
- nvd.nist.gov/vuln/detail/CVE-2026-35526ghsaADVISORY
- github.com/strawberry-graphql/strawberry/commit/0977a4e6b41b7cfe3e9d8ba84a43458a2b0c54c2ghsaWEB
- github.com/strawberry-graphql/strawberry/releases/tag/0.312.3ghsaWEB
News mentions
0No linked articles in our index yet.