diff --git a/packages/core/src/agent/presets/plan.ts b/packages/core/src/agent/presets/plan.ts index 23fddaa..78b8780 100644 --- a/packages/core/src/agent/presets/plan.ts +++ b/packages/core/src/agent/presets/plan.ts @@ -37,6 +37,8 @@ export const planAgent: Omit = { 'checkpoint_create', 'checkpoint_restore', 'undo', + // 工具发现(Plan 模式不应动态发现新工具) + 'tool_search', ], }, permission: { @@ -49,6 +51,52 @@ export const planAgent: Omit = { bash: { enabled: true, rules: [ + // ============================================================ + // 重要:deny 规则必须放在 allow 规则之前,确保优先匹配! + // ============================================================ + + // ============ 危险命令 - 拒绝 ============ + { pattern: 'rm *', action: 'deny' }, + { pattern: 'rmdir *', action: 'deny' }, + { pattern: 'mv *', action: 'deny' }, + { pattern: 'cp *', action: 'deny' }, + { pattern: 'mkdir *', action: 'deny' }, + { pattern: 'touch *', action: 'deny' }, + { pattern: 'chmod *', action: 'deny' }, + { pattern: 'chown *', action: 'deny' }, + { pattern: 'sudo *', action: 'deny' }, + { pattern: 'su *', action: 'deny' }, + { pattern: 'ln *', action: 'deny' }, + { pattern: 'install *', action: 'deny' }, + { pattern: 'truncate *', action: 'deny' }, + { pattern: 'dd *', action: 'deny' }, + { pattern: 'tee *', action: 'deny' }, + + // ============ 重定向操作 - 拒绝(必须在 cat/echo 等允许规则之前)============ + { pattern: '* > *', action: 'deny' }, + { pattern: '* >> *', action: 'deny' }, + { pattern: '* << *', action: 'deny' }, // heredoc 重定向 + + // ============ Git 写操作 - 拒绝 ============ + { pattern: 'git add *', action: 'deny' }, + { pattern: 'git commit *', action: 'deny' }, + { pattern: 'git push *', action: 'deny' }, + { pattern: 'git pull *', action: 'deny' }, + { pattern: 'git checkout *', action: 'deny' }, + { pattern: 'git reset *', action: 'deny' }, + { pattern: 'git rebase *', action: 'deny' }, + { pattern: 'git merge *', action: 'deny' }, + { pattern: 'git stash *', action: 'deny' }, + { pattern: 'git clean *', action: 'deny' }, + + // ============ find 危险操作 - 拒绝 ============ + { pattern: 'find * -delete*', action: 'deny' }, + { pattern: 'find * -exec*', action: 'deny' }, + + // ============================================================ + // 以下为只读操作的 allow 规则 + // ============================================================ + // ============ 文件查看 - 允许 ============ { pattern: 'ls', action: 'allow' }, { pattern: 'ls *', action: 'allow' }, @@ -100,38 +148,6 @@ export const planAgent: Omit = { { pattern: 'git blame *', action: 'allow' }, { pattern: 'git ls-files*', action: 'allow' }, { pattern: 'git rev-parse *', action: 'allow' }, - - // ============ 危险命令 - 拒绝 ============ - { pattern: 'rm *', action: 'deny' }, - { pattern: 'rmdir *', action: 'deny' }, - { pattern: 'mv *', action: 'deny' }, - { pattern: 'cp *', action: 'deny' }, - { pattern: 'mkdir *', action: 'deny' }, - { pattern: 'touch *', action: 'deny' }, - { pattern: 'chmod *', action: 'deny' }, - { pattern: 'chown *', action: 'deny' }, - { pattern: 'sudo *', action: 'deny' }, - { pattern: 'su *', action: 'deny' }, - - // ============ Git 写操作 - 拒绝 ============ - { pattern: 'git add *', action: 'deny' }, - { pattern: 'git commit *', action: 'deny' }, - { pattern: 'git push *', action: 'deny' }, - { pattern: 'git pull *', action: 'deny' }, - { pattern: 'git checkout *', action: 'deny' }, - { pattern: 'git reset *', action: 'deny' }, - { pattern: 'git rebase *', action: 'deny' }, - { pattern: 'git merge *', action: 'deny' }, - { pattern: 'git stash *', action: 'deny' }, - { pattern: 'git clean *', action: 'deny' }, - - // ============ find 危险操作 - 拒绝 ============ - { pattern: 'find * -delete*', action: 'deny' }, - { pattern: 'find * -exec*', action: 'deny' }, - - // ============ 重定向操作 - 拒绝 ============ - { pattern: '* > *', action: 'deny' }, - { pattern: '* >> *', action: 'deny' }, ], default: 'ask', // 其他命令询问用户 }, diff --git a/packages/core/src/permission/wildcard.ts b/packages/core/src/permission/wildcard.ts index e0eb5ee..f87c533 100644 --- a/packages/core/src/permission/wildcard.ts +++ b/packages/core/src/permission/wildcard.ts @@ -95,6 +95,35 @@ export function matchRules( return matchSingleCommand(parsed, rules, defaultAction); } +/** + * 检查原始命令字符串是否包含危险的重定向操作 + * 这是一个快速预检,在 tree-sitter 解析之前进行 + */ +function checkRedirectInRawCommand( + command: string, + rules: PermissionRule[] +): { action: PermissionAction; matchedPattern?: string } | null { + // 检查重定向相关的 deny 规则 + const redirectPatterns = ['* > *', '* >> *', '* << *']; + + for (const rule of rules) { + if (rule.action === 'deny' && redirectPatterns.includes(rule.pattern)) { + // 根据模式检查原始命令 + if (rule.pattern === '* > *' && / > /.test(command) && !/ >> /.test(command)) { + return { action: 'deny', matchedPattern: rule.pattern }; + } + if (rule.pattern === '* >> *' && / >> /.test(command)) { + return { action: 'deny', matchedPattern: rule.pattern }; + } + if (rule.pattern === '* << *' && / << /.test(command)) { + return { action: 'deny', matchedPattern: rule.pattern }; + } + } + } + + return null; +} + /** * 使用 tree-sitter 解析并检查所有命令的权限(异步版本) * 返回最严格的权限要求 @@ -109,6 +138,18 @@ export async function matchRulesAsync( allCommands: ParsedCommand[]; askPatterns: string[]; }> { + // 首先检查原始命令中的重定向操作(这是安全关键检查,必须在解析之前进行) + const redirectCheck = checkRedirectInRawCommand(command, rules); + if (redirectCheck) { + // 如果检测到重定向被拒绝,直接返回 + return { + action: redirectCheck.action, + matchedPattern: redirectCheck.matchedPattern, + allCommands: [parseCommandSimple(command)], + askPatterns: [], + }; + } + const result = await parseBashCommand(command); // 如果解析失败,降级到简单解析