From 707cddea9066d61bfdd9e0f87dae76b70dadef79 Mon Sep 17 00:00:00 2001 From: Pragadesh-45 <54320162+Pragadesh-45@users.noreply.github.com> Date: Fri, 29 Nov 2024 12:04:59 +0530 Subject: [PATCH 1/2] Fix/invalid file name handling (#3274) * feat: implement utility function `isValidFilename` * refactor: added filename validator checks for `rename-item` and `new-request` * chore: added `fileName.startsWith('.')` --- packages/bruno-electron/src/ipc/collection.js | 11 +++++++++-- packages/bruno-electron/src/utils/filesystem.js | 17 ++++++++++++++++- 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/packages/bruno-electron/src/ipc/collection.js b/packages/bruno-electron/src/ipc/collection.js index 30a891015..022ec9bc7 100644 --- a/packages/bruno-electron/src/ipc/collection.js +++ b/packages/bruno-electron/src/ipc/collection.js @@ -17,7 +17,8 @@ const { isWSLPath, normalizeWslPath, normalizeAndResolvePath, - safeToRename + safeToRename, + isValidFilename } = require('../utils/filesystem'); const { openCollectionDialog } = require('../app/collections'); const { generateUidBasedOnHash, stringifyJson, safeParseJSON, safeStringifyJSON } = require('../utils/common'); @@ -201,7 +202,9 @@ const registerRendererEventHandlers = (mainWindow, watcher, lastOpenedCollection if (fs.existsSync(pathname)) { throw new Error(`path: ${pathname} already exists`); } - + if (!isValidFilename(request.name)) { + throw new Error(`path: ${request.name}.bru is not a valid filename`); + } const content = jsonToBru(request); await writeFile(pathname, content); } catch (error) { @@ -366,6 +369,10 @@ const registerRendererEventHandlers = (mainWindow, watcher, lastOpenedCollection throw new Error(`path: ${oldPath} is not a bru file`); } + if (!isValidFilename(newName)) { + throw new Error(`path: ${newName} is not a valid filename`); + } + // update name in file and save new copy, then delete old copy const data = fs.readFileSync(oldPath, 'utf8'); const jsonData = bruToJson(data); diff --git a/packages/bruno-electron/src/utils/filesystem.js b/packages/bruno-electron/src/utils/filesystem.js index 0263939ae..a066edefc 100644 --- a/packages/bruno-electron/src/utils/filesystem.js +++ b/packages/bruno-electron/src/utils/filesystem.js @@ -160,6 +160,20 @@ const sanitizeDirectoryName = (name) => { return name.replace(/[<>:"/\\|?*\x00-\x1F]+/g, '-'); }; +const isValidFilename = (fileName) => { + const inValidChars = /[\\/:*?"<>|]/; + + if (!fileName || inValidChars.test(fileName)) { + return false; + } + + if (fileName.endsWith(' ') || fileName.endsWith('.') || fileName.startsWith('.')) { + return false; + } + + return true; +}; + const safeToRename = (oldPath, newPath) => { try { // If the new path doesn't exist, it's safe to rename @@ -204,5 +218,6 @@ module.exports = { searchForFiles, searchForBruFiles, sanitizeDirectoryName, - safeToRename + safeToRename, + isValidFilename }; From f2cfcab091ceaf2e4c54b21a8ad9ac272fb4fa43 Mon Sep 17 00:00:00 2001 From: Pragadesh-45 <54320162+Pragadesh-45@users.noreply.github.com> Date: Fri, 29 Nov 2024 12:18:11 +0530 Subject: [PATCH 2/2] feat: Graceful handle rename/move: Fix `EPERM` Error When Renaming Parent Folders on Windows (#3236) * refactor filesystem.js to use isWindowsOS() * add tempDir logic to gracefully rename the parent folder in a collection. fix: `EPERM` --------- Co-authored-by: Anoop M D --- packages/bruno-electron/src/ipc/collection.js | 23 ++++++++++++++----- .../bruno-electron/src/utils/filesystem.js | 7 +++++- 2 files changed, 23 insertions(+), 7 deletions(-) diff --git a/packages/bruno-electron/src/ipc/collection.js b/packages/bruno-electron/src/ipc/collection.js index 022ec9bc7..38261cdb7 100644 --- a/packages/bruno-electron/src/ipc/collection.js +++ b/packages/bruno-electron/src/ipc/collection.js @@ -1,5 +1,7 @@ const _ = require('lodash'); const fs = require('fs'); +const fsExtra = require('fs-extra'); +const os = require('os'); const path = require('path'); const { ipcMain, shell, dialog, app } = require('electron'); const { envJsonToBru, bruToJson, jsonToBru, jsonToCollectionBru } = require('../bru'); @@ -18,6 +20,7 @@ const { normalizeWslPath, normalizeAndResolvePath, safeToRename, + isWindowsOS, isValidFilename } = require('../utils/filesystem'); const { openCollectionDialog } = require('../app/collections'); @@ -361,11 +364,20 @@ const registerRendererEventHandlers = (mainWindow, watcher, lastOpenedCollection const newBruFilePath = bruFile.replace(oldPath, newPath); moveRequestUid(bruFile, newBruFilePath); } - return fs.renameSync(oldPath, newPath); + + if (isWindowsOS() && !isWSLPath(oldPath)) { + const tempDir = path.join(os.tmpdir(), `temp-folder-${Date.now()}`); + + await fsExtra.copy(oldPath, tempDir); + await fsExtra.move(tempDir, newPath, { overwrite: true }); + await fsExtra.remove(oldPath); + } else { + await fs.rename(oldPath, newPath); + } + return newPath; } - const isBru = hasBruExtension(oldPath); - if (!isBru) { + if (!hasBruExtension(oldPath)) { throw new Error(`path: ${oldPath} is not a bru file`); } @@ -374,14 +386,13 @@ const registerRendererEventHandlers = (mainWindow, watcher, lastOpenedCollection } // update name in file and save new copy, then delete old copy - const data = fs.readFileSync(oldPath, 'utf8'); + const data = await fs.promises.readFile(oldPath, 'utf8'); // Use async read const jsonData = bruToJson(data); - jsonData.name = newName; moveRequestUid(oldPath, newPath); const content = jsonToBru(jsonData); - await fs.unlinkSync(oldPath); + await fs.promises.unlink(oldPath); await writeFile(newPath, content); return newPath; diff --git a/packages/bruno-electron/src/utils/filesystem.js b/packages/bruno-electron/src/utils/filesystem.js index a066edefc..aeb0bdd96 100644 --- a/packages/bruno-electron/src/utils/filesystem.js +++ b/packages/bruno-electron/src/utils/filesystem.js @@ -160,6 +160,10 @@ const sanitizeDirectoryName = (name) => { return name.replace(/[<>:"/\\|?*\x00-\x1F]+/g, '-'); }; +const isWindowsOS = () => { + return os.platform() === 'win32'; +} + const isValidFilename = (fileName) => { const inValidChars = /[\\/:*?"<>|]/; @@ -184,7 +188,7 @@ const safeToRename = (oldPath, newPath) => { const oldStat = fs.statSync(oldPath); const newStat = fs.statSync(newPath); - if (os.platform() === 'win32') { + if (isWindowsOS()) { // Windows-specific comparison: // Check if both files have the same birth time, size (Since, Win FAT-32 doesn't use inodes) @@ -218,6 +222,7 @@ module.exports = { searchForFiles, searchForBruFiles, sanitizeDirectoryName, + isWindowsOS, safeToRename, isValidFilename };