CVE-2025-46338 Overview
Audiobookshelf is a self-hosted audiobook and podcast server used to organize and stream media libraries. Versions prior to 2.21.0 contain a reflected cross-site scripting (XSS) vulnerability in the /api/upload endpoint. The server reflects unsanitized input from the libraryId field into error responses, allowing an attacker to execute arbitrary JavaScript in a victim's browser. The issue is tracked as [CWE-79] and was patched in version 2.21.0.
Critical Impact
An attacker who convinces an authenticated Audiobookshelf user to submit a crafted upload request can execute arbitrary JavaScript in the victim's browser session, potentially leading to session theft, account compromise, or further client-side attacks.
Affected Products
- Audiobookshelf versions prior to 2.21.0
- Self-hosted deployments exposing the /api/upload endpoint
- All platforms running vulnerable Audiobookshelf server builds
Discovery Timeline
- 2025-04-29 - CVE-2025-46338 published to NVD
- 2026-06-17 - Last updated in NVD database
Technical Details for CVE-2025-46338
Vulnerability Analysis
The vulnerability resides in the upload controller at server/controllers/MiscController.js. When a client submits a multipart upload request to /api/upload, the server destructures the library field from the request body into a libraryId variable. It then queries the database using Database.libraryModel.findByIdWithFolders(libraryId). If no library matches, the controller returns an HTTP 404 response with an error string that concatenates the attacker-controlled libraryId directly into the response body.
Because the response is reflected to the client and rendered without sanitization by the frontend toast component in client/pages/upload/index.vue, any HTML or JavaScript embedded in libraryId executes in the victim's browser context. The frontend previously assigned the raw error.response.data value to a toast error, which extended the attack surface.
Root Cause
The root cause is improper input validation combined with unsafe reflection of user-supplied data into an error response. The controller neither validated the type nor sanitized the content of libraryId before including it in the response string. Compounding this, the Vue.js client rendered the returned string in a manner that allowed script execution.
Attack Vector
Exploitation requires network access to the Audiobookshelf server and a target user with upload permissions. An attacker crafts a malicious link or form that submits an upload request containing a JavaScript payload in the library field. When the victim triggers the request, the server reflects the payload into the 404 error response, and the client renders it, executing the attacker's script.
// Security patch in server/controllers/MiscController.js
// Source: https://github.com/advplyr/audiobookshelf/commit/35870a01583b2947030f4e3d4ac769c3ff298386
Logger.warn(`User "${req.user.username}" attempted to upload without permission`)
return res.sendStatus(403)
}
- if (!req.files) {
+ if (!req.files || !Object.values(req.files).length) {
Logger.error('Invalid request, no files')
return res.sendStatus(400)
}
const files = Object.values(req.files)
- const { title, author, series, folder: folderId, library: libraryId } = req.body
+ let { title, author, series, folder: folderId, library: libraryId } = req.body
+ // Validate request body
+ if (!libraryId || !folderId || typeof libraryId !== 'string' || typeof folderId !== 'string' || !title || typeof title !== 'string') {
+ return res.status(400).send('Invalid request body')
+ }
+ if (!series || typeof series !== 'string') {
+ series = null
+ }
+ if (!author || typeof author !== 'string') {
+ author = null
+ }
const library = await Database.libraryModel.findByIdWithFolders(libraryId)
if (!library) {
- return res.status(404).send(`Library not found with id ${libraryId}`)
+ return res.status(404).send('Library not found')
}
The patch enforces strict type checks on libraryId, folderId, title, series, and author, and removes the reflected identifier from the 404 response body.
Detection Methods for CVE-2025-46338
Indicators of Compromise
- HTTP POST requests to /api/upload containing <script>, onerror=, javascript:, or other HTML/JS syntax in the library form field.
- HTTP 404 responses from Audiobookshelf containing the string Library not found with id followed by unusual characters.
- Unexpected outbound requests from browsers to attacker-controlled domains shortly after users interact with Audiobookshelf.
Detection Strategies
- Inspect web server or reverse proxy logs for /api/upload requests with encoded HTML entities (%3C, %3E, %22) in the library parameter.
- Deploy web application firewall rules that flag XSS payload patterns targeting the upload endpoint.
- Monitor Content Security Policy (CSP) violation reports from browsers loading the Audiobookshelf frontend.
Monitoring Recommendations
- Enable verbose HTTP request logging for the Audiobookshelf application and forward logs to a centralized analytics platform.
- Alert on repeated 404 responses from /api/upload that correlate with authenticated user sessions.
- Review browser console errors and CSP reports for injected inline script executions on Audiobookshelf origins.
How to Mitigate CVE-2025-46338
Immediate Actions Required
- Upgrade Audiobookshelf to version 2.21.0 or later, which includes the input validation fix.
- Restrict access to the Audiobookshelf web interface using authenticated reverse proxies or VPN gateways until patched.
- Instruct users to avoid clicking untrusted links that trigger upload requests to Audiobookshelf instances.
Patch Information
The fix is committed in advplyr/audiobookshelf commit 35870a0 and documented in GitHub Security Advisory GHSA-47g3-c5hx-2q3w. Upgrade to Audiobookshelf 2.21.0 or newer to remediate the vulnerability.
Workarounds
- Place Audiobookshelf behind a reverse proxy that enforces a strict Content Security Policy blocking inline scripts.
- Configure the reverse proxy to reject requests to /api/upload containing angle brackets or script-related keywords in form fields.
- Limit upload permissions to a minimal set of trusted user accounts to reduce the exploitation surface.
# Example nginx rule to block suspicious payloads in /api/upload requests
location /api/upload {
if ($request_body ~* "(<script|onerror=|javascript:|<img)") {
return 403;
}
proxy_pass http://audiobookshelf_upstream;
}
Disclaimer: This content was generated using AI. While we strive for accuracy, please verify critical information with official sources.

