From 0cf5391cfac521d3418b9aae7b82a3111a353708 Mon Sep 17 00:00:00 2001 From: crStiv Date: Sun, 5 Jan 2025 01:30:35 +0100 Subject: [PATCH 1/2] Update fileManager.ts --- apps/remix-ide/src/app/files/fileManager.ts | 51 ++++++++++++++++++++- 1 file changed, 50 insertions(+), 1 deletion(-) diff --git a/apps/remix-ide/src/app/files/fileManager.ts b/apps/remix-ide/src/app/files/fileManager.ts index a821ced648f..3ede9988778 100644 --- a/apps/remix-ide/src/app/files/fileManager.ts +++ b/apps/remix-ide/src/app/files/fileManager.ts @@ -1,4 +1,3 @@ - 'use strict' import { saveAs } from 'file-saver' import JSZip from 'jszip' @@ -9,6 +8,8 @@ import { fileChangedToastMsg, recursivePasteToastMsg, storageFullMessage } from import helper from '../../lib/helper.js' import { RemixAppManager } from '../../remixAppManager' import { commitChange } from '@remix-ui/git' +import AES from 'crypto-js/aes' +import CryptoJS from 'crypto-js' /* attach to files event (removed renamed) @@ -209,6 +210,15 @@ class FileManager extends Plugin { try { path = this.normalize(path) path = this.limitPluginScope(path) + + // Add file path validation + if (!this.validatePath(path)) { + throw createError({ + code: 'EINVAL', + message: `Invalid file path: ${path}` + }) + } + if (await this.exists(path)) { await this._handleIsFile(path, `Cannot write file ${path}`) return await this.setFileContent(path, data, options) @@ -1095,6 +1105,45 @@ class FileManager extends Plugin { } throw new Error('copyFolderToJson not available') } + + private encryptData(data: string): string { + const key = process.env.STORAGE_KEY || 'default-key' + return AES.encrypt(data, key).toString() + } + + private decryptData(encryptedData: string): string { + const key = process.env.STORAGE_KEY || 'default-key' + const bytes = AES.decrypt(encryptedData, key) + return bytes.toString(CryptoJS.enc.Utf8) + } + + public saveFile(path: string, content: string) { + const encryptedContent = this.encryptData(content) + localStorage.setItem(path, encryptedContent) + } + + public getFile(path: string): string { + const encryptedContent = localStorage.getItem(path) + return encryptedContent ? this.decryptData(encryptedContent) : null + } + + // Adding a new method for file path validation + private validatePath(path: string): boolean { + // Check for null/undefined + if (!path) return false + + // Check for path traversal attack attempt + if (path.includes('..')) return false + + // Check for special characters + const invalidChars = /[<>:"|?*]/ + if (invalidChars.test(path)) return false + + // Check for the maximum path length + if (path.length > 255) return false + + return true + } } module.exports = FileManager From 4c2951b883a5e5274e34925d3ebdd03a5ff6bd99 Mon Sep 17 00:00:00 2001 From: crStiv Date: Sun, 5 Jan 2025 01:31:21 +0100 Subject: [PATCH 2/2] Create fileManager.spec.ts --- .../src/app/files/tests/fileManager.spec.ts | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 apps/remix-ide/src/app/files/tests/fileManager.spec.ts diff --git a/apps/remix-ide/src/app/files/tests/fileManager.spec.ts b/apps/remix-ide/src/app/files/tests/fileManager.spec.ts new file mode 100644 index 00000000000..2c95e386dad --- /dev/null +++ b/apps/remix-ide/src/app/files/tests/fileManager.spec.ts @@ -0,0 +1,36 @@ +import { FileManager } from '../fileManager' + +describe('FileManager', () => { + describe('validatePath', () => { + let fileManager + + beforeEach(() => { + fileManager = new FileManager() + }) + + it('should reject null/undefined paths', () => { + expect(fileManager.validatePath(null)).toBe(false) + expect(fileManager.validatePath(undefined)).toBe(false) + }) + + it('should reject path traversal attempts', () => { + expect(fileManager.validatePath('../test.sol')).toBe(false) + expect(fileManager.validatePath('folder/../test.sol')).toBe(false) + }) + + it('should reject paths with special characters', () => { + expect(fileManager.validatePath('test?.sol')).toBe(false) + expect(fileManager.validatePath('test*.sol')).toBe(false) + }) + + it('should reject too long paths', () => { + const longPath = 'a'.repeat(256) + expect(fileManager.validatePath(longPath)).toBe(false) + }) + + it('should accept valid paths', () => { + expect(fileManager.validatePath('contracts/test.sol')).toBe(true) + expect(fileManager.validatePath('folder/subfolder/test.sol')).toBe(true) + }) + }) +})