This is the second AI coding tool security story in two days. Yesterday it was Plugin4Shell, where four agents made the same checkout mistake. This one is different in kind: nothing was exploited, and nothing went wrong. ZCode worked exactly as built.
How It Was Found
On 17 September a developer ran out of disk on a 256GB MacBook Air and went looking for what was eating it. The ~/.zcode directory held over 700MB. Unpacking app.asar and reading the local metadata revealed what the directory was for: staging workspace snapshots before upload.
The write-up went up at 02:00 UTC on 18 September. Z.ai responded publicly the same day at 17:44.
What Was Actually Sent
One commercial project in the captured set came to 42,411 files and 345.5MB uncompressed, arriving as a 313MB encrypted archive. The composition is the part worth sitting with:
- 86.6% of the payload was Git data
.git/lfs/ alone was 56.8%
.git/objects/ was 29.6%
- The remainder was source, configuration and global application settings
Git objects are not a snapshot of your current code. They are every commit, every branch, every deleted file, and every secret anyone ever committed and then removed. A single active session logged up to 62 capture events.
The Encryption Detail
The archives used textbook envelope encryption: AES-256-CTR for the content, RSA-OAEP-SHA256 to wrap the key with a server-supplied public key.
The private key never touched the machine. The client could not decrypt its own uploads. Only the server could read them.
That is a legitimate design for protecting data in transit. It also means a user auditing their own outbound traffic cannot inspect what left their machine.
Why the Settings Did Not Help
ZCode exposed two toggles that read like privacy controls:
- Optimize Experience - controls only whether uploaded data may be used for model training
- Repo Snapshot Indexing - controls only whether the server indexes snapshots after they arrive
Neither governs capture. The pipeline ran unconditionally at startup. Both switches operate on data that has already been uploaded, which is a meaningfully different promise from the one the labels imply.
Where It Went
Upload credentials came from zcode.z.ai at /api/v1/snapshot/upload-credential. The archives themselves went by direct form POST to Aliyun OSS, bypassing Z.ai servers entirely, with OSS holding a callback registration back to Zhipu infrastructure.
What Z.ai Said
Z.ai confirmed the uploads and attributed them to a codebase indexing feature used to build local indexes and generate Repo Wiki summaries. Their position: the data was destroyed immediately after Wiki generation, the feature was on by default during early launch, and it is fixed in version 3.14.0.
On 21 September Z.ai open-sourced the ZCode harness under Apache-2.0, which is a real response rather than a press release. It lets anyone verify the current behaviour.
What open-sourcing cannot verify is the past. Whether encrypted snapshots already sitting in OSS were physically purged is not checkable from outside, and the original researcher said so plainly.
What To Do If You Ran It
Update to 3.14.0 or later first. Then treat it as a credential exposure, because that is what uploading .git/objects/ means:
- Rotate any secret that has ever been committed to an affected repository, including ones removed in later commits
- Check
.git/logs/ against your own history to see what period was captured
- Run
git log --all --full-history with a secret scanner rather than scanning the working tree only
- If the repository belongs to a client, the disclosure decision is theirs, not yours
The Wider Point
Every AI coding tool needs your code to be useful. The question is not whether it reads your files - it has to - but what leaves the machine, how far back in history it reaches, and whether the settings page describes it accurately.
Check what your current tool sends before you need to. ~/.zcode was 700MB and nobody looked for months.
Sources