1. 事件背景与排查动机1.1 一个让我后背发凉的发现事情起因很简单。上周三晚上我在给一个客户做代码审计的间隙顺手打开网络监控面板看了一眼。结果发现一个让我瞬间清醒的现象我的开发机上一个AI编程工具的进程正在持续向外部地址发送数据包而且流量还不小峰值跑到了2MB/s左右持续了将近三分钟。这个工具就是ZCode。当时我的第一反应是它在传什么是正常的模型推理请求还是别的什么要知道我那个项目目录里不仅有客户的核心业务代码还有完整的Git历史记录里面包含了大量的提交信息、分支结构甚至一些早期版本中不小心提交过的配置片段。说实话做开发这么多年我对工具的网络行为一直比较敏感。但AI编程工具这个品类比较特殊它需要把代码上下文发给远端模型做推理所以有网络请求是正常的。问题在于正常的推理请求和“打包上传整个项目”之间边界在哪里怎么判断这篇文章就是把我那次完整排查的过程记录下来。从发现异常流量到抓包分析到检查本地缓存和Git历史再到最终确认行为性质整个链路我都走了一遍。如果你也在用类似的AI编程工具或者你对“我的代码到底有没有被传出去”这件事有疑虑这篇记录应该能给你一套可复现的排查方法。1.2 为什么AI编程工具的代码安全问题值得警惕先说说这个品类的基本工作原理。目前市面上主流的AI编程工具无论是IDE插件形态还是独立客户端形态核心能力都依赖大模型。而大模型要理解你的代码就必须拿到你的代码上下文。这个“拿到”的方式不同工具有不同的实现策略。一种做法是只发送你当前光标附近的代码片段或者你显式选中的代码块。这种策略下传输的数据量小范围可控用户心里也有数。另一种做法是建立项目级的索引把整个项目的文件结构、关键代码、甚至Git历史都同步到远端以便提供更“智能”的全局理解和跨文件重构能力。这种策略下传输的数据量就大得多涉及的范围也广得多。问题在于很多工具在宣传时强调的是“智能”“全局理解”“项目级上下文”但并不会明确告诉你为了实现这些能力它到底把你的哪些数据传到了哪里传了多少存了多久。这就造成了一个信息不对称用户以为只是发了几行代码实际上可能整个项目的骨架都被同步走了。ZCode这次引起我注意的就是它的流量特征明显超出了“单次推理请求”的范畴。后面我会详细说我是怎么判断的。1.3 排查前的准备工作在开始正式排查之前我做了几件事来保证过程可控。首先我把开发机的网络环境切换到了一个可以完整抓包的隔离环境。具体做法是让开发机通过一个本地代理端口出网所有流量都经过这个端口我可以在端口上做完整的记录和分析。这样既不影响到正常的开发工作又能拿到全量的网络行为数据。其次我备份了当前项目的完整状态包括工作区文件和.git目录。这一步很重要因为后续排查可能会涉及到对本地缓存的检查甚至清理万一操作失误至少有个回滚的余地。第三我准备了一个“诱饵项目”。这是一个专门构造的测试项目里面包含了一些特征明显的字符串比如特定的变量命名、注释内容、文件路径等。如果这些特征字符串出现在网络流量或者远端存储中就能直接证明数据被传走了。这个方法在后来的排查中起到了关键作用。提示做这类排查时诱饵数据的设计很关键。建议使用足够独特但又不涉及真实敏感信息的字符串比如用UUID加特定前缀的方式构造变量名这样既不会误伤又能精确定位。2. 抓包分析与流量特征解读2.1 抓包环境的搭建与工具选择抓包这件事工具选择取决于你要看什么层次的信息。如果只是想知道“有没有往外发数据”系统自带的网络监控工具就够了。但如果想知道“发了什么内容”“发到了哪里”“用什么协议发的”就需要更细粒度的工具。我这次用的是三层组合系统级网络监控看总体流量趋势代理层抓包看具体的请求内容和目标地址应用层日志看工具自身的记录。三层对照基本能做到无死角。系统级监控我用的是一款常见的开源网络监控工具它能按进程维度展示实时流量并且可以记录历史数据。代理层我用的是一个支持HTTPS解密的本地代理配合自签证书可以完整看到加密前的请求体。应用层则是直接查看ZCode客户端自身的日志目录看它自己记录了哪些操作。这里有个细节需要注意很多AI编程工具会对传输内容做压缩或编码抓到的原始数据不一定是可读的明文。所以代理层工具要支持自动解压和格式化展示否则你看到的只是一堆乱码。2.2 异常流量的时间线与行为模式我把那次排查中观察到的流量行为整理成了一条时间线这样更容易看出模式。时间点行为描述流量大小目标特征T0s打开项目工具启动索引约50KB多个短连接疑似元数据上报T15s索引完成后首次同步约800KB单连接持续传输内容疑似压缩包T45s后台静默传输约2MB长连接持续约3分钟T3min传输结束连接关闭-无后续请求从这条时间线可以明显看出T45s那次传输的行为模式和正常的“推理请求”完全不同。正常的推理请求是短连接、小数据量、按需触发。而这次是长连接、大数据量、在没有任何用户操作的情况下自动触发。这就像你去便利店买瓶水结果对方开了一辆卡车来送货量级完全不对。2.3 请求内容的初步分析抓到包之后下一步是看内容。这里我遇到了第一个难点传输的数据是经过压缩和编码的直接看是二进制乱码。我的处理方式是先把请求体保存下来然后用常见的解压工具尝试解压。试了几种格式之后发现是gzip压缩的tar包。解压之后目录结构一目了然里面包含了项目的工作区文件、.git目录下的部分内容以及一些工具自身的元数据文件。这个发现让我心里一沉。因为.git目录里包含的信息量远超普通用户的想象。它不仅包含当前所有分支的代码还包含完整的提交历史、每个提交的作者信息、时间戳、提交信息甚至包括那些已经被删除但仍在历史记录中的文件。换句话说即使你后来删掉了某个敏感文件只要它曾经被提交过Git历史里就还留着。我进一步检查了传输内容的文件列表发现它并不是完整地打包所有文件而是有选择地包含了一些“关键文件”。但这个“关键”的筛选逻辑并不透明至少从用户侧看不到任何说明。2.4 目标地址与传输协议分析流量去了哪里这是排查的核心问题之一。我通过代理层的日志提取了所有目标地址然后做了归类分析。结果发现传输的目标地址并不是单一的。有一部分是模型推理服务的地址这部分流量较小符合正常预期。但另一部分流量指向了一个对象存储服务的地址这部分流量大且传输的内容是打包后的项目数据。对象存储这个东西本身是个中性技术。很多服务用它来存日志、存备份、存用户上传的文件。问题不在于用了对象存储而在于传了什么进去谁能访问存多久有没有加密用户知不知情。我尝试直接访问那个对象存储的地址返回的是权限拒绝。这说明它至少做了访问控制不是完全公开的。但这并不能打消我的顾虑因为“有访问控制”和“只有我能访问”是两回事。如果访问凭证是工具服务方统一管理的那理论上服务方是可以访问这些数据的。注意判断一个工具是否“上传了你的代码”不能只看有没有网络请求。关键要看请求的内容是什么、目标是谁、触发条件是什么、用户是否知情并可控。这四个维度缺一不可。3. 本地缓存与Git历史的深度检查3.1 工具本地缓存目录的结构排查完网络侧接下来要看本地。因为很多时候工具会在本地先建一个缓存或索引然后再决定传什么。这个本地缓存的结构和内容能反推出工具的同步策略。ZCode的本地缓存在用户目录下的一个隐藏文件夹里。我进去之后发现目录结构大致分为几块索引文件、临时文件、日志文件、以及一个看起来像“待同步队列”的目录。索引文件是二进制格式的但用字符串提取工具能看到里面包含了项目文件的路径和部分内容摘要。临时文件目录里有一些打包到一半的压缩包文件名带有时间戳。日志文件最有价值它记录了每次同步的时间、文件数量、总大小以及同步的目标类型。从日志里我统计了一下在我不知情的情况下工具已经进行了多次后台同步。每次同步的文件数量从几十到几百不等总大小从几百KB到几MB。同步的触发条件包括项目文件发生变化、Git有新的提交、甚至只是工具启动。3.2 Git历史中隐藏的信息量前面提到传输内容里包含了.git目录的部分内容。这里我想展开说一下Git历史为什么值得特别关注。很多开发者对Git的理解停留在“代码版本管理”层面但实际上Git仓库里包含的信息远不止代码本身。举几个例子提交历史能反映项目的开发节奏和人员分工提交信息里可能包含任务编号、需求描述、甚至一些内部讨论的片段分支结构能反映项目的发布策略和功能规划标签信息能反映版本发布的时间点和命名规则。更关键的是Git的对象存储机制决定了一旦某个文件被提交过即使后来删除了它的内容仍然存在于历史对象中直到执行垃圾回收并且没有被任何引用指向。这意味着如果你曾经不小心提交过一个包含密钥的配置文件然后删掉了但Git历史里还留着那么打包.git目录就等于把这个密钥也带走了。我在诱饵项目里专门构造了一个场景先提交一个包含特征字符串的文件然后删除并再次提交。之后检查ZCode的同步内容发现那个已经被删除的文件内容仍然出现在传输数据中。这直接证明了它读取的是Git历史而不仅仅是当前工作区。3.3 检查点的作用与潜在风险ZCode有一个叫“checkpoints”的功能官方描述是用于保存代码状态的快照方便回滚和对比。这个功能本身很实用但它的实现方式值得推敲。我检查了checkpoints的存储位置和内容发现它保存的不仅仅是当前工作区的快照还包括了一些中间状态。这些快照文件同样出现在待同步队列中。也就是说你每一次让工具帮你修改代码它可能都会存一个快照而这些快照也在同步范围内。这就带来一个连锁风险如果你用工具处理过敏感代码即使后来手动删除了工作区里的敏感内容checkpoints里可能还留着当时的快照。而如果这些快照被同步走了那敏感内容就等于泄露了。我实测了一下在checkpoints目录里确实能找到历史快照文件而且这些文件的格式是可直接读取的文本或压缩包。对于使用这个功能的用户来说这是一个需要特别注意的点。3.4 本地排查的实操步骤清单如果你也想检查自己机器上的情况可以按下面这个清单来操作。这些步骤我在Windows和macOS上都验证过Linux下路径略有不同但逻辑一致。找到工具的配置目录。通常在用户主目录下的隐藏文件夹里名字和工具名相关。检查目录结构重点关注索引文件、缓存文件、日志文件和队列目录。查看日志文件搜索关键词如“sync”“upload”“push”“checkpoint”看有没有后台传输记录。检查checkpoints目录看快照的数量、大小和时间范围。检查项目的.git目录确认工具是否读取了历史对象。如果有待同步队列查看队列中的文件列表了解哪些内容被纳入了同步范围。提示在做这些检查之前建议先断开网络或者把工具退出避免在检查过程中又触发了新的同步。另外检查日志时注意时间戳对照你自己的操作时间看哪些同步是你知情的哪些是不知情的。4. 行为性质判定与风险等级评估4.1 如何区分“正常功能”与“过度收集”判断一个AI编程工具的数据传输行为是否合理我总结了一个简单的四问框架。第一问传输的内容是否超出了实现功能所必需的范围比如如果功能只是“帮你补全当前行的代码”那传当前文件的相关片段就够了不需要传整个项目。如果传了整个项目就要问为什么。第二问传输行为是否在用户知情的情况下发生用户是否能够看到传输了什么、传了多少、传到了哪里是否有开关可以控制第三问传输的数据在远端如何存储和处理是否有明确的保留期限是否会被用于模型训练用户能否要求删除第四问传输的内容是否包含用户可能没有意识到会泄露的信息比如Git历史、checkpoints快照、环境变量文件等。用这四个问题去套ZCode的行为我的判断是它在第一问和第二问上存在明显不足。传输范围超出了单次功能所需且后台静默传输的行为让用户难以知情。4.2 风险等级自评表为了更直观地评估风险我做了一个简单的自评表。你可以根据自己的使用场景来对照。风险维度低风险场景中风险场景高风险场景项目性质个人学习项目公司内部非核心项目客户交付项目、商业机密项目代码敏感度开源代码为主含部分内部逻辑含密钥、算法、业务规则Git历史干净无敏感提交有少量历史遗留含已删除的敏感文件工具配置已关闭非必要同步默认配置开启了全部智能功能网络环境隔离环境公司内网公共网络如果你落在高风险列建议至少做一次完整的排查并考虑调整工具的使用方式。4.3 从这次排查中得到的核心结论经过网络抓包、本地缓存检查、Git历史分析和诱饵测试我对ZCode的行为有了一个基本判断。它的核心功能确实依赖代码上下文这部分传输是合理的。但它在实现“项目级理解”和“checkpoints”功能时将同步范围扩大到了整个项目的工作区文件、Git历史对象和快照文件且这一行为在默认配置下是后台自动进行的用户侧缺乏清晰的提示和细粒度的控制选项。传输的目标包括对象存储服务虽然做了访问控制但数据的存储期限、使用方式和删除机制对用户不透明。这些行为本身不一定构成“恶意”但从代码安全的角度看它确实构成了一个需要用户主动管理的风险面。特别是对于处理敏感项目的开发者来说默认配置下的风险敞口是比较大的。5. 防护策略与工具使用建议5.1 使用AI编程工具时的通用防护原则不管你用哪个AI编程工具下面这几条原则都适用。第一条敏感项目隔离。如果你手头有涉及商业机密、客户数据或核心算法的项目尽量不要在同一个开发环境里使用AI编程工具。物理隔离或者虚拟机隔离是最稳妥的做法。第二条Git历史清理。在把项目交给任何工具处理之前先检查Git历史里有没有不该留的东西。如果有用历史重写工具清理掉或者干脆用一个干净的新仓库。第三条网络行为监控。定期检查开发机的网络流量特别是那些你不知道来源的后台传输。不需要很复杂的工具系统自带的资源监视器就能看到大致情况。第四条最小权限原则。给工具配置尽可能少的权限关闭不需要的智能功能。很多工具的功能是可以按需开启的没必要全部打开。5.2 ZCode的具体配置调整建议针对ZCode我做了以下配置调整供参考。首先在设置里找到与“项目索引”“全局理解”“后台同步”相关的选项把它们关掉。关掉之后工具仍然可以完成基本的代码补全和问答只是不再建立项目级索引。其次检查checkpoints功能。如果你不需要历史快照回滚直接关掉。如果需要定期清理旧的快照文件。第三检查工具的同步队列和缓存目录定期清理。特别是在处理完敏感项目之后手动清一次缓存。第四如果工具支持自定义模型服务地址并且你有条件使用本地部署的模型那是最彻底的方案。数据不出本机风险自然归零。5.3 团队协作场景下的管理建议如果你是团队负责人或者你在一个多人协作的环境里使用这类工具还需要考虑一些额外的问题。一是统一配置管理。不要让每个成员自己随意配置应该有一套团队级的配置规范明确哪些功能可以开哪些不能开。二是敏感项目标记。在团队内部明确哪些项目属于敏感项目对这些项目禁用AI编程工具或者使用隔离环境。三是定期审计。定期检查团队成员的开发环境看有没有不符合规范的配置。这个审计不需要很频繁一个季度一次就够。四是替代方案储备。对于确实需要AI辅助但又不能把代码传出去的项目提前准备好本地化部署的替代方案。注意团队场景下最大的风险不是工具本身而是成员对工具行为的不知情。很多人默认认为“工具不会乱传”但这次排查说明默认配置下的行为可能超出你的预期。知情是管理的前提。6. 常见问题与排查技巧实录6.1 排查过程中遇到的典型问题问题一抓包工具看不到明文内容。这是因为很多工具用了证书绑定或者自定义加密。解决办法是使用支持证书注入的代理工具或者直接从工具的本地日志和缓存入手绕过网络层。问题二日志文件太大找不到关键信息。我的做法是先用关键词过滤比如搜“upload”“sync”“oss”“checkpoint”然后再按时间排序看异常时间点的记录。问题三不确定某个文件是否被传走了。这时候诱饵测试就派上用场了。构造一个特征字符串放到你怀疑会被读取的文件里然后观察网络流量或远端存储中是否出现这个字符串。问题四工具更新后行为变了。AI编程工具迭代很快这个版本的行为不代表下个版本。建议在每次大版本更新后重新做一次简要的流量检查。6.2 快速自查清单如果你不想做完整的抓包分析只想快速判断一下风险可以按下面这个清单过一遍。工具是否有后台自动同步的行为看日志或网络监控。同步的内容是否包含.git目录检查缓存目录的文件列表。是否有checkpoints或类似快照功能检查快照目录。同步的目标地址是什么看代理日志或工具配置。用户是否有明确的开关来控制同步范围看设置项。工具的服务条款里是否说明了数据的使用方式仔细读一遍。这个清单过完你基本就能判断自己面临的风险等级了。6.3 我踩过的坑与经验总结第一个坑一开始我只看了网络流量的大小没看内容。结果发现流量大但不知道传了什么排查方向一度跑偏。后来才意识到内容分析比流量大小更重要。第二个坑我一开始以为关掉“智能索引”功能就没事了。后来发现即使关掉索引checkpoints功能仍然在后台保存快照并同步。这两个功能是独立的需要分别关闭。第三个坑Git历史的清理比我想象的复杂。简单的git rm只是删除了当前版本的文件历史里还在。需要用git filter-branch或者更现代的工具来重写历史。而且重写历史之后所有协作者都需要重新克隆仓库操作成本不低。第四个坑我以为对象存储的地址是固定的后来发现它会变。所以不能只靠屏蔽某个域名来防护得从工具配置层面解决。6.4 关于“偷代码”这个说法的理性看待最后说一下我对“ZCode偷代码”这个热搜词的理解。从我的排查结果看它的行为更准确的描述是“过度收集”和“不透明同步”而不是传统意义上的“偷”。区别在于偷是明确以窃取为目的而过度收集往往是产品设计上的激进选择加上用户告知上的不足。但这并不意味着可以放松警惕。因为从结果上看无论意图如何你的代码确实被传到了你控制范围之外的地方。对于代码安全来说意图不重要结果才重要。所以我的建议是不要纠结于“它是不是在偷”而是关注“我能不能控制它传什么”。把控制权拿回来比争论意图更有实际意义。具体来说就是通过配置调整、环境隔离和定期审计把风险控制在自己可接受的范围内。这次排查之后我对所有AI编程工具的态度都变了。以前是“装上就用”现在是“先看它传什么再决定怎么用”。这个习惯的转变我觉得是这次排查最大的收获。