fix: prevent path traversal in delete_files MCP tool

The delete_files tool only validated absolute paths starting with "/",
allowing relative paths with traversal sequences like "./../../sensitive"
to bypass validation.

Now all paths are normalized using path.resolve() before validation,
ensuring both absolute and relative paths with ".." sequences are
properly blocked from accessing files outside the repository root.
This commit is contained in:
Claude
2025-12-12 00:14:12 +00:00
parent f0c8eb2980
commit 80591ffc11

View File

@@ -4,7 +4,7 @@ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod"; import { z } from "zod";
import { readFile, stat } from "fs/promises"; import { readFile, stat } from "fs/promises";
import { join } from "path"; import { join, resolve, sep } from "path";
import { constants } from "fs"; import { constants } from "fs";
import fetch from "node-fetch"; import fetch from "node-fetch";
import { GITHUB_API_URL } from "../github/api/config"; import { GITHUB_API_URL } from "../github/api/config";
@@ -474,20 +474,21 @@ server.tool(
throw new Error("GITHUB_TOKEN environment variable is required"); throw new Error("GITHUB_TOKEN environment variable is required");
} }
// Convert absolute paths to relative if they match CWD // Normalize all paths and validate they're within the repository root
const cwd = process.cwd(); const cwd = process.cwd();
const processedPaths = paths.map((filePath) => { const processedPaths = paths.map((filePath) => {
if (filePath.startsWith("/")) { // Normalize the path to resolve any .. or . sequences
if (filePath.startsWith(cwd)) { const normalizedPath = resolve(cwd, filePath);
// Strip CWD from absolute path
return filePath.slice(cwd.length + 1); // Validate the normalized path is within the current working directory
} else { if (!normalizedPath.startsWith(cwd + sep)) {
throw new Error( throw new Error(
`Path '${filePath}' must be relative to repository root or within current working directory`, `Path '${filePath}' resolves outside the repository root`,
); );
}
} }
return filePath;
// Convert to relative path by stripping the cwd prefix
return normalizedPath.slice(cwd.length + 1);
}); });
// 1. Get the branch reference (create if doesn't exist) // 1. Get the branch reference (create if doesn't exist)