出于隐私原因,我检查了一下 Claude Code。
大多数开发者都会给自己的 harness 很夸张的权限:文件系统、shell、git、浏览器访问,现在甚至还有 computer use。这本来就是它们的价值所在。它们需要足够的上下文,才能做出有用的工作。
但这也意味着,客户端本身也该经得起审视。如果一个 coding agent 能读取你的 repo、运行命令,那么承载这些能力的客户端二进制文件就应该本分、可预期(比如 pi harness)。
于是我看了一下本地安装的 Claude Code(2.1.196)。
标记
在 Claude Code 二进制文件里,有一个函数会修改插入到 system prompt 里的当前日期字符串。
正常字符串长这样:
Today's date is 2026-06-30.
Claude Code 可以静默修改两件事:
Today's里的撇号- 日期分隔符,从
-改成/
下面是相关代码,我从压缩后的 bundle 里清理了一下:
function Zup() {
if (Crt()) return null;
let host = Qup();
let timezone = e0t();
let cnTZ = timezone === "Asia/Shanghai" || timezone === "Asia/Urumqi";
if (!host) {
return {
known: false,
labKw: false,
cnTZ,
host: null,
};
}
return {
known: Jup().some((domain) => host === domain || host.endsWith("." + domain)),
labKw: Xup().some((keyword) => host.includes(keyword)),
cnTZ,
host,
};
}
function edp(known, labKw) {
if (!known && !labKw) return "'";
if (known && !labKw) return "\u2019";
if (!known && labKw) return "\u02BC";
return "\u02B9";
}
function Vla(date) {
let marker = Zup();
let apostrophe = edp(marker?.known ?? false, marker?.labKw ?? false);
let renderedDate = marker?.cnTZ ? date.replaceAll("-", "/") : date;
return `Today${apostrophe}s date is ${renderedDate}.`;
}
这就是 prompt 隐写(prompt steganography),也就是把数据藏在明面上的一种技术。
可见的句子读起来仍然像一个普通日期。模型和用户看到的,只是一段平平无奇的文本。原始请求里却藏着一个标记。
检查
function Crt() {
let baseUrl = process.env.ANTHROPIC_BASE_URL;
if (!baseUrl) return true;
return Rrt(baseUrl);
}
function Rrt(baseUrl) {
try {
let host = new URL(baseUrl).host;
return ["api.anthropic.com"].includes(host);
} catch {
return false;
}
}
function Qup() {
let baseUrl = process.env.ANTHROPIC_BASE_URL;
if (!baseUrl) return null;
try {
return new URL(baseUrl).hostname.toLowerCase();
} catch {
return null;
}
}
触发条件是 ANTHROPIC_BASE_URL,也就是 Claude Code 的 API base URL 覆盖项。
然后它会检查:
- 系统时区是不是
Asia/Shanghai或Asia/Urumqi - API base URL 的 hostname 是否匹配解码后的域名列表
- hostname 是否包含特定 AI lab 关键词
时区检查会把:
2026-06-30
改成:
2026/06/30
hostname 检查会修改撇号:
| 条件 | 撇号 |
|---|---|
| 正常 | ' |
| 已知域名 | \u2019 |
| Lab 关键词 | \u02BC |
| 已知域名 + Lab 关键词 | \u02B9 |
这些变化在视觉上非常细微,在大多数等宽字体里你根本不会注意到。
混淆列表
域名和关键词列表以 base64 字符串保存,并用 key 91 做 XOR 解码。
var Kup = 91;
function Gla(encoded) {
let bytes = Buffer.from(encoded, "base64");
let out = "";
for (let byte of bytes) {
out += String.fromCharCode(byte ^ Kup);
}
return out.split(",");
}
解码后的 lab 关键词列表是:
deepseek,moonshot,minimax,xaminim,zhipu,bigmodel,baichuan,stepfun,01ai,dashscope,volces
解码后的域名列表要大得多。里面包含中国公司域名、AI 公司域名,以及大量代理、reseller、gateway 域名。
一些例子:
cn
baidu.com
alibaba-inc.com
alipay.com
antgroup-inc.cn
bytedance.net
kuaishou.com
xiaohongshu.com
jd.com
bilibili.co
iflytek.com
stepfun-inc.com
moonshot.ai
anyrouter.top
claude-code-hub.app
claude-opus.top
openclaude.me
proxyai.com
yunwu.ai
zenmux.ai
你可以在这里查看完整列表:https://cdn.thereallo.dev/blog/assets/cc-domains.js
它是如何传进去的
构建 agent context 时会用到这个日期函数:
{
...userEmail && {
userEmail: `The user's email address is ${userEmail}.`
},
...attachedProject && {
attachedProject
},
currentDate: Vla(GSe())
}
所以这个标记会被放进发给模型的 system context 里。(Anthropic 很可能会在后端解析它。)
我的环境显示什么
我安装的二进制文件由 Anthropic 签名:
Identifier=com.anthropic.claude-code
TeamIdentifier=Q6L2SF6YDW
Timestamp=Jun 29, 2026
SHA256=6fc6e61ab7582c2bf241225ff90d9f79e91d69380cb9589fc9dedd3a30070f5a
我当前 shell 里没有设置 ANTHROPIC_BASE_URL,我的时区是:
Asia/Hong_Kong
所以在我的机器和当前环境下,这条路径会生成正常撇号和正常的 YYYY-MM-DD 日期字符串。
令人担忧之处
Anthropic 可能是想检测 API 转售商(reseller)、未经授权的 Claude Code gateway,以及模型“蒸馏攻击”(distillation attack)流水线。一个指向已知转售商域名的自定义 ANTHROPIC_BASE_URL 是有用信号。包含 deepseek 或 zhipu 的 hostname 也是有用信号。
这部分说得通,但实现方式很奇怪。
CC 会用近乎不可见的 Unicode 标记静默修改 system prompt。它把 proxy / gateway 分类编码进一个看起来像普通英文的句子里。它把域名列表藏在 XOR 和 base64 后面。这不是恶意功能,但对于一个需要用户信任的开发者工具来说,这是一个奇怪的选择。
Coding agent 本来就已经越过了一条让人紧张的边界。它们可以检查代码,可能不小心把 secret 摘出来,可以运行命令、安装包、编辑文件,还能在你的本地机器上 push commit。大多数开发者接受这一点,是因为生产力收益值得承担风险。
开发者真正愿意信任它,靠的是它在这些地方本分、可预期。
如果客户端想检测自定义 API gateway,它可以明说。它可以发送一个明确的遥测(telemetry)字段,并配上文档。它可以把规则摆明。它可以把这个行为写进发布说明。
把信号藏在 system prompt 里,会让其他所有隐私声明都更难令人相信。
实际影响
对大多数用户来说,这条逻辑大概率不会被触发。
如果你使用官方 Anthropic API endpoint,Crt() 会提前返回。如果没有设置 ANTHROPIC_BASE_URL,Crt() 会提前返回。如果你使用的是普通配置,日期 prompt 会保持正常。
真正值得注意的是那些让 CC 走自定义 base URL 的场景,包括:
- 内部 gateway
- 本地 proxy
- 模型 router
- 转售商
- 研究设置
在这种情况下,Claude Code 会分类 hostname,并把结果编码进 prompt。
绕过也很简单。换 hostname、换时区、patch 二进制、在外面再套一层。任何真正有心绕过的人都能让这个信号失效。
所以,这个功能主要惩罚的,恰恰是那些最容易被指纹识别的普通开发者:他们做的事奇怪,但正当。
想法
我觉得这完全可以明说。
开发者工具可以执行使用条款。API 提供商可以检测滥用。公司可以保护自己的模型。
但当一个拥有文件系统和 shell 访问权限的工具,开始把分类信号藏进近乎不可见的 prompt 标点里时,它就应该接受认真审视。
信任来自那些本分、可预期的细节。