Jul 30, 2026·6 min read·4 visits
An authenticated low-privileged user (Provider or Secretary) in Easy!Appointments v1.5.2 can query the customer search endpoint to leak sensitive appointment hashes of other users, allowing subsequent unauthorized cancellation, rescheduling, or takeover of any appointment.
An Excessive Data Exposure vulnerability in Easy!Appointments v1.5.2 allows low-privileged administrative users, such as restricted Providers and Secretaries, to harvest unique, stateless appointment hashes belonging to other providers. These hashes act as capability tokens, granting full authorization to reschedule, take over, or delete appointments via stateless endpoints, resulting in a complete Broken Object Level Authorization (BOLA) scenario.
Easy!Appointments is a widely used open-source, self-hosted appointment scheduler. The application is built using the CodeIgniter PHP framework. To enable seamless, stateless booking management for end customers without requiring formal account creation, the software relies on unique, cryptographically random appointment hashes. These hashes are sent via email and serve as single-factor authorization tokens (capability URLs) for rescheduling or cancelling reservations.
The vulnerability, identified as CVE-2026-55651, is located within the administrative search endpoint /customers/search. This endpoint exposes sensitive database properties—specifically nested appointment data containing these secret appointment hashes—to authenticated dashboard users. The exposed metadata is not restricted based on the requesting user's operational role or scope of authority.
Because the administrative dashboard has multiple tiers of access control (including Administrators, Secretaries, and restricted Providers), this over-exposure allows low-privileged actors to bypass logical isolation barriers. A restricted provider or secretary can harvest secret hashes belonging to appointments managed by entirely different providers, laying the groundwork for unauthorized downstream modifications.
The root cause of CVE-2026-55651 is a combination of CWE-200 (Exposure of Sensitive Information to an Unauthorized Actor) and CWE-639 (Authorization Bypass Through User-Controlled Key). The backend administrative search implementation failed to enforce tenant or ownership isolation rules before returning object trees.
When a dashboard user queries /customers/search, the application triggers the search() method inside application/controllers/Customers.php. The controller retrieves matching customer records and subsequently queries the Appointments_Model to resolve all historic and future appointments associated with each customer ID. The system does not verify whether the active session user has permission to manage those specific appointments.
Because the backend database maps multiple providers to shared customers, a restricted provider executing a customer lookup receives a complete record. This record contains the target customer's entire booking history, including the corresponding hash columns of records associated with peer providers. This architectural decision improperly treats the secret hash as generic, non-sensitive metadata within the internal representation, even though the state-changing endpoints rely on its secrecy.
The original implementation of the search() method in application/controllers/Customers.php blindly appended all returned appointments directly to the customer payload. Below is a comparative look at the vulnerable pattern and the server-side filtering implemented in the patch.
// VULNERABLE CODE PATH
$appointments = $this->appointments_model->get(['id_users_customer' => $customer['id']]);
// All appointments, including those of other providers, are left in $appointments
foreach ($appointments as &$appointment) {
$this->appointments_model->load($appointment, ['service', 'provider']);
}
$customer['appointments'] = $appointments;The security patch applied in Commit ebbe41130dafa58b0716426c56c8cfd4c22dbceb intercepts this collection. It dynamically checks the user's role slug and filters the collection in memory:
$user_id = session('user_id');
+ $role_slug = session('role_slug');
+
+ $secretary_provider_ids = [];
+
+ if ($role_slug === DB_SLUG_SECRETARY) {
+ $secretary_provider_ids = $this->secretaries_model->find($user_id)['providers'];
+ }
foreach ($customers as $index => &$customer) {
if (!$this->permissions->has_customer_access($user_id, $customer['id'])) {
@@ -211,6 +218,24 @@ public function search(): void
$appointments = $this->appointments_model->get(['id_users_customer' => $customer['id']]);
+ // If the current user is a provider, only include their own appointments.
+ if ($role_slug === DB_SLUG_PROVIDER) {
+ $appointments = array_filter($appointments, function ($appointment) use ($user_id) {
+ return (int) $appointment['id_users_provider'] === (int) $user_id;
+ });
+ $appointments = array_values($appointments);
+ }
+
+ // If the current user is a secretary, only include appointments of their providers.
+ if ($role_slug === DB_SLUG_SECRETARY) {
+ $appointments = array_filter($appointments, function ($appointment) use ($secretary_provider_ids) {
+ return in_array((int) $appointment['id_users_provider'], $secretary_provider_ids);
+ });
+ $appointments = array_values($appointments);
+ }While this fix prevents information exposure through this specific controller, it is an in-memory filter implemented after retrieving the entire dataset from the database. A highly optimal pattern would incorporate these role-based constraints directly into the SQL query execution layer to reduce database and memory resource allocation.
An attacker with valid administrative dashboard credentials (such as a restricted Provider) can exploit this flaw using basic HTTP interception tools. The attack sequence consists of two stages: information harvesting and stateless parameter execution.
First, the attacker logs into the dashboard and generates an API request using the browser's developer console or an external tool like Burp Suite. By sending a request such as GET /index.php/customers/search?keyword=John, the attacker retrieves a JSON structure containing customer profiles. Within this payload, the attacker targets the nested appointments array to retrieve the hash value for a booking overseen by a different provider.
Second, the attacker leverages the stateless behavior of the modification endpoints. By passing the captured hash directly to POST /index.php/calendar/delete_appointment or by navigating to /index.php/calendar/reschedule/{hash}, the attacker can alter or cancel the appointment. Because the application trusts the presence of the secret hash as absolute proof of authority, it completes the requested action without verifying if the attacker's active session is authorized to modify that specific provider's schedule.
The impact of successful exploitation is high because it allows malicious actors to systematically sabotage scheduling operations. By using automated search scripts, an attacker can harvest all active booking hashes and programmatically delete or reschedule every appointment in the system.
This flaw represents a logical Denial of Service (DoS) against business operations. It does not crash the database or web server, but it invalidates the integrity of the scheduling data. Additionally, it leaks the names, email addresses, phone numbers, and notes of patients or clients, violating confidentiality standards.
The CVSS v3.1 score is calculated as 7.1 (High) with vector string CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:H/A:N. The integrity impact is rated high because any booking can be altered, while the confidentiality impact is rated low because overall database access remains restricted to scheduling metadata.
The primary remediation for this vulnerability is upgrading the Easy!Appointments installation to version 1.6.0 or later. This release enforces server-side filtering on search queries, preventing the disclosure of appointment metadata to unauthorized users.
For administrators who cannot immediately perform an upgrade, a manual backport of the PHP changes shown in the patch analysis section is recommended. This modification must be carefully applied to the search method in application/controllers/Customers.php.
As a defense-in-depth practice, administrators should implement strict access control rules on stateless modification endpoints. For example, the application should be configured to verify that the creator of an appointment modification request is the owner of the appointment record, rather than relying solely on the secret hash.
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:H/A:N| Product | Affected Versions | Fixed Version |
|---|---|---|
easyappointments alextselegidis | = 1.5.2 | 1.6.0 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-200 |
| Attack Vector | Network (AV:N) |
| CVSS v3.1 Score | 7.1 (High) |
| EPSS Score | 0.00185 (0.185% probability) |
| Exploit Status | poc |
| CISA KEV Status | Not Listed |
The product exposes sensitive information to an actor who is not explicitly authorized to have access to that information.
GHSA-WCHH-9X6H-7F6P documents the critical deprecation of the cryptographic library libolm (Olm) and its Python binding wrapper python-olm, which matrix-commander depended upon via its downstream client library matrix-nio. Multiple cryptographic vulnerabilities (timing leaks, side-channels, signature malleability, and protocol confusion) were disclosed in 2022 and 2024. Because libolm is unmaintained, Python clients using matrix-commander are considered cryptographically unsafe until migrating to vodozemac.
Easy!Appointments prior to version 1.6.0 is vulnerable to Server-Side Request Forgery (SSRF) within its CalDAV integration module. The system handles user-supplied URLs in the connection test endpoint without verifying host constraints or network schemes, allowing authenticated backend users to probe internal infrastructure.
An authorization bypass and logical 'write-before-crash' vulnerability in Easy!Appointments versions prior to 1.6.0 allows authenticated users with the 'Provider' role to inject unauthorized bookings into foreign providers' schedules or reassign existing appointments via parameter manipulation on the 'store' and 'update' endpoints.
An unauthenticated Personally Identifiable Information (PII) disclosure vulnerability exists in Easy!Appointments versions up to and including 1.5.2. Requesting the booking reschedule endpoint with a valid appointment hash causes the system to embed the raw customer database record into the HTML response as inline JavaScript variables, exposing sensitive details. This includes email addresses, phone numbers, physical addresses, custom database metadata, and internal directory configurations such as LDAP DNs. The vulnerability has been resolved in version 1.6.0.
An authorization bypass vulnerability exists in Easy!Appointments before version 1.6.0. The application fails to validate ownership of the provider ID during the Google OAuth synchronization process. This allows authenticated backend users to link their personal Google Calendars to peer providers, leading to unauthorized access to scheduled appointments and associated customer metadata.
This report provides a comprehensive technical teardown of CVE-2026-52838 (GHSA-996f-334j-67g7), a stored Cross-Site Scripting (XSS) vulnerability in Easy!Appointments. The flaw occurs in how the application manages the 'booking disabled' custom message configuration, allowing high-privileged administrators to persist unsanitized payloads that execute on unauthenticated guest landing pages.