From 215256b2fe5072177f0980d83867873d780fec45 Mon Sep 17 00:00:00 2001 From: ramki-bruno <192604295+ramki-bruno@users.noreply.github.com> Date: Tue, 8 Jul 2025 15:28:01 +0530 Subject: [PATCH] Added validator to check if a given path is inside an open Collection (#4800) * Refactor: Renamed `Watcher` class and instance to `CollectionWatcher` The name `Watcher` sounds very generic, but in this case its tightly coupled with watching Bruno Collection paths. So it makes sense to name it accordingly. * Added validator to check if a given path is inside an open Collection And added an sample validation for _new-request_ IPC event. * Review fixes --- .../src/app/{watcher.js => collection-watcher.js} | 12 ++++++++++-- packages/bruno-electron/src/index.js | 12 +++++------- packages/bruno-electron/src/ipc/collection.js | 12 ++++++++++++ 3 files changed, 27 insertions(+), 9 deletions(-) rename packages/bruno-electron/src/app/{watcher.js => collection-watcher.js} (98%) diff --git a/packages/bruno-electron/src/app/watcher.js b/packages/bruno-electron/src/app/collection-watcher.js similarity index 98% rename from packages/bruno-electron/src/app/watcher.js rename to packages/bruno-electron/src/app/collection-watcher.js index 05fe9ebe8..72b161292 100644 --- a/packages/bruno-electron/src/app/watcher.js +++ b/packages/bruno-electron/src/app/collection-watcher.js @@ -511,7 +511,7 @@ const onWatcherSetupComplete = (win, watchPath) => { win.webContents.send('main:hydrate-app-with-ui-state-snapshot', collectionSnapshotState); }; -class Watcher { +class CollectionWatcher { constructor() { this.watchers = {}; } @@ -615,6 +615,14 @@ class Watcher { watcher?.add?.(itemPath); } } + + getAllWatcherPaths() { + return Object.entries(this.watchers) + .filter(([path, watcher]) => !!watcher) + .map(([path, _watcher]) => path); + } } -module.exports = Watcher; +const collectionWatcher = new CollectionWatcher(); + +module.exports = collectionWatcher; diff --git a/packages/bruno-electron/src/index.js b/packages/bruno-electron/src/index.js index 436403a49..047a35f31 100644 --- a/packages/bruno-electron/src/index.js +++ b/packages/bruno-electron/src/index.js @@ -27,7 +27,7 @@ const LastOpenedCollections = require('./store/last-opened-collections'); const registerNetworkIpc = require('./ipc/network'); const registerCollectionsIpc = require('./ipc/collection'); const registerPreferencesIpc = require('./ipc/preferences'); -const Watcher = require('./app/watcher'); +const collectionWatcher = require('./app/collection-watcher'); const { loadWindowState, saveBounds, saveMaximized } = require('./utils/window'); const registerNotificationsIpc = require('./ipc/notifications'); const registerGlobalEnvironmentsIpc = require('./ipc/global-environments'); @@ -55,7 +55,6 @@ setContentSecurityPolicy(contentSecurityPolicy.join(';') + ';'); const menu = Menu.buildFromTemplate(menuTemplate); let mainWindow; -let watcher; // Prepare the renderer once the app is ready app.on('ready', async () => { @@ -131,7 +130,6 @@ app.on('ready', async () => { ); } }); - watcher = new Watcher(); const handleBoundsChange = () => { if (!mainWindow.isMaximized()) { @@ -181,9 +179,9 @@ app.on('ready', async () => { // register all ipc handlers registerNetworkIpc(mainWindow); registerGlobalEnvironmentsIpc(mainWindow); - registerCollectionsIpc(mainWindow, watcher, lastOpenedCollections); - registerPreferencesIpc(mainWindow, watcher, lastOpenedCollections); - registerNotificationsIpc(mainWindow, watcher); + registerCollectionsIpc(mainWindow, collectionWatcher, lastOpenedCollections); + registerPreferencesIpc(mainWindow, collectionWatcher, lastOpenedCollections); + registerNotificationsIpc(mainWindow, collectionWatcher); }); // Quit the app once all windows are closed @@ -191,5 +189,5 @@ app.on('window-all-closed', app.quit); // Open collection from Recent menu (#1521) app.on('open-file', (event, path) => { - openCollection(mainWindow, watcher, path); + openCollection(mainWindow, collectionWatcher, path); }); diff --git a/packages/bruno-electron/src/ipc/collection.js b/packages/bruno-electron/src/ipc/collection.js index b0924bb6b..f76590768 100644 --- a/packages/bruno-electron/src/ipc/collection.js +++ b/packages/bruno-electron/src/ipc/collection.js @@ -42,6 +42,7 @@ const { getEnvVars, getTreePathFromCollectionToItem, mergeVars, parseBruFileMeta const { getProcessEnvVars } = require('../store/process-env'); const { getOAuth2TokenUsingAuthorizationCode, getOAuth2TokenUsingClientCredentials, getOAuth2TokenUsingPasswordCredentials, refreshOauth2Token } = require('../utils/oauth2'); const { getCertsAndProxyConfig } = require('./network'); +const collectionWatcher = require('../app/collection-watcher'); const environmentSecretsStore = new EnvironmentSecretsStore(); const collectionSecurityStore = new CollectionSecurityStore(); @@ -58,6 +59,16 @@ const envHasSecrets = (environment = {}) => { return secrets && secrets.length > 0; }; +const validatePathIsInsideCollection = (path) => { + const openCollectionPaths = collectionWatcher.getAllWatcherPaths(); + const isValid = openCollectionPaths.some((collectionPath) => { + return path.startsWith(collectionPath); + }); + if (!isValid) { + throw new Error(`Path: ${path} should be inside a collection`); + } +} + const registerRendererEventHandlers = (mainWindow, watcher, lastOpenedCollections) => { // browse directory ipcMain.handle('renderer:browse-directory', async (event, pathname, request) => { @@ -237,6 +248,7 @@ const registerRendererEventHandlers = (mainWindow, watcher, lastOpenedCollection if (!validateName(request?.filename)) { throw new Error(`${request.filename}.bru is not a valid filename`); } + validatePathIsInsideCollection(pathname); const content = await jsonToBruViaWorker(request); await writeFile(pathname, content); } catch (error) {