From 80b39abaa2723cf9bc0065fec702f7353721f340 Mon Sep 17 00:00:00 2001 From: advplyr Date: Tue, 21 Apr 2026 17:13:06 -0500 Subject: [PATCH] Update library item batch api endpoints check users per-item access & return 403 --- server/controllers/LibraryItemController.js | 39 +++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/server/controllers/LibraryItemController.js b/server/controllers/LibraryItemController.js index 1a6b8ac11..e524ebcfd 100644 --- a/server/controllers/LibraryItemController.js +++ b/server/controllers/LibraryItemController.js @@ -36,6 +36,24 @@ const ShareManager = require('../managers/ShareManager') * @typedef {RequestWithUser & RequestEntityObject & RequestLibraryFileObject} LibraryItemControllerRequestWithFile */ +/** + * Enforce per-item access for batch item routes + * + * @param {RequestWithUser} req + * @param {Response} res + * @param {import('../models/LibraryItem')[]} libraryItems + * @returns {boolean} true if the user may access every item; false if 403 was sent + */ +function ensureUserCanAccessLibraryItemsForBatch(req, res, libraryItems) { + for (const libraryItem of libraryItems) { + if (!req.user.checkCanAccessLibraryItem(libraryItem)) { + res.sendStatus(403) + return false + } + } + return true +} + class LibraryItemController { constructor() {} @@ -547,7 +565,13 @@ class LibraryItemController { return res.sendStatus(404) } + // Ensure user has permission to delete these library items + if (!ensureUserCanAccessLibraryItemsForBatch(req, res, itemsToDelete)) { + return + } + const libraryId = itemsToDelete[0].libraryId + for (const libraryItem of itemsToDelete) { const libraryItemPath = libraryItem.path Logger.info(`[LibraryItemController] (${hardDelete ? 'Hard' : 'Soft'}) deleting Library Item "${libraryItem.media.title}" with id "${libraryItem.id}"`) @@ -581,6 +605,7 @@ class LibraryItemController { } await Database.resetLibraryIssuesFilterData(libraryId) + res.sendStatus(200) } @@ -593,6 +618,11 @@ class LibraryItemController { * @param {Response} res */ async batchUpdate(req, res) { + if (!req.user.canUpdate) { + Logger.warn(`[LibraryItemController] User "${req.user.username}" attempted to batch update without permission`) + return res.sendStatus(403) + } + const updatePayloads = req.body if (!Array.isArray(updatePayloads) || !updatePayloads.length) { Logger.error(`[LibraryItemController] Batch update failed. Invalid payload`) @@ -615,6 +645,11 @@ class LibraryItemController { return res.sendStatus(404) } + // Ensure user has permission to update these library items + if (!ensureUserCanAccessLibraryItemsForBatch(req, res, libraryItems)) { + return + } + let itemsUpdated = 0 const seriesIdsRemoved = [] @@ -695,6 +730,10 @@ class LibraryItemController { const libraryItems = await Database.libraryItemModel.findAllExpandedWhere({ id: libraryItemIds }) + // Ensure user has permission to access these library items + if (!ensureUserCanAccessLibraryItemsForBatch(req, res, libraryItems)) { + return + } res.json({ libraryItems: libraryItems.map((li) => li.toOldJSONExpanded()) })