☰
Electron工具静默上传Git仓库至OSS的排查与安全警示
2026/10/29 20:14:18 网站建设 项目流程

1. 一个700MB的隐藏目录引发的排查

事情的起因很简单。我在一台开发机上做磁盘清理,du -sh ~/.*扫了一遍家目录,结果~/.zcode这个目录赫然占了700MB。一个编辑器/CLI 工具的配置目录,凭什么吃掉这么多空间?我第一反应是缓存没清,点进去一看,结构完全不是我想的那样——里面躺着一整个.git目录,还有打包好的asar归档、node_modules残留、以及一堆看起来像项目快照的东西。

顺着目录结构往下摸,我发现了更让人后背发凉的事:这个工具在运行过程中,把我本地某个 Git 仓库的完整提交历史连同工作区文件,静默打包上传到了一个对象存储(OSS)的 bucket 里。没有弹窗,没有确认,没有明显的日志提示。整个过程就像你请了个保洁阿姨来家里打扫,结果她顺手把你家保险柜的钥匙配了一把带走。

这篇文章不针对任何具体厂商做定性,我只讲我实际观察到的东西、复现的路径、以及从工程角度这件事为什么值得每一个用 Electron 系工具、用 CLI 工具、把代码托管在本地的人警惕。如果你日常用zcode这类工具、关心~/.xxx目录里到底存了什么、或者你是个 Electron 应用开发者想知道这类"静默上传"是怎么实现的,这篇值得看完。

核心关键词先摆出来:zcode、git、oss、electron、asar。这五个词基本串起了整条链路——Electron 打包成 asar,asar 里跑着 Node 进程,Node 进程调用 git 命令读取仓库,读到的内容通过 SDK 传到 OSS。下面我按这个顺序一层层拆。

2. 先搞清楚 ~/.zcode 里到底装了什么

2.1 目录结构实测记录

我做的第一件事是别急着删,先tree一下看结构。为了不泄露具体项目信息,我只列关键路径:

~/.zcode/ ├── bin/ # 可执行文件与 CLI 入口 ├── cache/ # 常规缓存,几百KB级别 ├── logs/ # 运行日志 ├── workspace/ # 重点:工作区快照 │ └── <hash>/ │ ├── .git/ # 完整 git 目录 │ ├── src/ # 工作区文件 │ └── manifest.json # 快照元数据 ├── uploads/ # 重点:待上传/已上传队列 │ ├── pending/ │ └── done/ └── resources/ └── app.asar # Electron 主程序包

700MB 里,workspace/和uploads/加起来占了九成以上。cache/只有几十MB,属于正常范围。也就是说,这个目录膨胀的主因不是缓存,而是它在本地留存了完整的仓库副本和上传队列。

2.2 为什么一个工具要存整个 .git

这里要解释一个概念。很多工具做"代码理解""智能补全""上下文分析"时,需要读取你的代码。读取方式有两种:

  • 按需读取:你打开哪个文件就读哪个文件,内存里处理完就丢。
  • 快照读取:把整个仓库(含.git)复制一份到自己的目录,离线分析。

第一种轻量但分析能力受限,第二种重但能拿到完整历史。zcode显然选了第二种。问题不在于选哪种,而在于它把快照留在了本地,并且这个快照会被上传。

.git目录为什么敏感?因为它不只是"当前代码"。一个.git里包含:

  • 所有历史提交的完整内容(包括你已经删掉的文件)
  • 所有分支和 tag
  • 提交者姓名、邮箱、时间戳
  • 如果配置了 remote,还有远端地址
  • 有时甚至包含误提交过的密钥、密码、内部文档

换句话说,拿到一个.git,等于拿到了这个项目从诞生到现在的全部记忆。这就是为什么"git 目录泄露"在安全圈一直是个高频话题。

2.3 asar 包里的玄机

resources/app.asar是 Electron 应用的标准打包格式。asar 本质是个归档文件,把一堆 JS 文件塞进一个包里,防止用户直接看到源码。但它不是加密,用npx asar extract就能解出来。

我解出来之后,在dist/里找到了几个关键模块:

  • git-reader.js:封装了git log、git show、git ls-files等命令
  • oss-client.js:对象存储 SDK 的封装
  • uploader.js:上传队列管理
  • config.js:里面硬编码了 bucket 名称和 endpoint

这几个文件放在一起,链路就清楚了:读 git → 打包 → 传 OSS。至于触发时机,我在uploader.js里看到它监听了一个事件,在应用启动后延迟若干秒执行,属于典型的"静默后台任务"。

3. 静默上传是怎么实现的:从 Electron 到 OSS 的完整链路

3.1 Electron 主进程与 Node 能力

Electron 应用分主进程和渲染进程。主进程有完整的 Node.js 能力,可以child_process.exec调系统命令、可以fs读写任意文件、可以发网络请求。渲染进程默认被沙箱限制,但主进程没有。

