Sep 23, 2026·7 min read·49 visits
An authorization vulnerability in WPGraphQL prior to 2.22.2 allows authenticated WordPress Contributors to bypass workflow restrictions and publish drafts or modify published posts without editorial oversight.
CVE-2026-88974 is an incorrect authorization vulnerability in the WPGraphQL plugin for WordPress. Due to a failure to perform object-level capability checks or validate status-transition requirements in the updatePost mutation handler, authenticated Contributor-level users can publish their own draft posts without editorial approval or modify their previously published posts.
CVE-2026-88974 represents an incorrect authorization vulnerability within the WPGraphQL plugin for WordPress. The flaw exists in the legacy updatePost mutation handler located in src/Mutation/PostObjectUpdate.php. The vulnerability affects all versions prior to 2.22.2, with vulnerability characteristics introduced starting in version 2.19.0. It exposes an attack surface to authenticated users holding low-privileged roles.
Under default WordPress access control configurations, a user with the Contributor role is permitted to write and edit draft posts but lacks permissions to publish them. This policy requires the publish_posts capability to ensure editorial oversight. Furthermore, Contributors are prohibited from modifying their own posts after publication unless they possess the edit_published_posts capability. WPGraphQL did not properly respect these fine-grained authorization boundaries within its GraphQL mutation API.
Instead of verifying dynamic, object-level capabilities, the plugin only checked high-level collection capabilities and post ownership. This oversight allows authenticated Contributors to execute mutations that publish draft posts or edit previously published content directly. This privilege escalation vector bypasses standard editorial workflows, leading to unauthorized modifications of published web content.
The root cause of CVE-2026-88974 is the failure of the WPGraphQL legacy mutation handler to evaluate WordPress object-level meta-capabilities. WordPress differentiates generic capabilities like edit_posts from dynamic meta-capabilities like edit_post. A meta-capability is evaluated against a specific post object dynamically to determine if the current user possesses authorization based on ownership and status.
When current_user_can('edit_post', $post_id) is invoked, WordPress maps the request to specialized primitive capabilities. If the target post status is a draft, it maps to edit_posts. If the target post status is published, it maps to edit_published_posts. Because normal Contributors do not possess edit_published_posts, any attempts to edit published content are rejected by the core capability framework.
WPGraphQL bypassed this mapping process in PostObjectUpdate.php. The handler verified only that the post author matched the executing user and that the user possessed the general edit_posts capability. By not invoking current_user_can on the specific object instance using edit_post, the code failed to detect that the user lacked permissions for published post modifications. Additionally, the code did not check the publish_posts capability during status transitions to public states.
The patch implemented in WPGraphQL version 2.22.2 introduces the missing validation checks in plugins/wp-graphql/src/Mutation/PostObjectUpdate.php. The code modification ensures that both object-level permissions and post-status transitions are checked against WordPress core security boundaries. This brings GraphQL mutation authorization parity with the native WordPress REST API.
The first critical fix implements validation of the dynamic meta-capability for the specific post object:
// 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 ) ) );
}The second critical check stops unauthorized users from executing status updates that would make a draft publicly visible. If the incoming payload attempts a status transition that requires publication capability, the system explicitly verifies the user's publish_posts permission:
// If the update requests a status that requires publishing capability (anything
// other than draft or pending) and the current user cannot publish, reject the
// request rather than silently changing the post's visibility. This mirrors the
// WordPress REST API, which returns rest_cannot_publish for the same request.
if (
isset( $post_args['post_status'] ) &&
! in_array( $post_args['post_status'], [ 'draft', 'pending' ], true ) &&
( ! isset( $post_type_object->cap->publish_posts ) || ! current_user_can( $post_type_object->cap->publish_posts ) )
) {
// 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 publish this %1$s', 'wp-graphql' ), $post_type_object->graphql_single_name ) ) );
}These additions systematically close both bypass paths. Any requests attempting to modify a published post or publish a draft without appropriate credentials will fail during the pre-mutation validation phase, raising an explicit UserError exception.
An attacker with valid Contributor-level credentials can exploit this vulnerability via network access to the target /graphql endpoint. The attack does not require user interaction from other roles. Exploitation relies on craft mutations sent to the GraphQL parser.
To publish an unauthorized post, the attacker first registers or retrieves a post ID representing their draft. They then compile a GraphQL mutation that Targets the updatePost schema. By specifying the target Relay Global ID and defining the state transition within the variables, the restriction is bypassed.
mutation BypassPublishPost($input: UpdatePostInput!) {
updatePost(input: $input) {
post {
databaseId
status
title
}
}
}The following payload is supplied to trigger the state modification:
{
"input": {
"id": "cG9zdDoxMjM=",
"status": "PUBLISH"
}
}When executed against a vulnerable instance of WPGraphQL, the backend modifies the database post state without conducting the appropriate publication check. The post is instantly available on the public site layout. The same mechanism can target existing, approved posts by modifying fields such as title or body text within the input variables.
The security impact of CVE-2026-88974 represents an application-level privilege escalation vector. While it does not facilitate Remote Code Execution (RCE) or sensitive database information exposure directly, it undermines the trust and integrity of the content publication pipeline.
On media websites or corporate sites where Contributor permissions are granted to external writers, this vulnerability permits the unauthorized injection of web content. Attackers can deface high-traffic pages, distribute malicious external links, or publish unapproved, misleading announcements. This compromises search engine optimization and can expose visitors to drive-by malware campaigns.
The vulnerability is scored with a CVSS v3.1 base score of 5.4. The vector string is CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:L/A:L. This reflects network-level accessibility with low complexity and low privileges, resulting in a low impact on availability and confidentiality, but a low-to-medium impact on system integrity.
The primary remediation strategy is the immediate upgrade of WPGraphQL to version 2.22.2 or higher. The patch enforces strict capability checks, neutralizing the logical bypass paths. Administrators can verify the active plugin version within the WordPress administration panel or via the Command Line Interface.
If immediate updates are unfeasible, temporary mitigation steps must be enacted. Administrators should temporarily suspend or downgrade accounts possessing the Contributor role to Subscriber status. Subscribers do not have baseline access to compile or submit mutations targeting the post collection, which mitigates the vulnerability.
Web Application Firewalls (WAFs) should be configured with custom rules designed to filter the GraphQL endpoint. Payload inspection rules can look for the combination of updatePost and status variables indicating PUBLISH within non-editorial user sessions. Security teams must monitor mutation access logs to identify attempts targeting the updatePost schema.
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:L/A:L| Product | Affected Versions | Fixed Version |
|---|---|---|
wp-graphql wp-graphql | >= 2.19.0, < 2.22.2 | 2.22.2 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-863 (Incorrect Authorization) |
| Attack Vector | Network |
| CVSS v3.1 Score | 5.4 |
| Exploit Status | PoC via test suite |
| Impact | Privilege Escalation / Unauthorized Publication |
| KEV Status | Not Listed |
The software performs an authorization check when an actor attempts to access a resource or perform an action, but it does not correctly prove that the actor is authorized to perform that action or access that resource.
CVE-2026-16454 is an object-level authorization bypass vulnerability (CWE-284, CWE-862) in Eclipse hawkBit versions 1.0.3 and prior. The flaw exists within the Direct Device Integration (DDI) REST controller (hawkbit-ddi-resource), where endpoints responsible for enumerating software module metadata and serving firmware binaries fail to verify whether the requested software module is assigned to the authenticated device. Consequently, any device with valid credentials (such as a TargetToken or GatewayToken) can enumerate software module identifiers and download arbitrary firmware artifacts across the entire tenant namespace.
CVE-2026-62284 (GHSA-5w5g-j6hw-6p8f) is a code injection and server-side request forgery vulnerability in the console-proxy component of the OSAC Operator. The RemoteConfigResolver parses Kubernetes Secrets containing kubeconfig files via clientcmd.RESTConfigFromKubeConfig without sanitizing execution-related fields. An attacker with RBAC permissions to create or modify labeled Secrets in monitored namespaces can supply a malicious exec credential plugin, resulting in arbitrary command execution within the console-proxy pod.
CVE-2026-62361 (GHSA-xgjr-7j9q-2h4r) is an authenticated SQL injection and authorization bypass vulnerability in listmonk, an open-source, self-hosted newsletter and mailing list manager written in Go. In versions prior to 6.2.0, the subscriber export endpoint (GET /api/subscribers/export) directly interpolates user-supplied SQL filter expressions into a PostgreSQL query without invoking the table allowlist validation function (validateQueryTables). Consequently, authenticated users possessing the subscribers:sql_query and subscribers:get_all permissions can bypass relational access controls, execute arbitrary subqueries against restricted tables such as users and settings, and modify database state via writable Common Table Expressions (CTEs).
CVE-2026-28385 (GHSA-3gq2-x4qg-p4g6) is a Server-Side Request Forgery (SSRF) vulnerability in Canonical LXD versions 4.12 through 6.9. An authenticated user possessing the can_create_images entitlement can force the LXD daemon to execute arbitrary outbound HTTP and HTTPS requests via the /1.0/images API endpoint. Due to the absence of egress destination validation, an attacker can probe host loopback services, RFC 1918 private network segments, and cloud Instance Metadata Services (IMDS) from the network position of the privileged LXD daemon.
Canonical LXD and LXC Incus contain an arbitrary file write vulnerability (CWE-73 / CWE-59) in the `/1.0/instances/<name>/exec` REST API endpoint. When an instance is unpacked from an image archive, top-level symbolic links are extracted directly onto the host filesystem within the instance storage directory. If a crafted image includes a symbolic link named `exec-output` pointing to an arbitrary host path, invoking the execution endpoint with `record-output: true` causes the privileged host daemon to follow the symlink and write command output files directly to the target host directory. An authenticated attacker with permissions to import images and execute commands can exploit this flaw to write controlled content to sensitive host directories such as `/etc/cron.d`, resulting in arbitrary code execution on the host as `root`.
CVE-2026-48749 (tracked as GHSA-vghh-5rfx-xhq8 for LXD and GHSA-2q3f-q5pq-g8wv for Incus) is a critical container escape vulnerability in the Canonical LXD and Linux Containers Incus system container managers. By importing a specially crafted container image archive containing duplicate tar member headers, an authenticated attacker can replace the instance rootfs directory with a symbolic link pointing to the underlying host root filesystem. When performing file operations on a stopped container, the privileged forkfile helper binary resolves the symbolic link without confinement and chroots directly into the host filesystem, granting arbitrary root-level file read and write access.