Skip to content

bug: Bash 在 && 短路时会把前置失败误判为“无匹配” #1344

Description

@ai-yang

问题概述

Bash 工具会把 && 右侧未执行命令的特殊退出码语义,错误地套用到整条命令上。当前置命令以 1 失败、右侧恰好是 rg / grep / diff / test / find 时,真实失败可能被解释成“无匹配”等非错误结果。

这不是安全问题,而是日常使用中的命令执行结果误判。

严重度

较严重(High)

  • 会把真实的测试、构建或业务命令失败静默转成成功 tool result。
  • Claude 可能基于一个根本没有执行的后续命令继续推理或修改代码。
  • 只要前置程序以退出码 1 失败即可稳定触发,常见测试/lint/CLI 均可能使用该退出码。

环境

  • 代码版本:3bb6b5746238c418138eb96d57765d79012edd96
  • Bun:1.3.13
  • 系统:Linux 5.15.0-186-generic x86_64
  • 源码方式运行

稳定复现

在仓库根目录创建 repro.ts

import { spawnSync } from 'node:child_process'
import { interpretCommandResult } from './packages/builtin-tools/src/tools/BashTool/commandSemantics.ts'

const commands = [
  "node -e 'process.exit(1)' && rg needle /dev/null",
  "node -e 'process.exit(1)' && grep needle /dev/null",
  "node -e 'process.exit(1)' && diff /dev/null /dev/null",
  "node -e 'process.exit(1)' && test -e /definitely-not-present",
  "node -e 'process.exit(1)' && find /dev/null -name needle",
]

for (const command of commands) {
  const run = spawnSync('bash', ['-lc', command], { encoding: 'utf8' })
  console.log({
    command,
    exitCode: run.status,
    interpretation: interpretCommandResult(
      command,
      run.status ?? 1,
      run.stdout,
      run.stderr,
    ),
  })
}

运行 bun repro.ts,稳定输出:

node ... && rg ...   -> exitCode=1, isError=false, "No matches found"
node ... && grep ... -> exitCode=1, isError=false, "No matches found"
node ... && diff ... -> exitCode=1, isError=false, "Files differ"
node ... && test ... -> exitCode=1, isError=false, "Condition is false"
node ... && find ... -> exitCode=1, isError=false, "Some directories were inaccessible"

这些命令中的 node 已经以 1 退出,因此 && 右侧命令根本没有执行。控制组 true && rg needle /dev/nullprintf x | rg needle 的退出码 1 确实来自 rg,此时“无匹配”语义合理。

实际行为

interpretCommandResult() 返回 isError: false。随后 BashTool 不抛出 ShellError,可能给模型一个空的成功结果。

预期行为

当无法证明 && 最后一个语法段实际执行时,应保守使用默认语义;非零退出码应保持为错误。

根因

packages/builtin-tools/src/tools/BashTool/commandSemantics.tsheuristicallyExtractBaseCommand() 使用 splitCommand_DEPRECATED() 丢弃控制运算符后,无条件选择最后一个语法段。它只知道整条命令的聚合退出码,不知道最后实际执行的是哪个段。

packages/builtin-tools/src/tools/BashTool/BashTool.tsx 随后依据该解释结果决定是否抛出 ShellError,因此误判会进入模型可见结果。

建议修复

  • 用保留控制运算符的解析结果识别顶层 &&
  • 在执行器尚未返回“最后实际执行命令/各段状态”前,对存在 && 的命令回退到默认退出码语义。
  • 保留简单 rg、管道以及纯 || 的现有语义。
  • 增加 rgdiff 等短路回归测试,以及 false || rg 控制组。

查重

已检索开放/关闭 issue 和全部 PR,关键词包括 commandSemanticsshort circuitrg exit codeNo matches found,未发现同因问题。

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions