From e8c21784cdf42964ad3598cf7f4684eda9c62177 Mon Sep 17 00:00:00 2001 From: qinlongli2024-ai Date: Thu, 5 Mar 2026 01:39:32 +0800 Subject: [PATCH] fix: validate paths in delete_files with validatePathWithinRepo The delete_files MCP tool used a simple startsWith(cwd) check for absolute paths and accepted relative paths without any validation. This is inconsistent with commit_files, which correctly calls validatePathWithinRepo() to prevent path-traversal via '..' segments and symlink escapes. Replace the ad-hoc check with the same validatePathWithinRepo() call used by commit_files, ensuring both tools enforce identical path safety guarantees. - Absolute paths that resolve outside the repo are now rejected - Relative paths with '..' traversal are caught by realpath resolution - Symlinks escaping the repository root are blocked - Relative path normalisation is consistent with commit_files --- src/mcp/github-file-ops-server.ts | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/src/mcp/github-file-ops-server.ts b/src/mcp/github-file-ops-server.ts index 4d61621b6..32baaa6f5 100644 --- a/src/mcp/github-file-ops-server.ts +++ b/src/mcp/github-file-ops-server.ts @@ -479,21 +479,18 @@ server.tool( throw new Error("GITHUB_TOKEN environment variable is required"); } - // Convert absolute paths to relative if they match CWD - const cwd = process.cwd(); - const processedPaths = paths.map((filePath) => { - if (filePath.startsWith("/")) { - if (filePath.startsWith(cwd)) { - // Strip CWD from absolute path - return filePath.slice(cwd.length + 1); - } else { - throw new Error( - `Path '${filePath}' must be relative to repository root or within current working directory`, - ); - } - } - return filePath; - }); + // Validate all paths are within the repository root — mirrors + // the validation used in commit_files to prevent path-traversal + // attacks via ".." sequences or symlinks escaping the repo. + const resolvedRepoDir = resolve(REPO_DIR); + const processedPaths = await Promise.all( + paths.map(async (filePath) => { + await validatePathWithinRepo(filePath, REPO_DIR); + // Normalise to a relative path for the Git tree entry + const normalizedPath = resolve(resolvedRepoDir, filePath); + return normalizedPath.slice(resolvedRepoDir.length + 1); + }), + ); // 1. Get the branch reference (create if doesn't exist) const baseSha = await getOrCreateBranchRef(