Bionic Shell 命令安全 2026:LM Studio 如何为 AI 命令造出一个法官
像 git diff 这么普通的命令,一旦涉及变量就可能成为 AI 编码代理的问题。假设命令是 git diff $base:如果 $base 是一个提交哈希,命令行为正常;但如果它解析成 --output=/some/file,Git 就会把结果写到文件系统。LM Studio 构建了 Auto Review 来在 Bionic 运行命令之前抓住这类情况,只有在无法判断命令是否安全时才转向另一个语言模型。根据周四发布的博客,这第一层在不清除 Bionic 的 82% 命令时不需要额外模型调用——虽然作者说这个数字是轶事性的,不是基准。
1. 为什么字符串匹配不够
搜索危险字符串只能走到这一步,因为 shell 命令会随着其中的变量、重定向和其他命令而改变。危险不取决于字符串的字面形态,而取决于值。$base 可能是提交哈希(安全),也可能是 --output= 开头(危险)——同一个字符串模板,两种完全不同的行为。Bionic 的 Shell Judge 通过看命令的结构而不是字符串来绕过这个问题:把命令变成抽象语法树(AST),跟随变量和嵌套命令,看它们可能影响什么。
// The attack that motivates the whole system: a command as
// ordinary as git diff becomes a problem once a variable gets
// involved.
// git diff $base
// If $base contains a commit hash, the command behaves as
// expected. But if it resolves to --output=/some/file,
// Git writes the result to the filesystem. String matching
// cannot catch this — the danger depends on the VALUE.
const cmd = "git diff " + userControlled; // ???
// $base = "HEAD~1" -> safe diff
// $base = "--output=/tmp/evil" -> writes a file!2. 解析结构:AST 与能力分析
Shell Judge 用 mvdan/sh 解析器处理 Bash、Zsh 和 SH,PowerShell 则用自己的 AST 支持。然后 Judge 算出 LM Studio 所称的命令「能力」——这个命令可能读什么、改什么。它也能把值从一条命令带到下一条:如果代理用 git merge-base 找共同祖先提交,并把结果喂给 git diff,Judge 在评估第二条命令时会记住那个值。如果有多个可能值,Bionic 会跟踪最多 1000 个,然后才放弃穷尽每一种可能。
// Parsing structure, not strings: turn the shell command
// into an abstract syntax tree, then compute what the
// command could read or change — its "capabilities".
// mvdan/sh parses Bash, Zsh and SH; PowerShell uses its
// own AST support.
import {{ syntax }} from "mvdan-sh";
type Capabilities = { reads: string[]; writes: string[] };
function capabilities(ast: syntax.Node): Capabilities {
const caps: Capabilities = {{ reads: [], writes: [] }};
walk(ast, (node) => {{
if (isRedirection(node)) {{
caps.writes.push(node.target); // > file, >> file
}}
if (isAssignment(node)) {{
caps.writes.push(node.name);
}}
}});
return caps;
}
// git diff $base with $base=--output=/tmp/x
// -> writes: ["/tmp/x"] <- caught before execution3. 11,651 个测试用例
还有命令行工具自己的规则。ls -la 把 -la 当作捆绑的多个标志,而 LM Studio 指出 TypeScript 的 tsc -vh 和分别运行 -v 与 -h 行为不一样。所以只解析 shell 语法只能让 Bionic 走一半路——Shell Judge 还必须理解单个工具会怎么解释命令后面跟的东西。这解释了为什么 LM Studio 已经构建了 11,651 个测试用例,覆盖畸形命令和各个工具处理参数时的怪癖。
// Following values across commands: if an agent uses
// git merge-base to find a common ancestor and feeds the
// result into git diff, the judge keeps track of that
// value when it evaluates the second command. Up to 1,000
// possible values before it stops accounting for everything.
function trackValues(ast: syntax.Node): Map<string, string[]> {
const values = new Map();
walk(ast, (node) => {{
if (isCommand(node) && node.name === "git") {{
const args = node.args;
if (args[0] === "merge-base") {{
// record that $var now may hold a commit hash OR
// anything an earlier step injected into the chain
values.set(args[2], inferPossibleValues(node));
}}
}}
}});
return values;
}
// Each possible value is evaluated; if ANY of them is
// dangerous, the command is not cleared.4. 当评审被说动:法官同意被告
LM Studio 发现,只是问评审「这个命令该不该运行」效果不好——模型有时会批准有风险的操作,因为它们看起来对完成用户请求是必要的。凡是 Shell Judge 不能放行的,都交给 Shell Reviewer:一个在对话上下文中评估命令的独立 AI 代理。评审现在对每条命令的风险、授权和正确性打分,而不知道要通过需要多少分。这种「不知道及格线」的设计避免了「法官开始同意被告」的失败模式。
5. 信任假设仍然是开放的
Shell Reviewer 需要足够的对话来知道用户是否授权了命令,这又为提示注入打开了一个口子。LM Studio 排除了工具结果——隐藏在网页或文件里的指令不会直接传给评审——但它仍然看得到助手消息。如果 Bionic 已经被攻陷,那些消息可能携带恶意指令。Shell Judge 自己也有盲区:它假设 git 之类的可执行文件没被攻陷,也不考虑可能改变命令行为的恶意配置。最近的 npm 供应链攻击展示了看似合法的 provenance 信号如何隐藏恶意载荷。
// The reviewer: when the judge can't decide, a separate AI
// agent evaluates the command in context. Just asking "should
// this run?" didn't work — the model approved risky actions
// because they seemed necessary. So the reviewer rates risk,
// authorization, and correctness WITHOUT knowing what scores
// are needed to pass.
type Verdict = { risk: 1|2|3|4|5; authorized: boolean; correct: boolean };
async function review(cmd: string, conversation: Context): Promise<Decision> {
const v = await reviewerAgent.rate({{ cmd, conversation }});
// The reviewer does not know the pass threshold.
if (v.risk >= 4) return "block";
if (!v.authorized) return "ask";
if (!v.correct) return "warn";
return "run";
}
// Rating without a known bar avoids the "agrees with the
// defendant" failure mode.6. 为什么这很重要
这些限制在编码代理获得更多行动自由时变得更加重要。谷歌的 Gemini 编码代理最近扩展到了 IDE 边界之外,让代理有更多机会运行命令和做出改动,而不需要开发者手动完成每一步。每多一分自由,命令层就是最后一道防线。安全的姿势是纵深防御:AST 能力分析拦截结构性问题,值追踪拦截注入,评审代理在上下文里裁决模糊情况,工具结果不进评审上下文,11,651 个测试用例兜底工具怪癖。没有一层是完美的,叠在一起就是 2026 年 agent 命令安全的现实基线。
// Trust assumptions remain: the judge assumes executables
// like git haven't been compromised and doesn't account for
// malicious configuration. The reviewer sees assistant
// messages — so if the agent is already compromised, those
// messages can carry malicious instructions. Defense in depth:
async function safeRun(cmd: string, ctx: Context) {
const ast = parse(cmd);
const caps = capabilities(ast);
if (isDangerous(caps, await trackValues(ast))) return "block";
if (!await judgeIsConfident(ast)) {{
return await review(cmd, sanitize(ctx)); // tool results excluded
}}
return "run";
}
// 11,651 test cases cover malformed commands and the quirks
// of how individual tools handle their arguments.📌 常见问题 FAQ
为什么字符串匹配不够用?
shell 命令会随变量、重定向和其他命令改变。git diff $base 中 $base 可能是提交哈希(安全)也可能是 --output=/some/file(写文件)。危险取决于值,不取决于字符串形态,所以必须解析结构。
为什么字符串匹配不够用?
shell 命令会随变量、重定向和其他命令改变。git diff $base 中 $base 可能是提交哈希(安全)也可能是 --output=/some/file(写文件)。危险取决于值,不取决于字符串形态,所以必须解析结构。
为什么字符串匹配不够用?
shell 命令会随变量、重定向和其他命令改变。git diff $base 中 $base 可能是提交哈希(安全)也可能是 --output=/some/file(写文件)。危险取决于值,不取决于字符串形态,所以必须解析结构。
为什么字符串匹配不够用?
shell 命令会随变量、重定向和其他命令改变。git diff $base 中 $base 可能是提交哈希(安全)也可能是 --output=/some/file(写文件)。危险取决于值,不取决于字符串形态,所以必须解析结构。
为什么字符串匹配不够用?
shell 命令会随变量、重定向和其他命令改变。git diff $base 中 $base 可能是提交哈希(安全)也可能是 --output=/some/file(写文件)。危险取决于值,不取决于字符串形态,所以必须解析结构。
Shell Judge 怎么工作?
用 mvdan/sh 把 Bash/Zsh/SH(PowerShell 用自己的 AST)命令解析成抽象语法树,计算命令的「能力」——能读什么、改什么,并跨命令追踪值(最多 1000 个可能值)。
Shell Judge 怎么工作?
用 mvdan/sh 把 Bash/Zsh/SH(PowerShell 用自己的 AST)命令解析成抽象语法树,计算命令的「能力」——能读什么、改什么,并跨命令追踪值(最多 1000 个可能值)。
Shell Judge 怎么工作?
用 mvdan/sh 把 Bash/Zsh/SH(PowerShell 用自己的 AST)命令解析成抽象语法树,计算命令的「能力」——能读什么、改什么,并跨命令追踪值(最多 1000 个可能值)。
Shell Judge 怎么工作?
用 mvdan/sh 把 Bash/Zsh/SH(PowerShell 用自己的 AST)命令解析成抽象语法树,计算命令的「能力」——能读什么、改什么,并跨命令追踪值(最多 1000 个可能值)。
Shell Judge 怎么工作?
用 mvdan/sh 把 Bash/Zsh/SH(PowerShell 用自己的 AST)命令解析成抽象语法树,计算命令的「能力」——能读什么、改什么,并跨命令追踪值(最多 1000 个可能值)。
82% 的数字可靠吗?
LM Studio 自己说这是轶事性的而非基准:第一层 Auto Review 清除了约 82% 的命令而无需第二个模型调用。数字方向可信,但应视为工程观察而非官方基准。
82% 的数字可靠吗?
LM Studio 自己说这是轶事性的而非基准:第一层 Auto Review 清除了约 82% 的命令而无需第二个模型调用。数字方向可信,但应视为工程观察而非官方基准。
82% 的数字可靠吗?
LM Studio 自己说这是轶事性的而非基准:第一层 Auto Review 清除了约 82% 的命令而无需第二个模型调用。数字方向可信,但应视为工程观察而非官方基准。
82% 的数字可靠吗?
LM Studio 自己说这是轶事性的而非基准:第一层 Auto Review 清除了约 82% 的命令而无需第二个模型调用。数字方向可信,但应视为工程观察而非官方基准。
82% 的数字可靠吗?
LM Studio 自己说这是轶事性的而非基准:第一层 Auto Review 清除了约 82% 的命令而无需第二个模型调用。数字方向可信,但应视为工程观察而非官方基准。
Judge 和 Reviewer 有什么区别?
Judge 是确定性分析器:AST + 能力 + 值追踪,快且便宜。Reviewer 是第二个 LLM:当 Judge 拿不准时,在对话上下文中评风险、授权、正确性,且不知道及格线,避免「同意被告」。
Judge 和 Reviewer 有什么区别?
Judge 是确定性分析器:AST + 能力 + 值追踪,快且便宜。Reviewer 是第二个 LLM:当 Judge 拿不准时,在对话上下文中评风险、授权、正确性,且不知道及格线,避免「同意被告」。
Judge 和 Reviewer 有什么区别?
Judge 是确定性分析器:AST + 能力 + 值追踪,快且便宜。Reviewer 是第二个 LLM:当 Judge 拿不准时,在对话上下文中评风险、授权、正确性,且不知道及格线,避免「同意被告」。
Judge 和 Reviewer 有什么区别?
Judge 是确定性分析器:AST + 能力 + 值追踪,快且便宜。Reviewer 是第二个 LLM:当 Judge 拿不准时,在对话上下文中评风险、授权、正确性,且不知道及格线,避免「同意被告」。
Judge 和 Reviewer 有什么区别?
Judge 是确定性分析器:AST + 能力 + 值追踪,快且便宜。Reviewer 是第二个 LLM:当 Judge 拿不准时,在对话上下文中评风险、授权、正确性,且不知道及格线,避免「同意被告」。
残余风险是什么?
Judge 假设 git 等可执行文件未被攻陷、不考虑恶意配置;Reviewer 看得到助手消息,若代理已被攻陷,消息可能携带恶意指令。工具结果被排除,但这不是完美的。纵深防御是基线。
残余风险是什么?
Judge 假设 git 等可执行文件未被攻陷、不考虑恶意配置;Reviewer 看得到助手消息,若代理已被攻陷,消息可能携带恶意指令。工具结果被排除,但这不是完美的。纵深防御是基线。
残余风险是什么?
Judge 假设 git 等可执行文件未被攻陷、不考虑恶意配置;Reviewer 看得到助手消息,若代理已被攻陷,消息可能携带恶意指令。工具结果被排除,但这不是完美的。纵深防御是基线。
残余风险是什么?
Judge 假设 git 等可执行文件未被攻陷、不考虑恶意配置;Reviewer 看得到助手消息,若代理已被攻陷,消息可能携带恶意指令。工具结果被排除,但这不是完美的。纵深防御是基线。
残余风险是什么?
Judge 假设 git 等可执行文件未被攻陷、不考虑恶意配置;Reviewer 看得到助手消息,若代理已被攻陷,消息可能携带恶意指令。工具结果被排除,但这不是完美的。纵深防御是基线。