I1 檢查結果:跟 GitHub/GitLab 連線的部分

I1 檢查結果:跟 GitHub/GitLab 連線的部分

檢查日期 2026-09-09|耗時 47 分鐘|對應卡片 CM-1613


§1

🔴 一句話結論

系統連 GitHub 的時候,沒有檢查對方是不是真的 GitHub——公司的 GitHub 密碼(存取權杖)可能被路上的人攔走。


§2

這一棒在檢查什麼

使用者在系統裡送「意見回饋」時,系統除了存在自己的資料庫,還可以同時去 GitHub 或 GitLab 開一張真的問題單。要去別人的網站開單,就得帶著公司的帳號密碼(叫做「存取權杖」)。

這一棒檢查的就是這段連線過程安不安全——25 個檔案。


§3

找到什麼

# 嚴重度 這是什麼問題 出事會怎樣 要先有什麼才打得到 在哪裡 修正卡
1 🟡 中 連 GitHub 時,系統不檢查對方是不是真的 GitHub。等於有人拿一張假名片來,我們也照樣把密碼給他 網路上的中間人(例如在公用 Wi-Fi、或公司網路設備被入侵)可以假冒 GitHub,把公司的 GitHub 存取權杖整個拿走。之後他能用那把權杖讀寫公司所有的程式碼 ① 管理員已在後台開啟 GitHub 整合並填入權杖(出貨預設是關的)
② 攻擊者要能站在伺服器對外連線的路徑上
github_issue_adapter.py 第 43 行 ✅ 已修(CM-2066;原卡 CM-1632 作廢)
2 🟡 中 同一個問題的另一處。這支是「共用的連線工廠」,成員查詢跟附件功能都會用到它 同上。只修一處沒用,權杖還是會從另一條路漏出去 同上 infra/github.py 第 22 行 ✅ 已修(CM-2066;原卡 CM-1632 作廢)
3 🟡 中
⚠️ 不在這次檢查範圍
很久以前有人把一個開發用的設定檔(裡面寫著 GitHub 和 GitLab 的密碼)提交進版控 拿得到程式碼倉庫的人,可以從舊紀錄裡把密碼挖出來。不過這兩把密碼在 CM-1573 那次已經作廢了 讀得到程式碼倉庫,或拿得到 Nexus 上兩個舊版本的套件檔 jedi_issue/.env(已從最新版刪掉,但舊紀錄還在) ✅ 已修(CM-2048;原卡 CM-1607 作廢)

§4

詳細說明

問題 1、2:連 GitHub 沒有檢查對方身分

白話:你上網買東西時,瀏覽器會檢查「這個網站是不是真的 PChome」,靠的是一張數位證書。這個檢查如果關掉,任何人都能假冒 PChome,而你會把信用卡號送給他。

我們的系統連 GitHub 時,這個檢查被關掉了:

# jedi-issue/jedi_issue/infra/github.py 第 22 行
return Github(auth=auth, per_page=100, timeout=10, verify=False)
#                                                  ↑ 這個 False 就是「不檢查」

# jedi-issue/jedi_issue/infra/issue/adapter/github/github_issue_adapter.py 第 43 行
self.gh = Github(auth=auth, per_page=100, timeout=10, verify=False)
#                                                     ↑ 同一個問題

兩處要一起修:一支是共用的連線工廠(成員查詢、附件功能都走它),一支是問題單功能自己建的。只修一邊,權杖還是會從另一邊漏出去。

檢查員已經驗證過這不是誤報:他們追了 PyGithub 這個套件的內部程式碼六層,確認 verify=False 一路傳到最底層真的生效,而且連環境變數都救不回來。

為什麼是「中」不是「高」:

  • 出貨時這個功能預設是關的(04-seed-core.sql 第 610 行寫著 enable: false、權杖是空字串,首腦已親自核對)
  • 要打到,得先有管理員主動開啟並填入權杖
  • 攻擊者還得剛好站在伺服器對外連線的路上

但要記得一件事:CM-1573 就是「這個模組的 GitHub 密碼外洩處置」——這個模組的權杖外洩過一次了。

怎麼修:拿掉 verify=False 就好。如果真的有「自己架的 GitHub 企業版 + 內部憑證」的需求(要先查證有沒有,不要假設),改成從設定檔讀憑證路徑,而不是關掉檢查。

首腦另外查了一件報告沒查的:GitLab 那一側沒有同樣的問題(gitlab.py 和兩支 GitLab 相關檔案都找不到 verify=False)。所以這是 GitHub 專屬的問題。

這是本專案第四次犯同樣的錯:CM-1560(LDAP)、CM-1565(Redis)、CM-1606(寄信),加上這次。修法可以直接抄前三張卡。

問題 3:舊的設定檔留在版控歷史裡

白話:有人把寫著密碼的設定檔提交進版控,後來刪掉了——但版控會記住所有歷史,刪掉只是「最新版看不到」,翻舊紀錄還是找得到。

首腦查證的結果:那個檔存在於三個舊提交(b740989、74d9626、5e3b9a3),由 d6a8d09 刪除。歷史還在。

好消息:這兩把密碼在 CM-1573 那次已經作廢了(決策者當時裁定「token 早已註銷」)。

CM-1573 沒收乾淨的兩件事:

  1. Nexus 上 0.0.14/0.0.15 兩個套件檔是否已下架——那兩個版本把這個設定檔打包進去發布了。程式碼查不到,要連 Nexus 才能確認。
  2. git 歷史還在——依決策者 2026-09-08 的裁定不重寫歷史(重寫會改動所有提交編號、影響每個下載過的人,而對已散出去的內容毫無補救效果)。

§5

這份結果可信到什麼程度

要分兩件事看。

「這三條是真的嗎」→ ✅ 可信

三條發現每一條都經過三個獨立檢查員投票,三票全過。首腦另外自己打開檔案核對了行號,也自己查了 GitLab 那側沒有同樣問題。

關於報告上那個 unverified 標記:那不是「檢查失敗」的意思。它的原因欄寫著 findings-refused——意思是「第 3 條指的那個檔案已經從最新版刪掉了,工具找不到檔案所以拒絕把它寫進正式報告」。投票本身是完整的:9 票全數投出、沒有漏投、沒有半途中斷。

(對照組:FR-077 R1 那次是真的失敗——105 個檢查員全部掛掉、21 張票 0 張投出。兩者完全不同。)

「是不是只有這三條」→ ❌ 不可信

這次用的是快篩模式,不是徹底審查。25 個檔案裡有 9 個是幾乎空白的檔案,實際有內容的大約 16 個。工具也沒有留下「每個檔案都讀到結論」的紀錄。

所以正確的說法是:「這三條問題確實存在」,而不是「這 25 個檔只有這三個問題」。


§6

執行概況(技術細節,工程師看的)

項目 數字
檢查範圍 25 個檔案
派出/回報的研究員 2 / 2
提出的候選問題 → 去除重複後 5 → 3
投票數 9(3 條 × 3 個檢查員)
沒投到票的 0
投票中斷的 0
被檢查員降低嚴重度的 0
耗時 47 分鐘