CVE-2026-49339 Overview
CVE-2026-49339 is a path traversal vulnerability in gonic, a free-software music streaming server that implements the Subsonic API. The flaw bypasses an ownership check introduced in commit 6dd71e6a3c966867ef8c900d359a7df75789f410 that was meant to prevent cross-user playlist access. Because playlist.UserID is derived from the first path segment of an attacker-controlled playlist id, any authenticated Subsonic user can read or delete another user's playlist and probe arbitrary host file paths for existence and readability. The issue is tracked under CWE-22 and fixed in version 0.21.0 via commit 0824bed88f6bbc490ba28bf09d28e5dfeb07b445.
Critical Impact
Authenticated users can bypass playlist ownership checks, access or delete other users' playlists, and enumerate arbitrary file paths on the gonic host.
Affected Products
- gonic music streaming server prior to version 0.21.0
- Subsonic API implementations exposed by vulnerable gonic builds
- Self-hosted deployments using the getPlaylist and deletePlaylist endpoints
Discovery Timeline
- 2026-06-19 - CVE-2026-49339 published to NVD
- 2026-06-23 - Last updated in NVD database
Technical Details for CVE-2026-49339
Vulnerability Analysis
The vulnerability is an Insecure Direct Object Reference (IDOR) combined with a path traversal primitive in the Subsonic playlist API. The maintainer's prior fix in commit 6dd71e6 added an ownership check by parsing the id parameter and extracting playlist.UserID from its first path segment. The check trusts attacker-controlled input as the source of the owner identity.
Because the resolved file path is not contained to a per-user base directory, an attacker can craft an id value whose first segment matches their own user ID while the remaining segments traverse to another user's playlist file or arbitrary paths on disk. The getPlaylist and deletePlaylist handlers then operate on the resolved target without verifying that the file resides within the legitimate playlist root.
Root Cause
The root cause is missing path containment when joining the playlist base directory with the attacker-supplied relative path. The pre-patch code used filepath.Join(s.basePath, relPath), which collapses .. segments but does not enforce that the result remains under s.basePath. Ownership was derived from the same untrusted input, so a single crafted parameter satisfied both authorization and path resolution.
Attack Vector
Exploitation requires only valid Subsonic credentials. An authenticated user sends a request to getPlaylist or deletePlaylist with an id parameter beginning with their own user ID, followed by traversal sequences pointing to another user's playlist or a sensitive file path. The server returns the file contents on read or deletes the target on delete, and timing or error responses reveal whether arbitrary host paths exist.
// Pre-patch (vulnerable) path resolution in playlist/playlist.go
absPath := filepath.Join(s.basePath, relPath)
stat, err := os.Stat(absPath)
if err != nil {
return nil, fmt.Errorf("stat m3u: %w", err)
}
// Post-patch in version 0.21.0
absPath, err := fileutil.SafeJoin(s.basePath, relPath)
if err != nil {
return nil, err
}
stat, err := os.Stat(absPath)
if err != nil {
return nil, fmt.Errorf("stat m3u: %w", err)
}
Source: gonic commit 0824bed
Detection Methods for CVE-2026-49339
Indicators of Compromise
- Subsonic API requests to getPlaylist or deletePlaylist with id values containing .., /, or URL-encoded traversal sequences such as %2e%2e%2f.
- Access logs showing one authenticated user repeatedly requesting playlist IDs whose first segment matches their own user ID but reference files outside their playlist directory.
- Unexpected playlist deletions or modifications reported by users running gonic versions earlier than 0.21.0.
Detection Strategies
- Parse gonic HTTP access logs and alert when the id or playlistId parameter contains path separators or parent-directory tokens.
- Compare the user ID extracted from authenticated sessions with the first path segment of the playlist id and the actual filesystem owner of the resolved file.
- Hunt for repeated 404/500 responses on getPlaylist from a single user, which can indicate file existence probing.
Monitoring Recommendations
- Enable verbose request logging on the gonic reverse proxy and forward logs to a central analytics platform for retention and search.
- Monitor filesystem access on the gonic playlist base directory for reads or deletions issued by the gonic process against files outside the expected per-user subtree.
- Track gonic version and commit hash across deployments to confirm that all instances are running 0.21.0 or later.
How to Mitigate CVE-2026-49339
Immediate Actions Required
- Upgrade gonic to version 0.21.0 or later, which includes commit 0824bed88f6bbc490ba28bf09d28e5dfeb07b445 and the fileutil.SafeJoin containment fix.
- Rotate Subsonic user credentials and API tokens if the deployment was internet-exposed or shared across untrusted users.
- Audit the playlist directory for unauthorized deletions or modifications that occurred prior to patching.
Patch Information
The fix is delivered in gonic 0.21.0 via commit 0824bed88f6bbc490ba28bf09d28e5dfeb07b445. It introduces fileutil.SafeJoin and a new ErrEscapesBase sentinel error, and it replaces the unsafe filepath.Join call in playlist/playlist.go so that any resolved path leaving s.basePath is rejected. Background is documented in GitHub Security Advisory GHSA-2fp4-5v5c-4448 and the earlier ownership check in commit 6dd71e6.
Workarounds
- Restrict gonic to trusted users only, for example by placing it behind an authenticating reverse proxy with per-user allowlists, until the upgrade is applied.
- Block requests where the id or playlistId query parameter contains .., /, \, or encoded equivalents at a reverse proxy or web application firewall.
- Run the gonic process under a dedicated low-privilege account with filesystem access limited to its media and playlist directories to reduce the blast radius of arbitrary path probing.
# Example nginx rule to drop traversal attempts against Subsonic playlist endpoints
location ~ ^/rest/(getPlaylist|deletePlaylist) {
if ($args ~* "(\.\.|%2e%2e|/|%2f|\\|%5c)") {
return 400;
}
proxy_pass http://gonic_upstream;
}
Disclaimer: This content was generated using AI. While we strive for accuracy, please verify critical information with official sources.

