Agent 用终端出问题:Cursor terminal 工具的权限与确认机制
同一条 npm test,昨天 Agent 一声不吭就跑了,今天却停下来等你点批准;同一个脚本你手动跑没事,交给 Agent 跑就报出一堆 CI 环境才有的行为。这类问题的根子几乎都不在命令本身,而在命令被送到 shell 之前要经过的那几道关。Cursor 官方文档把这些关口写得比较散,落在 cursor.com/docs/agent/tools/terminal、cursor.com/docs/agent/security/run-modes 和 cursor.com/help/ai-features/terminal 三页上,口径还不完全对齐(help 页用「Cursor Settings > Agents > Approvals & Execution」,docs 页写「Settings > Agents > Approvals & Execution」)。先把链路摆出来,再谈排查。
一条命令要过几道关
官方文档《Terminal》页开门见山写明:Cursor 在你的终端里直接运行 shell 命令,由 Run Mode 决定何时直接跑、何时询问、以及何时让终端命令进入 sandbox。
《Run Modes》页列了三种模式,这三种是文档表格里逐行列出的:
| 模式 | 不询问就运行的部分 | sandbox | 分类器 |
|---|---|---|---|
| Auto-review | allowlist 内的调用立即运行,其余 shell 命令尽量在 sandbox 里跑,不走 sandbox 的交给分类器 | 对 shell 命令启用 | 有 |
| Allowlist | 只有 allowlist 里的动作免批准 | 可选,对 shell 命令 | 无 |
| Run Everything | 所有工具调用自动运行 | 无 | 无 |
Auto-review 下的检查顺序是文档写明的:先看 allowlist,再看能不能进 sandbox,剩下的才交给分类器;分类器可以放行、可以让 Agent 换一种做法,也可以转成一次批准提示。文档还专门解释了「能进 sandbox」的判断依据——命令要能在 sandbox 的文件与网络限制下工作,需要完整系统访问的(比如往工作区外写、特权操作)没法沙箱化,于是转给分类器。
有两点容易被读反。其一,sandbox 是叠在 Run Modes 之上的一层,它管的是「受支持的终端命令在哪儿跑」,不管「这个模式用不用分类器」。其二,文档里有一节标题就叫「Auto-review is not a security boundary」,正文承认分类器会出错,既可能放过你本想拦的,也可能拦下你本想放的。所以不要把它当作访问控制来设计流程。
链路末端还有三条独立的保护,文档单列成表:Browser Protection、File-Deletion Protection(含 rm 命令)、External-File Protection(工作区外的创建、修改、删除)。这三条的作用是——即便当前模式本来会自动运行,它们仍可能要求你批准。
现象一:模式选了「少弹窗」,还是不停要批准
怎么确认是这个问题。 先按文档写明的位置去核对当前档位:官方文档写明在桌面端进入 Settings > Agents > Approvals & Execution。如果 Auto-review 这一档呈灰色不可选,那不是你设置错了:文档写明 Auto-review 的分类器跑在 Cursor 托管的小模型上,企业侧的 model access control 会生效,团队把这些模型全部屏蔽时,即使团队的 Run Modes 里包含 Auto-review,成员这里也会被禁用,只能退回 Allowlist。
第二个判定动作是看命令本身落在哪个分支。在 Linux/macOS 上,让 Agent 跑一条能回显环境变量的命令即可分辨它是否在 sandbox 内——文档列明 Cursor 会向每个沙箱化子进程注入 CURSOR_SANDBOX(macOS 为 "seatbelt",Linux 为 "native"),Linux 上另有 CURSOR_SANDBOX_LANDLOCK_STATUS,取值 fully_enforced(Landlock)或 bubblewrap(Bubblewrap 回退),文档明说这个变量用于诊断。变量为空,说明这条命令压根没走沙箱分支,那它要么在 allowlist 里,要么已经被送去分类器了。
文档语义给出的处置。 如果被拦的是一类固定命令,Auto-review 读 permissions.json,位置有两处:~/.cursor/permissions.json 覆盖本机所有项目目录,<你的项目目录>/.cursor/permissions.json 只覆盖单个项目、可以提交进仓库。两个文件都存在时会合并。文档给出的 schema 是自然语言句子:
{
"autoRun": {
"allow_instructions": [],
"block_instructions": [
"Every AWS CLI command should go through approval first.",
"Every command that modifies Kubernetes resources should go through approval first."
]
}
}
allow_instructions 描述倾向放行的动作,block_instructions 描述倾向拦下、让 Agent 改走别的路或转你批准的动作。注意优先级:文档写明团队在 dashboard 里定义了全局 Auto-review 配置时,团队配置优先,用户级和项目级文件会被忽略——这也是「我明明改了本地文件却没生效」的一个常见来源。
处置后怎么验证。 改完再跑同类命令,看是否还停在批准提示上;配合上面的 CURSOR_SANDBOX 回显,能区分它是被放行进了沙箱,还是仍被送去分类器。
什么情况说明不是这个原因。 如果弹的是删除文件、工作区外写入或浏览器工具,那是上面那三条独立保护在起作用,调模式和 permissions.json 都不会让它闭嘴。还有一种情况完全不适用:文档写明 Run Modes 只作用于本地 Agent,Cloud Agent 跑在自己的专属机器上,从不向你请求批准——在 Cloud Agent 里找批准设置是找不到的。
现象二:命令跑通了,行为却和你手动跑不一样
怎么确认。 三个高频原因,各有各的判定动作。
一是环境变量。官方 help 文档写明,Cursor 通过 Agent 运行终端命令时会设置 CI=1,用意是让终端工具输出更干净。判定方法很直接:让 Agent 回显一次这个变量,跟你自己终端里的值对一下。文档给出的处置就是显式取消:
unset CI && your-command-here
也可以按文档的写法把它固化进规则文件:
When running terminal commands, prefix with `unset CI &&` if the command's behavior changes in CI environments.
二是网络。sandbox 默认表里写明,网络默认是被阻断的,之后才由你的网络模式和 sandbox.json 打开。网络模式有三档:sandbox.json Only(只放行你 allowlist 里的域名,不追加 Cursor 默认值)、sandbox.json + Defaults(你的 allowlist 加上 Cursor 内置的常见包管理器与语言工具默认值,这是默认档)、Allow All。命令卡在拉依赖上时,先看它是不是在沙箱里跑,再看域名在不在放行范围内。
三是身份,这条只影响 Linux。文档写明沙箱会创建 user namespace 并把进程重映射到该命名空间内的 UID 0,因此沙箱内 id -u 和 $UID 返回 0 而不是你的真实用户 ID。要拿宿主机身份得读 CURSOR_ORIG_UID 与 CURSOR_ORIG_GID。文档给的 Docker 写法是:
docker run --rm \
--user "${CURSOR_ORIG_UID:-$(id -u)}:${CURSOR_ORIG_GID:-$(id -g)}" \
-v "$PWD:/work" -w /work \
my-image build
那个 :-$(id -u) 回退是文档特意解释过的,为的是命令在沙箱外(变量未设置)也能用。
验证与排除。 改完之后重跑,对比与手动执行的差异是否消失。如果你的项目根本不读 CI、命令也不联网、你也不在 Linux 上,那这一节都对不上,往下看输出层。
现象三:终端输出被截断或格式错乱
怎么确认。 《Terminal》页明确点名:某些 shell 主题(文档举的例子是 Powerlevel9k/Powerlevel10k)会干扰内联终端输出,命令输出看起来被截断或格式错乱时,就往这个方向查。判定动作是把 shell 配置里的主题初始化临时关掉再跑同一条命令。
处置。 文档给的做法是用 CURSOR_AGENT 环境变量识别 Cursor 正在运行,从而跳过花哨主题的初始化。zsh 侧:
# ~/.zshrc - disable Powerlevel10k when Cursor runs
if [[ -n "$CURSOR_AGENT" ]]; then
# Skip theme initialization for better compatibility
else
[[ -r ~/.p10k.zsh ]] && source ~/.p10k.zsh
fi
bash 侧:
# ~/.bashrc - fall back to a simple prompt in Cursor sessions
if [[ -n "$CURSOR_AGENT" ]]; then
PS1='\u@\h \W \$ '
fi
什么情况说明不是这个原因。 你的 shell 本来就是朴素提示符,或者输出异常发生在 Cursor CLI 而非编辑器内联终端,那就不在这一条的范围里。
Windows 侧:文档写到哪儿、没写哪儿
《Run Modes》的「How sandboxing works on your platform」一节只写了两个平台:macOS 走 Seatbelt(通过 sandbox-exec),要求 Cursor v2.0 或更高,无需额外配置;Linux 用 Landlock 与 seccomp,要求内核 6.2 或更高并具备 Landlock v3 支持(CONFIG_SECURITY_LANDLOCK=y),且启用非特权 user namespace,不满足时 Cursor 会回退成每次运行命令前请求批准。Windows 原生环境的沙箱行为,我们在这一页没有找到对应说明——这不等于没有,只是文档没写,请以官方文档最新内容为准,别按 macOS/Linux 的描述去推。
Windows 用户能核到的是另外几处。Cursor CLI 的全局配置文件路径,文档表格写明 Windows 为 $env:USERPROFILE\.cursor\cli-config.json,macOS/Linux 为 ~/.cursor/cli-config.json,项目级一律是 <project>/.cursor/cli.json;文档同时写明项目级只能配 permissions,其余设置必须全局配。hooks 的企业级(MDM 分发)路径,Windows 是 C:\ProgramData\Cursor\hooks.json,macOS 是 /Library/Application Support/Cursor/hooks.json,Linux/WSL 是 /etc/cursor/hooks.json。另外 Linux 上还有一段只针对远程环境与独立 CLI 的 AppArmor 说明:桌面安装包自带所需 profile,远程环境和独立 CLI 不带,沙箱创建时报 user-namespace 权限错误就需要装对应发行版的 AppArmor 包。
把拦截点写成代码:CLI 权限与 beforeShellExecution
编辑器里的模式设置之外,还有两处能把规则固化下来。
Cursor CLI 用权限令牌,格式是 Shell(commandBase),commandBase 取命令行的第一个 token,支持 glob 和 command:args 语法,例如 Shell(git)、Shell(curl:*)、Shell(rm)。文档明确写了 deny 优先于 allow。一份组合示例:
{
"version": 1,
"editor": { "vimMode": false },
"permissions": { "allow": ["Shell(ls)", "Shell(git)"], "deny": ["Shell(rm)"] },
"approvalMode": "allowlist"
}
approvalMode 的取值文档列了三个:allowlist、auto-review、unrestricted。另有 sandbox.mode 与 sandbox.networkAccess 两个可选字段,但文档在这张表里只给了字段名和一句用途说明,没有列出可取值,别自己猜着填。以上为按官方文档中的参数语义组合的示例,未经实测,以官方文档与 --help 的实际输出为准。
再往下一层是 hooks。beforeShellExecution 在任何 shell 命令执行前被调用,输入是 command、cwd 和一个布尔 sandbox,输出返回 permission(allow / deny / ask)以及 user_message、agent_message。它的匹配器是对整条 shell 命令字符串做匹配,不是按工具类型过滤。对应的 afterShellExecution 输入里同样带 sandbox 布尔字段,文档说明它表示这条命令是否在沙箱环境中运行——做审计时可以直接读这个字段,不必再让命令自己回显环境变量。
有一个默认值必须知道:文档写明 hook 失败(崩溃、超时、返回非法 JSON)时默认放行,即 fail-open;要改成失败即阻断,得在 hook 定义上设 failClosed: true,文档说这对安全敏感的 beforeMCPExecution 是推荐做法。这是文档写明的默认值,随版本可能变动。hooks 配置有四级来源,优先级从高到低是 Enterprise → Team → Project → User,所有匹配的 hook 都会运行,冲突时按优先级合并。
两个版本相关的坑
《Run Modes》页底部的 changelog 写明:3.6(2026 年 5 月 29 日)Auto-review 作为推荐默认档上线;3.5(2026 年 5 月 22 日)Ask Every Time 被废弃(deprecated),新用户无法再选择它,文档给的等价做法是用空 allowlist 的 Allowlist 模式,同时 Run in Sandbox 被并入「Allowlist + 启用 sandboxing」。所以你在旧教程或旧笔记里看到的三档名字,和现在设置里的三档对不上是正常的。help 页也重复了这一段,并写明 Cursor 3.6 及以上才是 Auto-review / Allowlist / Run Everything 这套命名。
最后提醒一句文档里写在《Agent Security》页的默认口径:终端命令默认需要你批准,Run Modes 是从简单 allowlist 到 Auto-review 分类器的一系列尽力而为的 guardrail,文档自己用的措辞是「best-effort guardrails rather than a hard security boundary」。把它当便利性开关来调,别当安全边界来依赖。
本文依据 Cursor 官方文档(cursor.com/docs 与 cursor.com/help)于 2026-08-18 的公开内容整理。
该产品闭源,本文只复述官方文档写明的机制,不推断其内部实现;
我们没有对文中涉及的功能做过实测,因此不涉及界面外观、操作手感与运行速度的任何描述。
该产品迭代频繁,文中涉及的设置项与命令随版本变动,请以官方文档最新内容为准。
本文不涉及订阅价格、额度与模型清单,相关信息请以官方定价与模型说明页为准。
安全与合规相关做法请结合自身环境评估,本文不构成安全方案建议。