【紧急处理ZCode事件】ZCode 工作区快照静默上传:检测方法与三层防护实践

ZCode 工作区快照静默上传:检测方法与三层防护实践

大家好,我是邵奈一,一个爱折腾的实战派技术博主。

文章对你有帮助是我的荣幸,欢迎评论区交流。
标签:#ZCode #隐私安全 #AI编程工具 #数据上传 #macOS防护
基于 ZCode 桌面版客户端的实测与代码逆向,梳理其工作区快照静默上传的行为特征、本地检测手段与防护方案。

核心结论

2026 年 9 月 18 日,技术博客《扒一扒 ZCode 静默上传全量 Git 历史的骚操作》(作者 ferstar,blog.ferstar.org)指出:ZCode(智谱 Z.ai 的 AI 编程工具)在登录状态下会把整个工作区(含完整 .git 历史)静默打包上传至云端,且 UI 内的隐私开关无法阻止该行为。本文通过实测验证,结论如下:

  • 该行为属实。实测环境中 5 个工作区被快照,其中 4 个已确认上传成功,内容包含完整 Git 历史、全局配置、MCP 服务器列表与对话记录。
  • 加密所用的 RSA 公钥由服务端下发,数据仅厂商可解密。
  • 即便关闭"优化智能体体验"等隐私开关,上传行为依旧发生。
  • 采用"文件锁 + 常驻守卫 + 网络层封锁"三层防御,可阻断换账号、更新版本后的上传行为,本文给出全部可复现命令。
  • 已上传数据无法本地撤回。

时效声明:以下结论基于 2026-09-19 的 ZCode 桌面版客户端(9 月 16 日更新版本)实测与客户端代码逆向,未来版本可能变化。向原始爆料作者 ferstar 致谢。


第一节 背景与风险

AI 编程工具的工作区通常同时承载多个项目:AI 方向的主项目(实测本地仓库约 178MB)、企业项目、个人小工具(如视频字幕插件、文档库)等。评估该上传行为的风险,需明确两点:

其一,Git 历史等同于项目档案馆。 .git 目录保存每一次 commit 记录,可能包含硬编码口令、内网地址、客户名称、未清理的调试代码、离职同事注释等敏感信息。上传"当前代码"与上传"完整历史"在数据暴露量级上完全不同。

其二,隐私开关不构成有效防护。 ZCode 设置项中 优化智能体体验repoSnapshotIndexingEnabled、自动更新均可关闭,但实测表明这些开关与该快照上传行为无关。

本节给出一套本地检测流程,约十分钟可判断本机是否受影响。


第二节 检测方法

打开终端,依次执行以下命令。

步骤 1:检查快照目录及占用空间

bash 复制代码
du -sh ~/.zcode/v2/checkpoints

实测结果:153MB。

步骤 2:列出被快照的工作区

bash 复制代码
grep -o '"workspacePath": "[^"]*"' ~/.zcode/v2/checkpoints/*/state.json

实测结果:5 个工作区(含主项目与企业项目)。

步骤 3:确认是否包含 Git 历史

bash 复制代码
python3 - <<'EOF'
import json, glob
for p in glob.glob('~/.zcode/v2/checkpoints/*/manifests/*.json'):
    d = json.load(open(p))
    n = sum(1 for f in d['files'] if f['path'].startswith('.git/'))
    print(d['workspaceKey'], '| 总文件', len(d['files']), '| .git 下', n)
EOF

实测主项目:6384 个文件,其中 3462 个位于 .git/ 下,完整历史无遗漏。

步骤 4:查询服务端接收回执

bash 复制代码
grep -l lastAcceptedManifestHash ~/.zcode/v2/checkpoints/*/state.json

代码逆向确认:该字段仅在服务端确认收到快照后写入。实测 4 个项目携带此回执,接收日期分别为 9 月 10 日、11 日、12 日、16 日,表明数据已落于厂商服务器。

例外情况:主项目 151MB 加密基线包上传失败,滞留于本地 pending 目录,可作为未上传成功的物证。

