Jul 30, 2026·6 min read·4 visits
A stored Cross-Site Scripting vulnerability in Easy!Appointments prior to version 1.6.0 allows authenticated administrators to store arbitrary JavaScript payloads in the booking settings. These payloads execute automatically in the browser context of unauthenticated visitors when the application is configured in booking disabled mode.
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.
Easy!Appointments is an open-source, self-hosted web application written in PHP, designed to provide online appointment scheduling capabilities. The platform exposes a configuration interface for administrators to control operational modes, including a feature to temporarily disable public bookings. This disabled state displays a custom notification message designed to inform public visitors about the scheduling downtime. This configuration vector constitutes the specific attack surface for CVE-2026-52838.
The vulnerability is classified as stored cross-site scripting (CWE-79). The flaw manifests because the administrative dashboard permits the input of arbitrary HTML data for formatting purposes, but fails to apply context-aware validation or sanitization prior to database serialization. The persisted data is subsequently retrieved and emitted dynamically into the Document Object Model (DOM) of the public-facing booking portal.
While the generation of the payload requires administrative access, the execution of the injected code targets unauthenticated guest visitors. This transition of execution privilege from an administrative security boundary to unauthenticated user contexts changes the security posture of the client session. The vulnerability affects all releases of Easy!Appointments prior to version 1.6.0.
The root cause of CVE-2026-52838 lies in a split-validation logic flow inside the administrative settings management. Standard settings submitted via the Booking_settings controller undergo basic string filtering via PHP's native strip_tags() function. This filter removes formatting and code tags to ensure data integrity. However, the system must allow a subset of fields, such as the disable_booking_message, to retain HTML markup to enable rich-text representation in the frontend.
To facilitate rich-text formatting via the Trumbowyg editor, the application bypassed the strip_tags() check for the disable_booking_message parameter. The controller accepted the raw input from the HTTP POST payload and passed it directly to the database layer without substituting an alternative sanitization routine. Consequently, the application database persisted potentially malicious elements, such as <script> tags, alongside legitimate HTML layout tags.
When the system is placed in the booking disabled state, any request to the public booking page triggers a redirect to booking_message.php. The template script retrieves the stored message and outputs it using PHP short echo tags without applying escaping functions. The lack of output encoding forces the browser client to parse and execute any script blocks contained within the database record.
The remediation of CVE-2026-52838 introduced a defense-in-depth sanitization pipeline. In application/controllers/Booking_settings.php, the developer implemented a strict whitelist filter to target fields containing rich text. This structural change ensures that fields exempt from standard tag stripping are still processed by an alternative security library.
// In application/controllers/Booking_settings.php (Patched version)
$rich_text_settings = [
'disable_booking_message',
];
$settings = request('booking_settings', []);
foreach ($settings as $setting) {
// Standard parameters undergo tag stripping
if (!in_array($setting['name'], $rich_text_settings, true)) {
$setting['value'] = strip_tags($setting['value'] ?? '');
}
// Rich-text parameters are sanitized via pure_html()
if (
!empty($setting['name']) &&
in_array($setting['name'], $rich_text_settings, true)
) {
$setting['value'] = pure_html($setting['value'] ?? '');
}
// ... persistence operations proceed ...
}The helper function pure_html() integrates HTMLPurifier to dynamically strip executable components from the string. In addition to the controller modifications, the patch modifies the rendering template at application/views/pages/booking_message.php. This change ensures that any pre-existing unsanitized strings in the database are filtered prior to execution.
// In application/views/pages/booking_message.php
// Vulnerable Implementation:
<p><?= vars('message_text') ?></p>
// Patched Implementation:
<p><?= pure_html(vars('message_text')) ?></p>This secondary defense layer is highly effective. Even if an attacker bypasses the controller-level sanitization via direct database manipulation, the template-level rendering function neutralizes the payload before it reaches the DOM of the target visitor. The fix is complete as it addresses both input ingestion and output presentation.
Exploiting this vulnerability requires authenticated access with administrative privileges. An attacker first authenticates into the administrative interface of Easy!Appointments. The attacker then navigates to the Booking Settings panel to locate the 'disable booking message' rich-text text area.
The attacker enters a payload designed to escape standard text boundaries and execute JavaScript within the guest visitor session. For example, the attacker inserts the following payload:
We are currently offline. Please check back later.<script>alert(document.domain)</script>The attacker submits the configuration modification. This action transmits a POST request to /index.php/booking_settings/save containing the URL-encoded script payload inside the settings array. The database successfully records the raw string. Finally, the administrator enables the 'Booking Disabled' state to activate the redirection logic on the public-facing application router.
When an unauthenticated visitor navigates to the scheduling interface, the server loads the booking view. The application pulls the malicious payload from the database and embeds the literal <script> tags directly into the HTML structure. The visitor's browser processes the incoming HTML document, encounters the script tag, and executes the payload in the context of the user's browser origin.
The impact of CVE-2026-52838 is categorized as stored Cross-Site Scripting targeting unauthenticated visitors. Although CVSS v3.1 rates this as Low (2.6) due to the high privileges required (PR:H), the vulnerability modifies the trust boundary. The scope of the attack transfers from the administrative origin to the client browser sessions of any external user visiting the service.
If the application does not deploy session cookies with the HttpOnly attribute, the executing script can read session tokens. This enables attackers to hijack active sessions. Furthermore, the script can modify the DOM to present fake login prompts, facilitating credential harvesting. The execution of arbitrary JavaScript also permits the redistribution of traffic to external malware-hosting servers.
In organizations where administrators use the same browser to verify the public-facing calendar, a self-targeting attack path is possible. An attacker can inject code that performs administrative actions silently. When the administrator visits the disabled page, the script executes and performs administrative tasks under the administrator's security context.
The definitive remediation for CVE-2026-52838 is updating Easy!Appointments to version 1.6.0 or higher. This release contains the patch incorporating HTMLPurifier into the application flow. This update ensures that the input is sanitized during both the save operation and the page generation phases.
If upgrading immediately is not possible, a manual hotfix must be implemented. Administrators should edit application/views/pages/booking_message.php and modify the echo statement. Wrapping the output in the pure_html function will secure the output against active exploitation:
<p><?= pure_html(vars('message_text')) ?></p>Organizations should also enforce a strict Content Security Policy (CSP) header. A policy that restricts inline scripts prevents the execution of elements injected into the HTML payload. Implementing the following header restricts script sources strictly to the local origin:
Content-Security-Policy: default-src 'self'; script-src 'self'; object-src 'none';CVSS:3.1/AV:N/AC:H/PR:H/UI:R/S:C/C:N/I:L/A:N| Product | Affected Versions | Fixed Version |
|---|---|---|
easyappointments alextselegidis | < 1.6.0 | 1.6.0 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-79 |
| Attack Vector | Network |
| CVSS v3.1 Score | 2.6 |
| EPSS Score | 0.00184 |
| Impact | Stored Cross-Site Scripting |
| Exploit Status | None / No Public PoC |
| KEV Status | Not Listed |
The application does not neutralize or incorrectly neutralizes user-controlled input before it is placed in output that is used as a web page that is served to other users.
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.
CVE-2026-54574 (GHSA-9xq3-3fqg-4vg7) is a critical Symlink Escape and Arbitrary Host File Write vulnerability in proot-distro, an open-source utility for managing rootless PRoot containers on Termux and general Linux environments. The vulnerability is rooted in an asymmetric validation flaw during the archive extraction process of container installations, Docker/OCI layers, and container backup restorations. While the extraction engine successfully validated file names to prevent standard directory traversal (e.g., rejecting components containing '..'), it failed to validate symbolic link targets. An attacker could craft a malicious tar archive or container image that plants an absolute host-path symlink. Subsequent file members within the same archive could then traverse through this symlink, writing arbitrary files directly onto the host filesystem under the privileges of the executing process.
A container isolation bypass vulnerability exists in proot-distro prior to version 5.1.6. The utility accepted hardlink entries pointing outside the container directory being restored, allowing cross-container file read and write capabilities.
A critical security flaw (CVE-2026-54680) in the Kubernetes Logging Operator allows authenticated attackers with namespace-level access to craft malicious Custom Resources that inject arbitrary configuration directives into the downstream Fluentd logging aggregator, resulting in unauthenticated remote code execution (RCE) in the context of the aggregator pod.
A protection mechanism failure in the netfoil DNS proxy prior to version 0.4.0 causes blocked domains to resolve to null IP addresses (0.0.0.0 or ::) with a NOERROR status instead of NXDOMAIN. On Linux systems, connections to these addresses are routed to the loopback interface (localhost), allowing local processes to intercept sensitive HTTP traffic, including authorization headers and session cookies, intended for the blocked domains.
A directory traversal vulnerability exists in the `activerecord-tenanted` Ruby gem's local storage path resolution logic. Prior to version 0.7.0, the `path_for` method failed to sanitize input keys, allowing remote attackers to traverse directories and access arbitrary files on the host filesystem.