CVE-2026-54768: Wp graphql/wp graphql WPGraphQL Has Deprecated `user` Field Vulnerability

MODERATE
Advisory Identifier: GHSA-jhh7-832h-f8hv
CWE-204
Published
Advisory Summary

CVE-2026-54768 is a MODERATE wpgraphql has deprecated `user` field vulnerability (CWE-204: Observable Response Discrepancy) affecting Wp graphql/wp graphql libraries in the Packagist ecosystem. The issue involves wpgraphql has deprecated `user` field on sendpasswordresetemailpayload that leaks user existence + profile (defeats explicit anti-enumeration design). Remediation is available in the latest vendor maintenance releases.

CVE-2026-54768 at a Glance

CVE Identifier CVE-2026-54768
GHSA Identifier GHSA-jhh7-832h-f8hv
Severity MODERATE
CVSS v3.1 Base Score N/A
CWE Classification CWE-204: Observable Response Discrepancy
Affected Product Wp graphql/wp graphql
Package Ecosystem Packagist
Published Date
Last Updated

How to Fix CVE-2026-54768

To remediate this vulnerability, upgrade all affected Wp graphql/wp graphql dependencies to a patched release. Both direct and transitive dependencies should be checked, as frameworks or security libraries may pull in vulnerable versions transitively.

wp-graphql/wp-graphql fixed: Latest secure release
Upgrade wp-graphql/wp-graphql to Latest secure release
Verify Direct and Transitive Dependencies

Run the following command in your project directory to verify that all resolved dependencies use the patched release:

inspect dependencies for wp-graphql/wp-graphql

How to Check if You Are Affected

To determine whether your application is affected by CVE-2026-54768, inspect your project dependencies for Wp graphql/wp graphql:

inspect dependencies for wp-graphql/wp-graphql

Analyze the terminal output to locate any version of Wp graphql/wp graphql in your dependency tree. If any resolved version matches the vulnerable ranges listed in the table below (for example, versions earlier than the patched release), your application is affected.

Note: Verify declared build manifests and deployed runtime container environments.

Technical Details

Vulnerability Mechanism & Technical Analysis

This vulnerability is classified under CWE-204: Observable Response Discrepancy, where the product provides different responses to incoming requests in a way that reveals internal state information to an unauthorized actor outside of the intended control sphere. In Wp graphql/wp graphql, software state validation or input boundaries are circumvented during processing. Review the official source advisory statement below for specific affected components, classes, and reproduction details.

Source Advisory Statement

## Summary The `sendPasswordResetEmail` mutation in WPGraphQL is explicitly designed to prevent user enumeration. The resolver in `src/Mutation/SendPasswordResetEmail.php` states in a code comment: `// We obsfucate the actual success of this mutation to prevent user enumeration.` The mutation always returns `success: true` regardless of whether the supplied username/email belongs to an existing user. The intended public output field is only `success: Boolean`. However, a deprecated `user` field is still registered on the `SendPasswordResetEmailPayload` output type in `src/Deprecated.php` (lines 433-450). This deprecated field resolves to a full `User` object when the supplied username/email corresponds to an existing author-class user, and `null` otherwise — completely undermining the anti-enumeration design. The `@todo remove in 3.0.0` comment acknowledges the field is scheduled for removal, but it remains active in all 2.x releases, including current 2.14.1. Discovered via source code review on May 29, 2026. ## Details The mutation resolver in `src/Mutation/SendPasswordResetEmail.php`: ```php $payload = ['success' => true, 'id' => null]; $user_data = self::get_user_data($input['username']); if (!$user_data) { graphql_debug(...); return $payload; // id stays null } // ...send email, then... return ['id' => $user_data->ID, 'success' => true]; ``` The intended public output field is only `success`. The `id` is internal-only state for downstream resolvers. `src/Deprecated.php` registers an additional `user` field on the same payload type: ```php register_graphql_field( 'SendPasswordResetEmailPayload', 'user', [ 'type' => 'User', 'deprecationReason' => static function () { return __('This field will be removed...'); }, 'resolve' => static function ($payload, $args, AppContext $context) { return !empty($payload['id']) ? $context->get_loader('user')->load_deferred($payload['id']) : null; }, ], ); ``` This field reads the internal `$payload['id']` and resolves it through the standard user loader. The User Model's `allowed_restricted_fields` policy permits unauthenticated reads of public author fields (`databaseId`, `name`, `firstName`, `lastName`, `slug`, `description`, `uri`, `url`). ## PoC ```graphql mutation EnumerateUser { sendPasswordResetEmail(input: { username: "victim@example.com" }) { success user { databaseId name firstName lastName slug description uri } } } ``` Behavior: - Non-existing user/email → `data.sendPasswordResetEmail.user` is `null` - - Existing author-class user → `data.sendPasswordResetEmail.user` is a full User object with the listed fields populated - - `success` always returns `true`, preserving the appearance of obfuscation — the deprecated `user` field is the leak ## Impact 1. **Username/email enumeration:** unauthenticated attacker can verify whether any username or email is registered, with no WPGraphQL-side rate limiting 2. 2. **Profile disclosure for author-class users:** for any user with published posts (including editors and administrators), the attacker obtains `databaseId`, `name`, `firstName`, `lastName`, `slug`, `description` (user bio), `uri` — substantially more than mere existence 3. 3. **Bypasses partial hardening:** sites that disabled the REST API user endpoint, the user XML sitemap, and `?author=N` author redirects may still be vulnerable through this WPGraphQL path 4. 4. **Spearphishing setup:** firstName/lastName/description for authors provides personalized phishing material ## Recommended fix Either remove the deprecated `user` field entirely (advance the existing `@todo remove in 3.0.0`) or change the resolver to always return `null`: ```diff 'resolve' => static function ($payload, $args, AppContext $context) { - return !empty($payload['id']) ? $context->get_loader('user')->load_deferred($payload['id']) : null; - + // Always null — this deprecated field previously leaked user existence, - + // undermining the anti-enumeration design of the sendPasswordResetEmail mutation. - + return null; - }, - ``` Defense in depth — change the mutation resolver itself to not populate `$payload['id']` on real success: ```diff return [ - 'id' => $user_data->ID, - + 'id' => null, - 'success' => true, - ]; - ``` Luke Granto — independent security researcher operating in good faith. Discovery via source code review of wp-graphql/wp-graphql v2.14.1, approximately 15 minutes from `git clone` to confirmed bug. No live exploitation against any third-party deployment.

