From 55966144ecf0440ea85dc9194b2defc84d5af688 Mon Sep 17 00:00:00 2001 From: rongquan1 Date: Wed, 15 Oct 2025 15:08:48 +0800 Subject: [PATCH 1/6] fix: add path traversal validation and safer file operations in wrap performance test --- performance-tests/wrap-performance-test.ts | 41 +++++++++++++++++----- tradetrust-cli | 1 + 2 files changed, 34 insertions(+), 8 deletions(-) create mode 160000 tradetrust-cli diff --git a/performance-tests/wrap-performance-test.ts b/performance-tests/wrap-performance-test.ts index d103491d..719b91fc 100644 --- a/performance-tests/wrap-performance-test.ts +++ b/performance-tests/wrap-performance-test.ts @@ -1,10 +1,10 @@ import { wrap } from "../src/implementations/wrap"; import { Output } from "../src/implementations/utils/disk"; import { performance } from "perf_hooks"; -import { existsSync, mkdirSync, rmdirSync, promises, constants } from "fs"; +import { existsSync, mkdirSync, rmSync, promises, constants } from "fs"; import { access } from "fs/promises"; import { SchemaId } from "@tradetrust-tt/tradetrust"; -import { join, parse, resolve } from "path"; +import { join, parse, resolve, normalize } from "path"; const DEFAULT_NUMBER_OF_FILE = 2; const DEFAULT_ITERATION = 1; @@ -19,6 +19,25 @@ const setup = async (filePath: string, numberOfFiles: number): Promise => const fileExtension = parse(filePath).ext; try { + // Validate the source file path to prevent directory traversal + const resolvedFilePath = resolve(filePath); + + // Ensure the source file path is within the allowed directory + if (!resolvedFilePath.startsWith(__dirname)) { + throw new Error("Source file path is outside the allowed directory."); + } + + // Additional validation: ensure no path traversal attempts + const normalizedFilePath = normalize(resolvedFilePath); + if (normalizedFilePath !== resolvedFilePath || normalizedFilePath.includes('..')) { + throw new Error("Path traversal attempt detected in source file path."); + } + + // Ensure the source file exists + if (!existsSync(resolvedFilePath)) { + throw new Error("Source file does not exist."); + } + existsSync(INPUT_UNWRAPPED_FILE_FOLDER) || mkdirSync(INPUT_UNWRAPPED_FILE_FOLDER, { recursive: true }); for (let index = 0; index < numberOfFiles; index++) { const outputPath = resolve(INPUT_UNWRAPPED_FILE_FOLDER, `${fileName + (index + 1)}${fileExtension}`); @@ -29,10 +48,10 @@ const setup = async (filePath: string, numberOfFiles: number): Promise => } // Ensure the file exists and is readable - await access(filePath, constants.R_OK); + await access(resolvedFilePath, constants.R_OK); - // Copy the file safely - await promises.copyFile(filePath, outputPath); + // Copy the file safely using the validated path + await promises.copyFile(resolvedFilePath, outputPath); } } catch (e) { console.error(e); @@ -42,8 +61,8 @@ const setup = async (filePath: string, numberOfFiles: number): Promise => // Destroy generated folder const destroy = (): void => { console.info("Cleaning generated files from setup"); - !existsSync(INPUT_UNWRAPPED_FILE_FOLDER) || rmdirSync(INPUT_UNWRAPPED_FILE_FOLDER, { recursive: true }); - !existsSync(OUTPUT_WRAPPED_FILE_FOLDER) || rmdirSync(OUTPUT_WRAPPED_FILE_FOLDER, { recursive: true }); + !existsSync(INPUT_UNWRAPPED_FILE_FOLDER) || rmSync(INPUT_UNWRAPPED_FILE_FOLDER, { recursive: true }); + !existsSync(OUTPUT_WRAPPED_FILE_FOLDER) || rmSync(OUTPUT_WRAPPED_FILE_FOLDER, { recursive: true }); }; // Monitor batched wrap feature for the response time @@ -62,8 +81,14 @@ const monitorWrapFeature = async (): Promise => { throw new Error("Unsafe file path detected."); } + // Additional validation: ensure no path traversal attempts + const normalizedFilePath = normalize(absoluteFilePath); + if (normalizedFilePath !== absoluteFilePath || normalizedFilePath.includes('..')) { + throw new Error("Path traversal attempt detected in input file path."); + } + // Setup Number of Files - await setup(filePath, numberOfFiles); + await setup(absoluteFilePath, numberOfFiles); const responseTime: Array = []; for (let index = 0; index < iteration; index++) { diff --git a/tradetrust-cli b/tradetrust-cli new file mode 160000 index 00000000..0e49230b --- /dev/null +++ b/tradetrust-cli @@ -0,0 +1 @@ +Subproject commit 0e49230bb41cb4b7fc74cec001d4f0677616a548 From ffb8b59354b3869bfed05189eea1557f31739510 Mon Sep 17 00:00:00 2001 From: rongquan1 Date: Wed, 15 Oct 2025 15:11:17 +0800 Subject: [PATCH 2/6] fix: remove --- tradetrust-cli | 1 - 1 file changed, 1 deletion(-) delete mode 160000 tradetrust-cli diff --git a/tradetrust-cli b/tradetrust-cli deleted file mode 160000 index 0e49230b..00000000 --- a/tradetrust-cli +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 0e49230bb41cb4b7fc74cec001d4f0677616a548 From 7c594b2bc82d697cc7b5b0a25f5177b1934a4f4d Mon Sep 17 00:00:00 2001 From: rongquan1 Date: Wed, 15 Oct 2025 15:12:45 +0800 Subject: [PATCH 3/6] fix: lint --- performance-tests/wrap-performance-test.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/performance-tests/wrap-performance-test.ts b/performance-tests/wrap-performance-test.ts index 719b91fc..f67536b6 100644 --- a/performance-tests/wrap-performance-test.ts +++ b/performance-tests/wrap-performance-test.ts @@ -21,7 +21,7 @@ const setup = async (filePath: string, numberOfFiles: number): Promise => try { // Validate the source file path to prevent directory traversal const resolvedFilePath = resolve(filePath); - + // Ensure the source file path is within the allowed directory if (!resolvedFilePath.startsWith(__dirname)) { throw new Error("Source file path is outside the allowed directory."); @@ -29,7 +29,7 @@ const setup = async (filePath: string, numberOfFiles: number): Promise => // Additional validation: ensure no path traversal attempts const normalizedFilePath = normalize(resolvedFilePath); - if (normalizedFilePath !== resolvedFilePath || normalizedFilePath.includes('..')) { + if (normalizedFilePath !== resolvedFilePath || normalizedFilePath.includes("..")) { throw new Error("Path traversal attempt detected in source file path."); } @@ -83,7 +83,7 @@ const monitorWrapFeature = async (): Promise => { // Additional validation: ensure no path traversal attempts const normalizedFilePath = normalize(absoluteFilePath); - if (normalizedFilePath !== absoluteFilePath || normalizedFilePath.includes('..')) { + if (normalizedFilePath !== absoluteFilePath || normalizedFilePath.includes("..")) { throw new Error("Path traversal attempt detected in input file path."); } From 4c193609e0953bb21956a0cbab296382ec9264f3 Mon Sep 17 00:00:00 2001 From: rongquan1 Date: Wed, 15 Oct 2025 15:22:01 +0800 Subject: [PATCH 4/6] fix: refactor --- performance-tests/wrap-performance-test.ts | 63 +++++++++------------- 1 file changed, 24 insertions(+), 39 deletions(-) diff --git a/performance-tests/wrap-performance-test.ts b/performance-tests/wrap-performance-test.ts index f67536b6..ce089e22 100644 --- a/performance-tests/wrap-performance-test.ts +++ b/performance-tests/wrap-performance-test.ts @@ -1,10 +1,9 @@ import { wrap } from "../src/implementations/wrap"; import { Output } from "../src/implementations/utils/disk"; import { performance } from "perf_hooks"; -import { existsSync, mkdirSync, rmSync, promises, constants } from "fs"; -import { access } from "fs/promises"; +import { existsSync, mkdirSync, rmSync, promises } from "fs"; import { SchemaId } from "@tradetrust-tt/tradetrust"; -import { join, parse, resolve, normalize } from "path"; +import { join, parse, resolve } from "path"; const DEFAULT_NUMBER_OF_FILE = 2; const DEFAULT_ITERATION = 1; @@ -12,6 +11,22 @@ const DEFAULT_FILE_PATH = join(__dirname, "unwrapped_document.json"); const INPUT_UNWRAPPED_FILE_FOLDER = join(__dirname, "setup", "raw-documents"); const OUTPUT_WRAPPED_FILE_FOLDER = join(__dirname, "setup", "wrapped-documents"); +// Shared helper function for file path validation +const validateFilePath = async (filePath: string, baseDir: string): Promise => { + const resolvedFilePath = resolve(filePath); + + if (!existsSync(resolvedFilePath)) { + throw new Error("File does not exist."); + } + + const canonicalPath = await promises.realpath(resolvedFilePath); + if (!canonicalPath.startsWith(resolve(baseDir))) { + throw new Error("File path is outside the allowed directory."); + } + + return canonicalPath; +}; + // Setup number of files const setup = async (filePath: string, numberOfFiles: number): Promise => { console.info("Setup up files for testing"); @@ -19,24 +34,8 @@ const setup = async (filePath: string, numberOfFiles: number): Promise => const fileExtension = parse(filePath).ext; try { - // Validate the source file path to prevent directory traversal - const resolvedFilePath = resolve(filePath); - - // Ensure the source file path is within the allowed directory - if (!resolvedFilePath.startsWith(__dirname)) { - throw new Error("Source file path is outside the allowed directory."); - } - - // Additional validation: ensure no path traversal attempts - const normalizedFilePath = normalize(resolvedFilePath); - if (normalizedFilePath !== resolvedFilePath || normalizedFilePath.includes("..")) { - throw new Error("Path traversal attempt detected in source file path."); - } - - // Ensure the source file exists - if (!existsSync(resolvedFilePath)) { - throw new Error("Source file does not exist."); - } + // Validate and resolve the source file path + const canonicalPath = await validateFilePath(filePath, __dirname); existsSync(INPUT_UNWRAPPED_FILE_FOLDER) || mkdirSync(INPUT_UNWRAPPED_FILE_FOLDER, { recursive: true }); for (let index = 0; index < numberOfFiles; index++) { @@ -47,11 +46,8 @@ const setup = async (filePath: string, numberOfFiles: number): Promise => throw new Error("Unsafe file path detected."); } - // Ensure the file exists and is readable - await access(resolvedFilePath, constants.R_OK); - // Copy the file safely using the validated path - await promises.copyFile(resolvedFilePath, outputPath); + await promises.copyFile(canonicalPath, outputPath); } } catch (e) { console.error(e); @@ -73,22 +69,11 @@ const monitorWrapFeature = async (): Promise => { const iteration: number = parseInt(process.argv[3], 10) || DEFAULT_ITERATION; const filePath: string = process.argv[4] || DEFAULT_FILE_PATH; - // Ensure the provided file path is absolute - const absoluteFilePath = resolve(filePath); - - // Ensure the file path is within the allowed directory - if (!absoluteFilePath.startsWith(__dirname)) { - throw new Error("Unsafe file path detected."); - } - - // Additional validation: ensure no path traversal attempts - const normalizedFilePath = normalize(absoluteFilePath); - if (normalizedFilePath !== absoluteFilePath || normalizedFilePath.includes("..")) { - throw new Error("Path traversal attempt detected in input file path."); - } + // Validate and resolve the input file path + const canonicalInputPath = await validateFilePath(filePath, __dirname); // Setup Number of Files - await setup(absoluteFilePath, numberOfFiles); + await setup(canonicalInputPath, numberOfFiles); const responseTime: Array = []; for (let index = 0; index < iteration; index++) { From d0e2defa3c4b0566eb8718dcd9af9c2ed4882fdb Mon Sep 17 00:00:00 2001 From: rongquan1 Date: Wed, 15 Oct 2025 18:15:48 +0800 Subject: [PATCH 5/6] fix: refactor --- performance-tests/wrap-performance-test.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/performance-tests/wrap-performance-test.ts b/performance-tests/wrap-performance-test.ts index ce089e22..d3b8c20f 100644 --- a/performance-tests/wrap-performance-test.ts +++ b/performance-tests/wrap-performance-test.ts @@ -3,7 +3,7 @@ import { Output } from "../src/implementations/utils/disk"; import { performance } from "perf_hooks"; import { existsSync, mkdirSync, rmSync, promises } from "fs"; import { SchemaId } from "@tradetrust-tt/tradetrust"; -import { join, parse, resolve } from "path"; +import { join, parse, resolve, relative, isAbsolute } from "path"; const DEFAULT_NUMBER_OF_FILE = 2; const DEFAULT_ITERATION = 1; @@ -20,7 +20,11 @@ const validateFilePath = async (filePath: string, baseDir: string): Promise Date: Thu, 16 Oct 2025 10:00:20 +0800 Subject: [PATCH 6/6] fix: refactor --- performance-tests/wrap-performance-test.ts | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/performance-tests/wrap-performance-test.ts b/performance-tests/wrap-performance-test.ts index d3b8c20f..3412e081 100644 --- a/performance-tests/wrap-performance-test.ts +++ b/performance-tests/wrap-performance-test.ts @@ -16,7 +16,7 @@ const validateFilePath = async (filePath: string, baseDir: string): Promise => const fileExtension = parse(filePath).ext; try { - // Validate and resolve the source file path - const canonicalPath = await validateFilePath(filePath, __dirname); - existsSync(INPUT_UNWRAPPED_FILE_FOLDER) || mkdirSync(INPUT_UNWRAPPED_FILE_FOLDER, { recursive: true }); for (let index = 0; index < numberOfFiles; index++) { const outputPath = resolve(INPUT_UNWRAPPED_FILE_FOLDER, `${fileName + (index + 1)}${fileExtension}`); - // Sanitize the file path to prevent directory traversal - if (!outputPath.startsWith(INPUT_UNWRAPPED_FILE_FOLDER)) { - throw new Error("Unsafe file path detected."); - } - // Copy the file safely using the validated path - await promises.copyFile(canonicalPath, outputPath); + await promises.copyFile(filePath, outputPath); } } catch (e) { console.error(e);