mirror of
https://github.com/advplyr/audiobookshelf.git
synced 2026-07-07 01:41:35 +00:00
Merge cdae28c848 into 6f03467f35
This commit is contained in:
commit
c62a19f78b
4 changed files with 59 additions and 9 deletions
|
|
@ -87,6 +87,59 @@ class Server {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
global.PodcastDownloadTimeout = toNumber(process.env.PODCAST_DOWNLOAD_TIMEOUT, 30000)
|
global.PodcastDownloadTimeout = toNumber(process.env.PODCAST_DOWNLOAD_TIMEOUT, 30000)
|
||||||
|
|
||||||
|
// SSRF filter wrapper that also allows NAT64 addresses (RFC 6052)
|
||||||
|
// The ssrf-req-filter package blocks NAT64 addresses (64:ff9b::/96) because
|
||||||
|
// ipaddr.js classifies them as "rfc6052" range instead of "unicast".
|
||||||
|
// We allow NAT64 but still validate the embedded IPv4 is not private.
|
||||||
|
const ssrfFilterLib = require('ssrf-req-filter')
|
||||||
|
const net = require('net')
|
||||||
|
const ipaddr = require('ipaddr.js')
|
||||||
|
// Check if an IPv4 address string is private/reserved
|
||||||
|
function isPrivateIPv4(ip) {
|
||||||
|
try {
|
||||||
|
const addr = ipaddr.IPv4.parse(ip)
|
||||||
|
return addr.range() !== 'unicast'
|
||||||
|
} catch {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
global.getSsrfFilter = (url) => {
|
||||||
|
const agent = ssrfFilterLib(url)
|
||||||
|
const originalCreateConnection = agent.createConnection.bind(agent)
|
||||||
|
agent.createConnection = function(options, callback) {
|
||||||
|
const { host: address } = options
|
||||||
|
// Allow NAT64 addresses (64:ff9b::/96 prefix) but validate embedded IPv4
|
||||||
|
if (address && address.startsWith('64:ff9b::')) {
|
||||||
|
try {
|
||||||
|
const addr = ipaddr.parse(address)
|
||||||
|
if (addr.kind() === 'ipv6') {
|
||||||
|
// Extract the embedded IPv4 from the last 32 bits of the NAT64 address
|
||||||
|
// NAT64 format: 64:ff9b::[96-bit prefix][32-bit IPv4]
|
||||||
|
// The IPv4 is encoded as two 16-bit hex groups in the last two positions
|
||||||
|
const parts = addr.toNormalizedString().split(':')
|
||||||
|
if (parts.length >= 2) {
|
||||||
|
// Last two 16-bit groups contain the 32-bit IPv4
|
||||||
|
const high = parseInt(parts[parts.length - 2], 16)
|
||||||
|
const low = parseInt(parts[parts.length - 1], 16)
|
||||||
|
if (!isNaN(high) && !isNaN(low)) {
|
||||||
|
const ipv4 = `${(high >> 8) & 0xff}.${high & 0xff}.${(low >> 8) & 0xff}.${low & 0xff}`
|
||||||
|
if (isPrivateIPv4(ipv4)) {
|
||||||
|
throw new Error(`NAT64 address embeds private IPv4: ${ipv4}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
throw new Error(`Call to ${address} is blocked: ${e.message}`)
|
||||||
|
}
|
||||||
|
return net.createConnection({ host: options.host, port: options.port }, callback)
|
||||||
|
}
|
||||||
|
return originalCreateConnection(options, callback)
|
||||||
|
}
|
||||||
|
return agent
|
||||||
|
}
|
||||||
|
|
||||||
global.MaxFailedEpisodeChecks = toNumber(process.env.MAX_FAILED_EPISODE_CHECKS, 24)
|
global.MaxFailedEpisodeChecks = toNumber(process.env.MAX_FAILED_EPISODE_CHECKS, 24)
|
||||||
|
|
||||||
if (!fs.pathExistsSync(global.ConfigPath)) {
|
if (!fs.pathExistsSync(global.ConfigPath)) {
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
const axios = require('axios')
|
const axios = require('axios')
|
||||||
const ssrfFilter = require('ssrf-req-filter')
|
|
||||||
const Ffmpeg = require('../libs/fluentFfmpeg')
|
const Ffmpeg = require('../libs/fluentFfmpeg')
|
||||||
const ffmpgegUtils = require('../libs/fluentFfmpeg/utils')
|
const ffmpgegUtils = require('../libs/fluentFfmpeg/utils')
|
||||||
const fs = require('../libs/fsExtra')
|
const fs = require('../libs/fsExtra')
|
||||||
|
|
@ -125,8 +124,8 @@ module.exports.downloadPodcastEpisode = (podcastEpisodeDownload) => {
|
||||||
'User-Agent': userAgent
|
'User-Agent': userAgent
|
||||||
},
|
},
|
||||||
timeout: global.PodcastDownloadTimeout,
|
timeout: global.PodcastDownloadTimeout,
|
||||||
httpAgent: global.DisableSsrfRequestFilter?.(podcastEpisodeDownload.url) ? null : ssrfFilter(podcastEpisodeDownload.url),
|
httpAgent: global.DisableSsrfRequestFilter?.(podcastEpisodeDownload.url) ? null : global.getSsrfFilter(podcastEpisodeDownload.url),
|
||||||
httpsAgent: global.DisableSsrfRequestFilter?.(podcastEpisodeDownload.url) ? null : ssrfFilter(podcastEpisodeDownload.url)
|
httpsAgent: global.DisableSsrfRequestFilter?.(podcastEpisodeDownload.url) ? null : global.getSsrfFilter(podcastEpisodeDownload.url)
|
||||||
})
|
})
|
||||||
|
|
||||||
Logger.debug(`[ffmpegHelpers] Successfully connected with User-Agent: ${userAgent}`)
|
Logger.debug(`[ffmpegHelpers] Successfully connected with User-Agent: ${userAgent}`)
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
const axios = require('axios')
|
const axios = require('axios')
|
||||||
const Path = require('path')
|
const Path = require('path')
|
||||||
const ssrfFilter = require('ssrf-req-filter')
|
|
||||||
const exec = require('child_process').exec
|
const exec = require('child_process').exec
|
||||||
const fs = require('../libs/fsExtra')
|
const fs = require('../libs/fsExtra')
|
||||||
const rra = require('../libs/recursiveReaddirAsync')
|
const rra = require('../libs/recursiveReaddirAsync')
|
||||||
|
|
@ -306,8 +305,8 @@ module.exports.downloadFile = (url, filepath, contentTypeFilter = null) => {
|
||||||
'User-Agent': 'audiobookshelf (+https://audiobookshelf.org)'
|
'User-Agent': 'audiobookshelf (+https://audiobookshelf.org)'
|
||||||
},
|
},
|
||||||
timeout: 30000,
|
timeout: 30000,
|
||||||
httpAgent: global.DisableSsrfRequestFilter?.(url) ? null : ssrfFilter(url),
|
httpAgent: global.DisableSsrfRequestFilter?.(url) ? null : global.getSsrfFilter(url),
|
||||||
httpsAgent: global.DisableSsrfRequestFilter?.(url) ? null : ssrfFilter(url)
|
httpsAgent: global.DisableSsrfRequestFilter?.(url) ? null : global.getSsrfFilter(url)
|
||||||
})
|
})
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
// Validate content type
|
// Validate content type
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
const axios = require('axios')
|
const axios = require('axios')
|
||||||
const ssrfFilter = require('ssrf-req-filter')
|
|
||||||
const Logger = require('../Logger')
|
const Logger = require('../Logger')
|
||||||
const { xmlToJSON, timestampToSeconds } = require('./index')
|
const { xmlToJSON, timestampToSeconds } = require('./index')
|
||||||
const htmlSanitizer = require('../utils/htmlSanitizer')
|
const htmlSanitizer = require('../utils/htmlSanitizer')
|
||||||
|
|
@ -373,8 +372,8 @@ module.exports.getPodcastFeed = (feedUrl, excludeEpisodeMetadata = false) => {
|
||||||
'Accept-Encoding': 'gzip, compress, deflate',
|
'Accept-Encoding': 'gzip, compress, deflate',
|
||||||
'User-Agent': userAgent
|
'User-Agent': userAgent
|
||||||
},
|
},
|
||||||
httpAgent: global.DisableSsrfRequestFilter?.(feedUrl) ? null : ssrfFilter(feedUrl),
|
httpAgent: global.DisableSsrfRequestFilter?.(feedUrl) ? null : global.getSsrfFilter(feedUrl),
|
||||||
httpsAgent: global.DisableSsrfRequestFilter?.(feedUrl) ? null : ssrfFilter(feedUrl)
|
httpsAgent: global.DisableSsrfRequestFilter?.(feedUrl) ? null : global.getSsrfFilter(feedUrl)
|
||||||
})
|
})
|
||||||
.then(async (data) => {
|
.then(async (data) => {
|
||||||
// Adding support for ios-8859-1 encoded RSS feeds.
|
// Adding support for ios-8859-1 encoded RSS feeds.
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue