mirror of
https://github.com/advplyr/audiobookshelf.git
synced 2025-11-10 01:19:37 +01:00
Fix codeQL failures
This commit is contained in:
parent
ce4ff4f894
commit
888190a6be
@ -4,7 +4,7 @@ const BookFinder = require('../finders/BookFinder')
|
|||||||
const PodcastFinder = require('../finders/PodcastFinder')
|
const PodcastFinder = require('../finders/PodcastFinder')
|
||||||
const AuthorFinder = require('../finders/AuthorFinder')
|
const AuthorFinder = require('../finders/AuthorFinder')
|
||||||
const Database = require('../Database')
|
const Database = require('../Database')
|
||||||
const { isValidASIN } = require('../utils')
|
const { isValidASIN, getQueryParamAsString } = require('../utils')
|
||||||
|
|
||||||
// Provider name mappings for display purposes
|
// Provider name mappings for display purposes
|
||||||
const providerMap = {
|
const providerMap = {
|
||||||
@ -139,9 +139,10 @@ class SearchController {
|
|||||||
* @param {Response} res
|
* @param {Response} res
|
||||||
*/
|
*/
|
||||||
async findBooks(req, res) {
|
async findBooks(req, res) {
|
||||||
const provider = req.query.provider || 'google'
|
// Safely extract query parameters, rejecting arrays to prevent type confusion
|
||||||
const title = req.query.title || ''
|
const provider = getQueryParamAsString(req.query.provider, 'google')
|
||||||
const author = req.query.author || ''
|
const title = getQueryParamAsString(req.query.title, '')
|
||||||
|
const author = getQueryParamAsString(req.query.author, '')
|
||||||
|
|
||||||
// Validate string parameters
|
// Validate string parameters
|
||||||
const validation = SearchController.validateStringParams({ provider, title, author }, 'findBooks')
|
const validation = SearchController.validateStringParams({ provider, title, author }, 'findBooks')
|
||||||
@ -164,9 +165,9 @@ class SearchController {
|
|||||||
async findCovers(req, res) {
|
async findCovers(req, res) {
|
||||||
const query = req.query
|
const query = req.query
|
||||||
const podcast = query.podcast === '1' || query.podcast === 1
|
const podcast = query.podcast === '1' || query.podcast === 1
|
||||||
const title = query.title || ''
|
const title = getQueryParamAsString(query.title, '')
|
||||||
const author = query.author || ''
|
const author = getQueryParamAsString(query.author, '')
|
||||||
const provider = query.provider || 'google'
|
const provider = getQueryParamAsString(query.provider, 'google')
|
||||||
|
|
||||||
// Validate required title
|
// Validate required title
|
||||||
const titleValidation = SearchController.validateRequiredString(title, 'title', 'findCovers')
|
const titleValidation = SearchController.validateRequiredString(title, 'title', 'findCovers')
|
||||||
@ -190,8 +191,8 @@ class SearchController {
|
|||||||
* @param {Response} res
|
* @param {Response} res
|
||||||
*/
|
*/
|
||||||
async findPodcasts(req, res) {
|
async findPodcasts(req, res) {
|
||||||
const term = req.query.term
|
const term = getQueryParamAsString(req.query.term)
|
||||||
const country = req.query.country || 'us'
|
const country = getQueryParamAsString(req.query.country, 'us')
|
||||||
|
|
||||||
// Validate required term
|
// Validate required term
|
||||||
const termValidation = SearchController.validateRequiredString(term, 'term', 'findPodcasts')
|
const termValidation = SearchController.validateRequiredString(term, 'term', 'findPodcasts')
|
||||||
@ -212,7 +213,7 @@ class SearchController {
|
|||||||
* @param {Response} res
|
* @param {Response} res
|
||||||
*/
|
*/
|
||||||
async findAuthor(req, res) {
|
async findAuthor(req, res) {
|
||||||
const query = req.query.q
|
const query = getQueryParamAsString(req.query.q)
|
||||||
|
|
||||||
// Validate query parameter
|
// Validate query parameter
|
||||||
const validation = SearchController.validateRequiredString(query, 'query', 'findAuthor')
|
const validation = SearchController.validateRequiredString(query, 'query', 'findAuthor')
|
||||||
@ -229,8 +230,8 @@ class SearchController {
|
|||||||
* @param {Response} res
|
* @param {Response} res
|
||||||
*/
|
*/
|
||||||
async findChapters(req, res) {
|
async findChapters(req, res) {
|
||||||
const asin = req.query.asin
|
const asin = getQueryParamAsString(req.query.asin)
|
||||||
const region = (req.query.region || 'us').toLowerCase()
|
const region = getQueryParamAsString(req.query.region, 'us').toLowerCase()
|
||||||
|
|
||||||
// Validate ASIN parameter
|
// Validate ASIN parameter
|
||||||
const asinValidation = SearchController.validateRequiredString(asin, 'asin', 'findChapters')
|
const asinValidation = SearchController.validateRequiredString(asin, 'asin', 'findChapters')
|
||||||
|
|||||||
@ -402,7 +402,8 @@ class BookFinder {
|
|||||||
let authorCandidates = new BookFinder.AuthorCandidates(cleanAuthor, this.audnexus)
|
let authorCandidates = new BookFinder.AuthorCandidates(cleanAuthor, this.audnexus)
|
||||||
|
|
||||||
// Remove underscores and parentheses with their contents, and replace with a separator
|
// Remove underscores and parentheses with their contents, and replace with a separator
|
||||||
const cleanTitle = title.replace(/\[.*?\]|\(.*?\)|{.*?}|_/g, ' - ')
|
// Use negated character classes to prevent ReDoS vulnerability
|
||||||
|
const cleanTitle = title.replace(/\[[^\]]*\]|\([^)]*\)|{[^}]*}|_/g, ' - ')
|
||||||
// Split title into hypen-separated parts
|
// Split title into hypen-separated parts
|
||||||
const titleParts = cleanTitle.split(/ - | -|- /)
|
const titleParts = cleanTitle.split(/ - | -|- /)
|
||||||
for (const titlePart of titleParts) authorCandidates.add(titlePart)
|
for (const titlePart of titleParts) authorCandidates.add(titlePart)
|
||||||
@ -668,7 +669,9 @@ function cleanTitleForCompares(title, keepSubtitle = false) {
|
|||||||
let stripped = keepSubtitle ? title : stripSubtitle(title)
|
let stripped = keepSubtitle ? title : stripSubtitle(title)
|
||||||
|
|
||||||
// Remove text in paranthesis (i.e. "Ender's Game (Ender's Saga)" becomes "Ender's Game")
|
// Remove text in paranthesis (i.e. "Ender's Game (Ender's Saga)" becomes "Ender's Game")
|
||||||
let cleaned = stripped.replace(/ *\([^)]*\) */g, '')
|
// Use a safe two-pass approach to prevent ReDoS vulnerability
|
||||||
|
let cleaned = stripped.replace(/\([^)]*\)/g, '') // Remove parenthetical content
|
||||||
|
cleaned = cleaned.replace(/\s+/g, ' ').trim() // Clean up any resulting multiple spaces
|
||||||
|
|
||||||
// Remove single quotes (i.e. "Ender's Game" becomes "Enders Game")
|
// Remove single quotes (i.e. "Ender's Game" becomes "Enders Game")
|
||||||
cleaned = cleaned.replace(/'/g, '')
|
cleaned = cleaned.replace(/'/g, '')
|
||||||
|
|||||||
@ -277,3 +277,22 @@ module.exports.timestampToSeconds = (timestamp) => {
|
|||||||
}
|
}
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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)
|
||||||
|
*
|
||||||
|
* @param {any} value - Query parameter value
|
||||||
|
* @param {string} defaultValue - Default value if undefined/null
|
||||||
|
* @returns {string|null} String value or null if invalid (array)
|
||||||
|
*/
|
||||||
|
module.exports.getQueryParamAsString = (value, defaultValue = '') => {
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user