Summary
A permission-checking flaw in Strawberry GraphQL can cause a protected synchronous field resolver to run when a custom permission function returns an awaitable that is not recognized and awaited. Because the awaitable object is truthy, authorization may be incorrectly granted even when the eventual permission result would be false.
A permission-checking flaw in Strawberry GraphQL can cause a protected synchronous field resolver to run when a custom permission function returns an awaitable that is not recognized and awaited. Because the awaitable object is truthy, authorization may be incorrectly granted even when the eventual permission result would be false.
Affected context
The issue can occur in Strawberry GraphQL applications that:
- Use synchronous field resolvers
- Apply custom permission extensions
- Implement
has_permission()as a normal function that returns an awaitable - Execute queries through
execute_sync()orexecute()
Applications using async def has_permission() or synchronous permission functions that directly return booleans are not generally affected by this specific behavior.
The issue can occur in Strawberry GraphQL applications that:
- Use synchronous field resolvers
- Apply custom permission extensions
- Implement
has_permission()as a normal function that returns an awaitable - Execute queries through
execute_sync()orexecute()
Applications using async def has_permission() or synchronous permission functions that directly return booleans are not generally affected by this specific behavior.
Exploit preconditions
- Attack surface: GraphQL fields protected by Strawberry permission extensions
- Required access: Ability to submit authenticated or unauthenticated GraphQL requests that reach the affected field, depending on the application's access model
- Trigger point: Authorization processing for a synchronous field resolver
- Why exploitation is possible: The permission framework evaluates an unawaited awaitable for truthiness rather than evaluating its eventual boolean result
- Attack surface: GraphQL fields protected by Strawberry permission extensions
- Required access: Ability to submit authenticated or unauthenticated GraphQL requests that reach the affected field, depending on the application's access model
- Trigger point: Authorization processing for a synchronous field resolver
- Why exploitation is possible: The permission framework evaluates an unawaited awaitable for truthiness rather than evaluating its eventual boolean result
Why this happens
The underlying bug is an authorization decision made from the truthiness of an awaitable object instead of its resolved value. A custom has_permission() function can return an awaitable while being declared as a normal function, causing asynchronous behavior detection to classify it incorrectly. This maps to CWE-863: Incorrect Authorization, because the application may permit access without correctly enforcing the intended permission result.
The underlying bug is an authorization decision made from the truthiness of an awaitable object instead of its resolved value. A custom has_permission() function can return an awaitable while being declared as a normal function, causing asynchronous behavior detection to classify it incorrectly. This maps to CWE-863: Incorrect Authorization, because the application may permit access without correctly enforcing the intended permission result.
Potential impact
- Unauthorized users may access protected GraphQL fields
- Sensitive application data could be disclosed
- Operations intended to be restricted may be performed
- Tenant or role boundaries could be weakened
- Downstream authorization assumptions may be bypassed
- Unauthorized users may access protected GraphQL fields
- Sensitive application data could be disclosed
- Operations intended to be restricted may be performed
- Tenant or role boundaries could be weakened
- Downstream authorization assumptions may be bypassed
General mitigation guidance
Mitigation focuses on ensuring that permission results are evaluated according to their actual execution model.
Common defensive patterns include:
- Use clearly defined asynchronous permission functions for awaitable checks
- Ensure permission callbacks return the expected boolean type for synchronous paths
- Keep authorization decisions centralized and explicit
- Apply deny-by-default behavior when permission results are ambiguous or improperly typed
- Enforce authorization independently at sensitive service or data-access boundaries
- Limit resolver and application privileges so an authorization defect has reduced impact
- Add regression tests covering synchronous resolvers, asynchronous permission results, and both execution APIs
Mitigation focuses on ensuring that permission results are evaluated according to their actual execution model.
Common defensive patterns include:
- Use clearly defined asynchronous permission functions for awaitable checks
- Ensure permission callbacks return the expected boolean type for synchronous paths
- Keep authorization decisions centralized and explicit
- Apply deny-by-default behavior when permission results are ambiguous or improperly typed
- Enforce authorization independently at sensitive service or data-access boundaries
- Limit resolver and application privileges so an authorization defect has reduced impact
- Add regression tests covering synchronous resolvers, asynchronous permission results, and both execution APIs
How teams detect issues like this
Organizations can look for this issue through:
- Static analysis identifying normal functions whose permission checks return awaitables
- Code review signals involving mixed synchronous and asynchronous resolver or permission APIs
- Tests that verify denied permissions remain denied under both synchronous and general execution paths
- Runtime authorization logging for unexpected access to protected fields
- Policy checks requiring explicit permission-result types and deny-by-default handling
Automated code review and type-checking tools may help identify inconsistent async declarations and return types.
Organizations can look for this issue through:
- Static analysis identifying normal functions whose permission checks return awaitables
- Code review signals involving mixed synchronous and asynchronous resolver or permission APIs
- Tests that verify denied permissions remain denied under both synchronous and general execution paths
- Runtime authorization logging for unexpected access to protected fields
- Policy checks requiring explicit permission-result types and deny-by-default handling
Automated code review and type-checking tools may help identify inconsistent async declarations and return types.

