给 AI 编程 Agent 装上“出站安检”:用 Casbin Gateway 识别整仓上传

2026-09-25 23 预计阅读时间: 1 分钟
来源: oschina.net AI 摘要 Original link

Disclaimer: This article is an AI-assisted summary. Read it together with the original source when precision matters. The summary may omit context, version differences, or edge cases and is not official documentation.

预计阅读时间:11 分钟

AI 编程工具能读取项目、修改文件、执行命令,也就天然具备把代码发送到外部服务的能力。九月中旬曝光的 ZCode 事件让风险变得非常具体:工具可能在后台打包整个代码仓库,甚至把 .git 历史一并上传。

针对这类问题,Apache 软件基金会旗下的开源项目 Casbin Gateway 新增了 Agent 外发监测能力。它所解决的并不只是“某个工具是否可信”,而是更基础的治理问题:开发者和企业应当知道 AI Agent 正在向哪里连接、传输了多少数据,以及一次请求是否疑似包含整个代码库。

为什么 .git 外泄比当前源码外泄更严重

一个工作目录中的源码只是仓库当前状态,而 .git 保存的是版本历史。它可能包含:

  • 已经从当前分支删除,但仍留在历史提交中的密钥;
  • 内部域名、测试地址、用户名和提交者邮箱;
  • 尚未发布的功能、回滚记录与安全修复过程;
  • 分支、标签、远程仓库地址及团队协作信息;
  • 曾经误提交,后来仅通过普通删除移除的配置文件。

因此,外发监测不能只匹配 .env、id_rsa 等少数文件名。更有价值的信号是组合行为:某个 Agent 在短时间内读取大量文件,访问 .git/objects,生成压缩包,随后向一个此前未批准的域名发起大流量请求。

单个信号很容易误报。例如,IDE 索引也会读取整个目录,Git 自身当然会访问 .git。但当“读取范围、目标域名、请求体积、进程身份和时间窗口”同时出现异常时,判断就可靠得多。

网关应该观察什么

把 Agent 流量统一经过本地网关,最大的价值是建立一个稳定的控制点。相比逐个审计插件或等待供应商说明,网关可以从几个维度记录外发行为:

  1. 进程与 Agent 身份:请求由哪个编辑器、CLI 或子进程产生。
  2. 目标地址:域名、IP、端口以及是否属于批准的模型服务。
  3. 传输规模:单次请求和短时间累计上传了多少字节。
  4. 仓库特征:请求是否疑似携带 Git 对象、文件清单或压缩后的整仓内容。
  5. 策略结果:允许、告警、阻断,还是要求人工确认。

这里需要区分“发现”和“阻断”。监测模式适合先建立基线,避免突然影响开发流程;涉及私有仓库、生产凭据或受监管数据时,则应考虑默认拒绝,并只允许经过审批的目的地。

HTTPS 也构成明确边界。如果网关只看到加密连接,它通常能观察目的地址、连接时间和流量大小,却未必能判断请求体中具体有哪些文件。若要做内容级检查,需要受控的 TLS 终止、Agent 原生集成或端点侧文件访问审计。证书固定、HTTP/3、QUIC 以及不遵守系统代理设置的客户端,也可能绕过普通转发代理。

动手验证:模拟一次包含 .git 的整仓上传

下面的实验不依赖真实代码仓库,用一个临时 Git 项目和本地 HTTP 接收端模拟整仓外发。它可以用于验证网关是否记录或阻断了这类请求。

先创建测试仓库和压缩包:

set -euo pipefail

LAB_DIR="$(mktemp -d)"
cd "$LAB_DIR"

git init demo-repo
cd demo-repo
git config user.name "Gateway Lab"
git config user.email "gateway-lab@example.test"

printf 'print("hello")\n' > app.py
printf 'LAB_CANARY=do-not-upload\n' > .env.test
git add app.py .env.test
git commit -m "create test repository"

cd ..
tar -czf demo-repo-with-git.tar.gz demo-repo

echo "Lab directory: $LAB_DIR"
echo "Archive size: $(wc -c < demo-repo-with-git.tar.gz) bytes"
tar -tzf demo-repo-with-git.tar.gz | grep -E '(^|/)\.git/' | head

接着在另一个终端启动一个只用于本机测试的接收器:

