mirror of
https://github.com/advplyr/audiobookshelf.git
synced 2025-12-09 13:29:37 +00:00
SearchController: simplify query param validation logic
This commit is contained in:
parent
538a5065a4
commit
fd593caafc
2 changed files with 124 additions and 137 deletions
|
|
@ -278,29 +278,56 @@ module.exports.timestampToSeconds = (timestamp) => {
|
|||
return null
|
||||
}
|
||||
|
||||
class ValidationError extends Error {
|
||||
constructor(paramName, message, status = 400) {
|
||||
super(`Query parameter "${paramName}" ${message}`)
|
||||
this.name = 'ValidationError'
|
||||
this.paramName = paramName
|
||||
this.status = status
|
||||
}
|
||||
}
|
||||
module.exports.ValidationError = ValidationError
|
||||
|
||||
class NotFoundError extends Error {
|
||||
constructor(message, status = 404) {
|
||||
super(message)
|
||||
this.name = 'NotFoundError'
|
||||
this.status = status
|
||||
}
|
||||
}
|
||||
module.exports.NotFoundError = NotFoundError
|
||||
|
||||
/**
|
||||
* Safely extracts a query parameter as a string, rejecting arrays to prevent type confusion
|
||||
* Express query parameters can be arrays if the same parameter appears multiple times
|
||||
* @example ?author=Smith => "Smith"
|
||||
* @example ?author=Smith&author=Jones => null (array detected)
|
||||
* @example ?author=Smith&author=Jones => throws error
|
||||
*
|
||||
* @param {any} value - Query parameter value
|
||||
* @param {Object} query - Query object
|
||||
* @param {string} paramName - Parameter name
|
||||
* @param {string} defaultValue - Default value if undefined/null
|
||||
* @param {boolean} required - Whether the parameter is required
|
||||
* @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)
|
||||
* @returns {string} String value
|
||||
* @throws {ValidationError} If value is an array
|
||||
* @throws {ValidationError} If value is too long
|
||||
* @throws {ValidationError} If value is required but not provided
|
||||
*/
|
||||
module.exports.getQueryParamAsString = (value, defaultValue = '', maxLength = 1000) => {
|
||||
module.exports.getQueryParamAsString = (query, paramName, defaultValue = '', required = false, maxLength = 1000) => {
|
||||
const value = query[paramName]
|
||||
if (value === undefined || value === null) {
|
||||
if (required) {
|
||||
throw new ValidationError(paramName, 'is required')
|
||||
}
|
||||
return defaultValue
|
||||
}
|
||||
// Explicitly reject arrays to prevent type confusion
|
||||
if (Array.isArray(value)) {
|
||||
return null
|
||||
}
|
||||
// Return default for undefined/null
|
||||
if (value == null) {
|
||||
return defaultValue
|
||||
throw new ValidationError(paramName, 'is an array')
|
||||
}
|
||||
// Reject excessively long strings to prevent ReDoS attacks
|
||||
if (typeof value === 'string' && value.length > maxLength) {
|
||||
return null
|
||||
throw new ValidationError(paramName, 'is too long')
|
||||
}
|
||||
return value
|
||||
return String(value)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue