mirror of
https://github.com/advplyr/audiobookshelf.git
synced 2025-01-25 23:39:35 +01:00
Update:Validate image URI content-type before writing image file
This commit is contained in:
parent
1f8372f5e5
commit
c98fac30b6
@ -3,7 +3,7 @@ const Logger = require('../Logger')
|
||||
const Path = require('path')
|
||||
const Audnexus = require('../providers/Audnexus')
|
||||
|
||||
const { downloadFile } = require('../utils/fileUtils')
|
||||
const { downloadImageFile } = require('../utils/fileUtils')
|
||||
|
||||
class AuthorFinder {
|
||||
constructor() {
|
||||
@ -45,7 +45,7 @@ class AuthorFinder {
|
||||
const filename = authorId + '.' + ext
|
||||
const outputPath = Path.posix.join(authorDir, filename)
|
||||
|
||||
return downloadFile(url, outputPath).then(() => {
|
||||
return downloadImageFile(url, outputPath).then(() => {
|
||||
return {
|
||||
path: outputPath
|
||||
}
|
||||
|
@ -5,7 +5,7 @@ const readChunk = require('../libs/readChunk')
|
||||
const imageType = require('../libs/imageType')
|
||||
|
||||
const globals = require('../utils/globals')
|
||||
const { downloadFile, filePathToPOSIX, checkPathIsFile } = require('../utils/fileUtils')
|
||||
const { downloadImageFile, filePathToPOSIX, checkPathIsFile } = require('../utils/fileUtils')
|
||||
const { extractCoverArt } = require('../utils/ffmpegHelpers')
|
||||
const CacheManager = require('../managers/CacheManager')
|
||||
|
||||
@ -122,7 +122,7 @@ class CoverManager {
|
||||
var temppath = Path.posix.join(coverDirPath, 'cover')
|
||||
|
||||
let errorMsg = ''
|
||||
let success = await downloadFile(url, temppath).then(() => true).catch((err) => {
|
||||
let success = await downloadImageFile(url, temppath).then(() => true).catch((err) => {
|
||||
errorMsg = err.message || 'Unknown error'
|
||||
Logger.error(`[CoverManager] Download image file failed for "${url}"`, errorMsg)
|
||||
return false
|
||||
@ -287,7 +287,7 @@ class CoverManager {
|
||||
await fs.ensureDir(coverDirPath)
|
||||
|
||||
const temppath = Path.posix.join(coverDirPath, 'cover')
|
||||
const success = await downloadFile(url, temppath).then(() => true).catch((err) => {
|
||||
const success = await downloadImageFile(url, temppath).then(() => true).catch((err) => {
|
||||
Logger.error(`[CoverManager] Download image file failed for "${url}"`, err)
|
||||
return false
|
||||
})
|
||||
|
@ -204,7 +204,16 @@ async function recurseFiles(path, relPathToReplace = null) {
|
||||
}
|
||||
module.exports.recurseFiles = recurseFiles
|
||||
|
||||
module.exports.downloadFile = (url, filepath) => {
|
||||
/**
|
||||
* Download file from web to local file system
|
||||
* Uses SSRF filter to prevent internal URLs
|
||||
*
|
||||
* @param {string} url
|
||||
* @param {string} filepath path to download the file to
|
||||
* @param {Function} [contentTypeFilter] validate content type before writing
|
||||
* @returns {Promise}
|
||||
*/
|
||||
module.exports.downloadFile = (url, filepath, contentTypeFilter = null) => {
|
||||
return new Promise(async (resolve, reject) => {
|
||||
Logger.debug(`[fileUtils] Downloading file to ${filepath}`)
|
||||
axios({
|
||||
@ -215,6 +224,12 @@ module.exports.downloadFile = (url, filepath) => {
|
||||
httpAgent: ssrfFilter(url),
|
||||
httpsAgent: ssrfFilter(url)
|
||||
}).then((response) => {
|
||||
// Validate content type
|
||||
if (contentTypeFilter && !contentTypeFilter?.(response.headers?.['content-type'])) {
|
||||
return reject(new Error(`Invalid content type "${response.headers?.['content-type'] || ''}"`))
|
||||
}
|
||||
|
||||
// Write to filepath
|
||||
const writer = fs.createWriteStream(filepath)
|
||||
response.data.pipe(writer)
|
||||
|
||||
@ -227,6 +242,21 @@ module.exports.downloadFile = (url, filepath) => {
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Download image file from web to local file system
|
||||
* Response header must have content-type of image/ (excluding svg)
|
||||
*
|
||||
* @param {string} url
|
||||
* @param {string} filepath
|
||||
* @returns {Promise}
|
||||
*/
|
||||
module.exports.downloadImageFile = (url, filepath) => {
|
||||
const contentTypeFilter = (contentType) => {
|
||||
return contentType?.startsWith('image/') && contentType !== 'image/svg+xml'
|
||||
}
|
||||
return this.downloadFile(url, filepath, contentTypeFilter)
|
||||
}
|
||||
|
||||
module.exports.sanitizeFilename = (filename, colonReplacement = ' - ') => {
|
||||
if (typeof filename !== 'string') {
|
||||
return false
|
||||
|
Loading…
Reference in New Issue
Block a user