# GitHub AI Scan 實測：找出兩處 Shell 注入風險，安全檢查卻顯示 Success

> Canonical: https://vxi.me/zh-tw/posts/github-ai-scan-pr-security-checks/
> Locale: zh-tw
> Published: 2026-10-16
> Updated: 2026-10-16
> Markdown: https://vxi.me/zh-tw/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](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 從建立到評論的觀察值，不是多次掃描的平均耗時。

兩個刻意植入的問題都收到評論；我並沒有另外建立完全正常的程式碼 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` 結束，兩則機器人評論直到 **01:33:52 UTC** 才出現，晚了 **18 秒**。

先有綠色檢查，後有漏洞評論。這個先後順序已說明，job 的 `Success` 不能解讀成「沒有安全發現」。

![GitHub AI Scan PR #1 的機制示意：兩處有預先標註的 Bash 風險收到建議性評論，Job 於 01:33:34 成功，評論在 01:33:52 出現，合併門禁須另外設定](/images/posts/github-ai-scan-pr/ai-scan-advisory.zh-tw.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 建立到評論出現的間隔，不是掃描本身的耗時。各語言頁面使用對應版本的 SVG。*

這不代表掃描程式出錯。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，個人私人儲存庫不支援；組織公開儲存庫可用，組織 private 或 internal 儲存庫則須符合 GitHub Code Security／GitHub Advanced Security 的授權條件。使用 AI Scan 本身不要求額外購買 Copilot 授權。

CodeQL default setup **不是前置條件**。測試時我記錄到的狀態是 `not-configured`，AI Scan 依然留下了兩則評論。[官方文件](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`；所有者 REST 查詢收據顯示 AI Scan 開關已恢復為 `disabled`。關閉 PR 不會刪除歷史評論與固定提交，公開連結仍可逐項核對。

這次實測證明，AI Scan 能在這個很小、明確標記的陽性樣本中提供有用的逐行提醒，但**掃描任務成功和合併安全是兩道不同的關卡**。前者幫你發現問題，後者還得靠團隊的規則與審查流程。

如果你也在意 Git 推送前的審查是否會失敗放行，可以再看 [OpenQodex 本地 pre-push Hook 實測](/posts/openqodex-prepush-review-fail-open/)；那是另一個執行位置，也有不同的門禁風險。

*實測日期：2026 年 10 月 16 日（UTC）。*
