本卡屬 FR-114 資安修正(母卡 CM-2019),第 5 批「檢測/問卷/流程/證據分類的行為修正」子集 B 卡 5B-1,修 SUMMARY #15(M03-1)/#76/#77/#78(出自 M03-2/M03-3/M03-4)。#15 高,其餘中。修法規格來自內化卡 CM-1993,計畫在 docs/features/FR-114-2609-security-fix-dispatch/batches/plan-b5B.md「卡 5-B1」段。
#15:租戶管理員在弱點檢測「掃描基準」頁上傳一包規則壓縮檔(或改填一個網址),伺服器重新打包時把裡面的 inspec.yml 原封交給外部工具 cinc-auditor,而那支工具讀這個檔時會先把內容當 Ruby 樣板(ERB)跑一遍再解析——等於一個客戶的管理員就能在我們後端主機上執行任意程式碼,拿到的是後端行程的全部權限(含簽發登入憑證的密鑰、繞過客戶隔離的資料庫身分、檔案儲存與代理程式憑證、內網跳板)。
#76:同一個「掃描基準」頁,改填一個網址、伺服器下載回來直接解開。防壓縮炸彈的三道上限(檔案數/大小/膨脹倍數)只接在「上傳檔案」那條路上,網址這條一道都不會碰到——45MB 檔案解成數十 GB 完全做得到,記憶體吃爆、所有客戶一起停擺。
#77:上傳規則包的「最多一萬個檔」上限對 .zip 形同虛設——Python 的 zip 套件在「打開檔案」那一瞬間就把整份目錄清單展開進記憶體,程式要等它讀完才開始數。實測一個 49.7MB、裝 59.5 萬個空檔的 zip,光打開就多吃 360MB,連送幾次就把伺服器打停,日誌看起來一切正常。
#78:租戶管理員登記一支 http://(沒加密)的基準網址,畫面不會警告;代理程式住在客戶內網、拿到網址後直接下載並執行裡面的規則——路徑上任何能動手腳的人換掉那包檔案,就等於在代理程式裡跑自己的程式碼,掃描報告也跟著造假;而且網址型來源完全不記指紋,無法對帳。
首腦核對:
/Users/chouraymond/Projects/Jedicogy/module/jedi-python-package/.claude/worktrees/jedi-wt-fix-security/<套件目錄>(branch fix/security-b1)改,只動自己那支套件的子目錄、只 git add 該子目錄下的檔。 本卡對應 jedi-detection,worktree 建議 wt-fix-b5-detection-archive。jedi-detection/jedi_detection/common/profile_extractor/inspec.py:330-358 _repack_flat(),#15/#76 上傳與網址兩條路共用,唯一要改的落點
jedi-detection/jedi_detection/common/profile_extractor/inspec.py:361 _read_entries(),全 monorepo 只此一支,兩種來源共用底層
jedi-detection/jedi_detection/common/profile_extractor/inspec.py:367-370 magic number 分流
jedi-detection/jedi_detection/common/profile_extractor/inspec.py:373 _read_zip_entries()
jedi-detection/jedi_detection/common/profile_extractor/inspec.py:389 _read_tar_entries()
jedi-detection/jedi_detection/app/service/detection_profile_service.py:952 _resolve_source 網址分支,只驗字串開頭
jedi-detection/jedi_detection/app/service/detection_profile_service.py:954 網址型直接回 sha256=None,#78 要補指紋
jedi-detection/jedi_detection/app/service/detection_profile_service.py:115 上傳路徑呼叫 ProfileArchiveValidator 的位置(對照組)
jedi-detection/jedi_detection/common/detection_profile_archive.py:118 validate()
jedi-detection/jedi_detection/common/detection_profile_archive.py:144 _scan_entries(),entry 數與膨脹倍數上限
jedi-detection/jedi_detection/common/detection_profile_archive.py:145-160 _read_all_with_limit(),大小上限;:145-148 是 #77 寫反的註解
jedi-detection/jedi_detection/common/detection_profile_archive.py:229 _iter_entries() 分流
jedi-detection/jedi_detection/common/detection_profile_archive.py:248 _iter_zip_entries
jedi-detection/jedi_detection/common/detection_profile_archive.py:249 #77 核心落點
jedi-detection/jedi_detection/common/detection_profile_archive.py:258 _iter_tar_entries
jedi-detection/jedi_detection/app/service/detection_profile_extraction_service.py:476 _download(),網址端大小上限要邊收邊算的落點
jedi-detection/jedi_detection/api/routes/detection_profile_route.py:184,359,410,542 四支上傳端點,共用同一支驗證器,改一處四支都好
jedi-detection/jedi_detection/common/detection_profile_ref.py:43 build_payload(),網址型只帶 {uid, source_type, url},要加指紋
jedi-detection/jedi_detection/common/safe_http_fetch.py:68 既有的 https-only 白名單 ALLOWED_SCHEMES,要對齊、不要自己寫一份
evidence-agent/core/profile_cache.py:137-144 代理端:網址型不經快取不對帳(本卡不做,D-b5B-1 裁定另開小卡)
evidence-agent/core/task_executor_connectors/inspec.py:915 代理端執行點(同上,本卡不動)
_repack_flat()(inspec.py:330-358)重新打包之前,先讀一次規則包裡的 inspec.yml,看到 ERB 樣板標記(<%)就拒收。⚠️ 開工前先確認 <% 不是合法規則包會用到的字元——若某些正常規則包真的用 ERB,改成拒收會打壞正常客戶,這時停下回寫問決策者,不要自己改成「關掉 ERB 渲染」。長期建議「把 cinc-auditor 關進沙箱跑」本卡不做,只在 commit message 註記。打折處要當場講:這道檢查只擋 inspec.yml 一個檔,若該工具對其他檔案也做樣板渲染,查到就寫進回報,不自己擴大範圍。detection_profile_archive.py 的 ProfileArchiveValidator 搬進 inspec.py:361 的 _read_entries()(兩種來源共用的底層),不要在服務層網址分支再補一個呼叫 ProfileArchiveValidator——那是補第二條路,第三種來源出現時又會漏。網址端的大小上限另外要處理:下載時邊收邊算,落點在 detection_profile_extraction_service.py:476 的 _download(),不是下載完才量。detection_profile_archive.py:145-160/:249)。把 :145-148 寫反的說明文字改掉——原註解說「不先 materialize 成 list」對 .tar 成立、對 .zip 不成立。新註解依 CLAUDE.md 規範只留「為什麼」與「陷阱」一到兩行。