diff --git a/apps/remix-ide/src/app/files/workspaceFileProvider.js b/apps/remix-ide/src/app/files/workspaceFileProvider.js index fe4e6825cb..70854929d9 100644 --- a/apps/remix-ide/src/app/files/workspaceFileProvider.js +++ b/apps/remix-ide/src/app/files/workspaceFileProvider.js @@ -1,6 +1,7 @@ 'use strict' const FileProvider = require('./fileProvider') +const pathModule = require('path') class WorkspaceFileProvider extends FileProvider { constructor () { @@ -32,8 +33,19 @@ class WorkspaceFileProvider extends FileProvider { if (path.startsWith(this.workspacesPath + '/' + this.workspace)) return path if (path.startsWith(this.workspace)) return this.workspacesPath + '/' + this.workspace path = super.removePrefix(path) - const ret = this.workspacesPath + '/' + this.workspace + '/' + (path === '/' ? '' : path) - return ret.replace(/^\/|\/$/g, '') + let ret = this.workspacesPath + '/' + this.workspace + '/' + (path === '/' ? '' : path) + + ret = ret.replace(/^\/|\/$/g, '') + if (!this.isSubDirectory(this.workspacesPath + '/' + this.workspace, ret)) throw new Error('Cannot read/write to path outside workspace') + return ret + } + + isSubDirectory (parent, child) { + if (!parent) return false + if (parent === child) return true + const relative = pathModule.relative(parent, child) + + return !!relative && relative.split(pathModule.sep)[0] !== '..' } resolveDirectory (path, callback) { diff --git a/libs/remix-ui/file-explorer/src/lib/file-explorer.tsx b/libs/remix-ui/file-explorer/src/lib/file-explorer.tsx index 60176dcd2d..2edab4d76c 100644 --- a/libs/remix-ui/file-explorer/src/lib/file-explorer.tsx +++ b/libs/remix-ui/file-explorer/src/lib/file-explorer.tsx @@ -314,25 +314,32 @@ export const FileExplorer = (props: FileExplorerProps) => { const createNewFile = (newFilePath: string) => { const fileManager = state.fileManager - helper.createNonClashingName(newFilePath, filesProvider, async (error, newName) => { - if (error) { - modal('Create File Failed', error, { - label: 'Close', - fn: async () => {} - }, null) - } else { - const createFile = await fileManager.writeFile(newName, '') - - if (!createFile) { - return toast('Failed to create file ' + newName) + try { + helper.createNonClashingName(newFilePath, filesProvider, async (error, newName) => { + if (error) { + modal('Create File Failed', error, { + label: 'Close', + fn: async () => {} + }, null) } else { - await fileManager.open(newName) - setState(prevState => { - return { ...prevState, focusElement: [{ key: newName, type: 'file' }] } - }) + const createFile = await fileManager.writeFile(newName, '') + + if (!createFile) { + return toast('Failed to create file ' + newName) + } else { + await fileManager.open(newName) + setState(prevState => { + return { ...prevState, focusElement: [{ key: newName, type: 'file' }] } + }) + } } - } - }) + }) + } catch (error) { + return modal('File Creation Failed', error.message, { + label: 'Close', + fn: async () => {} + }, null) + } } const createNewFolder = async (newFolderPath: string) => { @@ -353,8 +360,10 @@ export const FileExplorer = (props: FileExplorerProps) => { return { ...prevState, focusElement: [{ key: newFolderPath, type: 'folder' }] } }) } catch (e) { - console.log('error: ', e) - toast('Failed to create folder: ' + newFolderPath) + return modal('Folder Creation Failed', e.message, { + label: 'Close', + fn: async () => {} + }, null) } } diff --git a/libs/remixd/src/utils.ts b/libs/remixd/src/utils.ts index aa8cd28a70..067e5f4113 100644 --- a/libs/remixd/src/utils.ts +++ b/libs/remixd/src/utils.ts @@ -12,9 +12,25 @@ import * as pathModule from 'path' function absolutePath (path: string, sharedFolder:string): string { path = normalizePath(path) path = pathModule.resolve(sharedFolder, path) + if (!isSubDirectory(pathModule.resolve(process.cwd(), sharedFolder), path)) throw new Error('Cannot read/write to path outside shared folder.') return path } +/** + * returns a true if child is sub-directory of parent. + * + * @param {String} parent - path to parent directory + * @param {String} child - child path + * @return {Boolean} + */ +function isSubDirectory (parent: string, child: string) { + if (!parent) return false + if (parent === child) return true + const relative = pathModule.relative(parent, child) + + return !!relative && relative.split(pathModule.sep)[0] !== '..' +} + /** * return the relative path of the given @arg path * diff --git a/package.json b/package.json index 1ebb5e8243..a3414a0862 100644 --- a/package.json +++ b/package.json @@ -67,7 +67,7 @@ "nightwatch_local_signingMessage": "npm run build:e2e & nightwatch --config dist/apps/remix-ide-e2e/nightwatch.js dist/apps/remix-ide-e2e/src/tests/signingMessage.test.js --env=chrome", "nightwatch_local_specialFunctions": "npm run build:e2e & nightwatch --config dist/apps/remix-ide-e2e/nightwatch.js dist/apps/remix-ide-e2e/src/tests/specialFunctions.test.js --env=chrome", "nightwatch_local_solidityUnitTests": "npm run build:e2e & nightwatch --config dist/apps/remix-ide-e2e/nightwatch.js dist/apps/remix-ide-e2e/src/tests/solidityUnittests.test.js --env=chrome", - "nightwatch_local_remixd": "npm run build:e2e & nightwatch --config dist/apps/remix-ide-e2e/nightwatch.js dist/apps/remix-ide-e2e/src/tests/remixd.test.js --env=chrome", + "nightwatch_local_remixd": "npm run build:e2e && nightwatch --config dist/apps/remix-ide-e2e/nightwatch.js dist/apps/remix-ide-e2e/src/tests/remixd.test.js --env=chrome", "nightwatch_local_terminal": "npm run build:e2e & nightwatch --config dist/apps/remix-ide-e2e/nightwatch.js dist/apps/remix-ide-e2e/src/tests/terminal.test.js --env=chrome", "nightwatch_local_gist": "npm run build:e2e & nightwatch --config dist/apps/remix-ide-e2e/nightwatch.js dist/apps/remix-ide-e2e/src/tests/gist.test.js --env=chrome", "nightwatch_local_workspace": "npm run build:e2e & nightwatch --config dist/apps/remix-ide-e2e/nightwatch.js dist/apps/remix-ide-e2e/src/tests/workspace.test.js --env=chrome", @@ -85,7 +85,7 @@ "nightwatch_local_url": "npm run build:e2e & nightwatch --config dist/apps/remix-ide-e2e/nightwatch.js dist/apps/remix-ide-e2e/src/tests/url.test.js --env=chrome", "nightwatch_local_verticalIconscontextmenu": "npm run build:e2e & nightwatch --config dist/apps/remix-ide-e2e/nightwatch.js dist/apps/remix-ide-e2e/src/tests/verticalIconsPanel.test.js --env=chrome", "onchange": "onchange apps/remix-ide/build/app.js -- npm-run-all lint", - "remixd": "nx build remixd & nx serve remixd --folder=./apps/remix-ide/contracts --remixide=http://127.0.0.1:8080", + "remixd": "nx build remixd && nx serve remixd --folder=./apps/remix-ide/contracts --remixide=http://127.0.0.1:8080", "selenium": "selenium-standalone start", "selenium-install": "selenium-standalone install", "sourcemap": "exorcist --root ../ apps/remix-ide/build/app.js.map > apps/remix-ide/build/app.js",