# ZCode, open-sourced: what a remediation can and cannot establish

> Satyajit Ghana — Head of Engineering @ Inkers Technology
> canonical: https://ai.thesatyajit.com/articles/zcode-disclosure
> date: 2026-09-22
> tags: security, disclosure, agents, developer-tools, open-source, privacy
On 21 September 2026, Z.ai published the source of its AI coding workbench ZCode at
[`github.com/zai-org/ZCode`](https://github.com/zai-org/ZCode) under Apache-2.0, alongside a
public statement about security issues reported by the community. The statement says
remediation completed in client **v3.14.0**, that the Repo Wiki feature was removed and the
local-repository-snapshot generation and upload workflow disabled, and that two third parties
were engaged to assess the result. It apologises, thanks the reporters, and commits to a
vulnerability reporting process with rewards.

I want to be careful about what this article is. It is not an attack and it is not a summary of
the press release. A client that builds and uploads snapshots of a developer's working tree is a
specific and serious class of bug — the working tree of a professional repository is source,
unpushed branches, and whatever has ever been committed to it — and the shape of the response
here is notably complete: fix it, remove the feature, open the client, commission independent
review, publish. Most responses stop at the first two. That deserves saying plainly.

It also deserves reading precisely, because the published evidence is of four different kinds
and only one of them is the kind you can check yourself.

<RepoCard repo="zai-org/ZCode" />

## The feature, at the level it matters

<PipelineDiff />

The left-hand column is the reported pipeline. I did not read it in code, because it is not in
the published repository — that is the first thing the repository tells you. It is the shape
described by the researcher who reported it and corroborated, at the level of "this workflow
existed and has been disabled", by both commissioned assessments. A snapshot of the workspace
was built and encrypted on the machine, an upload credential was obtained from the service, and
the archive went directly to the vendor's object storage. The reported detail that makes it a
serious bug rather than a telemetry question is scope: the archive was of the workspace
directory, which on a real project means the `.git` directory comes along, and `.git` is every
version of every file the repository has ever held.

The right-hand column is what v3.14.0 does instead, and this one *is* in the repository, at
`packages/services/src/git/repo/gitCheckpointRepo.ts`. The replacement is a local git
checkpoint: a temporary `GIT_INDEX_FILE`, `git add -A -- <workspace pathspec>`, `write-tree`,
`commit-tree`, and `update-ref` into a hidden ref. The comment above it states the constraints
directly, and the third one is the incident, written as a rule:

```text
关键约束：
- 不污染用户真实 index
- 不产生用户可见分支或普通 commit
- scope 永远跟随当前 workspace，而不是整个 repo 无差别快照
```

*Key constraints: do not pollute the user's real index; do not produce a user-visible branch or
ordinary commit; scope always follows the current workspace, rather than an indiscriminate
snapshot of the entire repo.*

The engineering point worth taking away is that the feature survived. Checkpoint-and-restore in
an agent that edits your files is a good feature — arguably a necessary one. The fix was not to
delete it but to notice that git already provides content-addressed snapshot storage, locally,
in a repository the user already has, and that the only thing the archive-and-upload design
bought was a server-side index. Reusing the object store you are already standing on is both
cheaper and has no boundary to cross.

<Callout type="note">
One design pattern does survive in the published tree, and it is worth understanding rather
than alarming about. The `NOTICE.md` row on feedback attachments describes the same
request-a-credential-then-upload-directly-to-object-storage shape, for diagnostic files a user
explicitly attaches to a support ticket. That is a normal way to upload a file and the
difference is entirely consent and trigger: a ticket the user filled in, versus a workflow that
ran on its own. The mechanism was never the problem.
</Callout>

## What the repository can answer

<TreeAudit />

The greps come back empty, which is what the announcement says they should. The rows that
actually carry information are the other three.

`package.json` reads `3.14.0`, so the tree is the remediated client. `git log` has **two
commits** — *"Initial commit"* on 20 September and *"feat: open source"* on 21 September. There
is no development history. That is a completely ordinary way to open-source an internal
codebase, and it decides what this repository is for: it establishes what the client does now,
and it cannot be used to audit the removed feature, to date its introduction, or to see what
else changed alongside it. Anyone hoping to read the diff will not find one.

The strongest artefact in the repository is not code. `NOTICE.md` is a 27 KB document, written
by the vendor, enumerating in four sections what the client can execute, what leaves the
machine, what it stores locally, and its third-party licences. Section two is a table of
**seventeen** outbound-request scenarios — model calls, auth, billing, the Coding Plan gateway,
provider config, web search, MCP, SSH and container sync, attachments, conversation sharing,
idle-task tickets, feedback, updates, tool delivery, dev tooling, build and signing — each with
its trigger condition and data scope. A workspace-snapshot upload is not among them. An omission
from a document whose entire purpose is exhaustive self-description is a meaningfully stronger
signal than an empty grep, and publishing that document at all is the least-discussed and
possibly best part of this release.

The document is also unusually blunt about the general problem, in a way most agent vendors are
not:

> 当前共享 Agent 执行适配器不提供默认的操作系统沙箱
>
> *The current shared agent execution adapter does not provide a default operating-system
> sandbox.*

> 模型声称"已完成""已安全处理"或"已获授权"不构成事实或授权证明
>
> *A model claiming it has "finished", "handled this safely" or "been authorised" does not
> constitute evidence of fact or of authorisation.*

## What the assessments establish, and what they do not

Two organisations were engaged. Per the statement: the China Academy of Information and
Communications Technology (**CAICT**) confirmed the `zcode-prod` Alibaba Cloud OSS bucket is in
a zero-data state, and that v3.14.0 removed the Repo Wiki feature and cut the local-snapshot
generation and upload path. **NSFOCUS** confirmed that all data objects and the bucket itself
have been deleted, and that no functional path capable of triggering local repository snapshot
generation or transmitting local files externally was identified.

Both are real findings and both are worth having. They are also two different kinds of
statement, and the company's own claims are a third and fourth kind.

<EvidenceLedger />

Three things follow from that table, none of them accusations.

**A zero-data bucket is a fact about the present.** "The bucket is empty" and "the bucket has
always been empty" are different sentences, and only the first one is checkable by looking at a
bucket. Deletion — NSFOCUS's stronger finding — is better, because it removes the object rather
than merely finding none, but it still describes one named bucket at one moment. Neither
assessment claims otherwise; the phrasing in both summaries is careful. It is the reader who has
to not round it up.

**"No functional path identified" is a negative finding from a bounded review.** It is the
correct and honest way to report a review that found nothing, and it is exactly as strong as the
scope of the review — which is not published in either summary. A point-in-time assessment of a
stated scope is evidence; it is not a proof of absence, and no security assessment in the world
produces one. The statement's own promise that full reports will follow is the thing that would
change this, because a scope section is what turns "not identified" into a bounded, useful
claim.

**Two of the six claims are about internal state.** That the referenced code data is not
retained, and that it was never used for model training, are statements about what a company
holds and what it did with it. No external party can confirm an absence inside someone else's
systems. That is not a criticism — it is the structural limit of every such statement by every
vendor — but it means those two claims rest on the company's word, and are the two most load-
bearing claims for an affected user. Both assessments are scoped to the bucket and the client,
which is what a scoped assessment means, and is why the scope sections matter.

## Credit where it belongs

The reporting worked. The issue was found by a user looking at disk usage, written up in
public with specifics, and the vendor's statement came the same day; the open-sourcing and the
two assessments followed within three days. That is fast by the standards of this industry.

Four things here are better than the norm and should be named, because the norm is what gets
set by whether anyone notices:

1. **Open-sourcing the client rather than describing it.** Every claim about what the client
   does is now falsifiable by anyone with `git clone`, permanently, for every future version
   too. That is a larger commitment than a one-off audit and a harder one to walk back.
2. **Two independent assessments, not one**, with the second one's finding stated in the harder
   form — objects and bucket deleted, not merely empty.
3. **Publishing `NOTICE.md`.** A seventeen-row table of everything that leaves the machine, with
   triggers and data scope, written before anyone asked for it in that form. I would like this
   from every agent vendor and I have it from approximately none of them.
4. **Committing to a vulnerability reporting process with rewards.** The reporter here had no
   channel and used a blog. A channel is the difference between this timeline and one where the
   finder sells it.

What is still outstanding is short and specific: the full assessment reports with their scope
sections, rather than summaries; and a statement addressed to users of the affected versions
about what, if anything, they should do — rotate what, check what. The statement covers the
bucket and the client. It does not cover the user's next action, and for anyone whose `.git`
history contains a credential that was once committed and later removed, that is the question
they actually have.

## The general lesson, which is not about this vendor

Every agent that edits your files needs a way to undo that. Every implementation of undo needs
somewhere to put the previous state. The design decision is whether that somewhere is on your
machine or on someone else's, and it is a decision that gets made early, by one engineer, for
reasons that are usually about product surface — a server-side index enables a repo-wide wiki,
a cross-device history, a search box — rather than about data.

The v3.14.0 checkpoint is the proof that the local answer was available the whole time. `git
write-tree` against a temporary index is content-addressed, deduplicated, instant on a warm
index, and has no boundary to cross. If you are building this, that is the default, and the
burden of proof belongs on the design that leaves the machine.

<ChangeMyMind>
  <Falsifier claim="The published repository is the remediated v3.14.0 tree and contains no snapshot-upload path.">
    A grep of a fresh clone finding one. Mine was run on 2026-09-22 against the tip of `main`
    across 3,896 TypeScript files, for `repo.?wiki`, `repoSnapshot`, `encryptedSizeBytes`,
    `captureBeforePrompt`, `zcode-prod`, `PostObject` and `aliyuncs`; all returned zero outside
    four DashScope provider-config entries. A path under a different name would not be caught by
    a grep for the old names, and I did not audit the tree line by line — nobody has.
  </Falsifier>
  <Falsifier claim="The repository cannot be used to audit the removed feature.">
    A force-push or a second repository publishing the pre-remediation history. As of writing,
    `git log` is two commits dated 20 and 21 September and there are no tags and no releases, so
    there is no earlier tree to diff against.
  </Falsifier>
  <Falsifier claim="A zero-data bucket says nothing about what was in it before.">
    Access logs, object-versioning records, or a retention-policy audit covering the period
    before remediation — any of which would turn a point-in-time observation into a historical
    one. If the full assessment reports contain that, this claim is too strong and I would
    withdraw it.
  </Falsifier>
  <Falsifier claim="The remediation kept the feature and moved it local, rather than deleting it.">
    Read `packages/services/src/git/repo/gitCheckpointRepo.ts` and
    `gitCheckpointStore.ts`. Checkpoint creation, diffing, restore and delete are all present and
    all operate on a hidden ref in the user's own repository. If a later version reintroduces a
    remote index for checkpoints, that changes.
  </Falsifier>
  <Falsifier claim="The response is more complete than the industry norm.">
    A list of comparable incidents where a vendor open-sourced the affected client, commissioned
    two assessments and published a full outbound-request enumeration. I can think of few; my
    sample is what I have read, not a survey, and "notably complete" is a judgement rather than
    a measurement. It is also a judgement about the response and not about the bug.
  </Falsifier>
</ChangeMyMind>

---

*No figure, on purpose. A disclosure piece is the one place where a screenshot
is a liability: there is nothing here showing the reported pipeline in flight,
no captured working tree, no archive, no bucket listing, no endpoint, no request
or credential, and no reconstruction of any of them — anything of that kind
would be a recipe rather than evidence, however clearly it was labelled. What
the vendor published is text: a two-commit repository, a 27 KB `NOTICE.md`, a
statement and two assessment summaries. There is nothing to mirror either —
every one of the 2,370 image files in the tree is an application icon, a
file-type glyph from the Material icon set, installer artwork or the wordmark;
there is no architecture diagram, no screenshot and no recording anywhere in
it. So the three diagrams above are drawn from
the remediated code in
[`github.com/zai-org/ZCode`](https://github.com/zai-org/ZCode), read on
2026-09-22 at the tip of `main`, and every path and identifier in them is one
you can `git clone` and check.*
