- BiLo82026-09-1825637
总结:这简直就是z/ 正版用户有福了
省流版:
上传条件:需要oatuh登录z.ai和bigmodel的用户才会上传,用apikey的不会被上传。
上传内容:
你每一次回车发送的promot 附件(图片/视频/音频等),你的模型供应商url,模型
你的.zcode全局配置,Agent.md,MCP,Hooks,MemorySkills/Commands/Subagents/Plugins
你的项目文件:
他会过滤 .env/.npmrc/id_rsa/.pem/.key/.p12/.pfx,文件包括token/secret内容的文件,但是你的文件比如说叫env.local也一样传上去,虽然有过滤条件,但是只要提交过进了.git/object也一样会有记录一样会被传走。
总结:你的整个项目的源码,我管你这的哪的,只要提交过,哪怕你原文件删了,我从.git/object一样能还原你的源码,拿来吧你。 ^-^
这是一个非常恐怖的上传事件,哪怕你之前的代码没用zcode是手搓的,只要你点打开文件夹开始对话回车按下去,不管有没有.git,是不是zcode写的,直接整个文件夹扫描排除上传,请注意是整个目录<1mb的文件 .git无视大小多大都能传,promot,附件图片 20MB/视频 200MB/音频 20MB/其他 100MB、总 1GB,不管你什么语言和项目和附件promot,整个加密文件包2GB上限, 这把你整个人加项目蒸馏了没区别。
如果你的项目曾经提交过敏感key,哪怕你原文件已经删除,请立即更换,排除规则并不完善,zcode还可以从.git/object拿到。```
test-workspace/
│
│ ── 根目录文件 ──
├── README.md ✅ 传了(明文)
├── app.js ✅ 传了(明文)
├── notes.txt ✅ 传了(明文)
├── cred.txt ✅ 传了(明文,改名密钥穿透规则)
├── .gitignore ★新 ✅ 传了(明文,内容就是 "src/local_notes.txt")
├── .env ❌ 明文没传 → 但 blob 在 .git/objects/55/e9a9c4 里传走
├── api_token.txt ❌ 明文没传 → blob d3/6304 传走
├── bigfile.bin ❌ 明文没传 → blob 69/a7cc 完整 1MB 传走
│
│ ── 二级目录(本轮重点)──
├── src/
│ ├── core/
│ │ └── engine.js ★新 ✅ 传了(files/src/core/engine.js 明文,路径完整保留)
│ ├── utils/
│ │ └── crypto.js ★新 ✅ 传了 ★★ 最狠的一个:
│ │ files/src/utils/crypto.js 明文进包,
│ │ 内容: const API_KEY = 'sk-live-abc123-SECOND-LEVEL-SECRET'
│ │ 同一内容在 .git/objects/c7/3eed 里再存一份
│ │ (文件名"无害"→敏感名规则失效;源码内嵌密钥→任何规则都管不着)
│ └── local_notes.txt ★新 ❌ 没传(.gitignore 忽略 + 未被 git 跟踪 →
│ git ls-files 不列出 → 两层都没进)← 唯一真正安全的一个
├── config/
│ └── database.json ★新 ✅ 传了(明文:「db_host": "192.168.1.100", "db_password": "p@ssw0rd-二级目录」)
├── lib/
│ └── settings.js ★新 ✅ 传了(明文)
├── dist/bundle.js ❌ 明文没传 → blob 4e/f088 传走
├── node_modules/dep.js ❌ 明文没传 → blob d8/d73a 传走
│
│ ── .git 整目录(免检通道)──
└── .git/ ✅ 39+ 个文件全部原样
├── config ✅(含假 GitHub token 的 remote URL)
├── logs/HEAD、index、refs/... ✅
└── objects/ ✅ 本轮新增 8 个对象:
├── c7/3eed → src/utils/crypto.js 完整内容(含假 API key)★★
├── 2e/c0cd → config/database.json(含假数据库密码)★
├── 87/19fc → src/core/engine.js
├── f2/0628 → lib/settings.js
├── bd/95c7 → .gitignore 自身
├── a1/890d、5e/14ef 等 → 新 commit + tree(目录树,标记文件在 src/、config/ 下的位置)
└── (上一轮的全部 blob 原样随包再传一遍)
``**完整版复现过程:**``ZCode 桌面版会把你的整个工作区打包上传——逆向 + 动态复现全记录
TL;DR
ZCode 桌面版(Electron)里有一条平时完全无感的数据上传链路:只要你用 z.ai / BigModel 账号 OAuth 登录,每发一条消息,客户端就会把你的工作区文件、这条 prompt 的原文、prompt 附件、以及你的全局配置(AGENTS.md、hooks、MCP、memory 等)加密打包,300 毫秒内上传到服务端。加密用的是服务端下发的 RSA 公钥——OSS 存的是密文,但 zcode.z.ai 服务端可以解密还原全部内容。
我把 app.asar 解包做了完整逆向,又在沙箱里拉起副本 + mock 服务端做了动态复现,从 UI 发 prompt 到 OSS 收到加密包、再到用私钥解密还原出我的文件内容,全程跑通。以下是完整证据链。
先说清楚边界:
- 用自定义 API Key 直连(不登录账号)时,这条链路完全不执行——我本机就是这个状态,日志和目录审计零上传痕迹。
- 裸目录下过滤规则真实生效(.env、id_rsa、*.pem、文件名含token/secret、>1MB、二进制、node_modules、dist都不传)。但过滤只看文件名——密钥改个名照样上传。
- git 仓库是重灾区:.git整目录走免检通道(无大小/敏感名/二进制过滤),等于把"所有提交过的内容、含已删除的历史版本"整体打包。工作区层被排除的.env等,内容全部从.git/objects绕出去。
- 这不一定是"恶意行为",更像官方的云同步/上下文索引功能(代码里叫 repo-snapshot,服务端配合做增量)。问题是:默认开启、无感知、无明示告知,而且传的东西比大多数人想象的多。最重要的一句话:只要提交过 git,就全部上传——包括你已经删除的。
一、链路是怎么跑的
```你发送 prompt(回车)
│
├─ ① 前置条件:本地工作区 + OAuth 登录态(取不到 token 直接 return)
│
├─ ② GET https://zcode.z.ai/api/v1/snapshot/upload-credential
│ (Bearer token,返回 snapshot_id + RSA 公钥 + OSS 签名 + max_size)
│
├─ ③ 扫描工作区
│ git 仓库 → git ls-files --cached --others --exclude-standard
│ 非 git 目录 → 递归遍历(等于全量拷贝)
│ + 显式追加 .git/ 里的元数据文件(config、refs、reflog……)
│
├─ ④ 组装:工作区文件 + prompt.json(含 prompt 原文/模型/baseURL)
│ + prompt 附件原文件 + 9 类全局配置
│ (baseline 全量 / 增量 delta 两种模式)
│
├─ ⑤ 打明文 tar.gz → AES-256-CTR 加密(随机密钥)
│ 密钥用服务端 RSA 公钥 OAEP-SHA256 包裹 → .tar.gz.enc + envelope.json
│ 明文包立即删除
│
└─ ⑥ POST 到服务端下发的 OSS 地址(multipart + 签名 policy)
+ OSS callback 通知服务端;失败重试 3 次,状态持久化,重启续传```触发频率:代码里有
captureRepoSnapshotSidecar(发 prompt 时)和 repo-wiki 生成两个调用点。动态复证实测:每条 prompt 稳定触发 2 次上传(两个路径各一个包)。触发条件汇总:
条件 不满足时 本地工作区(非远程挂载) 跳过 OAuth 登录态有效 直接 return(API Key 用户永远满足不了这条) upload-credential 请求成功 等下次 prompt 磁盘配额(上限 min(服务端值,2GB),本地驻留 1/3) 丢弃旧 pending 后再试 加密后 ≤ max_size 删产物只记大小 注意:设置项
repoSnapshotIndexingEnabled我在代码里没有找到它会拦截上传——真正有效的闸门只有登录态这一条。二、到底传了什么(第一轮:裸目录实测解密)
沙箱复现时,我用文件监控器在上传瞬间抓到了 pending 目录里的 envelope(含被 RSA 包裹的 AES 密钥)和密文,完成端到端解密。tar.gz 内部结构:
```<snapshot_id>/
├── meta/
│ ├── prompt.json ← prompt 原文 + 会话 ID + 模型名 + 你配的 API baseURL
│ ├── manifest.json ← 文件清单(路径+大小)
│ └── delta.json ← 增量模式时的增删改清单
├── files/ ← 工作区文件本体(原样)
├── extra-files/
│ ├── references/ ← 你拖进对话框的附件(原文件二进制,不做敏感过滤)
│ └── global-configs/ ← 你的全局配置(见下)
└── extra-meta/```实测的
prompt.json(这就是每条消息都会带的东西):{ "schema": "repo_snapshot_prompt/v2", "sessionId": "a35e3e92-...", "captureStage": "prompt", "provider": "others", "model": "mock-model", "url": "http://127.0.0.1:18081", "content": "我修改了README,请重新看一下" }``` 全局配置逐类上传(`~/.zcode` 下): | 内容 | 说明 | |---|---| | `AGENTS.md` | 全文(上限 20MB) | | 行为设置 | 键值快照 | | MCP 服务器 | **完整定义**,含启动命令、参数、env(env 值键名叫 token 的会被 `<redacted>`,叫别的不会) | | Hooks | **完整定义**,含可执行命令行 | | Memory | **全文内容**(上限 20MB) | | Skills/Commands/Subagents/Plugins | 元数据级(名字/描述/开关) | prompt 附件规则:有本地路径的(包括图片/视频/音频)**原文件二进制**进包;纯文本附件按内容进包;最多 16 个,图片 20MB/视频 200MB/其他 100MB,总 1GB。 ### 排除规则实测(第一轮:裸目录,8 个文件只传了 3 个) | 文件 | 结果 | 生效规则 | |---|---|---| | README.md / app.js / notes.txt | ✅ 传了 | — | | `.env`(内容 SECRET_KEY=...) | ❌ | 文件名精确匹配 `.env` | | `api_token.txt` | ❌ | 文件名含 `token` | | `bigfile.bin`(1MB+10B) | ❌ | >1MB(就差 10 字节卡线,阈值精确) | | `dist/bundle.js` | ❌ | 顶层构建目录 | | `node_modules/dep.js` | ❌ | 依赖目录 | **但请记住:以上全是文件名匹配。** `looksLikeSecretPath` 只认 `.env/.npmrc/id_rsa/*.pem/*.key/*.p12/*.pfx` 和文件名里的 `token/secret` 字样。你的密钥叫 `data.txt`、AK/SK 写在普通代码里、`.env` 改名成 `env.local`——全部照传。 --- ## 三、第二轮:git 仓库才是重灾区(.git 是免检旁路) 把同一个工作区 `git init + commit` 后再测,结果完全变了。代码里 `.git` 目录被 `appendRootGitMetadataPaths` **主动追加**进采集列表,且过滤函数对路径含 `.git` 段的文件直接放行——**大小检查、敏感名检查、二进制检查全部跳过**:// shouldIncludeRepoSnapshotPath
路径含 .git 段 → { include: true } // 直接放行,后面所有检查不执行```解密后的包(19KB,对比第一轮 1.4KB):
.git整目录 35+ 个文件原样进包,加上 git 可见的 4 个文件。最关键的发现:上传物 实测内容 .git/configremote URL 里的 token 明文(我埋的假 token https://user:[email protected]/private/repo.git原样在内).git/logs/HEAD完整 reflog 操作历史 .git/objects/**所有提交过的文件内容的 zlib blob .git/objects/pack/big-pack-test.pack2MB 的 pack 原样上传——>1MB 限制对 .git不生效被"排除"的文件全部从 .git/objects 绕出去了
我把第一轮被规则拦住的每个文件的 blob 逐个解压验证:
被第一层排除的文件 藏在哪个 blob 解压后 .envobjects/55/e9a9c4...SECRET_KEY=should-be-excluded-by-nameapi_token.txtobjects/d3/6304...inline-token-file-should-be-excludedbigfile.binobjects/69/a7cc...完整 1,048,586 字节 dist/bundle.jsobjects/4e/f088...build output should be excludednode_modules/dep.jsobjects/d8/d73a...dependency should be excluded五个被规则明确排除的文件,内容全部随包传出。 服务端解包后跑一遍
git cat-file就能还原。删了的文件也会传
git 删除文件从不删 objects 里的 blob(历史 commit 依然引用它,连
git gc都不会清)。所以:你提交过一次密钥、后来删了、以为没事了——每次发消息,那个密钥都随.git/objects完整上传一遍。 这就是安全圈对"密钥进过 git 历史"的标准答案是"立即吊销轮换"的原因:在 git 里删除从来不是删除。一句话总结两轮差异
裸目录:排除规则全部真实生效(真没传)。
git 仓库:排除规则只锁了files/大门,旁边.git车库门敞开——提交过 = 全部上传,含已删除的。四、第三轮:二级目录与内嵌密钥(最贴近真实项目)
加了
src/、config/、lib/二级目录再测(每个里面埋了东西):二级目录文件 结果 src/core/engine.js✅ 明文上传,路径完整保留( files/src/core/engine.js)src/utils/crypto.js(源码里写死API_KEY = 'sk-live-...')✅ 明文上传——文件名无害 + 内容不检查,双重盲区 config/database.json(db_password明文)✅ 明文上传 lib/settings.js✅ 明文上传 src/local_notes.txt(被.gitignore忽略且未跟踪)❌ 唯一没传的—— git ls-files不列出,两层都躲过结论:
- 目录深度不是屏障。排除清单只认node_modules/dist/build/out/.next/coverage这几个名字(且dist/build/out只匹配顶层,src/dist/xxx反而会传)。src/lib/config/internal畅通无阻。
-.gitignore是唯一可靠的防线(未跟踪 + 被忽略 = 真不传),比 ZCode 自己的敏感名过滤靠谱。
- 写死在源码里的密钥没有任何规则能拦。大小限制的不对称(实测确认)
位置 单文件限制 工作区正常路径 >1MB 排除 .git/下任何文件无限制(免检) 整个快照包 加密后 ≤ min(服务端值, 2GB),超了不上传 实测:工作区 1MB+10B 的文件被排除,同内容的 git blob(1,048,586B)原样上传,2MB 的 pack 文件原样上传。2GB 是压缩加密后的大小,git blob/pack 本身已压缩,一个大仓库的
.git很容易整体塞进 2GB 限额内——对 git 项目来说,大小护栏基本形同虚设。超限时的行为:删产物、记录大小、下次 prompt 重试(表现为发消息时无提示的后台扫描卡顿)。加密与密钥
- AES-256-CTR 加密内容,密钥随机生成,用
upload-credential响应里服务端下发的 RSA 公钥(OAEP-SHA256)包裹。envelope 里有plaintextSha256、AAD(workspace 哈希、manifest 哈希等)。 - 我做了交叉验证:用 A 快照的密钥解 B 快照的密文,得到乱码——每个快照独立密钥,只有持私钥的服务端能解。OSS 拿到的是纯密文,但服务端随时可还原。
- 明文 tar.gz 生存期 <50ms(加密完立即删),但 envelope + 密文会在
~/.zcode/v2/checkpoints/<wsHash>/pending/短暂驻留,失败时保留 24 小时并跨重启续传。
五、动态复现怎么做的(可复现步骤)
给想自己验证的人,思路比结论重要:
1. 环境变量隔离副本(和正式版共存):
-ZCODE_DATA_BASE_DIR→ 沙箱数据目录
-ZCODE_DESKTOP_USER_DATA_DIR→ 独立 Electron userData(打包版会忽略--user-data-dir参数强制写AppData\Roaming\ZCode,不隔离会撞单实例锁直接退出)
-HOME→ 沙箱 home(host 进程读的是 HOME 下的 .zcode,跟主进程不是同一套目录,凭据要两边都放)
-ZCODE_BASE_URL=http://127.0.0.1:18080→ 这是官方预留的端点覆盖:所有https://zcode.z.ai的请求会被原样重写到这个 origin,一个环境变量就能把 OAuth、凭证、OSS 直传地址、callback 全部接管到本地 mock
2. 伪造登录态:按代码逻辑构造credentials.json(键名oauth:active_provider、oauth:bigmodel:access_token、zcodejwttoken等,值用enc:v1:AES-256-GCM格式自己加密,密钥来自ZCODE_CREDENTIAL_SECRET)。启动后 UI 显示已登录 MockUser。
3. mock 服务端:upload-credential返回自己生成的 RSA 公钥(私钥自己留着,就能解密上传内容)+ 假 OSS 签名;另起一个 Anthropic 兼容的/v1/messages假模型让 agent 会话能跑起来。
4. CDP 驱动 UI:打包版禁了内置 9229,但 Electron 仍接受--remote-debugging-port命令行参数,连上后直接 DOM 操作输入 prompt 点发送。
5. 抓包:50ms 轮询checkpoints/*/pending/抓 envelope + 密文;mock 端oss-upload落盘收到的 multipart。时间线实测(第一条 prompt):
```17:22:59.859 POST /api/v1/event/report
17:23:00.187 GET /api/v1/snapshot/upload-credential?workspace_id=c5280988b067
17:23:00.308 POST /oss-upload bytes=3765 ← 第1个包(prompt 路径)
17:23:00.977 GET /api/v1/snapshot/upload-credential
17:23:01.097 POST /oss-upload bytes=3733 ← 第2个包(wiki 路径)```六、其他上传通道(顺带确认)
通道 内容 触发 遥测 POST /api/v1/event/report事件名、device_mid(随机 UUID)、版本、分辨率、时区 自动,无文件内容 用户反馈上传 日志 zip + 附件 → OSS 仅手动提交反馈时 Git Checkpoint git add -A(隔离索引)→ commit-tree →refs/zcode/checkpoints/*纯本地,无网络行为 崩溃报告 uploadToServer:false仅本地 端点清单:快照凭证
zcode.z.ai/api/v1/snapshot/upload-credential;对象直传 OSS(host 由凭证响应动态下发,客户端无白名单);OAuthzcode.z.ai/api/v1/oauth/token;更新检查 CDN。另有一个开发内网地址http://192.168.6.166:8080作为 dev 回退,生产不生效。七、怎么判断自己有没有被传 / 怎么防
查痕迹:
-~/.zcode/v2/checkpoints/目录存在且有内容 = 传过(或至少生成过)
- 日志里搜upload-credential
- git 仓库里git for-each-ref refs/zcode/*(这个是本地 checkpoint,不代表上传)防护:
1. 最有效:不登录 OAuth 账号,纯 API Key 直连——token 取不到,链路第一行代码就 return。我的本机实测零上传。
2. git 项目用户要有清醒认知:在 ZCode 里打开 git 仓库 = 全仓库(含历史)可见。真要用的话把密钥文件.gitignore+ 不提交(实测唯一两层都躲过的情形);进过历史的密钥立即轮换,别指望删除文件。
3. 网络层面可以拦zcode.z.ai/api/v1/snapshot/*(凭证接口,拦它最彻底——OSS host 是动态下发的,拦 OSS 域名没意义)。
4. 期待官方把repoSnapshotIndexingEnabled做成真正的开关,并在 UI 里明示这个行为——目前我在代码里没找到这个设置能拦住上传。八、声明
- 分析对象:ZCode 桌面版 3.11.2/3.12.3(Windows x64),app.asar 静态解包 + 沙箱动态复现(三轮:裸目录 / git 仓库 / 二级目录,每轮均有解密还原的 tar.gz 为证)。
- 所有验证在本地沙箱 + 自建 mock 服务端完成,没有向任何真实服务端发送数据;测试工作区文件均为我自造的无害内容(含假 token、假密钥)。
- 证据包(三份报告、mock 代码、全部请求日志、密文与解密产物)随帖附上;对应版本的官方安装包也一并附上,方便自行解包核对。
- 如果这是官方有告知的云功能(比如我漏看了某处用户协议),欢迎指出;但"默认开、无感知、传 prompt 原文、git 历史免检上传"这个组合,我认为值得每个用户知道。
主楼 - AES-256-CTR 加密内容,密钥随机生成,用
- momo-linuxdo2026-09-1811305
这回真的是码都没了。grok刚刚被喷完,zcode就出来,还好我用codex,暂时没被锤 ^-^
- Taosu2026-09-1832844
这是个人数据一网打尽,被Z/顷刻炼化了。真是服了,拿对话数据训练还不满足,直接开偷了 ^-^
- Ys_Ltr2026-09-186193
- 纯真的薯薯2026-09-1814851
这下知道国产模型的训练来源之一了 ^-^ 原来是这样的
- Iza2026-09-184533
我想知道他这么偷已经偷多久了,感觉像是惯犯
- Eevee2026-09-185468
- LY992026-09-1830033
偷数据偷了个爽这是……
- Ringo2026-09-1810186
有佬反馈起码三个月,不确定真假。但是最近发鸡蛋用的人多了肯定比之前偷的爽多了。
- AIInAll2026-09-1834767
佬是怎么发现这个事的?看起来很难发现啊
- 汉武帝(的大熊猫)2026-09-186190
Aoe国模你也是牛逼。 kimi和dsh都是开源的。
- 南朝朴素的灯花2026-09-1811924
a/都不敢明偷