Security

ds2api: WebUI path traversal and API keys in access logs

ds2api - path traversal in the WebUI static fallback and caller API keys logged via Gemini-compatible query parameters (GHSA-rf34-c5jc-4ffw)

PUBLISHED SEVERITY LOWSTATUS PatchedGHSA-rf34-c5jc-4ffw
Patched

Fixed in ds2api 4.6.1 (2026-05-10) by maintainer PRs #476 and #481. 4.6.1 is the last release; the repository is now archived. Track the fix at CJackHwang/ds2api#476.

Product
ds2api (Go) in CJackHwang/ds2api
Affected versions
ds2api 2.0.0 up to but not including 4.6.1. Fixed in 4.6.1 (2026-05-10).
Severity
LOW
Status
Patched
Weaknesses
  • CWE-22Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal')
  • CWE-598Use of GET Request Method With Sensitive Query Strings
As filed on the advisory.
GitHub advisory
GHSA-rf34-c5jc-4ffw
Published by the maintainer on 2026-05-10
CVE
Pending (requested from MITRE)
Published
Credit
Finder: Aaron Elijah Mars of Aeon. Tool: Aeon (https://www.aeon.fun).

What users should do now

  1. Upgrade ds2api to 4.6.1. It is the last release; the repository is now archived and will not get further fixes.
  2. Search your access logs, container logs and any log aggregation for request lines that contain key= or api_key=. Rotate every caller API key that shows up there, and delete or restrict the old logs.
  3. Have clients send keys in a header (Authorization: Bearer, x-api-key or x-goog-api-key) instead of the query string. ds2api accepts all three.
  4. Do not place other folders next to the WebUI static folder whose names start with the same text (for example admin-backup next to admin).

Summary

ds2api is a Go proxy that exposes DeepSeek through OpenAI, Claude, Gemini and Ollama compatible APIs, with an admin WebUI. Before 4.6.1 it had two separate low-severity issues, both on handlers that run before any authentication.

  • WebUI static fallback (CWE-22). Requests under /admin/ that name a file are served from the WebUI static folder. The code checked that the joined file path started with the static folder path as a plain string, with no path separator boundary. A path that steps out of the static folder into a sibling folder whose name begins with the same text (for example /srv/admin-leak next to /srv/admin) passed the check, and the file was served.
  • API keys in access logs (CWE-598). For Gemini compatibility, ds2api accepts a caller API key in the key or api_key query parameter when no header key is present. The request logger passed the request to chi's default log formatter, which logs the full request URI including the query string. Those keys were written verbatim to stdout and from there to container logs and any log pipeline.

Affected versions

ds2api from 2.0.0 up to but not including 4.6.1, as listed on the advisory. Fixed in 4.6.1 (2026-05-10). The path traversal only matters where the resolved static folder has a sibling folder with a shared name prefix; the default repository layout does not. The logging issue affects any deployment where clients send keys in the query string.

Impact

  • Path traversal. An unauthenticated client can read files from a sibling folder that shares the static folder's name prefix. Impact depends on the deployment: custom mounts, volume layouts or container setups that put such a folder next to the static folder (set with DS2API_STATIC_ADMIN_DIR or found by the fallback search) can leak its contents. Files elsewhere on disk are still rejected by the prefix test.
  • Key exposure in logs. Anyone who can read the deployment's logs (log platform users, hosting provider staff, a compromised log pipeline) gets working caller API keys. Those keys let them use the proxy, and the DeepSeek accounts behind it, as that caller.
  • No code execution and no write access in either case.

Affected code

Permalinks at 22a00dc, the last commit before the fix.

Fix

  • 03ea372 (in #476) cleans the static root and only serves a file if its path equals the root or sits under it with a real path separator boundary. It also adds redactSensitiveQueryParams, which redacts the key and api_key query parameters (case-insensitive) on a copy of the request before it reaches the log formatter, so the live request is not changed. e393110 in the same PR refines the redaction.
  • 3569ae1 (in #481) moves the boundary check into isPathInsideRoot and handles a static root at the filesystem root.
  • Both ship in 4.6.1, with regression tests for the shared-prefix sibling case and for log redaction. The fix is present at the head of main (8316cf8).

Detection (for defenders)

In logs from before the upgrade, look for request lines that contain key= or api_key=; each one is an exposed key. For the path traversal, look for GET requests under /admin/ that contain parent-directory segments, and check whether any folder next to the WebUI static folder shares its name prefix. A working exploit is withheld.

Timeline

  1. Reported privately via GitHub PVR (GHSA-rf34-c5jc-4ffw).
  2. Maintainer merges #476 and #481, releases 4.6.1 and publishes the advisory, all the same day.
  3. CVE requested from MITRE.

Credit

Finder: Aaron Elijah Mars of Aeon. Tool: Aeon.

References