GHSA-pfvf-fwfp-25mp
Strawberry GraphQL: Synchronous permission checks can treat an awaitable authorization result as truthy
Packages
| Ecosystem | Package | Affected | Fixed in |
|---|---|---|---|
| PyPI | strawberry-graphql | >= 0.217.0, < 0.326.1 | 0.326.1 |
Details
As written in the advisory (Markdown source, shown as plain text).
### Summary
`PermissionExtension.resolve()` evaluates the return value of `has_permission()` for truthiness on the synchronous path. `supports_sync` only classifies a permission as asynchronous when `has_permission` 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 `execute_sync()` and `execute()`. Permissions declared with `async def has_permission()`, 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 `has_permission` is a normal `def` returning an awaitable reaches this path; the awaitable is never awaited and its truthiness grants access. `resolve_async()` is not affected because it uses `await_maybe()`.
### PoC
```python
import strawberry
from strawberry.permission import BasePermission
class DenyViaAwaitable(BasePermission):
message = "denied"
def has_permission(self, source, info, **kwargs):
async def result():
return False
return result()
@strawberry.type
class Query:
@strawberry.field(permission_classes=[DenyViaAwaitable])
def secret(self) -> str:
return "secret"
schema = strawberry.Schema(Query)
print(schema.execute_sync("{ secret }").data) # {'secret': 'secret'} instead of a permission error
```
### Impact
An application using a custom permission whose `has_permission` 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 `has_permission()` returns an awaitable, an error is raised instead of granting access.
References
External links from the advisory. They lead to third-party sites.
- https://github.com/strawberry-graphql/strawberry/security/advisories/GHSA-pfvf-fwfp-25mp
- https://nvd.nist.gov/vuln/detail/CVE-2026-107728
- https://github.com/strawberry-graphql/strawberry/pull/4605
- https://github.com/strawberry-graphql/strawberry/commit/2ebb79796c0e5ebb43cae1abd2b5a21363b1c00e
- https://github.com/strawberry-graphql/strawberry
- https://github.com/strawberry-graphql/strawberry/releases/tag/0.326.1