From 0cd97db0301ae3be0d65340195694d3d5defedac Mon Sep 17 00:00:00 2001 From: yann300 Date: Tue, 2 Mar 2021 11:47:28 +0100 Subject: [PATCH 1/6] forbids plugin to access root file system --- apps/remix-ide/src/app/files/fileManager.js | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/apps/remix-ide/src/app/files/fileManager.js b/apps/remix-ide/src/app/files/fileManager.js index 6f971f3331..72ac2972a0 100644 --- a/apps/remix-ide/src/app/files/fileManager.js +++ b/apps/remix-ide/src/app/files/fileManager.js @@ -57,6 +57,12 @@ class FileManager extends Plugin { this.mode = mode } + limitPluginScope (path) { + if (!this.currentRequest) return path // no plugin request, path shall not be modified. + if (this.appManager.isRequired(this.currentRequest.from)) return path // caller is a service plugin, path shall not be modified + return path.replace(/^\/browser\//, '').replace(/^browser\//, '') // forbids plugin to access the root filesystem + } + /** * Emit error if path doesn't exist * @param {string} path path of the file/directory @@ -110,6 +116,7 @@ class FileManager extends Plugin { * @returns {boolean} true if the path exists */ exists (path) { + path = this.limitPluginScope(path) const provider = this.fileProviderOf(path) const result = provider.exists(path, (err, result) => { if (err) return false @@ -149,6 +156,7 @@ class FileManager extends Plugin { * @returns {void} */ async open (path) { + path = this.limitPluginScope(path) await this._handleExists(path, `Cannot open file ${path}`) await this._handleIsFile(path, `Cannot open file ${path}`) return this.openFile(path) @@ -161,6 +169,7 @@ class FileManager extends Plugin { * @returns {void} */ async writeFile (path, data) { + path = this.limitPluginScope(path) if (await this.exists(path)) { await this._handleIsFile(path, `Cannot write file ${path}`) return await this.setFileContent(path, data) @@ -177,6 +186,7 @@ class FileManager extends Plugin { * @returns {string} content of the file */ async readFile (path) { + path = this.limitPluginScope(path) await this._handleExists(path, `Cannot read file ${path}`) await this._handleIsFile(path, `Cannot read file ${path}`) return this.getFileContent(path) @@ -189,6 +199,8 @@ class FileManager extends Plugin { * @returns {void} */ async copyFile (src, dest) { + src = this.limitPluginScope(src) + dest = this.limitPluginScope(dest) await this._handleExists(src, `Cannot copy from ${src}`) await this._handleIsFile(src, `Cannot copy from ${src}`) await this._handleIsFile(dest, `Cannot paste content into ${dest}`) @@ -204,6 +216,8 @@ class FileManager extends Plugin { * @returns {void} */ async rename (oldPath, newPath) { + oldPath = this.limitPluginScope(oldPath) + newPath = this.limitPluginScope(newPath) await this._handleExists(oldPath, `Cannot rename ${oldPath}`) const isFile = await this.isFile(oldPath) const newPathExists = await this.exists(newPath) @@ -230,6 +244,7 @@ class FileManager extends Plugin { * @returns {void} */ async mkdir (path) { + path = this.limitPluginScope(path) if (await this.exists(path)) { throw createError({ code: 'EEXIST', message: `Cannot create directory ${path}` }) } @@ -244,6 +259,7 @@ class FileManager extends Plugin { * @returns {string[]} list of the file/directory name in this directory */ async readdir (path) { + path = this.limitPluginScope(path) await this._handleExists(path) await this._handleIsDir(path) @@ -263,6 +279,7 @@ class FileManager extends Plugin { * @returns {void} */ async remove (path) { + path = this.limitPluginScope(path) await this._handleExists(path, `Cannot remove file or directory ${path}`) const provider = this.fileProviderOf(path) From 19a735d82e82aced3653518582d5aa2b460e1baf Mon Sep 17 00:00:00 2001 From: yann300 Date: Tue, 2 Mar 2021 15:02:57 +0100 Subject: [PATCH 2/6] fix do not allow using filesystem root at all --- apps/remix-ide/src/app/files/fileManager.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/apps/remix-ide/src/app/files/fileManager.js b/apps/remix-ide/src/app/files/fileManager.js index 72ac2972a0..2b5c7dbd04 100644 --- a/apps/remix-ide/src/app/files/fileManager.js +++ b/apps/remix-ide/src/app/files/fileManager.js @@ -58,8 +58,6 @@ class FileManager extends Plugin { } limitPluginScope (path) { - if (!this.currentRequest) return path // no plugin request, path shall not be modified. - if (this.appManager.isRequired(this.currentRequest.from)) return path // caller is a service plugin, path shall not be modified return path.replace(/^\/browser\//, '').replace(/^browser\//, '') // forbids plugin to access the root filesystem } From 465d8ce803823dbaa072b337ae10e4314dc7a235 Mon Sep 17 00:00:00 2001 From: yann300 Date: Tue, 2 Mar 2021 15:46:12 +0100 Subject: [PATCH 3/6] move workspace creation to file panel --- apps/remix-ide/src/app/files/fileManager.js | 14 ------------ apps/remix-ide/src/app/files/fileProvider.js | 4 +++- apps/remix-ide/src/app/panels/file-panel.js | 23 +++++++++++++++++--- 3 files changed, 23 insertions(+), 18 deletions(-) diff --git a/apps/remix-ide/src/app/files/fileManager.js b/apps/remix-ide/src/app/files/fileManager.js index 2b5c7dbd04..d1838738e2 100644 --- a/apps/remix-ide/src/app/files/fileManager.js +++ b/apps/remix-ide/src/app/files/fileManager.js @@ -600,20 +600,6 @@ class FileManager extends Plugin { if (callback) callback(error) }) } - - async createWorkspace (name) { - const workspaceProvider = this._deps.filesProviders.workspace - const workspacePath = 'browser/' + workspaceProvider.workspacesPath + '/' + name - const workspaceRootPath = 'browser/' + workspaceProvider.workspacesPath - if (!this.exists(workspaceRootPath)) await this.mkdir(workspaceRootPath) - if (!this.exists(workspacePath)) await this.mkdir(workspacePath) - } - - async workspaceExists (name) { - const workspaceProvider = this._deps.filesProviders.workspace - const workspacePath = 'browser/' + workspaceProvider.workspacesPath + '/' + name - return this.exists(workspacePath) - } } module.exports = FileManager diff --git a/apps/remix-ide/src/app/files/fileProvider.js b/apps/remix-ide/src/app/files/fileProvider.js index 75e471ee83..adc5840dfd 100644 --- a/apps/remix-ide/src/app/files/fileProvider.js +++ b/apps/remix-ide/src/app/files/fileProvider.js @@ -66,7 +66,9 @@ class FileProvider { exists (path, cb) { // todo check the type (directory/file) as well #2386 // currently it is not possible to have a file and folder with same path - return cb(null, this._exists(path)) + const ret = this._exists(path) + if (cb) cb(null, ret) + return ret } _exists (path) { diff --git a/apps/remix-ide/src/app/panels/file-panel.js b/apps/remix-ide/src/app/panels/file-panel.js index 8bf74a6756..03a7283dd3 100644 --- a/apps/remix-ide/src/app/panels/file-panel.js +++ b/apps/remix-ide/src/app/panels/file-panel.js @@ -170,13 +170,30 @@ module.exports = class Filepanel extends ViewPlugin { return await this.request.uploadFile() } + async processCreateWorkspace (name) { + const workspaceProvider = this._deps.fileProviders.workspace + const browserProvider = this._deps.fileProviders.browser + const workspacePath = 'browser/' + workspaceProvider.workspacesPath + '/' + name + const workspaceRootPath = 'browser/' + workspaceProvider.workspacesPath + if (!browserProvider.exists(workspaceRootPath)) browserProvider.createDir(workspaceRootPath) + if (!browserProvider.exists(workspacePath)) browserProvider.createDir(workspacePath) + } + + async workspaceExists (name) { + const workspaceProvider = this._deps.fileProviders.workspace + const browserProvider = this._deps.fileProviders.browser + const workspacePath = 'browser/' + workspaceProvider.workspacesPath + '/' + name + return browserProvider.exists(workspacePath) + } + async createWorkspace (workspaceName) { - if (await this._deps.fileManager.workspaceExists(workspaceName)) throw new Error('workspace already exists') + if (await this.workspaceExists(workspaceName)) throw new Error('workspace already exists') + const browserProvider = this._deps.fileProviders.browser const workspacesPath = this._deps.fileProviders.workspace.workspacesPath - await this._deps.fileManager.createWorkspace(workspaceName) + await this.processCreateWorkspace(workspaceName) for (const file in examples) { try { - await this._deps.fileManager.writeFile('browser/' + workspacesPath + '/' + workspaceName + '/' + examples[file].name, examples[file].content) + await browserProvider.set('browser/' + workspacesPath + '/' + workspaceName + '/' + examples[file].name, examples[file].content) } catch (error) { console.error(error) } From 7a96674853ac3ba14ec8719d2cf1cb9bf0a2d4d0 Mon Sep 17 00:00:00 2001 From: yann300 Date: Tue, 2 Mar 2021 15:46:23 +0100 Subject: [PATCH 4/6] fix migration --- apps/remix-ide/src/app.js | 2 +- apps/remix-ide/src/migrateFileSystem.js | 15 ++++++++------- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/apps/remix-ide/src/app.js b/apps/remix-ide/src/app.js index 7502e3541e..ede9ff3c0c 100644 --- a/apps/remix-ide/src/app.js +++ b/apps/remix-ide/src/app.js @@ -485,7 +485,7 @@ Please make a backup of your contracts and start using http://remix.ethereum.org // get the file list from the parent iframe loadFileFromParent(fileManager) - migrateToWorkspace(fileManager) + migrateToWorkspace(fileManager, filePanel) if (params.embed) framingService.embed() } diff --git a/apps/remix-ide/src/migrateFileSystem.js b/apps/remix-ide/src/migrateFileSystem.js index 42dea201e8..f626e1c20b 100644 --- a/apps/remix-ide/src/migrateFileSystem.js +++ b/apps/remix-ide/src/migrateFileSystem.js @@ -21,7 +21,7 @@ export default (fileProvider) => { fileStorageBrowserFS.set(flag, 'done') } -export async function migrateToWorkspace (fileManager) { +export async function migrateToWorkspace (fileManager, filePanel) { const browserProvider = fileManager.getProvider('browser') const workspaceProvider = fileManager.getProvider('workspace') const flag = 'status' @@ -31,19 +31,20 @@ export async function migrateToWorkspace (fileManager) { console.log(files) const workspaceName = 'default_workspace' const workspacePath = joinPath('browser', workspaceProvider.workspacesPath, workspaceName) - await fileManager.createWorkspace(workspaceName) - await populateWorkspace(workspacePath, files, fileManager) + await filePanel.createWorkspace(workspaceName) + filePanel.getWorkspaces() // refresh list + await populateWorkspace(workspacePath, files, browserProvider) fileStorageBrowserWorkspace.set(flag, 'done') } -const populateWorkspace = async (workspace, json, fileManager) => { +const populateWorkspace = async (workspace, json, browserProvider) => { for (const item in json) { const isFolder = json[item].content === undefined if (isFolder) { - await fileManager.mkdir(joinPath(workspace, item)) - await populateWorkspace(workspace, json[item].children, fileManager) + browserProvider.createDir(joinPath(workspace, item)) + await populateWorkspace(workspace, json[item].children, browserProvider) } else { - await fileManager.writeFile(joinPath(workspace, item), json[item].content) + await browserProvider.set(joinPath(workspace, item), json[item].content) } } } From 5d382763ef7e413b56d6369841d60e819cf68b08 Mon Sep 17 00:00:00 2001 From: yann300 Date: Tue, 2 Mar 2021 16:23:43 +0100 Subject: [PATCH 5/6] fix creation of "code-sample" && use of initialWorkspace --- apps/remix-ide/src/app/panels/file-panel.js | 6 +++--- .../workspace/src/lib/remix-ui-workspace.tsx | 14 ++++++++------ 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/apps/remix-ide/src/app/panels/file-panel.js b/apps/remix-ide/src/app/panels/file-panel.js index 03a7283dd3..cb0b5ebd10 100644 --- a/apps/remix-ide/src/app/panels/file-panel.js +++ b/apps/remix-ide/src/app/panels/file-panel.js @@ -139,12 +139,12 @@ module.exports = class Filepanel extends ViewPlugin { if (loadedFromGist) return if (params.code) { try { - await this._deps.fileManager.createWorkspace('code-sample') + await this.processCreateWorkspace('code-sample') this._deps.fileProviders.workspace.setWorkspace('code-sample') var hash = ethutil.bufferToHex(ethutil.keccak(params.code)) const fileName = 'contract-' + hash.replace('0x', '').substring(0, 10) + '.sol' - const path = 'browser/' + workspacesPath + '/code-sample/' + fileName - await this._deps.fileManager.writeFile(path, atob(params.code)) + const path = fileName + await this._deps.fileProviders.workspace.set(path, atob(params.code)) this.initialWorkspace = 'code-sample' await this._deps.fileManager.openFile(fileName) } catch (e) { diff --git a/libs/remix-ui/workspace/src/lib/remix-ui-workspace.tsx b/libs/remix-ui/workspace/src/lib/remix-ui-workspace.tsx index a23e143a0a..d21f42a64a 100644 --- a/libs/remix-ui/workspace/src/lib/remix-ui-workspace.tsx +++ b/libs/remix-ui/workspace/src/lib/remix-ui-workspace.tsx @@ -62,12 +62,7 @@ export const Workspace = (props: WorkspaceProps) => { useEffect(() => { const getWorkspaces = async () => { if (props.workspaces && Array.isArray(props.workspaces)) { - if (props.initialWorkspace) { - props.workspace.setWorkspace(props.initialWorkspace) - setState(prevState => { - return { ...prevState, workspaces: props.workspaces, currentWorkspace: props.initialWorkspace } - }) - } else if (props.workspaces.length > 0 && state.currentWorkspace === NO_WORKSPACE) { + if (props.workspaces.length > 0 && state.currentWorkspace === NO_WORKSPACE) { props.workspace.setWorkspace(props.workspaces[0]) setState(prevState => { return { ...prevState, workspaces: props.workspaces, currentWorkspace: props.workspaces[0] } @@ -91,6 +86,13 @@ export const Workspace = (props: WorkspaceProps) => { props.localhost.event.register('disconnected', (event) => { remixdExplorer.hide() }) + + if (props.initialWorkspace) { + props.workspace.setWorkspace(props.initialWorkspace) + setState(prevState => { + return { ...prevState, currentWorkspace: props.initialWorkspace } + }) + } }, []) const [state, setState] = useState({ From 63cfbe04600e8c17d778392ed1ebc7f8028c274a Mon Sep 17 00:00:00 2001 From: yann300 Date: Tue, 2 Mar 2021 16:41:37 +0100 Subject: [PATCH 6/6] linting --- apps/remix-ide/src/app/panels/file-panel.js | 1 - 1 file changed, 1 deletion(-) diff --git a/apps/remix-ide/src/app/panels/file-panel.js b/apps/remix-ide/src/app/panels/file-panel.js index cb0b5ebd10..dad049d335 100644 --- a/apps/remix-ide/src/app/panels/file-panel.js +++ b/apps/remix-ide/src/app/panels/file-panel.js @@ -131,7 +131,6 @@ module.exports = class Filepanel extends ViewPlugin { async initWorkspace () { const queryParams = new QueryParams() const gistHandler = new GistHandler() - const workspacesPath = this._deps.fileProviders.workspace.workspacesPath const params = queryParams.get() // get the file from gist const loadedFromGist = gistHandler.loadFromGist(params, this._deps.fileManager)