Skip to main content
Vulnerability Database/CVE-2026-88974

CVE-2026-88974: WPGraphQL Authentication Bypass Vulnerability

CVE-2026-88974 is an authentication bypass flaw in WPGraphQL that allows Contributors to publish drafts and modify published posts without proper authorization. This article covers technical details, affected versions, impact, and mitigation steps.

Published:

CVE-2026-88974 Overview

CVE-2026-88974 is a broken access control vulnerability [CWE-863] in WPGraphQL, a plugin that exposes a GraphQL API for WordPress sites. Versions prior to 2.22.2 fail to enforce object-level capability checks in the updatePost mutation defined in src/Mutation/PostObjectUpdate.php. The mutation only validates the collection-level edit_posts capability and post authorship. It does not check the object-level edit_post capability or require publish_posts for status transitions to public. An authenticated Contributor can self-publish their own drafts and modify their previously published posts without editorial approval.

Critical Impact

Authenticated Contributors can bypass editorial workflow controls to publish drafts and edit published posts owned by them, undermining WordPress content moderation.

Affected Products

  • WPGraphQL plugin for WordPress prior to version 2.22.2
  • WordPress sites exposing the GraphQL API via WPGraphQL
  • Editorial workflows relying on Contributor role restrictions

Discovery Timeline

  • 2026-09-23 - CVE-2026-88974 published to NVD
  • 2026-09-23 - Last updated in NVD database

Technical Details for CVE-2026-88974

Vulnerability Analysis

The flaw resides in the updatePost mutation resolver in plugins/wp-graphql/src/Mutation/PostObjectUpdate.php. WordPress uses a meta capability model in which edit_post maps dynamically to either edit_posts, edit_others_posts, or edit_published_posts depending on the post state. The vulnerable resolver only checks the generic edit_posts capability and confirms the requester is the post author. It never runs the edit_post meta capability against the specific post ID, and it does not require publish_posts when a status transition moves a post to publish.

A Contributor role, by default, holds edit_posts but lacks publish_posts and edit_published_posts. The missing object-level check allows a Contributor to promote their own draft to a published state, and to modify a post they authored after publication. Posts authored by other users remain protected because the author-match check still blocks cross-author edits.

Root Cause

The root cause is a missing object-level authorization check. The resolver relied exclusively on collection-level capability evaluation and an author identity comparison. WordPress core and the REST API map edit_post to edit_published_posts once a post is published, so an equivalent REST request returns rest_cannot_edit. WPGraphQL did not mirror this behavior, resulting in inconsistent authorization enforcement between the two APIs.

Attack Vector

An attacker requires an authenticated account with the Contributor role or an equivalent custom role holding edit_posts but not publish_posts. The attacker sends a GraphQL updatePost mutation targeting one of their own drafts with a status of publish, or targeting their own already-published post with modified fields. The mutation succeeds despite the missing capabilities.

php
// Patch applied in PostObjectUpdate.php (version 2.22.2)
// Source: https://github.com/wp-graphql/wp-graphql/commit/55441663eaa33c3f2e05de038c8286c845916461

throw new UserError( esc_html( sprintf( __( 'Sorry, you are not allowed to update another author\'s %1$s', 'wp-graphql' ), $post_type_object->graphql_single_name ) ) );
}

/**
 * Enforce the object-level edit capability for this specific post. WordPress maps
 * the `edit_post` meta capability to `edit_published_posts` once a post is
 * published, so a user who can create and edit drafts (e.g. a Contributor) cannot
 * edit a post after it has been published. This mirrors the WordPress REST API,
 * which returns `rest_cannot_edit` for the same request.
 */
if ( ! isset( $post_type_object->cap->edit_post ) || ! current_user_can( $post_type_object->cap->edit_post, $post_id ) ) {
    // translators: the placeholder is the singular name of the post type being mutated
    throw new UserError( esc_html( sprintf( __( 'Sorry, you are not allowed to update this %1$s', 'wp-graphql' ), $post_type_object->graphql_single_name ) ) );
}

$author_id = absint( $existing_post->post_author );

The fix inserts a current_user_can( $post_type_object->cap->edit_post, $post_id ) call that resolves the edit_post meta capability against the specific post, closing the authorization gap.

Detection Methods for CVE-2026-88974

Indicators of Compromise

  • GraphQL updatePost mutations from Contributor-level accounts that set status: publish on their own drafts.
  • Published posts whose author has a role that does not include publish_posts.
  • Edits to previously published posts by users who lack edit_published_posts, without corresponding REST or admin activity.
  • Post revisions where the status transitioned from draft or pending to publish without an editor-role user in the audit trail.

Detection Strategies

  • Inspect webserver access logs for POST requests to /graphql containing the updatePost operation from low-privilege session cookies.
  • Correlate WordPress wp_posts.post_status transitions with the acting user's role at the time of the change using the post_modified and revision tables.
  • Enable GraphQL query logging via WPGraphQL debug settings and alert on updatePost operations executed by Contributor accounts.

Monitoring Recommendations

  • Forward WordPress and webserver logs to a centralized analytics platform and build detections keyed on updatePost mutations paired with role metadata.
  • Track the WPGraphQL plugin version across managed WordPress fleets and flag any host running a version below 2.22.2.
  • Alert on any post published by an account whose role lacks the publish_posts capability.

How to Mitigate CVE-2026-88974

Immediate Actions Required

  • Upgrade the WPGraphQL plugin to version 2.22.2 or later on all WordPress installations.
  • Audit published posts authored by Contributor-level accounts since the vulnerable version was deployed.
  • Review recent GraphQL request logs for updatePost operations from non-editorial accounts.
  • Restrict GraphQL API exposure to authenticated internal traffic where public access is not required.

Patch Information

The issue is fixed in WPGraphQL 2.22.2. The patch adds an object-level edit_post capability check inside the updatePost resolver, aligning WPGraphQL authorization with the WordPress REST API. See the GitHub Release v2.22.2, the GitHub Pull Request #4270, and the GitHub Security Advisory GHSA-5mmc-8pc9-wggg for full details.

Workarounds

  • Temporarily disable the WPGraphQL plugin if editorial workflow integrity is critical and patching is delayed.
  • Remove or downgrade Contributor-level accounts until the plugin is updated.
  • Place the /graphql endpoint behind an authentication proxy or IP allowlist to limit attack surface.
  • Use a WordPress role-editor to strip the edit_posts capability from untrusted roles until patched.
bash
# Update WPGraphQL to the patched version using WP-CLI
wp plugin update wp-graphql --version=2.22.2

# Verify installed version
wp plugin get wp-graphql --field=version

# Optional: temporarily deactivate until patched
wp plugin deactivate wp-graphql

Disclaimer: This content was generated using AI. While we strive for accuracy, please verify critical information with official sources.

Default Legacy - Prefooter | Experience the World’s Most Advanced Cybersecurity Platform

Experience the Most Advanced Cybersecurity Platform

See how the world’s most intelligent, autonomous cybersecurity platform can protect your organization today and into the future.