CVE-2026-88974
WPGraphQL: Contributor can publish and modify posts without the required capabilities via updatePost
WPGraphQL provides a GraphQL API for WordPress sites. Prior to 2.22.2, the updatePost mutation in src/Mutation/PostObjectUpdate.php checks only the collection-level edit_posts capability and the post author, but does not enforce the object-level edit_post capability or require publish_posts for public status transitions. An authenticated Contributor can therefore publish the Contributor's own draft without editorial approval or modify the Contributor's previously published post despite lacking edit_published_posts, while posts owned by other authors remain protected. This issue is fixed in version 2.22.2.
Attack profile
How easy it is to exploit
- Attack vector
- Network
- Complexity
- Low
- Privileges needed
- Low
- User interaction
- None
What an attacker can affect
- Confidentiality
- None
- Integrity
- Low
- Availability
- Low
Exploit likelihood
What each source reports
CVE List Score used
5.4
WPGraphQL: Contributor can publish and modify posts without the required capabilities via updatePost
WPGraphQL provides a GraphQL API for WordPress sites. Prior to 2.22.2, the updatePost mutation in src/Mutation/PostObjectUpdate.php checks only the collection-level edit_posts capability and the post author, but does not enforce the object-level edit_post capability or require publish_posts for public status transitions. An authenticated Contributor can therefore publish the Contributor's own draft without editorial approval or modify the Contributor's previously published post despite lacking edit_published_posts, while posts owned by other authors remain protected. This issue is fixed in version 2.22.2.
- Record
CVE-2026-88974- CVSS 3.1
- 5.4 Medium
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:L/A:L - Weakness
- CWE-863
- Updated
- 2026-11-02
EUVDEUVD-2026-85310
5.4
WPGraphQL provides a GraphQL API for WordPress sites. Prior to 2.22.2, the updatePost mutation in src/Mutation/PostObjectUpdate.php checks only the collection-level edit_posts capability and the post author, but does not enforce the object-level edit_post capability or require publish_posts for public status transitions. An authenticated Contributor can therefore publish the Contributor's own draft without editorial approval or modify the Contributor's previously published post despite lacking edit_published_posts, while posts owned by other authors remain protected. This issue is fixed in version 2.22.2.
- Record
EUVD-2026-85310- CVSS 3.1
- 5.4 Medium
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:L/A:L - Updated
- 2026-11-02
GitHub AdvisoriesGHSA-5mmc-8pc9-wggg
5.4
WPGraphQL: Contributor can publish and modify posts without the required capabilities via updatePost
Summary
WPGraphQL 2.19.0 contains an authorization bypass in the updatePost mutation. An authenticated WordPress Contributor can change one of their own draft posts to PUBLISH despite lacking the publish_posts capability. The same mutation also permits the Contributor to modify their own previously published posts despite lacking edit_published_posts and failing WordPress's object-level edit_post capability check.
This bypasses the standard WordPress editorial workflow. The WordPress REST API correctly rejects the equivalent operations for the same user.
Affected software
- Plugin: WPGraphQL
- Plugin slug:
wp-graphql - Confirmed affected version:
2.19.0 - Plugin URL: https://wordpress.org/plugins/wp-graphql/
- Repository: https://github.com/wp-graphql/wp-graphql
- WordPress version used for testing:
7.0.2 - WPGraphQL configuration: default settings
Only version 2.19.0 is claimed as confirmed because that is the version tested. The same authorization pattern appears in earlier source history, but those releases were not independently tested.
Vulnerability type
- Broken access control / authorization bypass
- CWE-863: Incorrect Authorization
- OWASP 2021: A01 – Broken Access Control
- Minimum required role: Contributor
Technical cause
src/Mutation/PostObjectUpdate.php checks only the post type's collection-level edit_posts capability:
if ( ! isset( $post_type_object->cap->edit_posts ) || ! current_user_can( $post_type_object->cap->edit_posts ) ) {
// Reject request.
}
It separately prevents a user from changing another author's post, but it does not perform WordPress's object-level check:
current_user_can( $post_type_object->cap->edit_post, $post_id )
It also does not check publish_posts when the requested update changes the post status to publish. The mutation passes the requested status directly to wp_update_post(), which expects its caller to have already enforced authorization.
By comparison, WPGraphQL's createPost implementation explicitly checks publish_posts and changes an unauthorized requested status to pending. This protection is absent from updatePost.
Preconditions
- WPGraphQL 2.19.0 is installed and activated using its default settings.
- The attacker controls a normal WordPress Contributor account.
- The attacker can authenticate to
/graphql, for example with an Application Password over HTTPS or with their WordPress login cookie and a validwp_graphql/wp_restnonce.
No administrator action beyond assigning the standard Contributor role is required. The Contributor role has edit_posts, but does not have publish_posts or edit_published_posts.
Reproduction
The following requests use an Application Password for concise remote reproduction. Replace the URL, username, and Application Password with values from the test installation. WordPress Application Passwords should be tested over HTTPS.
1. Create a draft as the Contributor
curl --user 'gql_contributor:APPLICATION_PASSWORD' \
-H 'Content-Type: application/json' \
--data-binary '{"query":"mutation { createPost(input:{title:\"Contributor publication bypass test\", content:\"Created by a Contributor and not reviewed by an editor.\", status:DRAFT}) { post { databaseId title status } } }"}' \
'https://wordpress.example/graphql'
Example response:
{
"data": {
"createPost": {
"post": {
"databaseId": 21,
"title": "Contributor publication bypass test",
"status": "draft"
}
}
}
}
Record the returned databaseId. The example below uses 21.
2. Verify that WordPress REST denies publication
Using the same Contributor credentials, attempt to publish the draft through the WordPress REST API:
curl --user 'gql_contributor:APPLICATION_PASSWORD' \
-H 'Content-Type: application/json' \
--data-binary '{"status":"publish"}' \
…
- Record
GHSA-5mmc-8pc9-wggg- CVSS 3.1
- 5.4 Medium
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:L/A:L - Weakness
- CWE-863
- Updated
- 2026-11-02
NVD
5.4
WPGraphQL provides a GraphQL API for WordPress sites. Prior to 2.22.2, the updatePost mutation in src/Mutation/PostObjectUpdate.php checks only the collection-level edit_posts capability and the post author, but does not enforce the object-level edit_post capability or require publish_posts for public status transitions. An authenticated Contributor can therefore publish the Contributor's own draft without editorial approval or modify the Contributor's previously published post despite lacking edit_published_posts, while posts owned by other authors remain protected. This issue is fixed in version 2.22.2.
- Record
CVE-2026-88974- CVSS 3.1
- 5.4 Medium
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:L/A:L - Weakness
- CWE-863
- Updated
- 2026-11-02
OSV
5.4
WPGraphQL: Contributor can publish and modify posts without the required capabilities via updatePost
WPGraphQL provides a GraphQL API for WordPress sites. Prior to 2.22.2, the updatePost mutation in src/Mutation/PostObjectUpdate.php checks only the collection-level edit_posts capability and the post author, but does not enforce the object-level edit_post capability or require publish_posts for public status transitions. An authenticated Contributor can therefore publish the Contributor's own draft without editorial approval or modify the Contributor's previously published post despite lacking edit_published_posts, while posts owned by other authors remain protected. This issue is fixed in version 2.22.2.
- Record
CVE-2026-88974- CVSS 3.1
- 5.4 Medium
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:L/A:L - Weakness
- CWE-863
- Updated
- 2026-11-04
OSVGHSA-5mmc-8pc9-wggg
5.4
WPGraphQL: Contributor can publish and modify posts without the required capabilities via updatePost
Summary
WPGraphQL 2.19.0 contains an authorization bypass in the updatePost mutation. An authenticated WordPress Contributor can change one of their own draft posts to PUBLISH despite lacking the publish_posts capability. The same mutation also permits the Contributor to modify their own previously published posts despite lacking edit_published_posts and failing WordPress's object-level edit_post capability check.
This bypasses the standard WordPress editorial workflow. The WordPress REST API correctly rejects the equivalent operations for the same user.
Affected software
- Plugin: WPGraphQL
- Plugin slug:
wp-graphql - Confirmed affected version:
2.19.0 - Plugin URL: https://wordpress.org/plugins/wp-graphql/
- Repository: https://github.com/wp-graphql/wp-graphql
- WordPress version used for testing:
7.0.2 - WPGraphQL configuration: default settings
Only version 2.19.0 is claimed as confirmed because that is the version tested. The same authorization pattern appears in earlier source history, but those releases were not independently tested.
Vulnerability type
- Broken access control / authorization bypass
- CWE-863: Incorrect Authorization
- OWASP 2021: A01 – Broken Access Control
- Minimum required role: Contributor
Technical cause
src/Mutation/PostObjectUpdate.php checks only the post type's collection-level edit_posts capability:
if ( ! isset( $post_type_object->cap->edit_posts ) || ! current_user_can( $post_type_object->cap->edit_posts ) ) {
// Reject request.
}
It separately prevents a user from changing another author's post, but it does not perform WordPress's object-level check:
current_user_can( $post_type_object->cap->edit_post, $post_id )
It also does not check publish_posts when the requested update changes the post status to publish. The mutation passes the requested status directly to wp_update_post(), which expects its caller to have already enforced authorization.
By comparison, WPGraphQL's createPost implementation explicitly checks publish_posts and changes an unauthorized requested status to pending. This protection is absent from updatePost.
Preconditions
- WPGraphQL 2.19.0 is installed and activated using its default settings.
- The attacker controls a normal WordPress Contributor account.
- The attacker can authenticate to
/graphql, for example with an Application Password over HTTPS or with their WordPress login cookie and a validwp_graphql/wp_restnonce.
No administrator action beyond assigning the standard Contributor role is required. The Contributor role has edit_posts, but does not have publish_posts or edit_published_posts.
Reproduction
The following requests use an Application Password for concise remote reproduction. Replace the URL, username, and Application Password with values from the test installation. WordPress Application Passwords should be tested over HTTPS.
1. Create a draft as the Contributor
curl --user 'gql_contributor:APPLICATION_PASSWORD' \
-H 'Content-Type: application/json' \
--data-binary '{"query":"mutation { createPost(input:{title:\"Contributor publication bypass test\", content:\"Created by a Contributor and not reviewed by an editor.\", status:DRAFT}) { post { databaseId title status } } }"}' \
'https://wordpress.example/graphql'
Example response:
{
"data": {
"createPost": {
"post": {
"databaseId": 21,
"title": "Contributor publication bypass test",
"status": "draft"
}
}
}
}
Record the returned databaseId. The example below uses 21.
2. Verify that WordPress REST denies publication
Using the same Contributor credentials, attempt to publish the draft through the WordPress REST API:
curl --user 'gql_contributor:APPLICATION_PASSWORD' \
-H 'Content-Type: application/json' \
--data-binary '{"status":"publish"}' \
…
- Record
GHSA-5mmc-8pc9-wggg- CVSS 3.1
- 5.4 Medium
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:L/A:L - Weakness
- CWE-863
- Updated
- 2026-11-02
Affected products
Affected packages
| Package | Affected | Fixed |
|---|---|---|
wp-graphql/wp-graphqlPackagist |
before 2.22.2 | 2.22.2 |
wp-graphql/wp-graphqlPackagist |
All versions, plus listed versions 0.0.20.0.200.0.210.0.220.0.23 Show 45 moreShow less0.0.240.0.250.0.260.0.270.0.280.0.290.0.30.0.300.0.310.0.51.2.21.27.2v0.0.10v0.0.11v0.0.12v0.0.13v0.0.14v0.0.15v0.0.16v0.0.17v0.0.18v0.0.19v0.0.32v0.0.33v0.0.34v0.0.6v0.0.7v0.0.8v0.0.9v0.1.0v0.1.1v0.1.2v0.1.3v0.1.4v0.10.0v0.10.1v0.10.2v0.10.3v0.11.0v0.12.0v0.12.1v0.12.2v0.12.3v0.13.0v0.13.1 |
None known |
History
| Recorded | Changed | Sources |
|---|---|---|
| facts | OSV | |
| first indexed | CVE List, EUVD, GitHub Advisories, NVD, OSV |
Dates are when Every CVE recorded each change. Upstream dates may differ.
In short
CVE-2026-88974 is a medium vulnerability (CVSS 5.4) in Wp-graphql, published September 23, 2026. No source lists public exploit code. EPSS puts the chance of exploitation in the next 30 days at 0.38%, the 32nd percentile. Fixed in wp-graphql/wp-graphql 2.22.2.
References
github.com/wp-graphql/wp-graphql/security/advisories/GHSA-5mmc-8pc9-wggg
github.com/wp-graphql/wp-graphql/pull/4270
github.com/wp-graphql/wp-graphql/commit/55441663eaa33c3f2e05de038c8286c845916461
github.com/wp-graphql/wp-graphql/releases/tag/wp-graphql%2Fv2.22.2
github.com/advisories/GHSA-5mmc-8pc9-wggg
github.com/CVEProject/cvelistV5/tree/main/cves/2026/88xxx/CVE-2026-88974.json
nvd.nist.gov/vuln/detail/CVE-2026-88974
github.com/wp-graphql/wp-graphql
Left out here: 189 versions and the rest of 2 source descriptions. The JSON record has them.