From 2dea632e2e430fa58d40a618495bbc36ceaf54c1 Mon Sep 17 00:00:00 2001 From: ZHallen122 Date: Mon, 24 Feb 2025 17:13:37 -0500 Subject: [PATCH 1/3] Path protection --- .../FileOperationManager.ts | 59 +++---------------- .../utils/security/Path_protect.ts | 31 ++++++++++ 2 files changed, 39 insertions(+), 51 deletions(-) create mode 100644 backend/src/build-system/utils/security/Path_protect.ts diff --git a/backend/src/build-system/handlers/frontend-code-generate/FileOperationManager.ts b/backend/src/build-system/handlers/frontend-code-generate/FileOperationManager.ts index 3a536e2f..cb41a943 100644 --- a/backend/src/build-system/handlers/frontend-code-generate/FileOperationManager.ts +++ b/backend/src/build-system/handlers/frontend-code-generate/FileOperationManager.ts @@ -14,6 +14,7 @@ import { Logger } from '@nestjs/common'; import { writeFile, rename, readFile } from 'fs/promises'; import path from 'path'; import { removeCodeBlockFences } from 'src/build-system/utils/strings'; +import { FilePathSafetyChecks } from 'src/build-system/utils/security/Path_protect'; export interface FileOperation { action: 'write' | 'rename' | 'read'; @@ -93,7 +94,8 @@ export class FileOperationManager { */ private async handleWrite(op: FileOperation): Promise { const originalPath = path.resolve(this.projectRoot, op.originalPath); - this.safetyChecks(originalPath); + const securityOptions = { projectRoot: this.projectRoot }; + FilePathSafetyChecks(originalPath, securityOptions); this.logger.debug('start update file to: ' + originalPath); const parseCode = removeCodeBlockFences(op.code); @@ -109,7 +111,8 @@ export class FileOperationManager { private async handleRead(op: FileOperation): Promise { try { const originalPath = path.resolve(this.projectRoot, op.originalPath); - this.safetyChecks(originalPath); + const securityOptions = { projectRoot: this.projectRoot }; + FilePathSafetyChecks(originalPath, securityOptions); this.logger.debug(`Reading file: ${originalPath}`); @@ -135,9 +138,10 @@ export class FileOperationManager { private async handleRename(op: FileOperation): Promise { const originalPath = path.resolve(this.projectRoot, op.originalPath); const RenamePath = path.resolve(this.projectRoot, op.renamePath); + const securityOptions = { projectRoot: this.projectRoot }; - this.safetyChecks(originalPath); - this.safetyChecks(RenamePath); + FilePathSafetyChecks(originalPath, securityOptions); + FilePathSafetyChecks(RenamePath, securityOptions); this.logger.debug('start rename: ' + originalPath); this.logger.debug('change to name: ' + RenamePath); @@ -206,51 +210,4 @@ export class FileOperationManager { // this.logger.log('Extracted operations:', operations); return operations; } - - /** - * Performs security checks on a given file path to ensure it is within - * the allowed project scope and doesn’t target restricted files. - * - * @param filePath - The path to be checked. - * @throws If the path is outside the project root or is otherwise disallowed. - */ - private safetyChecks(filePath: string) { - const targetPath = path.resolve(this.projectRoot, filePath); // Normalize path - - // Prevent path traversal attacks - if (!targetPath.startsWith(this.projectRoot)) { - throw new Error('Unauthorized file access detected'); - } - - // Prevent package.json modifications - if (targetPath.includes('package.json')) { - throw new Error('Modifying package.json requires special approval'); - } - - // Security check - if (!this.isPathAllowed(targetPath)) { - throw new Error(`Attempted to access restricted path: ${targetPath}`); - } - - // Limit write anddelete write operations - // if (path.startsWith('src/')) { - // throw new Error('Can only delete or write files in src/ directory'); - // } - } - - /** - * Checks if the targetPath is within one of the allowed paths - * and not in node_modules or environment files. - * - * @param targetPath - The path to check. - * @returns True if allowed, false otherwise. - */ - private isPathAllowed(targetPath: string): boolean { - return this.allowedPaths.some( - (allowedPath) => - targetPath.startsWith(allowedPath) && - !targetPath.includes('node_modules') && - !targetPath.includes('.env'), - ); - } } diff --git a/backend/src/build-system/utils/security/Path_protect.ts b/backend/src/build-system/utils/security/Path_protect.ts new file mode 100644 index 00000000..af7c094e --- /dev/null +++ b/backend/src/build-system/utils/security/Path_protect.ts @@ -0,0 +1,31 @@ +// securityCheckUtil.ts +import path from 'path'; + +export interface SecurityCheckOptions { + projectRoot: string; + allowedPaths?: string[]; +} + +/** + * Performs security checks on a given file path to ensure it is within + * the allowed project scope and doesn’t target restricted files. + * + * @param filePath - The path to be checked. + * @param options - The security options, including projectRoot and allowedPaths. + * @throws If the path is outside the project root or is otherwise disallowed. + */ +export function FilePathSafetyChecks( + filePath: string, + options: SecurityCheckOptions, +) { + const { projectRoot, allowedPaths } = options; + + const targetPath = path.resolve(projectRoot, filePath); + const relativePath = path.relative(projectRoot, targetPath); + // Prevent path traversal attacks + if (relativePath.startsWith('..') || path.isAbsolute(relativePath)) { + throw new Error('Unauthorized file access detected'); + } + + // To do white list check +} From 54c77a18535d12e98d1f0c59bd06a085241975bb Mon Sep 17 00:00:00 2001 From: ZHallen122 Date: Tue, 25 Feb 2025 18:52:26 -0500 Subject: [PATCH 2/3] update file name --- .../handlers/frontend-code-generate/FileOperationManager.ts | 2 +- .../utils/security/{Path_protect.ts => path-check.ts} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename backend/src/build-system/utils/security/{Path_protect.ts => path-check.ts} (100%) diff --git a/backend/src/build-system/handlers/frontend-code-generate/FileOperationManager.ts b/backend/src/build-system/handlers/frontend-code-generate/FileOperationManager.ts index cb41a943..3a71c104 100644 --- a/backend/src/build-system/handlers/frontend-code-generate/FileOperationManager.ts +++ b/backend/src/build-system/handlers/frontend-code-generate/FileOperationManager.ts @@ -14,7 +14,7 @@ import { Logger } from '@nestjs/common'; import { writeFile, rename, readFile } from 'fs/promises'; import path from 'path'; import { removeCodeBlockFences } from 'src/build-system/utils/strings'; -import { FilePathSafetyChecks } from 'src/build-system/utils/security/Path_protect'; +import { FilePathSafetyChecks } from 'src/build-system/utils/security/path-check'; export interface FileOperation { action: 'write' | 'rename' | 'read'; diff --git a/backend/src/build-system/utils/security/Path_protect.ts b/backend/src/build-system/utils/security/path-check.ts similarity index 100% rename from backend/src/build-system/utils/security/Path_protect.ts rename to backend/src/build-system/utils/security/path-check.ts From 5ba71d582c469fe06b9840403297339936c6444f Mon Sep 17 00:00:00 2001 From: ZHallen122 Date: Tue, 25 Feb 2025 19:07:32 -0500 Subject: [PATCH 3/3] update function name --- .../frontend-code-generate/FileOperationManager.ts | 10 +++++----- backend/src/build-system/utils/security/path-check.ts | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/backend/src/build-system/handlers/frontend-code-generate/FileOperationManager.ts b/backend/src/build-system/handlers/frontend-code-generate/FileOperationManager.ts index 3a71c104..e185f2e4 100644 --- a/backend/src/build-system/handlers/frontend-code-generate/FileOperationManager.ts +++ b/backend/src/build-system/handlers/frontend-code-generate/FileOperationManager.ts @@ -14,7 +14,7 @@ import { Logger } from '@nestjs/common'; import { writeFile, rename, readFile } from 'fs/promises'; import path from 'path'; import { removeCodeBlockFences } from 'src/build-system/utils/strings'; -import { FilePathSafetyChecks } from 'src/build-system/utils/security/path-check'; +import { filePathSafetyChecks } from 'src/build-system/utils/security/path-check'; export interface FileOperation { action: 'write' | 'rename' | 'read'; @@ -95,7 +95,7 @@ export class FileOperationManager { private async handleWrite(op: FileOperation): Promise { const originalPath = path.resolve(this.projectRoot, op.originalPath); const securityOptions = { projectRoot: this.projectRoot }; - FilePathSafetyChecks(originalPath, securityOptions); + filePathSafetyChecks(originalPath, securityOptions); this.logger.debug('start update file to: ' + originalPath); const parseCode = removeCodeBlockFences(op.code); @@ -112,7 +112,7 @@ export class FileOperationManager { try { const originalPath = path.resolve(this.projectRoot, op.originalPath); const securityOptions = { projectRoot: this.projectRoot }; - FilePathSafetyChecks(originalPath, securityOptions); + filePathSafetyChecks(originalPath, securityOptions); this.logger.debug(`Reading file: ${originalPath}`); @@ -140,8 +140,8 @@ export class FileOperationManager { const RenamePath = path.resolve(this.projectRoot, op.renamePath); const securityOptions = { projectRoot: this.projectRoot }; - FilePathSafetyChecks(originalPath, securityOptions); - FilePathSafetyChecks(RenamePath, securityOptions); + filePathSafetyChecks(originalPath, securityOptions); + filePathSafetyChecks(RenamePath, securityOptions); this.logger.debug('start rename: ' + originalPath); this.logger.debug('change to name: ' + RenamePath); diff --git a/backend/src/build-system/utils/security/path-check.ts b/backend/src/build-system/utils/security/path-check.ts index af7c094e..589380d1 100644 --- a/backend/src/build-system/utils/security/path-check.ts +++ b/backend/src/build-system/utils/security/path-check.ts @@ -14,7 +14,7 @@ export interface SecurityCheckOptions { * @param options - The security options, including projectRoot and allowedPaths. * @throws If the path is outside the project root or is otherwise disallowed. */ -export function FilePathSafetyChecks( +export function filePathSafetyChecks( filePath: string, options: SecurityCheckOptions, ) {