CVE-2026-54768 Affected Wp graphql/wp graphql Versions

1 component(s)
Product / Package Ecosystem Affected Versions Fixed Version
wp-graphql/wp-graphql
Packagist
≤ 2.6.0

References

Frequently Asked Questions

What is CVE-2026-54768?
CVE-2026-54768 is a security vulnerability in Wp graphql/wp graphql involving WPGraphQL has deprecated `user` field on SendPasswordResetEmailPayload that leaks user existence + profile (defeats explicit anti-enumeration design) under CWE-204: Observable Response Discrepancy.
Which Wp graphql/wp graphql versions are affected?
Affected versions include releases prior to current release patches across affected packages: wp-graphql/wp-graphql.
What version fixes CVE-2026-54768?
Upgrading to current release patches (or the latest patched release for your respective branch) resolves the vulnerability.
Is wp-graphql/wp-graphql affected?
Yes, wp-graphql/wp-graphql is affected in releases prior to Latest secure release. Upgrade your dependency to Latest secure release or later.
What CWE is associated with CVE-2026-54768?
CVE-2026-54768 is classified under CWE-204: Observable Response Discrepancy (The product provides different responses to incoming requests in a way that reveals internal state information to an unauthorized actor outside of the intended control sphere.).
How can I check Packagist dependencies for CVE-2026-54768?
Run the dependency tree command: 'inspect dependencies for wp-graphql/wp-graphql'. This command lists direct and transitive dependencies in your build configuration so you can verify if a vulnerable version is resolved.
Machine-readable OSV JSON OSV v1.5 Schema
Normalized schema output
{
  "schema_version" : "1.4.0",
  "id" : "GHSA-jhh7-832h-f8hv",
  "modified" : "2026-07-31T22:24:29Z",
  "published" : "2026-07-31T22:24:29Z",
  "aliases" : [ "CVE-2026-54768" ],
  "summary" : "WPGraphQL has deprecated `user` field on SendPasswordResetEmailPayload that leaks user existence + profile (defeats explicit anti-enumeration design)",
  "details" : "## Summary\n\nThe `sendPasswordResetEmail` mutation in WPGraphQL is explicitly designed to prevent user enumeration. The resolver in `src/Mutation/SendPasswordResetEmail.php` states in a code comment:\n\n`// We obsfucate the actual success of this mutation to prevent user enumeration.`\n\nThe mutation always returns `success: true` regardless of whether the supplied username/email belongs to an existing user. The intended public output field is only `success: Boolean`.\n\nHowever, a deprecated `user` field is still registered on the `SendPasswordResetEmailPayload` output type in `src/Deprecated.php` (lines 433-450). This deprecated field resolves to a full `User` object when the supplied username/email corresponds to an existing author-class user, and `null` otherwise — completely undermining the anti-enumeration design.\n\nThe `@todo remove in 3.0.0` comment acknowledges the field is scheduled for removal, but it remains active in all 2.x releases, including current 2.14.1.\n\nDiscovered via source code review on May 29, 2026.\n\n## Details\n\nThe mutation resolver in `src/Mutation/SendPasswordResetEmail.php`:\n\n```php\n$payload = ['success' => true, 'id' => null];\n$user_data = self::get_user_data($input['username']);\nif (!$user_data) {\n    graphql_debug(...);\n    return $payload;  // id stays null\n}\n// ...send email, then...\nreturn ['id' => $user_data->ID, 'success' => true];\n```\n\nThe intended public output field is only `success`. The `id` is internal-only state for downstream resolvers.\n\n`src/Deprecated.php` registers an additional `user` field on the same payload type:\n\n```php\nregister_graphql_field(\n    'SendPasswordResetEmailPayload',\n    'user',\n    [\n        'type' => 'User',\n        'deprecationReason' => static function () { return __('This field will be removed...'); },\n        'resolve' => static function ($payload, $args, AppContext $context) {\n            return !empty($payload['id'])\n                ? $context->get_loader('user')->load_deferred($payload['id'])\n                : null;\n        },\n    ],\n);\n```\n\nThis field reads the internal `$payload['id']` and resolves it through the standard user loader. The User Model's `allowed_restricted_fields` policy permits unauthenticated reads of public author fields (`databaseId`, `name`, `firstName`, `lastName`, `slug`, `description`, `uri`, `url`).\n\n## PoC\n\n```graphql\nmutation EnumerateUser {\n  sendPasswordResetEmail(input: { username: \"victim@example.com\" }) {\n    success\n    user {\n      databaseId\n      name\n      firstName\n      lastName\n      slug\n      description\n      uri\n    }\n  }\n}\n```\n\nBehavior:\n- Non-existing user/email → `data.sendPasswordResetEmail.user` is `null`\n- - Existing author-class user → `data.sendPasswordResetEmail.user` is a full User object with the listed fields populated\n- - `success` always returns `true`, preserving the appearance of obfuscation — the deprecated `user` field is the leak\n## Impact\n\n1. **Username/email enumeration:** unauthenticated attacker can verify whether any username or email is registered, with no WPGraphQL-side rate limiting\n2. 2. **Profile disclosure for author-class users:** for any user with published posts (including editors and administrators), the attacker obtains `databaseId`, `name`, `firstName`, `lastName`, `slug`, `description` (user bio), `uri` — substantially more than mere existence\n3. 3. **Bypasses partial hardening:** sites that disabled the REST API user endpoint, the user XML sitemap, and `?author=N` author redirects may still be vulnerable through this WPGraphQL path\n4. 4. **Spearphishing setup:** firstName/lastName/description for authors provides personalized phishing material\n## Recommended fix\n\nEither remove the deprecated `user` field entirely (advance the existing `@todo remove in 3.0.0`) or change the resolver to always return `null`:\n\n```diff\n'resolve' => static function ($payload, $args, AppContext $context) {\n-    return !empty($payload['id']) ? $context->get_loader('user')->load_deferred($payload['id']) : null;\n- +    // Always null — this deprecated field previously leaked user existence,\n- +    // undermining the anti-enumeration design of the sendPasswordResetEmail mutation.\n- +    return null;\n- },\n- ```\nDefense in depth — change the mutation resolver itself to not populate `$payload['id']` on real success:\n\n```diff\nreturn [\n-    'id'      => $user_data->ID,\n- +    'id'      => null,\n-      'success' => true,\n- ];\n- ```\n\nLuke Granto — independent security researcher operating in good faith. Discovery via source code review of wp-graphql/wp-graphql v2.14.1, approximately 15 minutes from `git clone` to confirmed bug. No live exploitation against any third-party deployment.",
  "severity" : [ {
    "type" : "CVSS_V4",
    "score" : "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:L/VI:N/VA:N/SC:N/SI:N/SA:N"
  } ],
  "affected" : [ {
    "package" : {
      "ecosystem" : "Packagist",
      "name" : "wp-graphql/wp-graphql"
    },
    "ranges" : [ {
      "type" : "ECOSYSTEM",
      "events" : [ {
        "introduced" : "0"
      }, {
        "last_affected" : "2.6.0"
      } ]
    } ]
  } ],
  "references" : [ {
    "type" : "WEB",
    "url" : "https://github.com/wp-graphql/wp-graphql/security/advisories/GHSA-jhh7-832h-f8hv"
  }, {
    "type" : "PACKAGE",
    "url" : "https://github.com/wp-graphql/wp-graphql"
  }, {
    "type" : "WEB",
    "url" : "https://github.com/wp-graphql/wp-graphql/releases/tag/wp-graphql/v2.15.1"
  } ],
  "database_specific" : {
    "cwe_ids" : [ "CWE-204" ],
    "severity" : "MODERATE",
    "github_reviewed" : true,
    "github_reviewed_at" : "2026-07-31T22:24:29Z",
    "nvd_published_at" : null
  }
}