Skip to content

Ordinary Trip Members Can Retrieve `share_manage`-Protected Public Share Tokens

Moderate
mauriceboe published GHSA-cm52-wp2v-cjcq Aug 27, 2026

Software

liketrek/TREK

Affected versions

<= 3.4.1

Patched versions

4.0.0

Description

Ordinary Trip Members Can Retrieve share_manage-Protected Public Share Tokens

Summary

TREK v3.4.1 exposes a trip's public share token to any authenticated user who has ordinary read access to that trip. The GET /api/trips/:tripId/share-link handler verifies only trip membership and does not enforce the stronger share_manage permission used by the corresponding create and delete operations.

A low-privileged trip member can therefore retrieve a public bearer token created by the trip owner. The member can then use or distribute that token through the unauthenticated GET /api/shared/:token endpoint. Depending on the owner's sharing settings, this can expose reservations, accommodations, budget information, places, itinerary notes, and collaboration data outside the authenticated trip-membership boundary.

Details

The affected controller is protected by JwtAuthGuard, but that guard establishes only that the requester is authenticated. Authorization to manage or retrieve a public sharing credential must be checked separately.

The vulnerable handler at server/src/nest/share/share.controller.ts:51-57 accepts a user-controlled tripId and performs only the weaker trip-access check:

Core vulnerable code path:

@Get()
get(@CurrentUser() user: User, @Param('tripId') tripId: string) {
  if (!this.share.verifyTripAccess(tripId, user.id)) {
    throw new HttpException({ error: 'Trip not found' }, 404);
  }
  const info = this.share.get(tripId);
  return info ? info : { token: null };
}

This check confirms that the requester may access the trip, but it does not establish that the requester has the share_manage permission. By contrast, the create and delete paths in the same controller use the stronger management authorization implemented by requireManage()/canManage() at server/src/nest/share/share.controller.ts:21-48,60-64.

The source-to-sink chain is:

  1. An authenticated ordinary member supplies the victim trip identifier to GET /api/trips/:tripId/share-link. JwtAuthGuard at server/src/nest/share/share.controller.ts:16-18 verifies identity but not share_manage authorization.
  2. The handler at server/src/nest/share/share.controller.ts:51-57 checks only verifyTripAccess(tripId, user.id) and then calls this.share.get(tripId).
  3. The underlying getShareLink() logic at server/src/services/shareService.ts:80-91 queries share_tokens by tripId and returns the complete token and sharing flags. This service operation does not receive a user identifier and cannot independently enforce object-level authorization.
  4. The leaked token can be supplied to the intentionally unauthenticated public route at server/src/nest/share/share.controller.ts:100-106, corresponding to GET /api/shared/:token.
  5. The public sharing service resolves the trip from the token and returns data enabled by the owner's sharing flags at server/src/services/shareService.ts:105-108,239-254.

The trust-boundary failure is the substitution of ordinary trip-read authorization for permission to retrieve a transferable public access credential. Although a member may legitimately read a trip while authenticated, that does not imply authorization to obtain a bearer token that can be copied to arbitrary third parties or continue to work after the member is removed.

POC

Preconditions

  • The target is a TREK v3.4.1 instance with two accounts: trip owner A and ordinary user B.
  • B has been added to A's trip and can read the trip, but B does not have share_manage. Under the documented default permission model, share_manage remains at the trip_owner level.
  • A has created a public share link. To demonstrate the broader confidentiality impact, A can enable share_bookings, share_budget, or share_collab.
  • The trip ID 42 and hostname below are illustrative and should be replaced with values from the test environment.

Reproduction steps

  1. As owner A, create a public share link and enable sensitive sharing categories:
POST /api/trips/42/share-link HTTP/1.1
Host: trek.example
Cookie: trek_session=<OWNER_JWT>
Content-Type: application/json

{"share_map":true,"share_bookings":true,"share_budget":true,"share_collab":true}

Expected setup result: HTTP 200 or 201 and a response containing the token created by the owner.

  1. As ordinary member B, request the share-link management endpoint:
GET /api/trips/42/share-link HTTP/1.1
Host: trek.example
Cookie: trek_session=<MEMBER_JWT>
Accept: application/json

Expected vulnerable result: HTTP 200 with JSON containing the complete token and flags such as share_bookings, share_budget, and share_collab.

Expected secure result: HTTP 403 because B lacks share_manage.

  1. Remove all authentication information and use only the token disclosed in step 2:
GET /api/shared/<LEAKED_TOKEN> HTTP/1.1
Host: trek.example
Accept: application/json

Expected vulnerable result: HTTP 200 with the victim trip and the categories enabled by the owner, potentially including reservations, accommodations, budget data, collaboration data, days, and places. This demonstrates that the token is a complete anonymous access credential rather than harmless metadata.

Impact