cat > /tmp/upload_receiver.py <<'PY'
from http.server import BaseHTTPRequestHandler, HTTPServer
from pathlib import Path
import hashlib

OUTPUT = Path("/tmp/received-upload.bin")

class Handler(BaseHTTPRequestHandler):
    def do_POST(self):
        length = int(self.headers.get("Content-Length", "0"))
        body = self.rfile.read(length)
        OUTPUT.write_bytes(body)
        digest = hashlib.sha256(body).hexdigest()
        print(f"received={len(body)} sha256={digest} path={self.path}")
        self.send_response(204)
        self.end_headers()

HTTPServer(("127.0.0.1", 18080), Handler).serve_forever()
PY

python3 /tmp/upload_receiver.py

回到第一个终端执行上传:

cd "$LAB_DIR"
curl --fail --silent --show-error \
  -X POST \
  -H 'Content-Type: application/gzip' \
  --data-binary @demo-repo-with-git.tar.gz \
  http://127.0.0.1:18080/upload

sha256sum demo-repo-with-git.tar.gz /tmp/received-upload.bin

两个 SHA-256 值应当一致,说明压缩包被完整发送。接入 Casbin Gateway 后,可以把接收地址换成测试目的地,并根据项目当前文档配置代理或 Agent 接入方式,再观察是否出现以下结果:

  • 记录发起请求的 Agent 或进程;
  • 记录目标、请求体积和时间;
  • 将包含 .git 的归档标记为高风险;
  • 在阻断模式下拒绝请求,同时留下可审计事件。

不要在测试中使用真实仓库、真实密钥或生产服务。部分 Agent 不读取 HTTP_PROXY、HTTPS_PROXY 环境变量,另一些会直接使用 QUIC;验证时应同时检查操作系统连接,避免误以为流量已经进入网关:

sudo ss -tpn
sudo lsof -nP -iTCP -sTCP:ESTABLISHED

如果仍有 Agent 直连外网,仅部署代理并不够,还需要通过主机防火墙或容器网络限制直连路径。

策略不要只写成“禁止上传代码”

“禁止上传代码”既难以执行,也会让依赖云端模型的 Agent 完全失去作用。更可操作的规则应描述主体、目的地、数据范围和动作。下面是一个策略设计示意,字段名并不代表 Casbin Gateway 当前版本的固定配置格式,落地时需要按项目实际文档改写:

# 策略模型示意:不是 Casbin Gateway 固定配置格式
egress_policy:
  default_action: deny

  approved_destinations:
    - host: model-api.example.internal
      agents:
        - approved-editor-agent
      max_request_bytes: 1048576

  sensitive_paths:
    - ".git/**"
    - "**/.env"
    - "**/*.pem"
    - "**/*secret*"

  detections:
    - name: possible-full-repository-upload
      conditions:
        minimum_files_read: 200
        upload_bytes_over: 5242880
        window_seconds: 60
      action: require_confirmation

    - name: git-history-egress
      conditions:
        path_matches: ".git/**"
      action: deny

真实环境中还应为依赖下载、Git 托管平台和内部模型服务分别制定规则。git push 本身会传输 Git 对象,不能仅凭看到 .git 特征就一律阻断;应结合目标是否为批准的 Git 服务、当前进程是否为 Git,以及用户是否主动触发操作。

上线时采用“观察—收紧—阻断”

比较稳妥的接入方式不是第一天就阻断全部未知流量,而是分阶段推进:

  • 观察阶段:记录 Agent、域名、端口、上传大小和命中规则,不改变请求结果。
  • 基线阶段:整理合法模型 API、代码托管服务与插件更新地址,删除不必要的例外。
  • 告警阶段:对大体积 POST、未知目的地、后台静默上传以及 .git 访问组合发出告警。
  • 阻断阶段:拒绝未批准目的地、凭据文件、Git 历史和超过阈值的批量外发。
  • 持续验证:每次 Agent 或网关升级后,重复执行可控的整仓上传测试。

Casbin Gateway 这类控制面的意义,在于把“相信工具不会上传”转化为“能够看见、记录并限制工具上传”。不过网关不是唯一防线:最小权限工作区、短期凭据、仓库密钥扫描、网络出口限制和 Agent 白名单仍然需要同时存在。对于能读取完整代码库并执行命令的 AI Agent,应当按本地高权限程序治理,而不是按普通聊天网页对待。


相关推荐