本卡屬 FR-195(Agent 資安修正),第 1 批 F6。修 FR-186 掃描的 C1 F1(3/3 票)+人工 M1、M2、M3;S8 F1(3/3 票),嚴重度:中。
SonarQube 接頭四個相關的洞一起修。①伺服器位址沒寫 https:// 時程式自動補成明文 http://,token 就以明文送出,同網段的人看得到。②客戶上傳或 clone 下來的源碼裡若放一支 sonar-project.properties,掃描器會讀它,可以把要掃的檔排除掉、產出一份「乾淨」的假報告。③git clone 會跟著對方的轉址(302)走到清冊外的主機。④跑 git 與掃描器時把 Agent 全部環境變數帶過去,裡面有儲存金鑰與註冊 token。
b8b1158 重核過,仍然有效)guidant_agent/scan/adapters/connectors/sonarqube.py:336 base_url = raw_base if "://" in raw_base else f"http://{raw_base}",沒有 https 檢查。對照組:guidant_agent/scan/adapters/connectors/zap.py:152-161 預設補 https://、scheme == "http" and not tool_tls.is_loopback_host(...) 就 raise ValueError;:166-170 有 check_settings classmethod,掛在 guidant_agent/scan/adapters/connectors/__init__.py:111 的 _SETTINGS_CHECKERS(存檔前就擋)。sonarqube.py:1054-1061 _run_scanner 的命令列只鎖了 sonar.host.url、projectKey、projectBaseDir、sources、metadataFilePath;源碼目錄裡的 sonar-project.properties 沒刪。sonarqube.py:836-839 git -c credential.helper= clone --depth 1 --quiet <repo_url> <dir>,沒有 --、沒有關轉址、沒有限協定。sonarqube.py:840 env = {**os.environ, "GIT_TERMINAL_PROMPT": "0"}、:1062 env = {**os.environ, "SONAR_TOKEN": token}。test/test_file_and_function_size.py:19):淨增一行就紅。_clone_repo(68 行)與 _run_scan(98 行)也在函式白名單上,一樣不能變長。FR-177 的修正卡就是在這裡被退回一輪。guidant_agent/scan/adapters/connectors/sonarqube.py
guidant_agent/scan/adapters/connectors/sonarqube_guard.py(新檔)
guidant_agent/scan/adapters/connectors/__init__.py
test/test_sonarqube_connector.py
test/test_file_and_function_size.py(只准把 sonarqube.py 那個數字改小)
guidant_agent/scan/adapters/connectors/sonarqube_guard.py,四支純函式都放這裡,sonarqube.py 只留一行呼叫:resolve_base_url(raw_base: str) -> str:邏輯照抄 zap.py:152-161(沒 scheme 補 https://;只收 http/https;http 且非本機迴路就 raise ValueError,訊息仿 ZAP 那句,把「API Key」換成「token」)。sonarqube.py:336 那行換成呼叫它。child_env(extra: dict) -> dict:只帶白名單環境變數——PATH、HOME、LANG、LC_ALL、TMPDIR、JAVA_HOME、SONAR_SCANNER_OPTS、HTTP_PROXY/HTTPS_PROXY/NO_PROXY(大小寫兩種都收),再疊上 extra。:840 與 :1062 兩行改成呼叫它(各自傳 {"GIT_TERMINAL_PROMPT": "0"}、{"SONAR_TOKEN": token})。🔴 開工先 grep sonarqube.py 與測試裡還有沒有讀其他環境變數(例如 SONAR_USER_HOME、代理設定),有就加進白名單,寧可多帶工具需要的,不要帶 MINIO_*、SEAWEEDFS_*、REGISTRATION_TOKEN、AGENT_*。clone_command(git_bin, repo_url, dest) -> list:回 [git_bin, "-c", "credential.helper=", "-c", "http.followRedirects=false", "-c", "protocol.allow=never", "-c", "protocol.http.allow=always", "-c", "protocol.https.allow=always", "clone", "--depth", "1", "--quiet", "--", repo_url, dest]。:836-839 換成呼叫它。strip_project_settings(source_dir: str) -> int:os.walk(source_dir, followlinks=False) 找出所有檔名是 sonar-project.properties 的檔刪掉,回刪了幾個。在 _run_scanner 組命令列之前呼叫一次,刪到就記一行 logger.info(只記數量)。__init__.py:仿 :100-107 的 _check_zap 加一支 _check_sonarqube,在 :111 _SETTINGS_CHECKERS 登記 "sonarqube",讓「位址非本機要 https」在存檔時就擋,不用等第一次掃描。SonarQubeConnector 加一支 check_settings classmethod(內容一行:呼叫 sonarqube_guard.resolve_base_url)。🔴 這會讓 sonarqube.py 多幾行——用上面搬出去省下的行數抵掉;改完 wc -l 必須 ≤ 1711,並把 test_file_and_function_size.py:19 的數字改成新的行數(只准改小)。scan/adapters/connectors/ 底下,資安規則 .claude/rules/security/external-calls-and-tools.md 的 paths 已有 guidant_agent/*/adapters/**/*.py,自動命中。🔴 這張卡不要改任何 .claude/rules/ 底下的檔:同一批的 F3 會改同一支規則檔,兩邊都動合回會衝突。規則文字要補的那句,回寫時列為待辦,由首腦收尾時補。