From 8f3436f17d6390a776c4258c53475d4c6038a63e Mon Sep 17 00:00:00 2001 From: Paul Nothaft Date: Sat, 5 Sep 2026 08:33:03 +0200 Subject: [PATCH] fix(security): reject array values on the event update route too MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PUT /:id has the same weakness the create chains just had: express-validator runs isIn/isBoolean/isInt element-wise, so `image_quality: [72]` satisfies every check and stays an array. This handler spreads req.body straight into the update, so the array reached a scalar column — a PG insert error, and `[false]` read as true. Covers all six fields in that block, not only the four this PR is about. enable_devtools_protection and overlay_protection sit in the same list with the identical flaw, and leaving two known holes next to four closed ones would have been the odd choice. Refs #1296 --- backend/src/routes/adminEvents/crud.js | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/backend/src/routes/adminEvents/crud.js b/backend/src/routes/adminEvents/crud.js index 0555d773..6b18236b 100644 --- a/backend/src/routes/adminEvents/crud.js +++ b/backend/src/routes/adminEvents/crud.js @@ -1591,13 +1591,18 @@ module.exports = (router) => { body('source_mode').optional().isIn(['managed', 'reference']), body('external_path').optional({ nullable: true }).isString().trim(), body('require_password').optional().isBoolean(), - // Download protection settings - body('protection_level').optional().isIn(['basic', 'standard', 'enhanced', 'maximum']), - body('enable_devtools_protection').optional().isBoolean(), - body('use_canvas_rendering').optional().isBoolean(), - body('overlay_protection').optional().isBoolean(), - body('image_quality').optional().isInt({ min: 1, max: 100 }), - body('fragmentation_level').optional().isInt({ min: 1, max: 10 }), + // Download protection settings. .not().isArray() because + // express-validator runs isIn/isBoolean/isInt element-wise: a + // single-element array like `image_quality: [72]` satisfies every check + // and stays an array, and this handler spreads req.body straight into + // the update — so it reached a scalar column as an array (a PG error, + // and `[false]` read as true). Same guard as the create chain (#1296). + body('protection_level').optional().not().isArray().isIn(['basic', 'standard', 'enhanced', 'maximum']), + body('enable_devtools_protection').optional().not().isArray().isBoolean(), + body('use_canvas_rendering').optional().not().isArray().isBoolean(), + body('overlay_protection').optional().not().isArray().isBoolean(), + body('image_quality').optional().not().isArray().isInt({ min: 1, max: 100 }), + body('fragmentation_level').optional().not().isArray().isInt({ min: 1, max: 10 }), body('password').optional().isString().custom((value) => { if (value === undefined || value === null || value === '') { return true;