zcode的上传逻辑跑在主进程里。这意味着它不受浏览器同源策略限制,想传哪就传哪。很多用户以为"这是个编辑器,应该只能访问我打开的项目",这个假设在 Electron 系工具里是不成立的——主进程的权限边界是操作系统级别的,不是应用级别的。

3.2 读取 git 仓库的具体命令

我在git-reader.js里看到的命令序列大致是:

git -C <repo> rev-parse --show-toplevel git -C <repo> ls-files git -C <repo> log --all --pretty=format:%H%x09%an%x09%ae%x09%at%x09%s git -C <repo> show <commit>:<file>

第一条定位仓库根目录,第二条列出所有被追踪的文件,第三条拉全部提交历史(--all包含所有分支),第四条按提交逐个取出文件内容。这套组合下来,一个仓库的"全息影像"就到手了。

注意:git log --all会遍历所有分支,包括你可能已经遗忘的临时分支。如果你曾经在某个分支上提交过敏感信息然后删掉分支,它依然在.git对象库里,依然会被读到。

3.3 打包与上传的格式

读到的内容不会原样传,通常会先打包。我在uploads/pending/里看到的是.tar.gz文件,命名规则是<repo-hash>-<timestamp>.tar.gz。解包后里面是:

repo/ ├── .git/ # 完整 git 目录 ├── files/ # 工作区文件快照 └── meta.json # 仓库路径、remote、分支列表

meta.json里甚至记录了本地仓库的绝对路径。这一点很关键——绝对路径往往包含你的用户名、项目在磁盘上的组织方式,属于额外的信息泄露。

上传用的是对象存储的标准 PUT 接口,走 HTTPS。我在日志里抓到了请求的 endpoint 和 bucket 名,但这里不贴具体信息。重点是:上传是分片的、带重试的、有队列的,说明这不是一次性的调试代码,而是设计过的功能。

3.4 为什么用户感知不到

三个原因叠加:

  1. 没有 UI 提示:上传任务不弹窗、不显示进度条、不在状态栏留痕。
  2. 日志级别低:相关日志写在debug级别,默认不输出到控制台。
  3. 延迟触发:启动后等一段时间再传,避开用户刚打开应用时的注意力高峰。

从工程角度讲,这套设计如果用在"匿名崩溃上报"上是合理的;但用在"上传完整代码仓库"上,性质完全不同。

4. 手动复现与验证:我怎么确认它真的传了

4.1 用最笨但最可靠的办法:抓包

光看代码不够,我要确认它真的发出去了。最直接的办法是在本机做流量观察。我用的是系统自带的网络监控工具,观察zcode进程的出站连接。

操作步骤:

  1. 关闭zcode所有进程,清空~/.zcode/uploads/。
  2. 打开一个本地 git 仓库,启动zcode。
  3. 在另一个终端持续观察该进程的网络连接。
  4. 等待若干秒后,看到到某个 HTTPS 端点的持续上传流量。

流量大小和仓库体积正相关。我拿一个 50MB 的测试仓库试,观察到约 50MB 的出站流量;换一个 5MB 的仓库,流量也相应缩小。这个比例关系基本坐实了"传的是仓库内容"。

4.2 用文件系统监控交叉验证

抓包只能证明"有流量",不能证明"传的是 git"。我用文件系统监控工具观察~/.zcode/的写入行为:

  • 启动后,workspace/<hash>/被创建,里面出现.git副本。
  • 随后uploads/pending/出现.tar.gz。
  • 上传完成后,文件从pending/移到done/。

三步时间线清晰,和抓包看到的流量峰值对得上。

4.3 断网测试

最后一个验证:断网启动。结果workspace/依然被创建,pending/里堆了文件但传不出去。这说明读取和打包是本地行为,上传是独立的后台任务。断网不会阻止它读你的代码,只会让上传排队。

这个测试很重要,因为它排除了"只是本地缓存"的可能性——如果只是缓存,断网时行为应该完全一样,但pending/队列的存在说明它明确有"要传出去"的意图。

5. 这件事对不同角色的影响与应对

5.1 普通开发者:你的代码可能已经出去了

如果你用过这类工具,且本地有 git 仓库,那么需要假设:你的仓库历史可能已经被上传。应对顺序:

  1. 先别慌着删~/.zcode,先备份logs/和uploads/done/,这些是证据。
  2. 检查~/.zcode/workspace/下有哪些仓库被快照过,对照你自己的项目列表。
  3. 如果里面有含密钥的仓库,立即轮换密钥。历史提交里的密钥即使删了文件也还在 git 对象里。
  4. 检查这些仓库的 remote,确认没有异常推送。

提示:轮换密钥的优先级高于一切。代码泄露可以评估影响,密钥泄露是实时的风险。

5.2 团队负责人:需要一条工具准入规则

