<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>193577746 (kyriewen)</title>
    <link>https://www.w2solo.com/193577746</link>
    <description>独立开发者 / 前端工程师</description>
    <language>en-us</language>
    <item>
      <title>AI Agent 9 秒删光了生产数据库——我给自己的项目做了 5 个紧急检查</title>
      <description>&lt;h2 id="AI Agent 9秒删光了生产数据库——我给自己的项目做了5个紧急检查"&gt;AI Agent 9 秒删光了生产数据库——我给自己的项目做了 5 个紧急检查&lt;/h2&gt;
&lt;p&gt;突然看到之前 5 月份的一个新闻！&lt;/p&gt;

&lt;p&gt;一家叫 PocketOS 的租赁软件公司，创始人在用 Cursor 的 AI Agent 做开发。Agent 在执行任务时发现了一个平台的 Token——不是它该碰的文件里的 Token。然后它用这个 Token 连上了生产数据库，&lt;strong&gt;9 秒之内把整个数据库删光了，包括备份。&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;30 小时宕机。全公司只能从三个月前的异地备份恢复。三个月的客户数据，没了。&lt;/p&gt;

&lt;p&gt;更离谱的是另一个案例：Replit 的 AI Agent 删除了 1206 条高管记录后，&lt;strong&gt;自动生成了 4000 条假数据来填补空缺&lt;/strong&gt;——试图掩盖它刚刚造成的灾难。&lt;/p&gt;

&lt;p&gt;我当时看完这两个事件后做的第一件事，是打开自己的项目，逐条检查 AI 工具能接触到的所有东西。以下是我检查的 5 个点。&lt;/p&gt;
&lt;h2 id="检查一：Token权限范围"&gt;检查一：Token 权限范围&lt;/h2&gt;
&lt;p&gt;PocketOS 出事的根本原因：一个&lt;strong&gt;权限过大的 Token 被 AI 发现了&lt;/strong&gt;。&lt;/p&gt;

&lt;p&gt;AI 工具在执行任务时&lt;strong&gt;会读取工作目录下的所有文件&lt;/strong&gt;，包括&lt;code&gt;.env&lt;/code&gt;。如果 Token 有完整权限，AI 就有完整权限。&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# ❌ 典型的"图省事"配置&lt;/span&gt;
&lt;span class="nv"&gt;DATABASE_URL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;postgresql://admin:password@prod:5432/main
&lt;span class="nv"&gt;RAILWAY_TOKEN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;rly_xxxx  &lt;span class="c"&gt;# 完整项目权限&lt;/span&gt;

&lt;span class="c"&gt;# ✅ 最小权限原则&lt;/span&gt;
&lt;span class="nv"&gt;DATABASE_URL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;postgresql://readonly:xxx@dev:5432/dev_db
&lt;span class="c"&gt;# 生产Token通过CI/CD注入，本地不存在&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;检查方法：&lt;/strong&gt;&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="s2"&gt;"TOKEN&lt;/span&gt;&lt;span class="se"&gt;\|&lt;/span&gt;&lt;span class="s2"&gt;SECRET&lt;/span&gt;&lt;span class="se"&gt;\|&lt;/span&gt;&lt;span class="s2"&gt;PASSWORD"&lt;/span&gt; .env&lt;span class="k"&gt;*&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;铁律：AI 能看到的 Token = AI 拥有的权限。本地不放任何能写生产数据的凭证。&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id="检查二：分支保护"&gt;检查二：分支保护&lt;/h2&gt;
&lt;p&gt;AI Agent 完成任务后可能&lt;strong&gt;自动 commit 并 push&lt;/strong&gt;。如果 main 没保护，AI 代码不经 review 直接上线。&lt;/p&gt;

&lt;p&gt;必须设置：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;main 分支禁止直接 push&lt;/li&gt;
&lt;li&gt;合并需要至少 1 人 review&lt;/li&gt;
&lt;li&gt;CI 测试通过才能合并&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;再加个 pre-push hook：&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/sh&lt;/span&gt;
&lt;span class="c"&gt;# .husky/pre-push&lt;/span&gt;
&lt;span class="nv"&gt;branch&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;git rev-parse &lt;span class="nt"&gt;--abbrev-ref&lt;/span&gt; HEAD&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$branch&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"main"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
  &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"❌ 禁止直接push到main"&lt;/span&gt;
  &lt;span class="nb"&gt;exit &lt;/span&gt;1
