From 01349d48e70536dce897ba24e3e00de9d292369e Mon Sep 17 00:00:00 2001 From: Sebastion Date: Wed, 22 Jul 2026 22:15:09 +0100 Subject: [PATCH] fix(subtitle): validate url scheme and index to prevent local file reads --- server/routes/subtitle.js | 29 ++++++++++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/server/routes/subtitle.js b/server/routes/subtitle.js index e8a596c..5f924ff 100644 --- a/server/routes/subtitle.js +++ b/server/routes/subtitle.js @@ -15,17 +15,40 @@ router.get('/', (req, res) => { return res.status(400).json({ error: 'URL and index parameters are required' }); } + // Validate URL: only allow http(s) schemes. This prevents ffmpeg protocol + // handler abuse (file://, concat:, subfile:, data:, etc.) which could + // otherwise be used to read arbitrary local files from the server. + let parsedUrl; + try { + parsedUrl = new URL(url); + } catch (e) { + return res.status(400).json({ error: 'Invalid URL' }); + } + if (parsedUrl.protocol !== 'http:' && parsedUrl.protocol !== 'https:') { + return res.status(400).json({ error: 'Only http(s) URLs are allowed' }); + } + + // Validate index: must be a non-negative integer. Prevents injection of + // extra ffmpeg option tokens via the -map argument. + const indexNum = Number(index); + if (!Number.isInteger(indexNum) || indexNum < 0 || String(indexNum) !== String(index).trim()) { + return res.status(400).json({ error: 'Index must be a non-negative integer' }); + } + const ffmpegPath = req.app.locals.ffmpegPath || 'ffmpeg'; - // console.log(`[Subtitle] Extracting track ${index} from: ${url}`); + // console.log(`[Subtitle] Extracting track ${indexNum} from: ${parsedUrl.href}`); const args = [ '-hide_banner', '-loglevel', 'warning', + // Restrict ffmpeg to safe network protocols only, as defense in depth + // against protocol handler abuse or redirects into local file reads. + '-protocol_whitelist', 'http,https,tcp,tls,crypto', '-user_agent', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36', '-probesize', '5000000', '-analyzeduration', '5000000', - '-i', url, - '-map', `0:${index}`, + '-i', parsedUrl.href, + '-map', `0:${indexNum}`, '-c:s', 'webvtt', '-f', 'webvtt', '-'