feat: 升级至 v0.3.3 — 新增 delete_file 工具 + 文件工具全面优化
## 主要变更 ### 1. 新增 delete_file 工具(26 → 27 个) - 支持:删除文件/空目录(默认)/递归删除非空目录(recursive: true) - 5 层安全防护: * isPathWithinWorkspace — 路径遍历防护 * isProtectedWorkspaceFile — MEMORY.md 拦截 * 工作空间根目录保护 — 禁止删除 workspace 本身 * 递归删除需显式开启 — 默认仅删空目录 * riskLevel: HIGH + requiresPermission — 破坏性操作必须确认 - 友好错误处理:ENOTEMPTY 时提示设置 recursive: true ### 2. 文件工具全面优化(6 个工具) - 抽取共享代码到 file-guard.ts: * safeResolvePath — 合并路径遍历 + MEMORY.md 校验 * matchGlob — 简易通配符匹配 * extractErrorMessage — 统一错误提取 * MAX_FILE_SIZE_BYTES (10MB) / FILE_TOOL_TIMEOUT_MS (15s) / MAX_LINE_LENGTH (10000) - read_file:stat 预检 + 超长行截断 + 二进制检测 + 大小上限 - write_file:原子写入(临时文件+rename)+ 内容大小上限 + append 返回 new_file_size - list_directory:1000 结果上限 + modified time + include_hidden 参数 + 提前终止优化 - search_files:regex lastIndex 修复 + context_lines + include_hidden + 大文件跳过 - file_editor:dry_run 预览模式 + 文件大小上限 ### 3. 审计修复(1 FAIL + 3 WARN) - FAIL: isBinaryFile 用 bytesRead 限制循环,修复 < 8KB 文本误判为二进制 - WARN-1: file-editor dry_run preview 分模式计算,修复 insert 范围过大 - WARN-2: write_file 允许空字符串创建空文件 - WARN-3: list_directory listDir 提前终止,避免大目录全量遍历
This commit is contained in:
@@ -106,3 +106,74 @@ export function commandTouchesProtectedFile(command: string): boolean {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// ===== 共享工具函数(v0.3.2 抽取,消除 filesystem.ts 与 file-editor.ts 的重复)=====
|
||||
|
||||
/**
|
||||
* 共享路径解析 + 安全校验
|
||||
*
|
||||
* 合并两层安全检查:
|
||||
* 1. isPathWithinWorkspace — 路径遍历防护(含符号链接 realpathSync 二次校验)
|
||||
* 2. isProtectedWorkspaceFile — MEMORY.md 拦截
|
||||
*
|
||||
* @param filePath 用户传入的文件路径(绝对或相对)
|
||||
* @param workspacePath 当前工作空间根路径
|
||||
* @returns 解析后的绝对路径
|
||||
* @throws Error 路径越界或访问受保护文件时抛出
|
||||
*/
|
||||
export function safeResolvePath(filePath: string, workspacePath: string): string {
|
||||
const resolved = resolve(workspacePath, filePath);
|
||||
// 安全检查:路径遍历防护(修复前缀碰撞漏洞)
|
||||
if (!isPathWithinWorkspace(filePath, workspacePath)) {
|
||||
throw new Error(`Path traversal detected: ${filePath}`);
|
||||
}
|
||||
// 受保护文件检查:MEMORY.md 仅由系统内部管理
|
||||
if (isProtectedWorkspaceFile(filePath, workspacePath)) {
|
||||
throw new Error('Access denied: MEMORY.md is managed by the memory system and cannot be accessed via file tools');
|
||||
}
|
||||
return resolved;
|
||||
}
|
||||
|
||||
/**
|
||||
* 共享 glob 匹配(简易通配符 → 正则)
|
||||
*
|
||||
* 支持 `*`(任意字符序列)和 `?`(单字符),大小写不敏感。
|
||||
* 其他正则元字符会被转义。
|
||||
*
|
||||
* @param name 待匹配的文件名
|
||||
* @param glob 通配符模式(如 "*.ts"、"test?.js")
|
||||
* @returns 是否匹配
|
||||
*/
|
||||
export function matchGlob(name: string, glob: string): boolean {
|
||||
const pattern = glob.replace(/[.+^${}()|[\]\\]/g, '\\$&').replace(/\*/g, '.*').replace(/\?/g, '.');
|
||||
return new RegExp(`^${pattern}$`, 'i').test(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* 共享错误提取
|
||||
*
|
||||
* 统一从 unknown 错误对象中提取 message 字符串
|
||||
*
|
||||
* @param error catch 块中的 unknown 错误
|
||||
* @returns 错误消息字符串
|
||||
*/
|
||||
export function extractErrorMessage(error: unknown): string {
|
||||
return error instanceof Error ? error.message : String(error);
|
||||
}
|
||||
|
||||
/**
|
||||
* 共享文件大小限制常量
|
||||
*
|
||||
* read_file/write_file/file_editor 共用,防止 OOM
|
||||
*/
|
||||
export const MAX_FILE_SIZE_BYTES = 10 * 1024 * 1024; // 10MB
|
||||
|
||||
/**
|
||||
* 共享超时常量(v0.3.2 统一文件工具 timeoutMs)
|
||||
*/
|
||||
export const FILE_TOOL_TIMEOUT_MS = 15_000;
|
||||
|
||||
/**
|
||||
* 共享单行最大长度(防止超长行爆 token)
|
||||
*/
|
||||
export const MAX_LINE_LENGTH = 10_000;
|
||||
|
||||
Reference in New Issue
Block a user