CVE case study

CVE-2026-59979: Missing Tenant Checks in Yosemite-Crew Routes

Several read, update, and delete routes either lacked authentication or stopped at identity checks before globally keyed lookups.

Weakness
CWE-639 · CWE-862
Affected
Main branch through bc1a1824b1a7eba2bc61176cdb61d9a2a11bce57
Remediation state
No complete fixed release is listed by the advisory
Advisory published
21 Jun 2026

Official vectorCVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:H/A:H

Why it matters

Affected routes either skipped authentication or checked identity without checking organization membership and operation permission.

The service queried records by global ID, so a caller with another tenant's ID could reach that record.

How I found it

I compared middleware across create, read, update, and delete routes in the same Yosemite-Crew router.

The create routes carried authentication, withOrgPermissions(), and requirePermission(). The update and delete routes carried only authorizeCognito, while the single-service read route had no authentication middleware. I opened authorizeCognito to avoid assuming its name implied authorization; it verified the token and set userId, but performed no organization or role lookup.

I followed :id through the controller. No tenant identity reached the service, which used a global findById(). The PMS task-library and template routes had the same gap, showing an incomplete authorization update across the route family.

Root cause

Affected routes omitted tenant and permission middleware, and the service queried by ID without organisationId.

Source-to-sink trace

  1. 01
    Intent controlservice.router.ts · create routes

    Sibling operations use authentication, organization membership, and an explicit permission requirement.

  2. 02
    Missing route guardservice.router.ts · GET/PATCH/DELETE /:id

    Read has no guard; update and delete use authentication only.

  3. 03
    Identity-only middlewaremiddlewares/auth.ts · authorizeCognito

    The middleware establishes user identity but not tenant membership or function permission.

  4. 04
    Unscoped data sinkservice.service.ts · findById()

    The controller supplies only a global object ID, and the service lookup does not filter by organisationId.

Safe proof of concept

Prerequisites

  • Git and rg installed locally.
  • A clone of the public Yosemite-Crew repository; no account, token, database, or hosted target is needed.

Step-by-step reproduction

  1. Clone the public repository and check out bc1a1824b1a7eba2bc61176cdb61d9a2a11bce57, the revision named by the advisory.
  2. Run the assertions below. They verify the full RBAC stack on sibling create routes, authentication-only update/delete routes, and the unguarded read route.
  3. Open authorizeCognito and confirm that it establishes userId but performs no organization or permission lookup.
  4. Trace updateService from router to controller to service. Confirm that only :id and the DTO reach a global findById() query.
  5. Repeat the same check for task-library and template routes.
  6. If you build an optional runtime lab, seed two synthetic organizations through the project’s supported fixtures, obtain a valid local token, fetch a complete valid ServiceRequestDTO, change one benign field, and PATCH that full DTO. Do not use a one-field body or a production record.

Clone the audited revision

git clone https://github.com/YosemiteCrew/Yosemite-Crew.git yosemite-cve-lab
cd yosemite-cve-lab
git checkout bc1a1824b1a7eba2bc61176cdb61d9a2a11bce57

Offline authorization assertions

SERVICE_ROUTER=apps/backend/src/routers/service.router.ts
AUTH=apps/backend/src/middlewares/auth.ts
SERVICE=apps/backend/src/services/service.service.ts
TASK_ROUTER=apps/backend/src/routers/task.router.ts

# Intent control: sibling create routes have organization + permission guards.
rg -n 'withOrgPermissions|requirePermission\("specialities:edit:any"\)' "$SERVICE_ROUTER"

# Gap: read is unguarded; update/delete are authentication-only.
rg -n 'router\.(get|patch|delete)\("/:id"' "$SERVICE_ROUTER"

# Authentication establishes identity, not tenant or role authorization.
rg -n 'authorizeCognito|authReq.userId = payload.sub' "$AUTH"

# Controller/service path reaches a global lookup with no tenant key.
rg -n 'updateService|findById\(oid\)|organisationId' "$SERVICE"

# Same census for the task library/template family.
rg -n 'pms/library|pms/templates|withTaskOrgPermissions|requirePermission' "$TASK_ROUTER"

Expected evidence

  • The output shows the complete authorization stack on sibling create/task operations and its absence from the disclosed read/update/delete family.
  • The authentication middleware sets caller identity without loading organization membership or permissions.
  • The update path reaches findById() without an organisationId predicate; the proof contacts no application endpoint and changes no data.
Negative control

The guarded create routes show the expected stack: authentication, organization membership, and explicit permission checks.

Fixed-version re-test

The vendor advisory still lists no complete fixed release. A candidate fix must add authentication, organization membership, permission enforcement, and organisationId-scoped queries across every named route family. Only then should an owned two-tenant integration test assert 401, 403, or a non-enumerating 404 and an unchanged synthetic record.

Impact

The advisory documents cross-tenant disclosure, modification, and deletion of service-catalogue and PMS task-definition data. Integrity and availability impact are high because state-changing operations can affect downstream workflows such as bookings and billing.

Most affected write paths require only a low-privilege account and a target identifier. The disclosed service-by-ID read path was reachable without authentication, further demonstrating why every object operation needs its own policy check.

Fix and retest

Apply authentication, organization membership, and explicit permission middleware to every affected route. Backend methods should also accept the tenant identity and query by both object ID and organisationId, so a missed route guard fails closed.

As of this review, the vendor advisory lists no fixed release. Public source shows partial hardening of service update and delete paths, but it does not justify claiming the entire disclosed route family is fixed. Deployment owners should compare their version with the vendor advisory and verify every named operation.

Engineering lesson

Test create, list, read, update, delete, archive, and template routes against the same authorization matrix.

References

Further reading

Evidence connected to this article.

Back to article start