Every CVE Search/ Get API key

CVE-2026-107727

Strawberry legacy graphql-ws retains naturally completed subscription slots

3.7 LowFix available

Strawberry GraphQL is a library for creating GraphQL APIs. From 0.312.3 until 0.327.2, the legacy graphql-ws subscription handler in strawberry/subscriptions/protocols/graphql_ws/handlers.py does not remove naturally completed operations from self.tasks and self.subscriptions. When max_subscriptions_per_connection is configured, a client on a persistent WebSocket connection can use distinct operation IDs for one-shot subscriptions to fill the connection's configured slots even after those subscriptions send complete, causing later legitimate operations on that connection to be rejected with Subscription limit reached. The modern graphql-transport-ws protocol and deployments without the configured per-connection cap are not affected. This issue is fixed in version 0.327.2.

Attack profile

How easy it is to exploit

Attack vector
Network
Complexity
High
Privileges needed
None
User interaction
None

What an attacker can affect

Confidentiality
None
Integrity
None
Availability
Low

Exploit likelihood

0.43%Up 0.1 points since Oct 9, 2026
90%50%10%1%

What each source reports

CVE List Score used 3.7

Strawberry legacy graphql-ws retains naturally completed subscription slots

Strawberry GraphQL is a library for creating GraphQL APIs. From 0.312.3 until 0.327.2, the legacy graphql-ws subscription handler in strawberry/subscriptions/protocols/graphql_ws/handlers.py does not remove naturally completed operations from self.tasks and self.subscriptions. When max_subscriptions_per_connection is configured, a client on a persistent WebSocket connection can use distinct operation IDs for one-shot subscriptions to fill the connection's configured slots even after those subscriptions send complete, causing later legitimate operations on that connection to be rejected with Subscription limit reached. The modern graphql-transport-ws protocol and deployments without the configured per-connection cap are not affected. This issue is fixed in version 0.327.2.

Record
CVE-2026-107727
CVSS 3.1
3.7 LowCVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:N/A:L
Weakness
CWE-400
Updated
2026-11-02
EUVDEUVD-2026-95391 3.7

Strawberry GraphQL is a library for creating GraphQL APIs. From 0.312.3 until 0.327.2, the legacy graphql-ws subscription handler in strawberry/subscriptions/protocols/graphql_ws/handlers.py does not remove naturally completed operations from self.tasks and self.subscriptions. When max_subscriptions_per_connection is configured, a client on a persistent WebSocket connection can use distinct operation IDs for one-shot subscriptions to fill the connection's configured slots even after those subscriptions send complete, causing later legitimate operations on that connection to be rejected with Subscription limit reached. The modern graphql-transport-ws protocol and deployments without the configured per-connection cap are not affected. This issue is fixed in version 0.327.2.

Record
EUVD-2026-95391
CVSS 3.1
3.7 LowCVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:N/A:L
Updated
2026-11-02
GitHub AdvisoriesGHSA-m952-2w3f-6r8h 3.7

Strawberry legacy graphql-ws retains naturally completed subscription slots

Summary

Strawberry's legacy graphql-ws subscription handler can retain task and subscription bookkeeping after a one-shot subscription has naturally sent its complete message. When the application configures max_subscriptions_per_connection, the handler counts those completed operations in len(self.tasks). A client that uses distinct operation IDs can therefore reach the configured subscription limit even though the earlier subscriptions have already completed, causing subsequent legitimate subscriptions on the same persistent WebSocket connection to receive Subscription limit reached.

This is a conditional connection-level availability and resource-accounting issue. It requires explicit use of the legacy graphql-ws protocol, a persistent WebSocket connection, one-shot subscriptions that naturally complete, and a configured max_subscriptions_per_connection limit. It is not an unconditional issue in a default Strawberry installation and is distinct from the previously fixed single-connection infinite-subscription issue.

Details