An authenticated low-privileged trip member can convert the owner's public-sharing capability into a transferable anonymous read channel. The exposed bearer token can be used from an unrelated client without a TREK session and can be distributed to an unlimited number of third parties.

The exact confidentiality impact depends on the sharing flags selected by the owner. At maximum verified scope, exposed data can include hotel and transportation reservations, confirmation details, addresses, notes, budget information, itinerary locations, and collaboration messages. A disclosed token can remain valid for up to its configured lifetime, identified in the reviewed context as a default maximum of 90 days, and removing the member from the trip does not automatically invalidate a token already obtained. The token remains usable until it expires or the owner deletes or rotates it.

No direct modification or availability impact was established.

Why the confidentiality metric is Low

The attacker in this advisory is already a trip member, and TREK has no read-side
permissions at all: every key in PERMISSION_ACTIONS
(server/src/nest/permissions/permissions.service.ts) is a write or management
action, and read access is granted solely by membership via canAccessTrip
(server/src/db/database.ts). trip_members carries no role column, so the case
of "a member without budget access meeting a link with share_budget=1" cannot
arise.

The public payload is also a strict subset of what a member already sees:
getSharedTripData() (server/src/services/shareService.ts) returns budget rows
without payers or members, reservations without endpoints or travellers, packing
items filtered to is_private = 0, and collaboration messages without reply
context. The only field it adds is the link creator's default_currency.

The confidentiality metric is therefore scored C:L rather than C:H: what the
attacker gains is a bearer credential they are not authorised to hold, not access
to data that was previously out of reach. The substantive harm is the loss of the
owner's control over who may read the trip.

Remediation

  • Change the affected GET handler to invoke the same requireManage(tripId, user) authorization used by the create and delete operations before calling this.share.get(tripId).
  • Do not return a public share token to users who have only ordinary trip-read access.
  • Centralize all read, create, update, and delete operations involving public share credentials behind a server-side authorization function that enforces share_manage.
  • Add regression tests confirming that a default ordinary member receives HTTP 403 from GET /api/trips/:tripId/share-link.
  • Add a positive test confirming that access is allowed only when the user has the required management level, including deployments where an administrator has explicitly lowered share_manage to trip_member.
  • Consider rotating existing share tokens when deploying the fix because tokens might already have been disclosed through the vulnerable endpoint. Notify owners if automatic rotation is not feasible.

Disclosure Notes

This report is based on a completed static source-to-sink analysis. The controller authorization mismatch, token-returning service path, and unauthenticated token-consumption path were identified in source code. No request was executed against a live deployment during the recorded audit session, so dynamic confirmation remains recommended.

The reviewed version label is v3.4.1 on the main branch. A specific commit SHA and the status of any vendor fix are to be confirmed. The distinction relevant to disclosure is that ordinary members may legitimately read a trip while authenticated, but they should not receive the owner-controlled, transferable public bearer token.

Supplemental Information

Affected products

liketrek/TREK v3.4.1. Other affected versions are to be confirmed.

Severity

Medium — CVSS v3.1: 4.3 (CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:N/A:N). Exploitation requires an authenticated low-privileged trip member, and only confidentiality is affected. The metric is Low rather than High because that member can already read the same data while authenticated (see below); what the flaw grants is a transferable bearer credential, not new visibility.

  • Scoring method: CVSS v3.1
  • Score: 6.5
  • Vector string: CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:N/A:N

Weaknesses

  • CWE: CWE-862, CWE-639

CWE-862 — Missing Authorization; CWE-639 — Authorization Bypass Through User-Controlled Key.

Severity

Moderate

CVSS overall score

This score calculates overall vulnerability severity from 0 to 10 and is based on the Common Vulnerability Scoring System (CVSS).
/ 10

CVSS v3 base metrics

Attack vector
Network
Attack complexity
Low
Privileges required
Low
User interaction
None
Scope
Unchanged
Confidentiality
Low
Integrity
None
Availability
None

CVSS v3 base metrics

Attack vector: More severe the more the remote (logically and physically) an attacker can be in order to exploit the vulnerability.
Attack complexity: More severe for the least complex attacks.
Privileges required: More severe if no privileges are required.
User interaction: More severe when no user interaction is required.
Scope: More severe when a scope change occurs, e.g. one vulnerable component impacts resources in components beyond its security scope.
Confidentiality: More severe when loss of data confidentiality is highest, measuring the level of data access available to an unauthorized user.
Integrity: More severe when loss of data integrity is the highest, measuring the consequence of data modification possible by an unauthorized user.
Availability: More severe when the loss of impacted component availability is highest.
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:N/A:N

CVE ID

No known CVE

Weaknesses

Exposure of Sensitive Information to an Unauthorized Actor

The product exposes sensitive information to an actor that is not explicitly authorized to have access to that information. Learn more on MITRE.

Missing Authorization

The product does not perform an authorization check when an actor attempts to access a resource or perform an action. Learn more on MITRE.

Credits