From 87f9ecc69899600bd5317c9f227051cf7840c705 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Paul=20B=C3=BCtof?= Date: Tue, 2 Dec 2025 21:33:04 +0100 Subject: [PATCH] Handle redirect loops and maximum redirect limits The implementation of the experimental DNS resolution was vulnerable to infinite redirect loops. This change enforces the maximum number of redirects per web request and detects redirect loops early by tracking visited URLs in a chain of redirects. --- server/Server.js | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/server/Server.js b/server/Server.js index 991b1d703..f35617608 100644 --- a/server/Server.js +++ b/server/Server.js @@ -7,6 +7,7 @@ const fs = require('./libs/fsExtra') const fileUpload = require('./libs/expressFileupload') const cookieParser = require('cookie-parser') const axios = require('axios') +const { AxiosError } = require('axios') const dns = require('dns').promises const { version } = require('../package.json') @@ -130,13 +131,29 @@ class Server { }) // Manually handle redirects, otherwise axios would bypass custom dns resolution on redirects + const maxRedirects = axios.defaults.maxRedirects || 21 // axios default axios.defaults.maxRedirects = 0 axios.interceptors.response.use( (response) => response, async (error) => { if (error.response && [301, 302, 303, 307, 308].includes(error.response.status) && error.response.headers.location) { const redirectUrl = error.response.headers.location - Logger.debug(`[Server] Following ${error.response.status} redirect to ${redirectUrl}`) + if (!error.config._redirectCount) { + error.config._redirectCount = 0 + error.config._redirectUrls = new Set() + } + if (error.config._redirectUrls.has(redirectUrl)) { + const visitedUrls = Array.from(error.config._redirectUrls).join(' -> ') + Logger.error(`[Server] Redirect loop detected: ${visitedUrls} -> ${redirectUrl}`) + return Promise.reject(new AxiosError(`Redirect loop detected: ${redirectUrl}`, 'ERR_FR_TOO_MANY_REDIRECTS', error.config, error.request)) + } + if (error.config._redirectCount >= maxRedirects) { + Logger.error(`[Server] Maximum redirect limit (${maxRedirects}) exceeded`) + return Promise.reject(new AxiosError(`Maximum number of redirects exceeded (${maxRedirects})`, 'ERR_FR_TOO_MANY_REDIRECTS', error.config, error.request)) + } + Logger.debug(`[Server] Following ${error.response.status} redirect to ${redirectUrl} (${error.config._redirectCount + 1}/${maxRedirects})`) + error.config._redirectUrls.add(redirectUrl) + error.config._redirectCount++ return axios({ ...error.config, url: redirectUrl