&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="检查三：网络隔离"&gt;检查三：网络隔离&lt;/h2&gt;
&lt;p&gt;PocketOS 的 Agent 能连接外部数据库——说明&lt;strong&gt;没有网络隔离&lt;/strong&gt;。&lt;/p&gt;

&lt;p&gt;开发时用 Docker 限制 AI 的网络范围：&lt;/p&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# docker-compose.yml&lt;/span&gt;
&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;dev&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;.&lt;/span&gt;
    &lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;dev-only&lt;/span&gt;  &lt;span class="c1"&gt;# 只能访问开发库&lt;/span&gt;

&lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;dev-only&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;internal&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;  &lt;span class="c1"&gt;# 禁止外网&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;退一步说：就算不用 Docker，至少确保 AI 工具的配置里没有生产环境连接信息。&lt;/p&gt;
&lt;h2 id="检查四：备份验证"&gt;检查四：备份验证&lt;/h2&gt;
&lt;p&gt;PocketOS 的备份是&lt;strong&gt;三个月前的&lt;/strong&gt;。设了自动备份但从来没验证过能不能恢复。&lt;/p&gt;
&lt;table class="table table-bordered table-striped"&gt;
&lt;tr&gt;
&lt;th&gt;平台&lt;/th&gt;
&lt;th&gt;备份方式&lt;/th&gt;
&lt;th&gt;恢复粒度&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Supabase&lt;/td&gt;
&lt;td&gt;自动每日 (Pro 有 PITR)&lt;/td&gt;
&lt;td&gt;秒级&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PlanetScale&lt;/td&gt;
&lt;td&gt;自动分支快照&lt;/td&gt;
&lt;td&gt;分支级&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Vercel Postgres&lt;/td&gt;
&lt;td&gt;每日快照&lt;/td&gt;
&lt;td&gt;需手动验证&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;铁律：备份≠恢复能力。每月至少跑一次恢复演练。&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id="检查五：操作审计"&gt;检查五：操作审计&lt;/h2&gt;
&lt;p&gt;Replit 案例最恐怖的点：AI*&lt;em&gt;生成假数据掩盖&lt;/em&gt;*。没有日志，你甚至不知道数据被改过。&lt;/p&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Prisma中间件 — 记录所有破坏性操作&lt;/span&gt;
&lt;span class="nx"&gt;prisma&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;$use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;destructive&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;delete&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;deleteMany&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;update&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;destructive&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;before&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;prisma&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;model&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;findFirst&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;where&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;where&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;prisma&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;auditLog&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;action&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;before&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;before&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="na"&gt;after&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="na"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;习惯：每次 AI 会话结束后，花 30 秒扫一眼 git diff。AI 改了什么，你得知道。&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id="紧急检查清单"&gt;紧急检查清单&lt;/h2&gt;&lt;table class="table table-bordered table-striped"&gt;
&lt;tr&gt;
&lt;th&gt;检查项&lt;/th&gt;
&lt;th&gt;通过标准&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Token 权限&lt;/td&gt;
&lt;td&gt;本地只有 dev/readonly token&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;分支保护&lt;/td&gt;
&lt;td&gt;main 禁止直接 push&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;网络隔离&lt;/td&gt;
&lt;td&gt;开发环境连不到生产&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;备份新鲜度&lt;/td&gt;
&lt;td&gt;最近备份&amp;lt;24 小时&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;操作审计&lt;/td&gt;
&lt;td&gt;关键操作有记录&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;&lt;h2 id="AI不是敌人，但它不该有钥匙"&gt;AI 不是敌人，但它不该有钥匙&lt;/h2&gt;
&lt;p&gt;PocketOS 创始人说了一句话："AI didn't hack us. We just never locked the door."&lt;/p&gt;

&lt;p&gt;AI 编程工具让效率翻了好几倍。但&lt;strong&gt;你不会把房子钥匙交给一个你认识三天的人&lt;/strong&gt;——AI Agent 本质上就是这个角色。它很能干，但它不理解"后果"。&lt;/p&gt;

&lt;p&gt;你的项目里，AI 能碰的东西有哪些？你设了什么限制？&lt;/p&gt;

&lt;hr&gt;

&lt;p&gt;&lt;strong&gt;觉得有用？点个「赞」👍 让更多人看到&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;关注我，获取更多技术干货。&lt;/p&gt;</description>
      <author>193577746</author>
      <pubDate>Thu, 30 Jul 2026 19:19:59 +0800</pubDate>
      <link>https://www.w2solo.com/topics/7908</link>
      <guid>https://www.w2solo.com/topics/7908</guid>
    </item>
  </channel>
</rss>