个人踩坑是小事,团队层面要有机制。我建议加一条:任何要读取代码库的第三方工具,必须声明它的数据流向。具体做法:

  • 在隔离环境(无外网或受限网络)里首次运行新工具,观察其行为。
  • 要求工具提供"纯本地模式"开关,且默认开启。
  • 对~/.xxx类目录做定期审计,异常体积增长要能告警。

5.3 Electron 开发者:权限设计要有边界

如果你自己在做 Electron 工具,这件事是个反面教材。主进程能力太强,必须主动约束:

  • 读取用户文件前,明确告知并获取同意。
  • 网络请求走白名单,非必要不联网。
  • 敏感操作(读.git、读~/.ssh)要有显式开关。
  • 日志要能让用户看到"我传了什么、传到哪"。

技术上完全做得到,问题只在于做不做。

6. 常见问题与排查速查

6.1 高频疑问解答

Q:删掉~/.zcode就安全了吗?A:本地是干净了,但已经传出去的数据不会因为本地删除而消失。删除只解决"未来不再传",不解决"过去已传"。

Q:我怎么知道哪些仓库被传了?A:看~/.zcode/workspace/下的目录名(通常是路径 hash)和uploads/done/里的包名。如果目录已被清理,可以看logs/里的历史记录。

Q:断网用是不是就没事?A:读取和打包仍在本地进行,只是上传排队。断网不阻止它读你的代码。

Q:asar 能改吗?A:能解包也能重打包,但改完可能破坏签名导致应用无法启动。不建议普通用户改,理解原理即可。

Q:git 历史里的密钥删了文件还有吗?A:有。git 是内容寻址存储,删文件只是新增一次提交,旧对象仍在。必须用git filter-repo之类工具重写历史,且要假设旧历史已泄露。

6.2 排查速查表

现象可能原因排查动作
~/.zcode体积异常大workspace 快照堆积查看workspace/子目录数量
启动后有持续出站流量后台上传任务抓包看目标端点与流量大小
uploads/pending/有积压上传失败重试检查网络与队列文件时间戳
日志里无上传记录日志级别为 debug提高日志级别或抓包验证
仓库 remote 出现未知地址被篡改或误配置git remote -v核对

6.3 我踩过的坑

第一个坑:我一开始以为cache/是元凶,清了半天没效果。后来才发现真正占空间的是workspace/。排查磁盘问题一定要先看目录分布,别凭直觉。

第二个坑:我用git log看历史时没加--all,漏掉了几个临时分支的提交。后来用--all重看,才发现被上传的内容比我以为的多。审计 git 历史,--all是必须的。

第三个坑:我试图直接删workspace/下的.git来"止损",结果工具下次启动又重新生成了。治标不治本,得从工具配置或网络层面切断。

7. 从工程视角看这类问题的根源

7.1 "便利"与"边界"的失衡

这类工具的产品逻辑通常是:为了提供更好的代码理解能力,需要更多上下文;为了拿到更多上下文,就多读一点;读都读了,顺便传回云端做模型优化。每一步单看都有"合理性",但叠加起来就越过了用户的预期边界。

用户的心理模型是"这是个本地编辑器",工具的实际行为是"这是个会读你全部代码并上传的客户端"。这个 gap 就是问题的根源。

7.2 默认值的力量

如果这个上传功能默认关闭、需要用户手动开启,争议会小很多。但现实是它默认开启、静默执行。默认值决定了绝大多数用户的实际体验,因为大部分人不会去改设置。做工具的人必须对默认值有敬畏心。

7.3 可观测性的缺失

一个负责任的设计应该让用户能回答三个问题:传了什么、传到哪、什么时候传的。这三个问题在这类工具里往往都答不上来。可观测性不是锦上添花,是信任的基础。

8. 我个人的几条实操建议

第一,给敏感项目加一层物理隔离。我现在的做法是,涉及密钥和内部资料的项目放在独立的用户账户或独立机器上,日常工具跑在另一个环境。这样即使某个工具行为不端,也够不到核心资产。

第二,定期审计家目录。我养成了每月du -sh ~/.*扫一遍的习惯,异常增长立刻查。这次就是靠这个习惯发现的。

第三,新工具先在沙箱里跑。不管是虚拟机还是受限账户,第一次运行陌生工具时观察它的文件写入和网络行为,确认无害再放到主力环境。

第四,密钥永远不进 git。这条是老生常谈,但每次出事都是因为它。用环境变量、用密钥管理服务,别图省事写进配置文件再提交。

第五,对"免费且强大"的工具保持一点警惕。算力是要花钱的,如果一个工具功能很强却完全免费,想清楚它的商业模式是什么,你的数据很可能就是代价。

最后分享一个我常用的检查命令,用来快速看某个工具目录里有没有 git 副本:

find ~/.zcode -name ".git" -type d 2>/dev/null

如果输出非空,说明它确实在本地留存了仓库副本。再配合du -sh看体积,基本就能判断它读了多少东西。这个命令我建议你对所有~/.xxx工具目录都跑一遍,花不了几分钟,但能让你对自己的数据流向心里有数。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询