The audited snapshot is Strawberry 0.324.4, commit c3caabd1188acda62045e7fd9ba9e37ac430cf96. The relevant implementation is in [strawberry/subscriptions/protocols/graphql_ws/handlers.py](https://github.com/strawberry-graphql/strawberry/blob/c3caabd1188acda62045e7fd9ba9e37ac430cf96/strawberry/subscriptions/protocols/graphql_ws/handlers.py), particularly the operation-start, result-handling, and cleanup paths.

When a new operation is started, the handler rejects it once the task count reaches the configured limit:

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

The operation task is stored in self.tasks, and its result source is stored in self.subscriptions. On normal exhaustion of the result source, handle_async_results() sends a completion message:

async for result in result_source:
    await self.send_data_message(result, operation_id)

await self.send_message(
    CompleteMessage(type="complete", id=operation_id)
)

The natural completion path does not call cleanup_operation() before returning. The deletion of the stored operation state is implemented separately:

async def cleanup_operation(self, operation_id: str) -> None:
    if operation_id in self.subscriptions:
        await self.subscriptions[operation_id].aclose()
        del self.subscriptions[operation_id]

    self.tasks[operation_id].cancel()
    await self.tasks[operation_id]
    del self.tasks[operation_id]

Consequently, after a one-shot operation has sent complete, its task entry can remain in self.tasks until the client explicitly stops the operation, reuses the same operation ID, or the connection is cleaned up. New operation IDs are compared against the retained task count and can be rejected.

The connection-slot impact requires both parts of the behavior:

  1. the natural-completion path leaves the completed operation accounted for; and
  2. the legacy handler has max_subscriptions_per_connection enabled.

PoC

The following reproduction is local-only and bounded. It uses an in-memory Channels WebSocket fixture, one connection, three one-shot subscriptions, and the legacy graphql-ws subprotocol. It does not connect to a public endpoint or start a network service.

1. Environment installation

Create an isolated virtual environment and install the official Channels integration extra together with the local ASGI test dependency:

python -m pip install "strawberry-graphql[channels]==0.324.4" daphne

…

Record
GHSA-m952-2w3f-6r8h
CVSS 3.1
3.7 LowCVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:N/A:L
Weakness
CWE-400
Updated
2026-11-02
NVD 3.7

Strawberry GraphQL is a library for creating GraphQL APIs. From 0.312.3 until 0.327.2, the legacy graphql-ws subscription handler in strawberry/subscriptions/protocols/graphql_ws/handlers.py does not remove naturally completed operations from self.tasks and self.subscriptions. When max_subscriptions_per_connection is configured, a client on a persistent WebSocket connection can use distinct operation IDs for one-shot subscriptions to fill the connection's configured slots even after those subscriptions send complete, causing later legitimate operations on that connection to be rejected with Subscription limit reached. The modern graphql-transport-ws protocol and deployments without the configured per-connection cap are not affected. This issue is fixed in version 0.327.2.

Record
CVE-2026-107727
CVSS 3.1
3.7 LowCVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:N/A:L
Weakness
CWE-400
Updated
2026-11-02
OSV 3.7

Strawberry legacy graphql-ws retains naturally completed subscription slots

Strawberry GraphQL is a library for creating GraphQL APIs. From 0.312.3 until 0.327.2, the legacy graphql-ws subscription handler in strawberry/subscriptions/protocols/graphql_ws/handlers.py does not remove naturally completed operations from self.tasks and self.subscriptions. When max_subscriptions_per_connection is configured, a client on a persistent WebSocket connection can use distinct operation IDs for one-shot subscriptions to fill the connection's configured slots even after those subscriptions send complete, causing later legitimate operations on that connection to be rejected with Subscription limit reached. The modern graphql-transport-ws protocol and deployments without the configured per-connection cap are not affected. This issue is fixed in version 0.327.2.

Record
CVE-2026-107727
CVSS 3.1
3.7 LowCVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:N/A:L
Weakness
CWE-400
Updated
2026-11-02
OSVGHSA-m952-2w3f-6r8h 3.7

Strawberry legacy graphql-ws retains naturally completed subscription slots

Summary

Strawberry's legacy graphql-ws subscription handler can retain task and subscription bookkeeping after a one-shot subscription has naturally sent its complete message. When the application configures max_subscriptions_per_connection, the handler counts those completed operations in len(self.tasks). A client that uses distinct operation IDs can therefore reach the configured subscription limit even though the earlier subscriptions have already completed, causing subsequent legitimate subscriptions on the same persistent WebSocket connection to receive Subscription limit reached.

This is a conditional connection-level availability and resource-accounting issue. It requires explicit use of the legacy graphql-ws protocol, a persistent WebSocket connection, one-shot subscriptions that naturally complete, and a configured max_subscriptions_per_connection limit. It is not an unconditional issue in a default Strawberry installation and is distinct from the previously fixed single-connection infinite-subscription issue.

Details

The audited snapshot is Strawberry 0.324.4, commit c3caabd1188acda62045e7fd9ba9e37ac430cf96. The relevant implementation is in [strawberry/subscriptions/protocols/graphql_ws/handlers.py](https://github.com/strawberry-graphql/strawberry/blob/c3caabd1188acda62045e7fd9ba9e37ac430cf96/strawberry/subscriptions/protocols/graphql_ws/handlers.py), particularly the operation-start, result-handling, and cleanup paths.

When a new operation is started, the handler rejects it once the task count reaches the configured limit:

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

The operation task is stored in self.tasks, and its result source is stored in self.subscriptions. On normal exhaustion of the result source, handle_async_results() sends a completion message:

async for result in result_source:
    await self.send_data_message(result, operation_id)

await self.send_message(
    CompleteMessage(type="complete", id=operation_id)
)

The natural completion path does not call cleanup_operation() before returning. The deletion of the stored operation state is implemented separately:

async def cleanup_operation(self, operation_id: str) -> None:
    if operation_id in self.subscriptions:
        await self.subscriptions[operation_id].aclose()
        del self.subscriptions[operation_id]

    self.tasks[operation_id].cancel()
    await self.tasks[operation_id]
    del self.tasks[operation_id]

Consequently, after a one-shot operation has sent complete, its task entry can remain in self.tasks until the client explicitly stops the operation, reuses the same operation ID, or the connection is cleaned up. New operation IDs are compared against the retained task count and can be rejected.

The connection-slot impact requires both parts of the behavior:

  1. the natural-completion path leaves the completed operation accounted for; and
  2. the legacy handler has max_subscriptions_per_connection enabled.

PoC

The following reproduction is local-only and bounded. It uses an in-memory Channels WebSocket fixture, one connection, three one-shot subscriptions, and the legacy graphql-ws subprotocol. It does not connect to a public endpoint or start a network service.

1. Environment installation

Create an isolated virtual environment and install the official Channels integration extra together with the local ASGI test dependency:

python -m pip install "strawberry-graphql[channels]==0.324.4" daphne

…

Record
GHSA-m952-2w3f-6r8h
CVSS 3.1
3.7 LowCVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:N/A:L
Weakness
CWE-400
Updated
2026-11-02
OSVPYSEC-2026-4302 3.7

Strawberry legacy graphql-ws retains naturally completed subscription slots

Summary

Strawberry's legacy graphql-ws subscription handler can retain task and subscription bookkeeping after a one-shot subscription has naturally sent its complete message. When the application configures max_subscriptions_per_connection, the handler counts those completed operations in len(self.tasks). A client that uses distinct operation IDs can therefore reach the configured subscription limit even though the earlier subscriptions have already completed, causing subsequent legitimate subscriptions on the same persistent WebSocket connection to receive Subscription limit reached.

This is a conditional connection-level availability and resource-accounting issue. It requires explicit use of the legacy graphql-ws protocol, a persistent WebSocket connection, one-shot subscriptions that naturally complete, and a configured max_subscriptions_per_connection limit. It is not an unconditional issue in a default Strawberry installation and is distinct from the previously fixed single-connection infinite-subscription issue.

Details

The audited snapshot is Strawberry 0.324.4, commit c3caabd1188acda62045e7fd9ba9e37ac430cf96. The relevant implementation is in [strawberry/subscriptions/protocols/graphql_ws/handlers.py](https://github.com/strawberry-graphql/strawberry/blob/c3caabd1188acda62045e7fd9ba9e37ac430cf96/strawberry/subscriptions/protocols/graphql_ws/handlers.py), particularly the operation-start, result-handling, and cleanup paths.

When a new operation is started, the handler rejects it once the task count reaches the configured limit:

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

The operation task is stored in self.tasks, and its result source is stored in self.subscriptions. On normal exhaustion of the result source, handle_async_results() sends a completion message:

async for result in result_source:
    await self.send_data_message(result, operation_id)

await self.send_message(
    CompleteMessage(type="complete", id=operation_id)
)

The natural completion path does not call cleanup_operation() before returning. The deletion of the stored operation state is implemented separately:

async def cleanup_operation(self, operation_id: str) -> None:
    if operation_id in self.subscriptions:
        await self.subscriptions[operation_id].aclose()
        del self.subscriptions[operation_id]

    self.tasks[operation_id].cancel()
    await self.tasks[operation_id]
    del self.tasks[operation_id]

Consequently, after a one-shot operation has sent complete, its task entry can remain in self.tasks until the client explicitly stops the operation, reuses the same operation ID, or the connection is cleaned up. New operation IDs are compared against the retained task count and can be rejected.

The connection-slot impact requires both parts of the behavior:

  1. the natural-completion path leaves the completed operation accounted for; and
  2. the legacy handler has max_subscriptions_per_connection enabled.

PoC

The following reproduction is local-only and bounded. It uses an in-memory Channels WebSocket fixture, one connection, three one-shot subscriptions, and the legacy graphql-ws subprotocol. It does not connect to a public endpoint or start a network service.

1. Environment installation

Create an isolated virtual environment and install the official Channels integration extra together with the local ASGI test dependency:

python -m pip install "strawberry-graphql[channels]==0.324.4" daphne

…

Record
PYSEC-2026-4302
CVSS 3.1
3.7 LowCVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:N/A:L
Updated
2026-11-02

Affected products

Affected packages

Affected packages
PackageAffectedFixed
strawberry-graphql
PyPI
0.312.3 up to 0.327.2
0.327.2
strawberry-graphql
PyPI
0.312.3 up to 0.327.2, plus listed versions
0.312.40.313.00.314.00.314.10.314.2
Show 40 moreShow less
0.314.30.315.00.315.10.315.20.315.30.315.40.315.50.315.60.315.70.316.00.317.00.317.10.317.20.318.00.318.10.319.00.320.00.320.10.320.20.320.30.320.40.321.00.321.10.322.00.322.10.322.20.323.00.323.10.323.20.324.00.324.10.324.20.324.30.324.40.324.50.325.00.326.00.326.10.327.00.327.1
0.327.2

History

History
RecordedChangedSources
first indexedCVE List, EUVD, GitHub Advisories, NVD, OSV

Dates are when Every CVE recorded each change. Upstream dates may differ.

In short

CVE-2026-107727 is a low vulnerability (CVSS 3.7) in Strawberry-graphql Strawberry, published October 8, 2026. No source lists public exploit code. EPSS puts the chance of exploitation in the next 30 days at 0.43%, the 37th percentile. Fixed in strawberry-graphql 0.327.2.

References

This page doesn't list everything

Left out here: the rest of 3 source descriptions. The JSON record has them.

MoveEnter Open