From 3f6162f53c3246963cbbaaa1697043a3119cfd89 Mon Sep 17 00:00:00 2001 From: mikiher Date: Wed, 15 Oct 2025 18:54:29 +0300 Subject: [PATCH] CodeQL fix: limit parameter sizes --- server/finders/BookFinder.js | 9 +++++++-- server/utils/index.js | 16 ++++++++++++---- 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/server/finders/BookFinder.js b/server/finders/BookFinder.js index 6dc90c44f..fe1a61027 100644 --- a/server/finders/BookFinder.js +++ b/server/finders/BookFinder.js @@ -385,6 +385,11 @@ class BookFinder { if (!title) return books + // Truncate excessively long inputs to prevent ReDoS attacks + const MAX_INPUT_LENGTH = 500 + title = title.substring(0, MAX_INPUT_LENGTH) + author = author?.substring(0, MAX_INPUT_LENGTH) || author + const isTitleAsin = isValidASIN(title.toUpperCase()) let actualTitleQuery = title @@ -402,7 +407,7 @@ class BookFinder { let authorCandidates = new BookFinder.AuthorCandidates(cleanAuthor, this.audnexus) // Remove underscores and parentheses with their contents, and replace with a separator - // Use negated character classes to prevent ReDoS vulnerability + // Use negated character classes to prevent ReDoS vulnerability (input length validated at entry point) const cleanTitle = title.replace(/\[[^\]]*\]|\([^)]*\)|{[^}]*}|_/g, ' - ') // Split title into hypen-separated parts const titleParts = cleanTitle.split(/ - | -|- /) @@ -669,7 +674,7 @@ function cleanTitleForCompares(title, keepSubtitle = false) { let stripped = keepSubtitle ? title : stripSubtitle(title) // Remove text in paranthesis (i.e. "Ender's Game (Ender's Saga)" becomes "Ender's Game") - // Use a safe two-pass approach to prevent ReDoS vulnerability + // Use negated character class to prevent ReDoS vulnerability (input length validated at entry point) let cleaned = stripped.replace(/\([^)]*\)/g, '') // Remove parenthetical content cleaned = cleaned.replace(/\s+/g, ' ').trim() // Clean up any resulting multiple spaces diff --git a/server/utils/index.js b/server/utils/index.js index 4421fbade..0661d14f8 100644 --- a/server/utils/index.js +++ b/server/utils/index.js @@ -286,13 +286,21 @@ module.exports.timestampToSeconds = (timestamp) => { * * @param {any} value - Query parameter value * @param {string} defaultValue - Default value if undefined/null - * @returns {string|null} String value or null if invalid (array) + * @param {number} maxLength - Optional maximum length (defaults to 10000 to prevent ReDoS attacks) + * @returns {string|null} String value or null if invalid (array or too long) */ -module.exports.getQueryParamAsString = (value, defaultValue = '') => { +module.exports.getQueryParamAsString = (value, defaultValue = '', maxLength = 1000) => { // Explicitly reject arrays to prevent type confusion if (Array.isArray(value)) { return null } - // Return default for undefined/null, otherwise return the value - return value == null ? defaultValue : value + // Return default for undefined/null + if (value == null) { + return defaultValue + } + // Reject excessively long strings to prevent ReDoS attacks + if (typeof value === 'string' && value.length > maxLength) { + return null + } + return value }