Skip to content

A private app is readable without authentication because the Playground share id is derivable from the app id

Moderate
c121914yu published GHSA-frwj-pr6v-v44w Oct 12, 2026

Software

fastgpt

Affected versions

>= 4.15.0-beta4, <= 4.16.2

Patched versions

v4.17.1

Description

The anonymous share-link gate looks a share id up without checking which channel it belongs to, and the Playground channel mints its id deterministically. So the unguessable-token assumption the gate rests on does not hold for that channel.

packages/service/support/permission/publish/authLink.ts:72:

const outLinkConfig = await MongoOutLink.findOne({ shareId }).lean<OutLinkSchemaType<T>>();

No channel discriminator. Its own sibling thirty lines below, loadOutlinkProviderConfig at :105, does pin it with type: channel. The gate is safe for real share links because their ids are unguessable: outLink/create.ts:33 uses getNanoid(24) and the route requires ManagePermissionVal.

Playground breaks that assumption. projects/app/src/pages/api/support/outLink/playground/update.ts:31,42 requires only WritePermissionVal and writes shareId: \playground-${appId}`. That row is not a publication: the Playground panel's own link is ${origin}/chat?appId=${appId}&pane=..., the logged-in internal page, and the row exists to hold preview toggles whose stored defaults are permissive (showCite, showFullText, canDownloadSourceandshowWholeResponse` all true).

An owner creates a private app, never publishes it, and saves Playground visibility once. An anonymous caller with no session and no key derives playground-<appId>:

[outLink rows created] [{"shareId":"playground-6a9b0313...b453","type":"playground"}]
[ANON outLink/init] code= 200  error= undefined
  "name": "VictimPrivateApp", "intro": "CONFIDENTIAL-INTRO-MARKER",
  "type": "simple", "chatConfig": {...}
[CONTROL bogus shareId] code= 500  error= "linkUnInvalid"

The control matters: an invented share id is rejected, so this is not a blanket pass. The route is GET /api/core/chat/outLink/init through authOutLink to authOutLinkValid.

The caller starts with zero privilege and needs only an app id, which appears in /app/detail?appId= and in the panel's own copyable link.

Suggested CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N, 5.3, for what I actually ran.

Not executed and therefore not claimed: the escalation through authOutLinkChatStart (auth/outLink.ts:43), which returns the victim's teamId and tmbId and would mean workflow execution billed to the victim. I traced that by reading only. If it holds it is materially worse, and it should be run before anyone leans on it.

On how this relates to GHSA-q4pr-3qpg-9q5v: that advisory told you authOutLinkValid is not an authorization boundary, and you fixed it by moving the two WeChat endpoints to authOutLinkCrud, which I confirmed at HEAD, while deliberately leaving authOutLinkValid as the anonymous gate. That is a reasonable design and this is not that bug. The new element is that playground-${appId} makes the bearer token predictable, which collapses the precondition from knowing a published 24-character nanoid to knowing an app id.

Introduced in 82fa431a (2026-06-06, PR #7070), first tag v4.15.0-beta4, confirmed present at released v4.16.2 and at HEAD c311ae9e. Not in your other 34 published advisories, not in .agents/issue/, and not in open PRs #7559 or #7652.

On how I found it and a note on tooling: I was checking which callers of the anonymous gate could have a predictable share id. I used AI assistance while investigating, but I set up the victim app and made the anonymous request myself, including the negative control, so the transcript above is measured.

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
None
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:N/UI:N/S:U/C:L/I:N/A:N

CVE ID

No known CVE

Weaknesses

Generation of Predictable Numbers or Identifiers

The product uses a scheme that generates numbers or identifiers that are more predictable than required. Learn more on MITRE.

Credits