⚠️ 歷史紀錄:本報告描述的問題已於 2026-11-07 全部修正並合回 Agent 主線(修正卡 CM-3559~3564、3573~3576、3584,驗收紀錄見母卡 CM-3507)。文中的檔名與行號是掃描當下(commit
bab2aa3)的版本,修正後已變動,請勿拿來定位現行程式。
沒有新的高風險問題。 工具找到 1 個問題(中):repo_url 沒過授權目標清冊。這條和已登記的總表 #224(B5 F2)是同一件事,不重報,只是從 connector 內部再確認一次。人工逐條查了卡上 7 個重點,沒有新的高風險,但補到 3 個值得記下的小面向(都是低):
sonar-project.properties 沒被刪掉,違反本 repo 規則 SR-04.10;project_key 沒限制只能讀 Agent 自己建的專案。Agent 代客戶跑 SonarQube 有三種模式:
本棒看 connector 拿到參數之後有沒有再過濾,以及哪些欄位會進到外部指令(git clone、sonar-scanner)或 HTTP 請求。
範圍只有 guidant_agent/scan/adapters/connectors/sonarqube.py 這 1 檔。為了查證,翻了範圍外的 guidant_agent/shared/archive_safety.py(只為查證、不當發現)。
| # | 這是什麼問題 | 出事會怎樣 | 要先有什麼 | 在哪裡 | 怎麼修 | 嚴重度 |
|---|---|---|---|---|---|---|
| F1(=#224,不重報) | repo_url 只檢查開頭是不是 http/https,沒過授權目標清冊 |
Agent 被當跳板,去 git clone 內網任意主機;git 的錯誤原文最多 500 字回給使用者,可以拿來探測內網 |
能派 SonarQube scan 任務(雲端或主控台 run-local-scan)、Agent 主機裝有 git |
sonarqube.py:836-838(檢查在 :430-443) |
把 repo_url 納入清冊檢查,或設允許的 Git 主機清單;clone 加 -c http.followRedirects=false |
中 |
| M1(人工) | 源碼包或 repo 裡的 sonar-project.properties 沒刪 |
包內設定可以改 sonar.exclusions/inclusions/scm.*,讓掃描少掃一塊,報告看起來乾淨 |
能提供源碼的人(本來就控制源碼) | sonarqube.py:950-970、:1054-1060 |
解壓或 clone 後刪掉該檔,或命令列明示 exclusions= 等關鍵參數 |
低 |
| M2(人工) | 逾時或取消時只對主行程送 SIGTERM/SIGKILL | sonar-scanner 若由殼層腳本再開 JVM,JVM 可能變成孤兒繼續吃 CPU 和記憶體 | 掃描逾時或被取消 | sonarqube.py:1180(Popen 沒設 start_new_session)、_terminate |
start_new_session=True 加 os.killpg(記得容器 PID 1 的限制) |
低 |
| M3(人工) | pull 模式的 project_key 是使用者填的,沒有限制範圍 |
能讀到客戶 SonarQube 上 token 看得到的其他專案結果,寫進證據報告 | 能派 pull 任務;範圍由 token 權限決定 | sonarqube.py:424-428 |
改成產品決策:限制 Guidant-AI- 前綴,或要求客戶給最小權限 token |
低 |
repo_url 不過清冊(=總表 #224)白話: 使用者填的 Git 位址只檢查開頭是不是 http:// 或 https://(:438),然後直接交給 git clone(:836-838)。「只准連哪些主機」的授權目標清冊(allowed_targets.iter_targets)只看 target/urls 這類欄位,不看 repo_url。
實際拼出的指令:
git -c credential.helper= clone --depth 1 --quiet <repo_url> <workspace>/source
env 為 {**os.environ, "GIT_TERMINAL_PROMPT": "0"}。
三位檢查員 3:0 成立。已登記 #224,修法照那條走。
sonar-project.properties 沒刪白話: sonar-scanner 會讀源碼目錄裡的這支設定檔。命令列已經明示 sonar.host.url、projectKey、projectBaseDir、sources,命令列的 -D 參數優先於檔案內容,所以沒辦法用它把 token 送到別的伺服器(卡上擔心的那條不成立)。但 exclusions/inclusions/scm.* 沒有鎖死。
為什麼是低: 工具面板 3:0 判不成立,理由是能放這支檔的人本來就控制源碼。但這違反本 repo .claude/rules/security/external-calls-and-tools.md 的明文規則(關鍵參數命令列鎖死,或掃描前刪掉包內設定檔)。對稽核來說,「客戶自己把漏洞藏起來」是真實的動機,所以仍記為低。
Popen(command, stdout=..., stderr=..., env=env) 沒有 start_new_session,_terminate 用的是 process.terminate()/kill()。官方 sonar-scanner 啟動腳本最後通常用 exec java,同一個 PID 就殺得到。所以這條只在打包版本不同時才會成立,未實機驗證。
scan/upload 模式的 key 都強制加 Guidant-AI- 前綴並走字元白名單(:445-463),pull 模式不加。這比較接近設計取捨,交首腦判斷要不要開卡。
| 卡上重點 | 人工結果 |
|---|---|
:836-842 git clone:ext::、file://、-u 開頭、--upload-pack、--、ssh、env |
不成立(除 #224)。 開頭必須是 http(s),所以 ext::、file://、ssh://、git@、- 開頭都進不來。少一個 -- 也不會被當成選項;--upload-pack 只能從選項位置進,URL 位置不行。http(s) 不走 SSH,不會帶出 SSH 金鑰。GIT_SSH_COMMAND、GIT_CONFIG_* 沒清,但它們只來自 Agent 自己的環境,使用者控制不到。剩下的是 redirect:git 預設跟第一次 redirect,已含在 #224 的修法 |
:1054-1064 -D 參數被當 Java properties 解析、project_key 白名單、源碼包內 sonar-project.properties |
部分成立,見 M1。 -D 是 argv 的一個元素,Java 當成系統屬性的鍵值直接用,不會解析 \u 或換行;scan/upload 的 key 有白名單。branch 不進 scanner(只用在 pull 的 HTTP 參數,httpx 會編碼)。包內設定檔改不了 host.url,但改得了 exclusions,見 M1 |
:1064 繼承整個 os.environ;輸出檔有沒有遮 token |
不成立。 繼承的是 Agent 自己的 env,子行程是 Agent 信任的工具,沒有新的外送路徑。token 走 SONAR_TOKEN 環境變數,不進 argv。scanner 不會把 token 印出來;錯誤訊息只取 ERROR 行、最多 1000 字。輸出檔落在 workspace,結束時刪除 |
:348-355 httpx:verify、redirect、錯誤原文 |
不成立(另見 #232)。 沒寫 verify=,預設就是驗證。follow_redirects=True,但 httpx 跨主機 redirect 時會拿掉 Authorization,token 不會跟著跳。base_url 沒填 scheme 時預設補 http://(:336),面板 3:0 判為管理員設定,不算漏洞。錯誤訊息帶 server 原話,是 SonarQube 的 msg,不含 token |
:475-492 源碼包:archive_safety、symlink、workspace 刪除 |
不成立。 解壓走 archive_safety:tar 用 filter="data",zip 有路徑消毒,逃逸型 symlink 被擋(tar 計數 DoS 同 #226)。workspace 由 tempfile.mkdtemp 建、finally 用 rmtree 刪(:767-781)。本機上傳檔(keep_archive=True)刻意保留到保留期滿,屬設計 |
:1162-1221 _run_with_cancel 殺不殺得到整組 |
可能成立,見 M2(低) |
| pull 模式讀其他專案 | 成立但屬設計取捨,見 M3(低) |
verified。3 個候選,9 張票。F1 3:0 成立;另兩條(包內設定檔、http 預設)各 0:3 被否決。研究員派出 2 位、回來 2 位,沒有被看門狗砍(journal.jsonl 零 failed)。wf_e0943356-2d7,effort low,focus attack-surface,revision 3d5aa2averification:status=verified、candidates=3、candidates_deduped=3、panel_votes=9、panel_reviewed_findings=1、panel_quorum_findings=1、unreviewed_candidate_sites=0、incomplete_panel_candidates=0、researchers_dispatched=2、researchers_returned=2、reason=nulldocs/security-scan/FR-177/runs/CLAUDE-SECURITY-20261106-025642/