本卡屬 FR-114 資安修正(母卡 CM-2019),第 4 批「憑證殘留」卡 4-1,修 SUMMARY #10/#11/#72/#73/#122(出自 M15-external-platform/M16/M17)。中。修法規格來自內化卡 CM-1607(#11、#122)與 CM-1608(#72),#10 與 #73 查無獨立內化工單,計畫在 docs/features/FR-114-2609-security-fix-dispatch/batches/plan-b4.md「卡 4-1」段。
問題是什麼(白話)
五件事,都是「憑證的文字內容不小心被存進了版本控制系統(git)裡」,本卡只做清除殘留字串(不涉及金鑰本身是否要重新發放,那是決策者要裁的事,見下方紅字提醒):
- #10:外部程式碼平台(GitLab)的存取通行證,材料檔說有四把,實查只找到一把真值、一把是假的範例值,另外兩把哪裡都查不到
- #11:一個測試用設定檔曾經推上版控,裡面是四把真的對外 AI 服務金鑰(Anthropic/OpenAI/Google/LangSmith),原始檔案已在 2026-09-08 commit 5746cef1 刪除,本卡只清「對話紀錄存檔」裡的殘留字串
- #72:系統管理員帳號密碼寫死在腳本裡,其中有腳本設計成「環境變數沒設定就用這個密碼」,本卡除了清殘留字串,還要真的修這個「靜默使用真密碼」的程式問題
- #73:公司內部套件倉庫與檔案儲存服務的帳密寫在一份交接文件裡
- #122:簽發登入憑證的金鑰、資料庫密碼、檔案儲存服務金鑰寫在同一個測試設定檔裡(其中資料庫密碼與檔案儲存部分分別在卡 4-2 與本卡 #73 已涵蓋,這裡只處理 JWT 簽名金鑰那部分;決策者已裁定這把 DEV 機器上的金鑰不需要重新發放,因為安裝程式每套安裝都會各自隨機產生新的,這把外洩不會擴散到出貨版本)
🔴 本節所有清單都是用 key 名稱或憑證前 4 碼去 grep 查出來的路徑與檔數,計畫檔內、本卡任何地方都不寫任何憑證真實內容值。runner 動手時自己從 .env 取值,絕對不可把值寫進 commit message 或 Notion 回寫。
首腦核對:
- #10 實查:git grep 抓 GitLab PAT 格式(glpat-…)→ 8 個檔(不是材料檔說的 26),全部在 docs/conversation-history/ 下;其中一把前 4 碼是假的範例值(6 個檔命中,不用清),另一把前 4 碼是真值(2 個檔命中,要清);GitHub 格式(ghp_/github_pat_/gho_/glrt-/gldt-)在 BE 與套件 monorepo 都是 0 命中
- #11 實查:git grep 抓四種金鑰格式(Anthropic sk-ant-…/OpenAI sk-proj-…/Google AIza…)→ 9 個檔,全部在 docs/conversation-history/ 下;LangSmith 格式(lsv2_pt_… 或 lsv2_sk_…)尚未查過,runner 動手前要自己補跑一次
- ⚠️ Google 那把金鑰的值與現行 .env 的 GOOGLE_API_KEY 完全一致——這代表它可能根本沒有被真的撤銷換發(見下方「怎麼修」最前面的回報提醒);Anthropic/OpenAI 兩把的值與現行 .env 不同,判斷是真的已換發
- #72 實查:材料檔說 3 支腳本,實查是 5 支:scripts/migrate_2026-08-09_fr062_existing_tenant_licenses.py:80(環境變數沒設就 fallback 用真密碼,:127 用它打真實 /login)、scripts/seed_2026-08-01_fr059_detection_profiles.py:65(同樣模式,:150 也打 /login)、scripts/e2e_test_module_frame_with_docx.py:34、scripts/smoke_test_ssp_docx_parser.py:34、scripts/smoke_test_ssp_docx_preselect.py:34(這三支是純寫死,沒有環境變數這層)
- #72 字串殘留實查:同一組密碼共 151 個檔案命中——docs/conversation-history/(63)、docs/features/(38)、docs/features-site/(39,🔴 這是 build 產出,不能手改,要改回原始 md 再重 build)、docs/claude/(4)、docs/system-design/(2)、scripts/(5,就是上面那 5 支腳本本身)
- ⚠️ 這組密碼的值與現行 DEV 登入帳密(docs/claude/memory/reference_dev_login.md 記錄的 blsadmin/blsit 帳號)完全一致——這代表「已換發」的認定可能不成立(見下方「怎麼修」最前面的回報提醒)
- #73 實查:docs/features/FR-039-2606-distributed-file-agent/handoff/2026-06-18-FR039-handoff.md:89(Nexus 私有套件倉庫帳密,admin/密碼 這種形式)與同檔 :59(一段 SQL 指令內嵌 MinIO 存取金鑰/密鑰/端點 192.168.50.171:9002);只抓密碼本身(不含 admin/ 前綴)→ 6 個檔命中(來源 md、它的 html 鏡像、features-site 的 md 鏡像、features-site 的 html、docs/features-site/site/search/search_index.json——🔴 這是 MkDocs 搜尋索引,最容易漏掉,也含有憑證原文——以及一個 conversation-history 檔);只抓密碼子字串(不含 admin/ 前綴,範圍更寬)→ 13 個檔命中(多出 7 個純密碼命中,要一併檢視)
- #122 實查:JWT 簽名金鑰(.env 的 JWT_SECRET_KEY,36 字元)→ 30 個檔,全部在 docs/conversation-history/ 下;同一批還查到 TURNSTILE_SECRET_KEY(.env 值)→ 30 個檔(28 在 conversation-history、1 在 config/、1 在 repo 根目錄)——⚠️ 這把是 Cloudflare 的公開測試用假金鑰(永遠會驗證通過的那種),不是真憑證,不要清,但要在卡上記一句「查到但判定非憑證」避免以後又被誤判要清;另外 ANTHROPIC_API_KEY/DETECTION_TOOL_ENCRYPTION_KEY/AI_PROVIDER_ENCRYPTION_KEY 三個查證後零殘留,不用處理
工作區
- BE 側:在 /Users/chouraymond/Projects/Billows/Audit-Manager/compliance-manager-be/.claude/worktrees/wt-fix-security(branch fix/security-b1)改,不要碰主 checkout(掃描線正在讀它)。跑服務用 PORT=8001。