步骤 5(可选):查看加密信封

bash 复制代码
cat ~/.zcode/v2/checkpoints/*/pending/*.envelope.json

输出组合为 aes-256-ctr + rsa-oaep-sha256。其安全边界见第三节分析。


第三节 上传机制技术分析

对客户端 app.asar 反编译并通读上传链路后,确认以下三个事实:

1. 解密密钥由服务端掌握。 RSA 公钥非客户端内置,而是每次上传时由服务端接口 /api/v1/snapshot/upload-credential 临时下发。数据用服务端公钥加密后传回,形成仅厂商可解的信封。厂商称为"传输安全",其实质是用户无法获知代码去向。

2. 隐私开关不参与上传决策。 任务结束时触发快照的标志在代码中硬编码为 true;从"打包 → 加密 → 上传"全链路,无任何读取隐私开关的逻辑。关闭开关两日后快照仍正常生成。

3. 触发条件单一:已登录且一次任务结束。 无弹窗、无首次授权、无提示。上传失败静默重试(主项目加密包即留有完整滞留记录)。

综合评估:此类功能可能服务于跨设备体验或代码索引等产品能力。但"默认开启 + 无开关 + 含全量 Git 历史 + 私钥在服务端"四项叠加,已构成越界的数据收集。


第四节 防护方案:三层防御体系

基本原理:上传前客户端须先将打包文件写入本地磁盘,阻断写盘即可阻断上传。

第一层:文件系统不可变锁

bash 复制代码
# 先将滞留的加密包隔离(如有)
mkdir -p ~/snapshot-quarantine
find ~/.zcode/v2/checkpoints -name '*.tar.gz.enc' -o -name '*.envelope.json' \
  | xargs -I{} mv {} ~/snapshot-quarantine/

# 整树加 macOS 用户级不可变锁
chflags -R uchange ~/.zcode/v2/checkpoints

# 顺手锁住两个同族目录(代码中的备选路径)
mkdir -p ~/.zcode/v2/repo-snapshots ~/.zcode/v2/repo-wiki
chflags uchange ~/.zcode/v2/repo-snapshots ~/.zcode/v2/repo-wiki

验证:

bash 复制代码
touch ~/.zcode/v2/checkpoints/test
# 应报错:Operation not permitted
ls -lO ~/.zcode/v2/checkpoints   # 应看到 uchg 标志

效果:上传流程第一步(写清单与加密包)当场失败,无字节发出。该锁与账号无关,退出登录、换账号、加账号均被拦截。

第二层:常驻守卫(自动自愈)

文件锁的弱点是:客户端大版本更新改路径或重装清目录后,锁失效。为此部署 launchd 守卫:每分钟巡检,发现快照目录缺失则重建并锁死;发现任何位置出现快照产物(*.tar.gz.enc / *.envelope.json)则搬至隔离区。

实测该守卫非常驻进程(launchd 拉起、跑完即退,内存占用为零),单次约 0.3 秒 CPU,合计约 1% 单核。

守卫脚本 ~/Library/Application Support/zcode-snapshot-guard/guard.sh

bash 复制代码
#!/bin/bash
# zcode-snapshot-guard --- 阻断 ZCode 工作区快照静默上传的常驻守卫
set -u
GUARD_DIR="$HOME/Library/Application Support/zcode-snapshot-guard"
QUARANTINE="$GUARD_DIR/quarantine"
LOG="$HOME/Library/Logs/zcode-snapshot-guard.log"
STATE="$GUARD_DIR/lastrun"
LOCKDIR="/tmp/zcode-snapshot-guard.lock"
DEFAULT_DATA="$HOME/.zcode"
SNAPSHOT_SUBDIRS="v2/checkpoints v2/repo-snapshots v2/repo-wiki"

mkdir -p "$QUARANTINE"

# 节流:25 秒内不重复执行
now=$(date +%s)
if [ -f "$STATE" ]; then
  last=$(cat "$STATE" 2>/dev/null || echo 0)
  [ $((now - last)) -lt 25 ] && exit 0
fi
echo "$now" > "$STATE"
if ! mkdir "$LOCKDIR" 2>/dev/null; then exit 0; fi
trap 'rmdir "$LOCKDIR" 2>/dev/null' EXIT

if [ -f "$LOG" ] && [ "$(stat -f %z "$LOG" 2>/dev/null || echo 0)" -gt 262144 ]; then
  mv "$LOG" "$LOG.1" 2>/dev/null
fi
log() { printf '%s %s\n' "$(date '+%Y-%m-%d %H:%M:%S')" "$*" >> "$LOG"; }

# 兼容设置里的 dataBaseDir 覆盖
db_override=$(grep -o '"dataBaseDir"[[:space:]]*:[[:space:]]*"[^"]*"' \
  "$DEFAULT_DATA/v2/setting.json" 2>/dev/null | sed 's/.*:[[:space:]]*"//;s/"$//' | xargs)
DATA_LIST="$DEFAULT_DATA"
[ -n "${db_override:-}" ] && [ "$db_override" != "$DEFAULT_DATA" ] && [ -d "$db_override" ] && \
  DATA_LIST="$DATA_LIST
$db_override"

log "----- run start -----"
printf '%s\n' "$DATA_LIST" | while IFS= read -r DATA; do
  [ -z "$DATA" ] && continue

  # 1) 锁死/预创建已知快照目录
  for sub in $SNAPSHOT_SUBDIRS; do
    d="$DATA/$sub"
    if [ ! -d "$d" ]; then
      if mkdir -p "$d" 2>/dev/null; then
        chflags uchange "$d" 2>/dev/null && log "pre-created+locked $d"
      fi
    else
      chflags -R uchange "$d" 2>/dev/null
    fi
  done

  # 2) 全目录扫描快照产物并隔离(锁定目录内的搬不动=已受保护)
  while IFS= read -r -d '' f; do
    skip=0
    for sub in $SNAPSHOT_SUBDIRS; do
      case "$f" in "$DATA/$sub"/*) skip=1; break;; esac
    done
    [ "$skip" -eq 1 ] && continue
    chflags nouchange "$f" 2>/dev/null
    dest="$QUARANTINE/$(basename "$f").$(date +%s).$$"
    if mv "$f" "$dest" 2>/dev/null; then
      log "QUARANTINED $f"
    else
      log "WARN unmovable artifact (unknown locked path): $f"
    fi
  done < <(find "$DATA" -type f \( -name '*.tar.gz.enc' -o -name '*.envelope.json' \) -print0 2>/dev/null)
done
log "----- run end -----"

安装(把 你的用户名 换成自己的,脚本先 chmod +x):

bash 复制代码
cat > ~/Library/LaunchAgents/com.user.zcode-snapshot-guard.plist <<'PLIST'
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
 "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
  <key>Label</key><string>com.user.zcode-snapshot-guard</string>
  <key>ProgramArguments</key>
  <array>
    <string>/bin/bash</string>
    <string>你的用户主目录/Library/Application Support/zcode-snapshot-guard/guard.sh</string>
  </array>
  <key>RunAtLoad</key><true/>
  <key>StartInterval</key><integer>60</integer>
  <key>WatchPaths</key>
  <array>
    <string>你的用户主目录/.zcode</string>
    <string>你的用户主目录/.zcode/v2</string>
  </array>
  <key>StandardErrorPath</key>
  <string>你的用户主目录/Library/Logs/zcode-snapshot-guard.err.log</string>
</dict>
</plist>
PLIST

launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/com.user.zcode-snapshot-guard.plist

验证测试:在陌生路径伪造两个快照产物,数秒内被守卫隔离,日志留下 QUARANTINED 记录。守卫首次运行亦将此前手动隔离的 151MB 加密包再次转移至其自身隔离区。

第三层:网络层封锁(终极保险)

文件层防御存在理论竞态窗口:若未来版本改用新路径且小包在两次巡检间隙传完,文件锁可能漏挡。网络层封锁可彻底封死,二选一:

方案 A(优先,单命令)------在 DNS 层拉黑已确认的 OSS 上传端点:

bash 复制代码
sudo sh -c 'echo "0.0.0.0 zcode-codebase-snapshot.oss-cn-beijing.aliyuncs.com" >> /etc/hosts' \
  && sudo dscacheutil -flushcache

该 bucket 域名经 DNS 验证真实存在。

方案 B(终极方案) ------安装 LuLu(免费开源)或 Little Snitch,为 ZCode 建立规则:只允许连接 zcode.z.ai,拒绝其余一切地址。无论未来版本上传至哪个存储端点均无法出网,是唯一无视版本变化的方案。


第五节 常见问题

Q1:退出登录是否安全?

是。上传流程第一步校验登录令牌,未登录直接返回,连打包都不发生。但退出后内置模型基本不可用,实操意义有限。

Q2:换新账号呢?

换账号重新获得有效令牌,捕获流程会恢复尝试;但第一层文件锁与账号无关,写盘失败,仍无一字节发出。前提:已实施第一层。

Q3:守卫是否长期占用内存?

不会。非常驻进程,内存占用为零;CPU 实测约 1% 单核(每分钟至多 2 次、每次 0.3 秒)。介意可将节流与间隔放宽,或装 LuLu 后直接移除。

Q4:已上传数据如何处理?

本地操作无法删除服务端数据,只能联系官方。重点应复盘 Git 历史中的口令、密钥、内网地址、客户信息,该换则换、该改则改。

Q5:如何恢复官方功能?

bash 复制代码
chflags -R nouchange ~/.zcode/v2/checkpoints
launchctl bootout gui/$(id -u)/com.user.zcode-snapshot-guard

第六节 小结

本事件的核心结论是:关闭可见开关不等于阻断数据上传。ZCode 的快照上传行为与 UI 隐私开关无关联。

对厂商的三点建议:默认关闭而非默认开启;首次触发明确告知(至少一个角标);若需上传则排除 .git 目录或采用本地持钥的端到端加密。做到这三条,该能力可成为合理功能。

对使用者:AI 编程工具读取代码是其品类成立的前提,但"对话时读取"与"将整库历史静默搬入对象存储"性质不同。工具越强,越应确认其数据去向。


本文操作环境为 macOS(Apple Silicon,macOS 15.x)。Windows 用户思路相同:NTFS 无 chflags,可用 ACL 拒绝写入 + 计划任务巡检实现同款防线。


转载声明:本文为博主原创内容,转载请注明出处,并保留原始爆料来源(ferstar,《扒一扒 ZCode 静默上传全量 Git 历史的骚操作》)。

邵奈一 三百篇原创沉淀,十万+读者同行。感谢你的阅读,咱们下篇接着聊。

相关推荐
烟雨江南7851 小时前
会议语音识别私有化部署方案:音频不出内网,如何接入现有会议系统
人工智能·websocket·音视频·语音识别·ai客服
hzxxxz1 小时前
从 2.4T 到 284B:模型变小之后,企业 AI 投入的 3 笔账
人工智能
4SAPI1 小时前
大模型 API 聚合平台选型指南:企业与个人用户的接入架构、稳定性与成本评估
人工智能·php·agent
小K讲AI营销1 小时前
AI基础设施融资路径的中美比较:资本市场模式与政策金融模式
大数据·数据库·人工智能
bmxy小明同学1 小时前
2026-09-19-多模态看懂PDF
人工智能
那年窗外下的雪.1 小时前
AIDC 学习日志|第 27 天|EVPN 多归属收敛时间线与 Leaf2 接管验证
网络协议·学习·http·tcpdump
是翎1 小时前
2026 Agent Eval 全景图:从“会做”到“值得部署”
人工智能·驱动开发·深度学习·开源协议·imagen
净水深流1 小时前
中国冷链冷库行业数据分析:从规模扩张到技术驱动
大数据·数据库·人工智能·冷库冷链