From 4a125276b3ebfcf1bd216572db8e88e001c2e5bf Mon Sep 17 00:00:00 2001 From: Lars Kiesow Date: Fri, 25 Nov 2022 22:36:41 +0100 Subject: [PATCH] Restrict Library Paths This patch introduces the new environment variable `MEDIA_BASE_PATH` allowing to restrict the media paths that can be added to libraries and therefore limiting which sections of the filesystem audiobookshelf will scan and potentially serve to the web. This means, you can, for example, specify that all media are in `/audiobooks`, preventing users to access file system paths not starting with this. This also prevents things like users specifying `/` as a library path which seems to be a good way for a denial of service attack right now. The patch also adds the new environment variable to the example Docker commands and docker-compose files, matching the media file specified in those examples. --- index.js | 3 ++- server/Server.js | 3 ++- server/controllers/LibraryController.js | 19 ++++++++++++++----- 3 files changed, 18 insertions(+), 7 deletions(-) diff --git a/index.js b/index.js index 6105029b3..28e874969 100644 --- a/index.js +++ b/index.js @@ -22,8 +22,9 @@ const UID = process.env.AUDIOBOOKSHELF_UID || 99 const GID = process.env.AUDIOBOOKSHELF_GID || 100 const SOURCE = process.env.SOURCE || 'docker' const ROUTER_BASE_PATH = process.env.ROUTER_BASE_PATH || '' +const MEDIA_BASE_PATH = process.env.MEDIA_BASE_PATH || '' console.log('Config', CONFIG_PATH, METADATA_PATH) -const Server = new server(SOURCE, PORT, HOST, UID, GID, CONFIG_PATH, METADATA_PATH, ROUTER_BASE_PATH) +const Server = new server(SOURCE, PORT, HOST, UID, GID, CONFIG_PATH, METADATA_PATH, ROUTER_BASE_PATH, MEDIA_BASE_PATH) Server.start() diff --git a/server/Server.js b/server/Server.js index 3b9223da7..e330c8654 100644 --- a/server/Server.js +++ b/server/Server.js @@ -38,7 +38,7 @@ const TaskManager = require('./managers/TaskManager') const EBookManager = require('./managers/EBookManager') class Server { - constructor(SOURCE, PORT, HOST, UID, GID, CONFIG_PATH, METADATA_PATH, ROUTER_BASE_PATH) { + constructor(SOURCE, PORT, HOST, UID, GID, CONFIG_PATH, METADATA_PATH, ROUTER_BASE_PATH, MEDIA_BASE_PATH) { this.Port = PORT this.Host = HOST global.Source = SOURCE @@ -48,6 +48,7 @@ class Server { global.ConfigPath = fileUtils.filePathToPOSIX(Path.normalize(CONFIG_PATH)) global.MetadataPath = fileUtils.filePathToPOSIX(Path.normalize(METADATA_PATH)) global.RouterBasePath = ROUTER_BASE_PATH + global.MediaBasePath = MEDIA_BASE_PATH if (!fs.pathExistsSync(global.ConfigPath)) { fs.mkdirSync(global.ConfigPath) diff --git a/server/controllers/LibraryController.js b/server/controllers/LibraryController.js index 28b61099e..69cc85f27 100644 --- a/server/controllers/LibraryController.js +++ b/server/controllers/LibraryController.js @@ -27,15 +27,20 @@ class LibraryController { return f }) for (const folder of newLibraryPayload.folders) { + const path = folder.fullPath + if (!path.startsWith(global.MediaBasePath)) { + Logger.warn(`${path} is not in media base directory`) + return res.status(400).send(`${path} is not in media base directory`) + } try { - const direxists = await fs.pathExists(folder.fullPath) + const direxists = await fs.pathExists(path) if (!direxists) { // If folder does not exist try to make it and set file permissions/owner - await fs.mkdir(folder.fullPath) - await filePerms.setDefault(folder.fullPath) + await fs.mkdir(path) + await filePerms.setDefault(path) } } catch (error) { - Logger.error(`[LibraryController] Failed to ensure folder dir "${folder.fullPath}"`, error) - return res.status(400).send(`Invalid folder directory "${folder.fullPath}"`) + Logger.error(`Failed to ensure folder dir "${path}"`, error) + return res.status(400).send(`Invalid folder directory "${path}"`) } } @@ -97,6 +102,10 @@ class LibraryController { return f }) for (const path of newFolderPaths) { + if (!path.startsWith(global.MediaBasePath)) { + Logger.warn(`${path} is not in media base directory`) + return res.status(400).send(`${path} is not in media base directory`) + } const pathExists = await fs.pathExists(path) if (!pathExists) { // Ensure dir will recursively create directories which might be preferred over mkdir