From 74fcdbb738add4ab9c8eaf32574568c0f6dcb54f Mon Sep 17 00:00:00 2001 From: MasterAcnolo <68693319+MasterAcnolo@users.noreply.github.com> Date: Wed, 18 Feb 2026 14:43:40 +0100 Subject: [PATCH] Fix: Undo Path Validation for coming back on a classic one --- main.js | 24 ++++++++++---------- public/script/custompath.js | 2 +- server/helpers/validation.js | 33 ++++------------------------ server/services/download.services.js | 20 ++++++++++++----- 4 files changed, 32 insertions(+), 47 deletions(-) diff --git a/main.js b/main.js index 5f815a8..ed75eb1 100644 --- a/main.js +++ b/main.js @@ -143,23 +143,23 @@ function validateDownloadPath(userPath) { // IPC ipcMain.handle("select-download-folder", async () => { - try { - const result = await dialog.showOpenDialog({ properties: ["openDirectory"] }); - if (result.canceled) { - logger.info("Folder selection cancelled by user"); - return null; - } - if (result.filePaths.length > 0) { - const selectedPath = result.filePaths[0]; + const result = await dialog.showOpenDialog({ properties: ["openDirectory"] }); + if (result.canceled) { + logger.info("Folder selection cancelled by user"); + return null; + } + if (result.filePaths.length > 0) { + const selectedPath = result.filePaths[0]; + try { const validatedPath = validateDownloadPath(selectedPath); logger.info(`Folder selected and validated: ${validatedPath}`); return validatedPath; + } catch (err) { + logger.warn(`Unsafe or invalid folder rejected: ${err.message}`); + throw err; // Propagate error to UI } - return null; - } catch (err) { - logger.warn(`Unsafe or invalid folder rejected: ${err.message}`); - return null; } + return null; }); ipcMain.handle("validate-download-path", (event, userPath) => { diff --git a/public/script/custompath.js b/public/script/custompath.js index 992f896..e530c1e 100644 --- a/public/script/custompath.js +++ b/public/script/custompath.js @@ -53,7 +53,7 @@ window.addEventListener("DOMContentLoaded", async () => { await applyPathFromBack(validatedPath); } catch (err) { - alert("Folder not allowed."); + alert("This folder is not allowed. Only specific folders (Users, Downloads, Documents) are authorized for downloads."); } }); }); diff --git a/server/helpers/validation.js b/server/helpers/validation.js index 37f1a05..b7e3da1 100644 --- a/server/helpers/validation.js +++ b/server/helpers/validation.js @@ -12,35 +12,10 @@ function isValidUrl(url) { } function isSafePath(folder) { - if (!folder || typeof folder !== "string") return false; - - try { - // Normalize path and resolve symlinks - const resolved = path.resolve(folder).toLowerCase().replace(/\//g, "\\"); - - // Block Windows system directories (on any drive) - const unsafePaths = [ - "\\windows\\", - "\\system32\\", - "\\program files\\", - "\\program files (x86)\\", - "\\programdata\\", - "\\$recycle.bin\\", - "\\system volume information\\" - ]; - - // Check if path contains any unsafe directory - if (unsafePaths.some(unsafe => resolved.includes(unsafe))) { - return false; - } - - // Allow all drives (C:, D:, E:, etc.) but block system folders - return true; - - } catch (err) { - // In case of path resolution error - return false; - } + if (!folder || folder.length < 3) return false; + const unsafe = ["System32", "\\Windows"]; + const resolved = path.resolve(folder); + return !unsafe.some(u => resolved.includes(u)); } module.exports = { isValidUrl, isSafePath }; diff --git a/server/services/download.services.js b/server/services/download.services.js index 8957a71..d869c4a 100644 --- a/server/services/download.services.js +++ b/server/services/download.services.js @@ -3,23 +3,33 @@ const { userYtDlp, defaultDownloadFolder } = require("../helpers/path"); const fs = require("fs"); const { logger } = require("../logger"); const { buildYtDlpArgs } = require("../helpers/buildArgs"); -const notify = require("../helpers/notify") +const notify = require("../helpers/notify"); +const path = require("path"); +const { isSafePath } = require("../helpers/validation"); function fetchDownload(options, listeners, speedListeners) { return new Promise((resolve, reject) => { const outputFolder = options.outputFolder || defaultDownloadFolder; + // Normalize path and validate it's safe (within Users folder) + let safeOutputFolder = path.resolve(outputFolder); + + if (!isSafePath(safeOutputFolder)) { + logger.warn(`Path not allowed, using default instead: ${safeOutputFolder}`); + safeOutputFolder = path.resolve(defaultDownloadFolder); + } + // Create download folder if it doesn't exist try { - fs.mkdirSync(outputFolder, { recursive: true }); - logger.info(`Output folder ready: ${outputFolder}`); + fs.mkdirSync(safeOutputFolder, { recursive: true }); + logger.info(`Output folder ready: ${safeOutputFolder}`); } catch (err) { logger.error(`Failed to create output folder: ${err.message}`); return reject(new Error(`Unable to create download folder: ${err.message}`)); } - const args = buildYtDlpArgs({ ...options, outputFolder }); + const args = buildYtDlpArgs({ ...options, outputFolder: safeOutputFolder }); logger.info(`[yt-dlp args] ${args.join(" ")}`); const child = execFile(userYtDlp, args); @@ -28,7 +38,7 @@ function fetchDownload(options, listeners, speedListeners) { child.on("close", code => { listeners.forEach(fn => fn("done")); - if (code === 0) resolve(outputFolder); + if (code === 0) resolve(safeOutputFolder); else reject(new Error(`YT-DLP failed with code : ${code}`)); });