Where
-Infinity
0
Severity
2.7
AV:N/AC:L/PR:H/UI:N/S:U/C:N/I:L/A:N

OpenReception's appointment booking software provides an end-to-end encrypted appointment booking platform. Prior to version 1.0.6, when a TENANTADMIN deletes an existing staff user, the underlying StaffService.deleteStaffMember() runs an additional invite cleanup that deletes from the central userinvite table by email. The email clause has no tenantId predicate. Any pending invite in any tenant that shares the deleted staff's email is removed. A TENANTADMIN of tenant A who deletes a staff record with email victim[@]example[.]com also deletes the pending invite for victim[@]example[.]com in tenant B, even though they have no relationship to tenant B. The user-side delete is correctly scoped (eq(user.id, staffId), eq(user.tenantId, tenantId)), and the pending-invite-only delete path (when staffId is itself an invite ID) is also tenant-scoped. The bug is specifically in the invite cleanup that runs as a side effect of deleting an existing staff user. Version 1.0.6 patches the issue.

First published (updated )
Severity
5.3
AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N

OpenReception's appointment booking software provides an end-to-end encrypted appointment booking platform. Prior to version 1.1.0, the GET handler at /api/tenants/{id}/appointments/{appointmentId} performs no authorization check before returning the appointment record. Any party who knows or obtains a valid appointment UUID receives the full row, including channel and agent IDs, time and timezone, status, and the AES-GCM ciphertext components (encryptedPayload, iv, authTag, dataKey). The same file's DELETE handler calls checkPermission(locals, tenantId, true) before allowing deletion. The intent is clear: appointment records are tenant-scoped and require authentication to access. The GET handler is missing the equivalent call. The middleware chain (apiAuthHandle, authGuard) does not compensate: API paths bypass authGuard entirely, and apiAuthHandle does not block requests to non-admin paths when no token is present. Version 1.1.0 patches the issue.

First published (updated )
Severity
5.8
AV:N/AC:L/PR:N/UI:N/S:C/C:N/I:N/A:L

OpenReception's appointment booking software provides an end-to-end encrypted appointment booking platform. Prior to version 1.0.4, the PIN-type challenge throttle uses emailHash as the only key. The throttle rows live in the central challengethrottle table, which is shared across all tenants. Every tenant's /api/tenants/{id}/appointments/verify-challenge endpoint increments the same row when a PIN response fails, and every tenant's /api/tenants/{id}/appointments/challenge endpoint reads the same row when deciding whether to issue a new challenge. When the same emailHash exists in multiple tenants on the same OpenReception instance (the same patient holding tunnels in two different clinics that share the platform), an attacker who knows the patient's email can lock out that patient on tenant B by issuing failed challenge responses against tenant A. The attacker needs no relationship to tenant B; the lockout propagates through the shared throttle row. The lockout escalates with repeated failures. The first lockout triggers at 4 failed attempts and lasts approximately 60 seconds. Subsequent failures escalate the lockout duration to 5 minutes, 30 minutes, and 60 minutes per the throttle service's escalation logic. Repeated bursts produce sustained denial of service against the targeted email. Version 1.0.4 patches the issue.

First published (updated )
Severity
6.5
AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:L/A:L

OpenReception's appointment booking software provides an end-to-end encrypted appointment booking platform. The new-client booking flow in versions 1.0.1 and prior consists of three calls: bootstrap-challenge (returns a 16-bit PoW challenge with difficulty=4 leading hex zeroes), bootstrap-verify (validates the PoW and issues a Bearer booking access token), and create-new-client (consumes the token and creates the tunnel and first appointment). The token correctly binds to tenantId, tunnelId, clientPublicKey, and emailHash, but never to channelId. The bootstrap-challenge request schema does not even accept a channelId, and the issued token's payload contains no channel information. Independently, the service function createNewClientWithAppointment checks only channel.archived = false. The channel.isPublic check that protects addAppointmentToTunnel is missing in the new-client path. The combination means: an attacker completes the bootstrap flow normally (16-bit PoW, completes in well under one second on commodity hardware, no rate limiting beyond the throttle store), receives a valid booking access token, and then submits the create-new-client payload with channelId pointing to a private (isPublic = false) channel. The booking lands as CONFIRMED if the target channel has requiresConfirmation = false (the default), otherwise as NEW. The patient-facing UI does not list private channels in its picker (/api/public/channels correctly filters isPublic = true), so the channel ID must be obtained out of band. The companion finding V-10 (schedule endpoint discloses private channels) provides exactly that: a single unauthenticated GET reveals every private channel ID for any tenant. V-10 plus V-11 together make private channels fully reachable to anonymous attackers. As of time of publication, no known patched versions are available.

