Skip to content

Prevent permission bypass when sync has_permission returns an awaitable - #4605

Merged
patrick91 merged 1 commit into
mainfrom
fix/awaitable-permissions
Aug 31, 2026
Merged

Prevent permission bypass when sync has_permission returns an awaitable#4605
patrick91 merged 1 commit into
mainfrom
fix/awaitable-permissions

Conversation

@patrick91

@patrick91 patrick91 commented Aug 31, 2026

Copy link
Copy Markdown
Member

Summary by Sourcery

Fail closed when synchronous permission checks return awaitables to prevent protected fields from being accessed accidentally.

Bug Fixes:

  • Prevent synchronous permission checks from granting access when has_permission returns an awaitable by failing closed with a clear error.

Enhancements:

  • Add a dedicated exception explaining how to declare asynchronous permissions correctly and avoid leaked coroutine warnings.

Documentation:

  • Document the permission bypass fix and clarify supported synchronous and asynchronous has_permission implementations in the release notes.

Tests:

  • Add synchronous and asynchronous execution coverage ensuring awaitable-returning permissions block protected fields and prevent resolver execution.

@github-actions

Copy link
Copy Markdown
Contributor

Thanks for adding the RELEASE.md file!

Below is the changelog that will be used for the release.


This release fixes a permission bypass (GHSA-pfvf-fwfp-25mp) where a custom
permission could unintentionally authorize access to a protected field.

When a permission's has_permission was a normal def that returned an
awaitable (for example a wrapper returning a coroutine), Strawberry classified
the permission as synchronous because only async def methods are detected as
async. On the synchronous resolve path the returned awaitable was evaluated for
truthiness directly, and an awaitable is always truthy — so the check passed and
the protected resolver ran even when the awaitable resolved to False. This
affected any field with a synchronous resolver, under both execute_sync and
execute.

Strawberry now detects this case and fails closed: the synchronous permission
path raises a clear error instead of trusting the awaitable, so access is never
granted by accident. Permissions written as async def has_permission continue
to work as before. If you intend a permission to be asynchronous, declare it
with async def (or return a plain boolean from a synchronous one).

This release was contributed by @patrick91 in #4605

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've reviewed your changes and they look great!

Sourcery assessment

Approved.


Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

@greptile-apps

greptile-apps Bot commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This patch closes an authorization bypass caused by synchronous permission methods returning awaitables.

  • Detects awaitable permission results before truth-testing them on synchronous resolver chains.
  • Closes coroutine objects and raises a dedicated configuration error instead of invoking the protected resolver.
  • Adds coverage for synchronous and asynchronous schema execution and documents the security fix.

Confidence Score: 5/5

The PR appears safe to merge and closes the permission bypass across the reachable synchronous and asynchronous execution paths.

Synchronous permission chains now reject awaitable results before truthiness can authorize access, while asynchronous chains continue to await those results correctly.

Important Files Changed

Filename Overview
strawberry/permission.py Adds a fail-closed guard for awaitables on synchronous permission chains while preserving correct awaiting on asynchronous chains.
strawberry/exceptions/init.py Introduces and exports a clear exception for awaitables returned in a synchronous permission context.
tests/schema/test_permission.py Verifies that the protected resolver remains uncalled under both execute_sync and execute.
RELEASE.md Accurately documents the permission bypass, affected execution paths, and required permission implementation contract.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[Field resolution] --> B[Call synchronous has_permission]
    B --> C{Result is awaitable?}
    C -- Yes --> D{Result is coroutine?}
    D -- Yes --> E[Close coroutine]
    D -- No --> F[Raise configuration error]
    E --> F
    C -- No --> G{Result is truthy?}
    G -- No --> H[Return unauthorized response]
    G -- Yes --> I[Invoke protected resolver]
Loading

Reviews (1): Last reviewed commit: "Prevent permission bypass when sync has_..." | Re-trigger Greptile

@patrick91
patrick91 enabled auto-merge (squash) August 31, 2026 21:56
@codspeed-hq

codspeed-hq Bot commented Aug 31, 2026

Copy link
Copy Markdown

Merging this PR will not alter performance

✅ 34 untouched benchmarks


Comparing fix/awaitable-permissions (4da3ba0) with main (073c4b4)1

Open in CodSpeed

Footnotes

  1. No successful run was found on main (4e975a2) during the generation of this report, so 073c4b4 was used instead as the comparison base. There might be some changes unrelated to this pull request in this report.

@patrick91
patrick91 merged commit 2ebb797 into main Aug 31, 2026
86 checks passed
@patrick91
patrick91 deleted the fix/awaitable-permissions branch August 31, 2026 22:02
@botberry

Copy link
Copy Markdown
Member

This PR was published as 0.326.1. Thank you for contributing!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants