Fix: Undo Path Validation for coming back on a classic one

This commit is contained in:
MasterAcnolo
2026-02-18 14:43:40 +01:00
parent 32c9a66a35
commit 74fcdbb738
4 changed files with 32 additions and 47 deletions

View File

@@ -143,7 +143,6 @@ function validateDownloadPath(userPath) {
// IPC // IPC
ipcMain.handle("select-download-folder", async () => { ipcMain.handle("select-download-folder", async () => {
try {
const result = await dialog.showOpenDialog({ properties: ["openDirectory"] }); const result = await dialog.showOpenDialog({ properties: ["openDirectory"] });
if (result.canceled) { if (result.canceled) {
logger.info("Folder selection cancelled by user"); logger.info("Folder selection cancelled by user");
@@ -151,15 +150,16 @@ ipcMain.handle("select-download-folder", async () => {
} }
if (result.filePaths.length > 0) { if (result.filePaths.length > 0) {
const selectedPath = result.filePaths[0]; const selectedPath = result.filePaths[0];
try {
const validatedPath = validateDownloadPath(selectedPath); const validatedPath = validateDownloadPath(selectedPath);
logger.info(`Folder selected and validated: ${validatedPath}`); logger.info(`Folder selected and validated: ${validatedPath}`);
return validatedPath; return validatedPath;
}
return null;
} catch (err) { } catch (err) {
logger.warn(`Unsafe or invalid folder rejected: ${err.message}`); logger.warn(`Unsafe or invalid folder rejected: ${err.message}`);
return null; throw err; // Propagate error to UI
} }
}
return null;
}); });
ipcMain.handle("validate-download-path", (event, userPath) => { ipcMain.handle("validate-download-path", (event, userPath) => {

View File

@@ -53,7 +53,7 @@ window.addEventListener("DOMContentLoaded", async () => {
await applyPathFromBack(validatedPath); await applyPathFromBack(validatedPath);
} catch (err) { } catch (err) {
alert("Folder not allowed."); alert("This folder is not allowed. Only specific folders (Users, Downloads, Documents) are authorized for downloads.");
} }
}); });
}); });

View File

@@ -12,35 +12,10 @@ function isValidUrl(url) {
} }
function isSafePath(folder) { function isSafePath(folder) {
if (!folder || typeof folder !== "string") return false; if (!folder || folder.length < 3) return false;
const unsafe = ["System32", "\\Windows"];
try { const resolved = path.resolve(folder);
// Normalize path and resolve symlinks return !unsafe.some(u => resolved.includes(u));
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;
}
} }
module.exports = { isValidUrl, isSafePath }; module.exports = { isValidUrl, isSafePath };

View File

@@ -3,23 +3,33 @@ const { userYtDlp, defaultDownloadFolder } = require("../helpers/path");
const fs = require("fs"); const fs = require("fs");
const { logger } = require("../logger"); const { logger } = require("../logger");
const { buildYtDlpArgs } = require("../helpers/buildArgs"); 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) { function fetchDownload(options, listeners, speedListeners) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const outputFolder = options.outputFolder || defaultDownloadFolder; 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 // Create download folder if it doesn't exist
try { try {
fs.mkdirSync(outputFolder, { recursive: true }); fs.mkdirSync(safeOutputFolder, { recursive: true });
logger.info(`Output folder ready: ${outputFolder}`); logger.info(`Output folder ready: ${safeOutputFolder}`);
} catch (err) { } catch (err) {
logger.error(`Failed to create output folder: ${err.message}`); logger.error(`Failed to create output folder: ${err.message}`);
return reject(new Error(`Unable to create download 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(" ")}`); logger.info(`[yt-dlp args] ${args.join(" ")}`);
const child = execFile(userYtDlp, args); const child = execFile(userYtDlp, args);
@@ -28,7 +38,7 @@ function fetchDownload(options, listeners, speedListeners) {
child.on("close", code => { child.on("close", code => {
listeners.forEach(fn => fn("done")); 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}`)); else reject(new Error(`YT-DLP failed with code : ${code}`));
}); });