GHSA-PFVF-FWFP-25MP

Vulnerability from github – Published: 2026-10-09 14:07 – Updated: 2026-10-09 14:07
VLAI
Summary
Strawberry GraphQL: Synchronous permission checks can treat an awaitable authorization result as truthy
Details

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

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.

Show details on source website

{
  "affected": [
    {
      "database_specific": {
        "last_known_affected_version_range": "\u003c= 0.326.0"
      },
      "package": {
        "ecosystem": "PyPI",
        "name": "strawberry-graphql"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0.217.0"
            },
            {
              "fixed": "0.326.1"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2026-107728"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-863"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2026-10-09T14:07:16Z",
    "nvd_published_at": "2026-10-08T23:16:58Z",
    "severity": "HIGH"
  },
  "details": "### Summary\n\n`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.\n\nThe 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.\n\n### Details\n\nThe 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()`.\n\n### PoC\n\n```python\nimport strawberry\nfrom strawberry.permission import BasePermission\n\n\nclass DenyViaAwaitable(BasePermission):\n    message = \"denied\"\n\n    def has_permission(self, source, info, **kwargs):\n        async def result():\n            return False\n\n        return result()\n\n\n@strawberry.type\nclass Query:\n    @strawberry.field(permission_classes=[DenyViaAwaitable])\n    def secret(self) -\u003e str:\n        return \"secret\"\n\n\nschema = strawberry.Schema(Query)\nprint(schema.execute_sync(\"{ secret }\").data)  # {\u0027secret\u0027: \u0027secret\u0027} instead of a permission error\n```\n\n### Impact\n\nAn 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.\n\n### Fix\n\nThe synchronous path now fails closed: if `has_permission()` returns an awaitable, an error is raised instead of granting access.",
  "id": "GHSA-pfvf-fwfp-25mp",
  "modified": "2026-10-09T14:07:17Z",
  "published": "2026-10-09T14:07:16Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/strawberry-graphql/strawberry/security/advisories/GHSA-pfvf-fwfp-25mp"
    },
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-107728"
    },
    {
      "type": "WEB",
      "url": "https://github.com/strawberry-graphql/strawberry/pull/4605"
    },
    {
      "type": "WEB",
      "url": "https://github.com/strawberry-graphql/strawberry/commit/2ebb79796c0e5ebb43cae1abd2b5a21363b1c00e"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/strawberry-graphql/strawberry"
    },
    {
      "type": "WEB",
      "url": "https://github.com/strawberry-graphql/strawberry/releases/tag/0.326.1"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N",
      "type": "CVSS_V3"
    }
  ],
  "summary": "Strawberry GraphQL: Synchronous permission checks can treat an awaitable authorization result as truthy"
}



Log in or create an account to share your comment.




Tags
Taxonomy of the tags.


Loading…

Loading…

Loading…

Forecast uses a logistic model when the trend is rising, or an exponential decay model when the trend is falling. Fitted via linearized least squares.

Sightings

Author Source Type Date Other

Nomenclature

  • Seen: The vulnerability was mentioned, discussed, or observed by the user.
  • Confirmed: The vulnerability has been validated from an analyst's perspective.
  • Published Proof of Concept: A public proof of concept is available for this vulnerability.
  • Exploited: The vulnerability was observed as exploited by the user who reported the sighting.
  • Patched: The vulnerability was observed as successfully patched by the user who reported the sighting.
  • Not exploited: The vulnerability was not observed as exploited by the user who reported the sighting.
  • Not confirmed: The user expressed doubt about the validity of the vulnerability.
  • Not patched: The vulnerability was not observed as successfully patched by the user who reported the sighting.

Loading…

Loading…

Loading…

Related by attack behaviour

Vulnerabilities whose description is nearest to this one in the vector space of the CIRCL/vulnerability-attack-technique-biencoder model. This is a similarity search over the bi-encoder space (plain cosine), not a classification, and it has no measured accuracy.


Loading…