mirror of
https://github.com/catlog22/Claude-Code-Workflow.git
synced 2026-02-10 02:24:35 +08:00
feat: 实现 MCP 工具集中式路径验证,增强安全性和可配置性
- 新增 path-validator.ts:参考 MCP filesystem 服务器设计的集中式路径验证器
- 支持 CCW_PROJECT_ROOT 和 CCW_ALLOWED_DIRS 环境变量配置
- 多层路径验证:绝对路径解析 → 沙箱检查 → 符号链接验证
- 向后兼容:未设置环境变量时回退到 process.cwd()
- 更新所有 MCP 工具使用集中式路径验证:
- write-file.ts: 使用 validatePath()
- edit-file.ts: 使用 validatePath({ mustExist: true })
- read-file.ts: 使用 validatePath() + getProjectRoot()
- smart-search.ts: 使用 getProjectRoot()
- core-memory.ts: 使用 getProjectRoot()
- MCP 服务器启动时输出项目根目录和允许目录信息
- MCP 管理界面增强:
- CCW Tools 安装卡片新增路径设置 UI
- 支持 CCW_PROJECT_ROOT 和 CCW_ALLOWED_DIRS 配置
- 添加"使用当前项目"快捷按钮
- 支持 Claude 和 Codex 两种模式
- 添加中英文国际化翻译
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -9,6 +9,7 @@ import { getCoreMemoryStore, findMemoryAcrossProjects } from '../core/core-memor
|
||||
import * as MemoryEmbedder from '../core/memory-embedder-bridge.js';
|
||||
import { StoragePaths } from '../config/storage-paths.js';
|
||||
import { join } from 'path';
|
||||
import { getProjectRoot } from '../utils/path-validator.js';
|
||||
|
||||
// Zod schemas
|
||||
const OperationEnum = z.enum(['list', 'import', 'export', 'summary', 'embed', 'search', 'embed_status']);
|
||||
@@ -108,7 +109,7 @@ type OperationResult = ListResult | ImportResult | ExportResult | SummaryResult
|
||||
* Get project path from current working directory
|
||||
*/
|
||||
function getProjectPath(): string {
|
||||
return process.cwd();
|
||||
return getProjectRoot();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -15,6 +15,7 @@ import { z } from 'zod';
|
||||
import type { ToolSchema, ToolResult } from '../types/tool.js';
|
||||
import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'fs';
|
||||
import { resolve, isAbsolute, dirname } from 'path';
|
||||
import { validatePath } from '../utils/path-validator.js';
|
||||
|
||||
// Define Zod schemas for validation
|
||||
const EditItemSchema = z.object({
|
||||
@@ -71,8 +72,8 @@ interface LineModeResult {
|
||||
* @param filePath - Path to file
|
||||
* @returns Resolved path and content
|
||||
*/
|
||||
function readFile(filePath: string): { resolvedPath: string; content: string } {
|
||||
const resolvedPath = isAbsolute(filePath) ? filePath : resolve(process.cwd(), filePath);
|
||||
async function readFile(filePath: string): Promise<{ resolvedPath: string; content: string }> {
|
||||
const resolvedPath = await validatePath(filePath, { mustExist: true });
|
||||
|
||||
if (!existsSync(resolvedPath)) {
|
||||
throw new Error(`File not found: ${resolvedPath}`);
|
||||
@@ -524,7 +525,7 @@ export async function handler(params: Record<string, unknown>): Promise<ToolResu
|
||||
const { path: filePath, mode = 'update', dryRun = false } = parsed.data;
|
||||
|
||||
try {
|
||||
const { resolvedPath, content } = readFile(filePath);
|
||||
const { resolvedPath, content } = await readFile(filePath);
|
||||
|
||||
let result: UpdateModeResult | LineModeResult;
|
||||
switch (mode) {
|
||||
|
||||
@@ -13,6 +13,7 @@ import { z } from 'zod';
|
||||
import type { ToolSchema, ToolResult } from '../types/tool.js';
|
||||
import { readFileSync, readdirSync, statSync, existsSync } from 'fs';
|
||||
import { resolve, isAbsolute, join, relative, extname } from 'path';
|
||||
import { validatePath, getProjectRoot } from '../utils/path-validator.js';
|
||||
|
||||
// Max content per file (truncate if larger)
|
||||
const MAX_CONTENT_LENGTH = 5000;
|
||||
@@ -233,7 +234,7 @@ export async function handler(params: Record<string, unknown>): Promise<ToolResu
|
||||
maxFiles,
|
||||
} = parsed.data;
|
||||
|
||||
const cwd = process.cwd();
|
||||
const cwd = getProjectRoot();
|
||||
|
||||
// Normalize paths to array
|
||||
const inputPaths = Array.isArray(paths) ? paths : [paths];
|
||||
@@ -242,7 +243,7 @@ export async function handler(params: Record<string, unknown>): Promise<ToolResu
|
||||
const allFiles: string[] = [];
|
||||
|
||||
for (const inputPath of inputPaths) {
|
||||
const resolvedPath = isAbsolute(inputPath) ? inputPath : resolve(cwd, inputPath);
|
||||
const resolvedPath = await validatePath(inputPath);
|
||||
|
||||
if (!existsSync(resolvedPath)) {
|
||||
continue; // Skip non-existent paths
|
||||
|
||||
@@ -22,6 +22,7 @@ import {
|
||||
executeCodexLens,
|
||||
} from './codex-lens.js';
|
||||
import type { ProgressInfo } from './codex-lens.js';
|
||||
import { getProjectRoot } from '../utils/path-validator.js';
|
||||
|
||||
// Define Zod schema for validation
|
||||
const ParamsSchema = z.object({
|
||||
@@ -659,7 +660,7 @@ async function executeRipgrepMode(params: Params): Promise<SearchResult> {
|
||||
|
||||
return new Promise((resolve) => {
|
||||
const child = spawn(command, args, {
|
||||
cwd: path || process.cwd(),
|
||||
cwd: path || getProjectRoot(),
|
||||
stdio: ['ignore', 'pipe', 'pipe'],
|
||||
});
|
||||
|
||||
@@ -1518,7 +1519,7 @@ async function executeFindFilesAction(params: Params): Promise<SearchResult> {
|
||||
}
|
||||
|
||||
const child = spawn('rg', args, {
|
||||
cwd: path || process.cwd(),
|
||||
cwd: path || getProjectRoot(),
|
||||
stdio: ['ignore', 'pipe', 'pipe'],
|
||||
});
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ import { z } from 'zod';
|
||||
import type { ToolSchema, ToolResult } from '../types/tool.js';
|
||||
import { writeFileSync, readFileSync, existsSync, mkdirSync, renameSync, statSync } from 'fs';
|
||||
import { resolve, isAbsolute, dirname, basename } from 'path';
|
||||
import { validatePath } from '../utils/path-validator.js';
|
||||
|
||||
// Define Zod schema for validation
|
||||
const ParamsSchema = z.object({
|
||||
@@ -153,8 +154,8 @@ export async function handler(params: Record<string, unknown>): Promise<ToolResu
|
||||
encoding,
|
||||
} = parsed.data;
|
||||
|
||||
// Resolve path
|
||||
const resolvedPath = isAbsolute(filePath) ? filePath : resolve(process.cwd(), filePath);
|
||||
// Validate and resolve path
|
||||
const resolvedPath = await validatePath(filePath);
|
||||
const fileExists = existsSync(resolvedPath);
|
||||
|
||||
// Create parent directories if needed
|
||||
|
||||
Reference in New Issue
Block a user