First published (updated )
Severity
8
Infoleak, SSRF
AV:N/AC:H/PR:H/UI:N/S:C/C:H/I:H/A:H

OpenReception's appointment booking software provides an end-to-end encrypted appointment booking platform. Prior to version 1.0.2, the GET /api/tenants/{id} endpoint returns the full tenant record to any authenticated TENANTADMIN of that tenant, including the databaseUrl field. This field contains the live PostgreSQL connection string the application uses to connect to that tenant's database. In the tested official docker-compose.prod.yml deployment, the connection string contained the user postgres with rolsuper=true and the plaintext password from secrets/postgrespassword.txt. Operators who configure a non-superuser PostgreSQL user via secrets/postgresuser.txt would expose a less privileged credential, but the disclosure of the connection string itself is independent of that choice. The same credential applies to every database managed by that PostgreSQL instance: the central appointmentbooking database, every per-tenant database (one per tenant), and the postgres administrative database. A TENANTADMIN of one tenant who can reach postgres:5432 (directly via internal network, indirectly via any SSRF, RCE, or file-read in the application) can read every other tenant's appointment ciphertexts, key shares, and metadata; read the central user table, including all GLOBALADMIN accounts, password hashes, and session records; modify or delete any data in any tenant database; and/or i a superuser-scoped deployment: use PostgreSQL's pgreadserverfiles, COPY ... FROM PROGRAM, and CREATE EXTENSION for further escalation inside the database container. This breaks the per-tenant database isolation that is otherwise the primary cross-tenant control in the application. The application code carefully scopes most queries to the calling tenant's database, but those scopings are irrelevant once the attacker holds the credentials that bypass the application entirely. Version 1.0.2 fixes the issue.

First published (updated )
Severity
6.5
AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:H/A:L

OpenReception's appointment booking software provides an end-to-end encrypted appointment booking platform. Prior to version 1.0.5, the add-to-tunnel endpoint creates a new appointment row in any client tunnel without any caller authentication. A request that supplies any valid tunnelId and any valid emailHash (the two need not belong to the same tunnel) results in an inserted appointment with status = "CONFIRMED", attacker-controlled ciphertext fields, attacker-controlled date and duration, and an attacker-chosen agent. The endpoint validates only that some tunnel exists with the given emailHash, then writes the appointment using the attacker-supplied tunnelId directly. The emailHash lookup is effectively an existence check on the tenant; it does not authenticate the caller as the owner of the supplied tunnelId. Combined with the absence of any session, Authorization header, booking access token, or PoW, this makes the endpoint accept arbitrary appointment writes into arbitrary tunnels. By contrast, the sibling endpoint create-new-client (used to bootstrap a brand-new client tunnel) requires a Bearer bootstrap booking access token issued by the bootstrap-challenge / bootstrap-verify flow. The add-to-tunnel endpoint, intended for return-clients booking additional appointments, has no equivalent gate. The application's own middleware confirms this is intentional: add-to-tunnel is explicitly listed in the apiAuthHandle public-route allowlist alongside the bootstrap and challenge endpoints (which legitimately have no session). Version 1.0.5 fixes the issue.

First published (updated )
Severity
6.5
AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:L/A:L

