CVE-2026-107728: Strawberry GraphQL: Synchronous permission checks can treat an awaitable authorization result as truthy
Summary
PermissionExtension.resolve() evaluates the return value of haspermission() for truthiness on the synchronous path. supportssync only classifies a permission as asynchronous when haspermission is declared with async def (via inspect.iscoroutinefunction), so a plain def that returns an awaitable is treated as synchronous. An awaitable is always truthy, so the check passes even when it resolves to False and the protected resolver runs.
The resolve path is chosen by the field resolver, not by the execution method, so any field with a synchronous resolver is affected under both executesync() and execute(). Permissions declared with async def haspermission(), or a plain def returning a boolean, are not affected.
Details
The affected code is PermissionExtension.resolve() in strawberry/permission.py. A permission attached to a field whose haspermission is a normal def returning an awaitable reaches this path; the awaitable is never awaited and its truthiness grants access. resolveasync() is not affected because it uses awaitmaybe().
PoC
python import strawberry from strawberry.permission import BasePermission
class DenyViaAwaitable(BasePermission): message = "denied"
def haspermission(self, source, info, kwargs): async def result(): return False
return result()
@strawberry.type class Query: @strawberry.field(permissionclasses=[DenyViaAwaitable]) def secret(self) -> str: return "secret"
schema = strawberry.Schema(Query) print(schema.executesync("{ secret }").data) # {'secret': 'secret'} instead of a permission error
Impact
An application using a custom permission whose haspermission is a normal def returning an awaitable can unintentionally grant access to the protected field. Standard permissions (a def returning a boolean, or an async def) are not affected, so exploitability depends on the application using this specific permission shape.
Fix
The synchronous path now fails closed: if haspermission() returns an awaitable, an error is raised instead of granting access.
Other sources
Strawberry GraphQL is a library for creating GraphQL APIs. From 0.217.0 until 0.326.1, PermissionExtension.resolve() on a synchronous field resolver evaluates the result of haspermission() for truthiness. When a custom permission declares haspermission() as a normal function but returns an awaitable, supportssync does not classify it as asynchronous, the awaitable is not awaited, and its inherently truthy object value permits the protected resolver to run even when the result would resolve to false. This affects synchronous field resolvers under both executesync() and execute(); permissions declared with async def haspermission() and synchronous permissions returning a boolean are not affected. This issue is fixed in version 0.326.1.
— MITRE
Affected Software
Remediation
Recommended actions to resolve this vulnerability, in priority order.
- Upgrade
Upgrade
pip/strawberry-graphqlto a version that resolves this vulnerability.Fixed in 0.326.1 - Upgrade
Upgrade
Strawberry GraphQLto a version that resolves this vulnerability.Fixed in 0.326.1
Event History
Frequently Asked Questions
Which applications are exposed to an authorization bypass?
Applications are exposed if they use Strawberry GraphQL versions 0.217.0 through 0.326.0, have synchronous field resolvers, and attach custom permissions whose normal-function has_permission() method returns an awaitable. The issue applies when those resolvers are run through either execute_sync() or execute().
What permission implementations are not affected?
Permissions implemented with async def has_permission() are not affected. Synchronous has_permission() implementations that return a boolean are also not affected.
How can teams identify whether their code is vulnerable?
Review custom permission classes used on synchronous resolvers for has_permission() methods declared as normal functions that return a coroutine or other awaitable rather than a boolean. In the affected versions, a false authorization result from that awaitable can be treated as allowed because the awaitable object itself is truthy.
What is the available remediation?
Upgrade strawberry-graphql to version 0.326.1, which fixes the issue. If an immediate upgrade is not possible, avoid using normal-function has_permission() implementations that return awaitables on synchronous field resolvers; use an async has_permission() implementation or a synchronous boolean-returning implementation instead.