# GitHub AI Scan 实测：两处 Shell 注入被指出，安全检查却是 Success

> Canonical: https://vxi.me/posts/github-ai-scan-pr-security-checks/
> Locale: zh-cn
> Published: 2026-10-16
> Updated: 2026-10-16
> Markdown: https://vxi.me/posts/github-ai-scan-pr-security-checks.md

# GitHub AI Scan 实测：两处 Shell 注入被指出，安全检查却是 Success

今天，我在一个公开测试仓库提交了 [PR #1](https://github.com/kuangyongjie/cliproxyapi-codex-deploy/pull/1)，特意放进去两段危险的 Bash 代码。大约 81 秒后，`github-advanced-security[bot]` 在 Files changed 留下两条评论，两处都找到了。

但打开 [GitHub Actions 运行记录 #38835686338](https://github.com/kuangyongjie/cliproxyapi-codex-deploy/actions/runs/38835686338)，`github-advanced-security` 这个 job 的结论却是 `Success`。漏洞提示是真实的，绿色的检查也是真实的。

这次实测的前一天，GitHub 宣布 [AI Scan for pull requests 正式 GA](https://github.blog/changelog/2026-10-15-ai-scan-for-pull-requests-generally-available/)。这次我想弄清的不是“AI 安全扫描厉不厉害”，而是一个更实际的问题：**它指出了危险代码，为什么 PR 的安全检查还会显示成功？**

答案很关键：**AI Scan 扫描成功，不等于代码通过了安全审查；它给出的发现目前是建议，不是强制合并门禁。**

## 两段 Bash 代码，它具体发现了什么

我只在临时 PR 的 `ai-scan-test/` 目录放了测试文件，没有修改生产业务代码，也没有用到真实凭据或服务器。完整脚本留在 [固定提交 `29e24722d3`](https://github.com/kuangyongjie/cliproxyapi-codex-deploy/blob/29e24722d3f8868322da3aa28ec01c9648f90726/ai-scan-test/vulnerable-deploy.sh)；下面是故意留下的危险语句，请勿直接用于生产：

```bash
eval "systemctl restart ${service_name}"

cmd="curl -fsSL http://${host}/${path} -o /tmp/svc.conf"
$cmd
```

第一处出在重启服务的函数里。外部传入的 `service_name` 被拼接进 `eval`，Shell 会把结果重新作为命令解析。只要名字包含恶意 Shell 语法，就不再只是“传一个服务名”。[机器人在第 17 行的评论](https://github.com/kuangyongjie/cliproxyapi-codex-deploy/pull/1#discussion_r4289992720) 把风险归为 CWE-78，并建议去掉 `eval`，直接传入有引号的参数，再限制允许重启的服务。

第二处看起来只是少了引号，实际问题却有些不同。脚本先把一整条 `curl` 命令放进 `cmd`，随后直接执行 `$cmd`。未加引号的变量展开会发生单词拆分，让可控的 `host` 或 `path` 影响 `curl` 接收到的参数和选项。[第 27 行的评论](https://github.com/kuangyongjie/cliproxyapi-codex-deploy/pull/1#discussion_r4289992738) 除了指出这一点，还提醒用明文 `http://` 下载配置会带来传输被篡改的风险。

| 位置 | AI Scan 的具体发现 | 原始评论 |
| --- | --- | --- |
| 第 17 行，`eval` | 用户输入可被重新解释为 Shell 命令（CWE-78） | [查看评论](https://github.com/kuangyongjie/cliproxyapi-codex-deploy/pull/1#discussion_r4289992720) |
| 第 27 行，`$cmd` | 命令参数／选项注入，另有明文 HTTP 风险 | [查看评论](https://github.com/kuangyongjie/cliproxyapi-codex-deploy/pull/1#discussion_r4289992738) |

这里值得区分：第一处能让输入进入 Shell 语法解释；第二处已确认的机制主要是**参数／选项注入**，不能仅凭变量未加引号，就断言它同样允许执行任意独立 Shell 命令。

还有一个会影响判断的细节：这份夹具并没有试图隐藏风险。[README.md](https://github.com/kuangyongjie/cliproxyapi-codex-deploy/blob/29e24722d3f8868322da3aa28ec01c9648f90726/ai-scan-test/README.md) 和脚本注释直接写了 `INTENTIONAL VULNERABILITY (CWE-78)`，还标出预期的 `String injection` 类别。机器人评论给出的具体利用示例不在源码里，但这些预先写明的提示可能影响识别表现；因此本次 2/2 不代表对未标注真实漏洞的检测率。

时间也可以复查。[PR #1](https://github.com/kuangyongjie/cliproxyapi-codex-deploy/pull/1) 创建于 **2026-10-16 01:32:31 UTC**，两条评论都发布于 **01:33:52 UTC**，相差 **81 秒**。这是一次 PR 的观察，不是多次运行的平均速度。

两处预埋风险都被指出，可以写成“这次 2/2 命中”，却不能据此推断通用检出率。更重要的是，我没有另建一份完全正常的代码 PR 作为阴性对照，因此也没有测出误报率。

## 有两条漏洞评论，为什么 Actions 仍然是绿色

回看 [动态扫描运行 #38835686338](https://github.com/kuangyongjie/cliproxyapi-codex-deploy/actions/runs/38835686338)，工作流名称是 `Code scanning AI findings on PR #1`。GitHub 记录显示，`github-advanced-security` job 在 **01:33:34 UTC** 就以 `success` 结束，两条 bot 评论直到 **01:33:52 UTC** 才出现，晚了 **18 秒**。

先有绿色检查，后有漏洞评论：这个顺序本身就说明，job 的 `Success` 不能被理解为“没有安全发现”。

![GitHub AI Scan PR #1 的机制示意图：带预标注的两处 Bash 风险收到 Advisory 评论；扫描作业 01:33:34 成功，评论 01:33:52 出现；合并门禁仍需另外配置](/images/posts/github-ai-scan-pr/ai-scan-advisory.zh-cn.svg)

*图：根据公开 [PR #1](https://github.com/kuangyongjie/cliproxyapi-codex-deploy/pull/1) 与 [Actions 运行记录](https://github.com/kuangyongjie/cliproxyapi-codex-deploy/actions/runs/38835686338) 绘制的机制示意，**不是 GitHub 页面截图**。图中 81 秒指 PR 创建至评论出现的时间差，不是扫描耗时。正式发布时图片随站点语言使用对应版本。*

这不是扫描出错。GitHub 的 [AI Scan 官方文档](https://docs.github.com/en/code-security/concepts/code-scanning/ai-powered-security-detections) 写得很清楚：发现属于 **advisory**，不会让 code scanning check 失败，也不能直接通过 Ruleset 的要求阻止 PR 合并。它负责提醒你关注哪些改动，而不是替你作合并决定。

实际使用时，至少要把下面几种信号分开看：

| 你在 GitHub 看到的东西 | 它表示什么 |
| --- | --- |
| AI Scan 的逐行评论 | 代码可能存在风险，供作者和审阅者处理；当前不直接阻断合并 |
| AI Scan 动态 job 的 `Success` | 扫描任务执行成功；即便有风险评论也可能为绿色 |
| CodeQL 等受支持的扫描结果及 Ruleset | 可以按仓库实际配置要求扫描结果并限制合并，但要验证规则是否生效 |
| Required checks 与人工审阅 | 决定检查失败、缺失或未获批准时能不能合并，取决于仓库设置 |

GitHub 另外提供 [Code scanning merge protection 的配置说明](https://docs.github.com/en/code-security/how-tos/find-and-fix-code-vulnerabilities/manage-your-configuration/set-merge-protection)。如果你的目标是“发现高危问题就不能合入”，需要确认配置的是有强制效果的规则，而不是把 AI Scan 的建议性评论误当作 required check。

我没有在这个带危险代码的 PR 上实际测试 Ruleset 阻断，因此不把 `Success` 解读为“GitHub 会允许所有漏洞合并”。本次观察能够说明的是：**AI Scan 的绿色任务状态与两条安全建议可以同时存在**。

## 启用之前，先弄清 CodeQL、仓库资格和计费

测试用的是个人账号的**公开仓库**。按照 [2026 年 10 月 GA 后的官方说明](https://docs.github.com/en/code-security/concepts/code-scanning/ai-powered-security-detections)，个人公开仓库可以启用 AI Scan，个人私有仓库则不支持；组织公开仓库可用，组织私有或 internal 仓库需要满足 GitHub Code Security 或 GitHub Advanced Security 的许可条件。使用 AI Scan 本身不要求另买 Copilot 许可证。

CodeQL 也不是必须先跑通的前置环节。测试时，我记录的 CodeQL default setup 状态为 `not-configured`，AI Scan 仍然在 PR 留下了两条评论。GitHub [官方文档](https://docs.github.com/en/code-security/concepts/code-scanning/ai-powered-security-detections) 也明确表示：AI Scan 不要求 CodeQL default setup，且不依赖 CodeQL 分析是否成功。

不过，它不是一次全仓库历史漏洞扫描。AI Scan 主要看符合条件的 PR 改动，支持 Shell/Bash、Terraform HCL、Dockerfile 等 CodeQL 未覆盖的部分场景；来自 Fork 或 Dependabot 创建的 PR 不在此次官方支持范围内。团队若有大量这类 PR，需要另外安排扫描和审阅。

启用时，在仓库 Code scanning 设置中检查 AI Scan 开关；组织仓库还要核对组织策略和相关安全功能是否启用。

费用也值得先看一眼：GA 后按 **Code Security AI Credits** 计量。哪些账号承担费用、是否有包含额度，以及超过额度后如何受预算限制，取决于 [GitHub 的计费规则](https://docs.github.com/en/code-security/concepts/code-scanning/ai-powered-security-detections)。本次实际扣费金额没有最终确认，不写成 `$0`；对于 PR 很活跃的仓库，先看 Billing 里的额度与硬预算更稳妥。

## 真正要拦住危险合并，应该做什么

先说代码本身。[两条评论](https://github.com/kuangyongjie/cliproxyapi-codex-deploy/pull/1) 给出的修复思路很朴素：不要把参数再交给 `eval`，也不要把整条命令拼成字符串后让 Shell 拆分。调用方式可以改成这样：

```bash
# 调用形式示意：service_name 和 url 仍需单独校验
systemctl restart "$service_name"
curl -fsSL "$url" -o /tmp/svc.conf
```

这只是修复方向，不是经过复测的安全补丁。真正落地时，`service_name` 需要允许列表；下载地址要限制 HTTPS、目标主机和重定向行为，并根据配置文件的重要程度做完整性校验。

仓库层面的做法同样明确：把 CodeQL 或其他实际支持阻断的扫描工具接入适当的 Ruleset / required checks，并验证受保护分支确实会拒绝失败或缺失的检查。AI Scan 评论依旧留给审阅者逐条判断，别因为动态扫描的 job 是绿色就直接略过。

测试结束后，我关闭了 [PR #1](https://github.com/kuangyongjie/cliproxyapi-codex-deploy/pull/1)，没有合并，测试分支也已经删除；公开仓库 `main` 仍在原来的 `87624c26a5e6` 提交。AI Scan 开关也按测试结束时的 REST 查询记录恢复到了 `disabled`。**关闭 PR 不会自动抹掉它的历史评论和固定提交**，这些链接仍可逐项复核当时的记录。

这次实测证明了一件足够实用的事：AI Scan 能在这个很小的正样本里给出有价值的风险提示，甚至把问题指出到具体行，但**扫描任务成功和合并安全是两道不同的关**。前者帮你发现问题，后者仍要靠仓库配置和审阅流程。

如果你正在考虑另一端的安全边界——Git 推送前的 AI 代码审阅是否可能失效放行——可以接着看 [OpenQodex 本地 pre-push Hook 实测](/posts/openqodex-prepush-review-fail-open/)。那是另一种执行位置，也有不同的门禁风险。

*实测日期：2026 年 10 月 16 日（UTC）。*