OpenReception's appointment booking software provides an end-to-end encrypted appointment booking platform. Prior to version 1.0.2, the /api/log endpoint accepts unauthenticated POST requests, applies no schema validation to the message body, writes attacker-controlled content directly into the application's stdout log, interprets newline characters as real line breaks, and enforces no size or rate limits. Three independent abuse modes follow: log injection (forge log lines that look like legitimate system events), log volume DoS (saturate the logging pipeline at sustained 100+ requests per second of small messages), and oversized-payload submission (100 KB payloads accepted; larger sizes not tested). The most operationally damaging mode is log injection. An attacker can inject lines that an operator scanning logs would mistake for real system errors, mask their own activity behind fake noise, or pollute SIEM alerting rules with crafted false positives. A line such as [error]: injected admin error injected from an unauthenticated source is indistinguishable from the application's own error output once written to disk. Version 1.0.2 fixes the issue.

First published (updated )
Severity
7.4
AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:N

OpenReception's appointment booking software provides an end-to-end encrypted appointment booking platform. Prior to version 1.0.2, when a user navigates to the /logout page, the page's server-side load handler deletes the accesstoken cookie before calling /api/auth/logout via an internal event.fetch(). The internal fetch consequently runs without the auth cookie, so apiAuthHandle rejects it, the logout handler never executes, and SessionService.revokeSession() is never called for the current session. The DB session row remains valid until its natural expiry (one week by default). The user sees a successful logout (cookie gone, UI returns to login), but any party still holding a copy of the now-deleted access token can continue making authenticated API calls until the session naturally expires. The root cause is a simple ordering mistake. The same auth subsystem implements the correct order in /api/auth/logout: revoke the current DB session first, then delete the cookie. The page-level wrapper does the opposite. Version 1.0.2 initiates server-side logout before removing authentication cookies and first appears in version 1.0.2. Version 2.0.0 later replaces this with a race-free client-side logout flow.

First published (updated )
Severity
9.8
AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H

OpenReception's appointment booking software provides an end-to-end encrypted appointment booking platform. Prior to version 1.0.2, the registration handler at POST /api/auth/register/{userId} validates the relationship between the WebAuthn challenge and the registration cookie's email but never validates that the userId in the URL belongs to that email. An unauthenticated attacker requests a challenge for their own email, generates a registration response with their own authenticator, and submits it against any victim user's URL. The challenge-vs-cookie email match passes, the WebAuthn ceremony validates, and addPasskey writes the attacker's credential into the victim's userpasskey rows. The next victim-email login accepts a passkey assertion from the attacker's authenticator and issues a session as the victim. User IDs are not strictly secret on this platform, but the exact set of exposure surfaces should be assessed by the maintainers. Staff-list endpoints return user IDs to authenticated tenant members per the route signature; live verification of all exposure surfaces (whether user IDs leak through any unauthenticated route, through invite-confirmation URLs, or through other administrative views) is part of the pending live PoC. Where the attacker knows the victim's email and userId, the analysis below becomes account takeover. Version 1.0.2 fixes the issue.

First published (updated )
Severity
5.3
Infoleak
AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N

OpenReception's appointment booking software provides an end-to-end encrypted appointment booking platform. Prior to version 1.0.5, the unauthenticated /api/tenants/{id}/schedule endpoint returns every non-archived channel for a tenant regardless of the channel's isPublic flag. Channels marked isPublic = false are intended to be invisible to public callers; the dashboard creates them deliberately to hide internal-only services from the patient booking UI. The schedule endpoint ignores the flag entirely and discloses channel names, descriptions, IDs, agent associations, pause status, confirmation requirements, and computed slot availability for the requested date range. The asymmetry between addAppointmentToTunnel (which enforces eq(channel.isPublic, true)) and the schedule endpoint (which does not) confirms the design intent: private channels exist as a real access boundary in the booking flow, just not in the schedule disclosure. Version 1.0.5 patches the issue.

First published (updated )

Contact

SecAlerts Pty Ltd.
132 Wickham Terrace
Fortitude Valley,
QLD 4006, Australia
info@secalerts.co
By using SecAlerts services, you agree to our services end-user license agreement. This website is safeguarded by reCAPTCHA and governed by the Google Privacy Policy and Terms of Service. All names, logos, and brands of products are owned by their respective owners, and any usage of these names, logos, and brands for identification purposes only does not imply endorsement. If you possess any content that requires removal, please get in touch with us.
© 2026 SecAlerts Pty Ltd.
ABN: 70 645 966 203, ACN: 645 966 203