FR-114 資安修正派工
這是第 5 批「設定與信任鏈」的子集 B——20 件、5 張卡,分屬弱點檢測、問卷、稽核流程、證據自動分類四個互不相依的模組。兩條高風險(規則包當程式碼跑、流程圖打掛服務)排在最前面;M07 舊線那五件經查證不開修正卡,理由見卡 5-B5。
這四個模組的共同病是「相信外面送進來的東西」——客戶上傳的規則包被當程式碼跑、畫面送上來的名字被當成身分、送什麼欄位就收什麼、沒檢查過的流程圖存得進資料庫。現在就能開,四個模組互不相依、四張卡可完全平行(動的檔案零重疊)。不依賴其他批;唯一的外部關係是 M07 那五件與第 3 批「M07 舊線整組退役」交疊,本子集不開修正卡、只留一張驗證卡等第 3 批刪完再確認。
四個模組 ↔︎ 四個 repo 落點(都已實查過檔案存在,不是按模組名猜):
| 模組 | 套件目錄 | 這批動到的另一個 repo |
|---|---|---|
| M03 弱點檢測 | jedi-detection/ |
無(全在套件內) |
| M05 問卷 | jedi-survey/ |
無 |
| M06 稽核流程 | jedi-flow-engine/ |
BE 主專案(app/flow_engine/、api/flow_engine/——第 88 件的寫入端與第 128 件的署名都在 BE 側) |
| M07 證據分類 | jedi-evidence-classification/ |
無(本子集只剩驗證卡) |
| 卡 | 卡名(做什麼) | repo/套件 | 涵蓋 SUMMARY # | 建議 model/effort | 序列組 |
|---|---|---|---|---|---|
| 5-B1 | 規則包解析前先看內容,並把三道防炸彈上限搬到所有來源都會經過的那一層 | 套件 jedi-detection |
#15(🟠 高)/#76/#77/#78 | opus/high | A |
| 5-B2 | 弱點檢測三處資源上限:測試連線收斂、重新解析去重、換工具時用生效值重查 | 套件 jedi-detection |
#79/#80/#81 | opus/high | A |
| 5-B3 | 流程圖走訪記路+補上限,並讓範本的新增修改與起輪次一定會經過檢查 | 套件 jedi-flow-engine + BE |
#16(🟠 高)/#87/#88/#128 | opus/high | B |
| 5-B4 | 問卷資料夾與即時填答:身分改用登入身分、送進來的欄位逐一收掉 | 套件 jedi-survey |
#85/#86/#126/#127 | opus/high | C |
| 5-B5 | 確認 M07 這五件是否已隨舊線退役消失(驗證卡,不是修正卡) | 套件 jedi-evidence-classification |
#89/#129/#130/#131/#132 | opus/medium | D(第 3 批之後) |
序列組說明:A 兩張都在 jedi-detection 內,但動的檔案零重疊(5-B1 在 common/ 的解析與打包層,5-B2 在 app/service/ 的三支服務),實測可平行;標同一字母只是提醒同一套件、commit 時各自顯式 git add。B 跨兩個 repo。C、D 各自獨立。
~/Projects/Jedicogy/module/jedi-python-package,只動 jedi-detection/ 子目錄。worktree 建議 wt-fix-b5-detection-archive。涵蓋 #15/#76/#77/#78。#15(🟠 高)客戶上傳的規則包會被外部工具當成程式碼直接執行
inspec.yml 原封交給外部工具 cinc-auditor,而那支工具讀這個檔時會先把內容當 Ruby 樣板(ERB)跑一遍再解析。等於一個客戶的管理員就在我們後端主機上執行任意程式碼——拿到的是後端行程的全部權限(含用來簽發登入憑證的那把密鑰、繞過客戶隔離的資料庫身分、檔案儲存與代理程式憑證、內網跳板)。inspec.yml,看到 ERB 樣板標記(<%)就拒收。🔴 要放進 _repack_flat() 本身——只補服務層的上傳分支會漏掉網址分支(決策者裁定語即此意:「要放在打包那一步本身,只補上傳那條路會漏掉網址那條」)。長期建議另提:把 cinc-auditor 關進沙箱跑(本卡不做,只在 commit message 註記)。jedi-detection/jedi_detection/common/profile_extractor/inspec.py:330-358(_repack_flat(),上傳與網址兩條路共用它——這是唯一要改的落點)inspec.yml)必須照樣建得起來。⚠️ 要確認 <% 不是合法規則包會用到的字元——若某些正常規則包真的用 ERB,那就只能改成「把 ERB 渲染關掉」而不是拒收,這時停下回寫問決策者。手測:上傳一份既有的正常規則包 → 建基準 → 看抽取成功、規則清單數量與修改前一致。inspec.yml 這一個檔。若那支外部工具對其他檔案也做樣板渲染,這道檢查就補不完——runner 要順手查一次該工具還對哪些檔做 ERB,查到就寫進回報(不自己擴大範圍)。#76 網址型規則來源完全繞過壓縮檔的檢查
_read_entries(),不要在服務層再補一個呼叫點——這樣現有兩種來源與未來的第三種來源天然都涵蓋(模組頁裁定語即此意)。jedi-detection/jedi_detection/app/service/detection_profile_service.py:952(_resolve_source 的網址分支,目前只驗字串開頭是 http:///https://)_read_entries() 不在 detection_profile_archive.py,它在另一支檔案——材料檔把兩支搞混了,開卡時要寫對,不然 runner 會在錯的檔案裡找不到目標。
_read_entries() 的真正位置:jedi-detection/jedi_detection/common/profile_extractor/inspec.py:361(全 monorepo 只有這一支,grep -rn "_read_entries" 三個命中都指向它)。它 :367-370 靠 magic number 分流成 _read_zip_entries()(:373)/_read_tar_entries(:389),兩支都是先把整份 entry 讀進記憶體再回 list,一道上限都沒有。:331,在 _repack_flat()(:325)裡的第一行——這正是 #15 要改的同一支函式,兩件事落在同一個位置,順序上先做 #15 的 ERB 檢查、再把上限搬進 _read_entries()(或反之皆可,但同一個 runner 同一次改完)。jedi-detection/jedi_detection/common/detection_profile_archive.py 的 ProfileArchiveValidator——validate()(:118)→ _read_all_with_limit()(:188,大小上限,docstring 明寫「邊讀邊累計,超限即斷」)→ _scan_entries()(:144,entry 數與膨脹倍數)→ _iter_entries()(:229)分流 _iter_zip_entries(:248)/_iter_tar_entries(:258)。這支 validator 只被上傳路徑呼叫(detection_profile_service.py:115),網址路徑走 _resolve_source(:952)→ 直接下載 → inspec._read_entries(),完全不經過它——這就是 #76 說的「三道上限只接在上傳那條路上」。inspec.py:361 的 _read_entries()(兩種來源共用的底層),不是在 detection_profile_service.py 的網址分支再補一個呼叫 ProfileArchiveValidator。後者是「補第二條路」,第三種來源出現時又會漏。_read_all_with_limit() 那道大小上限只在上傳端;網址端的大小上限要一起處理(下載時就要邊收邊算,不是下載完才量),落點在 detection_profile_extraction_service.py:476 的 _download()。這一句要寫進卡片,否則搬完 entry 數上限、大小上限仍只擋一條路。jedi-detection/jedi_detection/api/routes/detection_profile_route.py:184/359/410/542(共用同一支驗證器,改一處四支都好,本卡不必逐支改)#77 上傳規則包的「最多一萬個檔」上限對 .zip 形同虛設
.tar 是逐筆讀、數到上限立刻擋;但 Python 的 zip 套件在「打開檔案」那一瞬間就把整份目錄清單展開進記憶體,程式要等它讀完才開始數。實測一個 49.7MB、裝 59.5 萬個空檔的 zip,光打開就多吃 360MB,連送幾次就把伺服器打停。最難察覺的是日誌看起來一切正常(照樣回「壓縮檔不安全」),但代價已經付掉了。.tar 成立、對 .zip 不成立,這段寫反的註解正是這個洞通過歷次 review 的原因。⚠️ 依 CLAUDE.md 新註解規範,改後的註解只留「為什麼」與「陷阱」一到兩行,不寫施工日誌。jedi-detection/jedi_detection/common/detection_profile_archive.py:145-160(寫反的註解在 :145-148)jedi-detection/jedi_detection/common/detection_profile_archive.py:249jedi-detection/jedi_detection/app/service/detection_profile_service.py:115ps 看常駐記憶體對照修前修後)。#78 填網址建掃描基準時放行不加密的連線,而且網址型來源完全不記指紋
http://(沒加密)的基準網址,畫面不會警告。代理程式住在客戶內網、手上有主機帳密,它拿到網址後直接下載並執行裡面的規則——路徑上任何能動手腳的人換掉那包檔案,就等於在代理程式裡跑自己的程式碼,掃描報告也跟著造假。沒有加密要破解,因為根本沒有加密;而我們後端自己的下載器只准加密連線,同一個系統兩套標準。_resolve_source 只收 https://,與後端既有下載器的白名單對齊(common/safe_http_fetch.py 的 ALLOWED_SCHEMES——先查再寫,用既有常數不要另立一份);② 網址型第一次抽取成功時記下 sha256 並放進派給代理程式的資料裡讓它對帳(上傳那條路本來就這樣做,網址這條只是沒補上)。jedi-detection/jedi_detection/app/service/detection_profile_service.py:952(_resolve_source 收 scheme)/:954(網址型直接回 sha256=None)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 白名單,照它對齊、不要自己寫一份)~/Projects/Billows/Audit-Manager/evidence-agent)core/profile_cache.py:137-144(網址型不經快取不對帳,要補對帳);執行點 core/task_executor_connectors/inspec.py:915http:// 基準。往安全預設值倒的做法是:新登記一律只收 https;既有已登記的 http 基準不強制失效,但抽取時記一筆明顯的警告日誌,不要讓客戶的既有基準突然全部建不起來。這張卡的手測總清單:
inspec.yml 含 <% 的規則包 → 被拒、訊息明確http:// 網址 → 被拒;https:// → 成功且有 sha256需決策者先裁的:
jedi-detection/jedi_detection/app/service/ 下三支服務。worktree 建議 wt-fix-b5-detection-limits。涵蓋 #79/#80/#81。#79「測試連線」要連到哪台主機是呼叫者自己指定的
jedi-detection/jedi_detection/app/service/detection_tool_service.py:224#80 手動重新解析無條件開一條背景工作,可以把主機打掛
running 狀態且有在寫入,目前的重試邏輯只是沒去看它。jedi-detection/jedi_detection/app/service/detection_profile_extraction_service.py:97(STATUS_RUNNING = "running" 常數——既有狀態,用它不要另立)jedi-detection/jedi_detection/app/service/detection_profile_extraction_service.py:463/:472(既有的 running 狀態寫入點)retry(),jedi-detection/jedi_detection/app/service/detection_profile_extraction_service.py:178。
:202-203:self._write_status(version, STATUS_PENDING, error=None) 緊接 self.schedule(version_uid)。這兩行就是要改的地方——新加的「同一版已在跑就拒絕」判斷必須放在 :202 之前,否則壓回 pending 之後再看狀態,永遠看到 pending、擋不到任何東西(計畫上面那段紅字講的正是這裡)。:180-182):「先把狀態壓回 pending 再排——否則前端輪詢會看到還停在舊的 failed,誤以為『按了沒反應』」。這句話就是改動的風險說明,卡片要原樣帶上:拒絕時若不給前端一個可辨識的狀態或錯誤,就會退回當初要解決的那個觀感問題。:198-199):if version.scope == "SYSTEM" and not viewer_is_platform_admin(): raise ForbiddenError(...)。docstring :184-195 解釋得很清楚——公版只有平台管理員能重抽,不擋的話 RLS 會讓 worker 背景 UPDATE 0 rows、拋 StaleDataError,使用者按了收 200 說 pending、那一版永遠停在 pending,錯誤只留在後端 log。新加的並行上限判斷要放在這道守門之後(先判「你能不能重抽」再判「現在能不能再排一條」),回應也要照這支既有的 raise 形狀,不要靜默 return。STATUS_RUNNING = "running" 在 :97;寫入在 :463 與 :472,都在 _prepare()(:433)內。判斷「是不是已在跑」直接讀這個狀態,不要另立新欄位或新常數。_mark_pending_for_outdated_source()(:357)也會把狀態寫成 pending。它是來源檔變更時的自動重抽路徑,若新加的並行上限只擋 retry(),這條路仍可繞過。runner 要查一次它會不會也排背景工作,查到會就一併擋、查到不會就在 commit message 寫「已確認此路徑不排工作」(不要默默跳過)。#81 換掃描工具時,「要掃哪些機器」不會拿實際生效的值重新檢查台數上限
jedi-detection/jedi_detection/app/service/detection_job_binding_handler.py:201-204(第一個入口)jedi-detection/jedi_detection/app/service/detection_job_binding_handler.py:331-333(第二個入口——換工具時連「要派給哪些機器」的清單都不送,同一套「沒送=不要動舊值」一樣放行)jedi-detection/jedi_detection/app/service/detection_orchestration_service.py:555(expand_spec() 的呼叫,在 _expand_scan_target_fields() 內,該方法起於 :519)。 ⚠️ 已補查(2026-09-21)::536 不是第二個程式落點,那一行是 docstring 內的文字,材料檔記錯了。
:533-536 是 _expand_scan_target_fields() docstring 裡的一段說明:「超上限在此不擋:這裡是派工路徑,擋在這裡使用者會在『按下執行』才看到錯誤,而設定是幾天前存的。守門在寫入端(job_service._validate_scan_target_specs),這裡只做展開。真的走到這裡還超量(存量資料、或上限被調小)仍照展開——寧可跑一張大工單,也不要讓一個已經存在的任務突然變成執行不了。」expand_spec 全套件只有這一個呼叫點(grep -rn "expand_spec" jedi_detection/ 命中四處::555 呼叫,其餘三處在 common/scan_target_spec.py:16/:177/:224 是定義與註解)。所以「展開邏輯」是單一落點,不存在兩入口對稱問題——兩入口對稱的是上面 detection_job_binding_handler.py:201-204 與 :331-333 那兩處,那兩處仍要逐一核對。jedi-common/jedi_common/.../base_repository_impl.py:424(只讀理解,不要改這支——它是全套件共用語意,改它會炸別的模組)feedback_parallel_runners_two_entrypoints_one_fixed)::201-204 與 :331-333 是同一個病的兩個入口,驗收要逐一核對兩處都補了,只補一處等於沒修。這張卡的手測總清單:
:201-204 與 :331-333 兩個入口各測一次,都被擋需決策者先裁的:
~/Projects/Jedicogy/module/jedi-python-package/jedi-flow-engine/ + BE 主專案 ~/Projects/Billows/Audit-Manager/compliance-manager-be(app/flow_engine/、api/flow_engine/)。worktree 建議兩邊各開 wt-fix-b5-flow-bpmn。涵蓋 #16/#87/#88/#128。#16(🟠 高)一張惡意設計的流程圖可以讓伺服器永遠算不完
save_bpmn_to_file/export_xml/import_xml 三支吃檔案路徑的方法全 repo 零呼叫(套件 monorepo 與 BE repo 各 grep 一次都是零),是裝好的陷阱(日後有人把使用者字串接上去就是任意檔案讀寫)。依原則十五刪除前零引用已查證(引用來源:FR-088 P1 第 5 節第 3 項)。jedi-flow-engine/jedi_flow_engine/common/utils/bpmn_uilts.py:361(_get_jobs_from_exclusive_gateway)與 :276(get_next_jobs)——兩支互相呼叫成環,這是治本要改的地方jedi-flow-engine/jedi_flow_engine/common/utils/bpmn_uilts.py:294/:295(同一份清單算了兩次)+ :340(_get_exclusive_gateway_default_job 又算一遍)——改成算一次傳進去,「每過一個分岔點工作量翻倍」直接消掉jedi-flow-engine/jedi_flow_engine/common/utils/bpmn_uilts.py:963(save_bpmn_to_file)/:1220(export_xml)/:1241(import_xml)#87 讀取流程範本時無條件解析內容、沒有防呆
jedi-flow-engine/jedi_flow_engine/infra/mapper/workflow_template_mapper.py:21(讀取端無條件解析;實際解析動作在 bpmn_uilts.py:34)if entity.xml: 有兩處、不是一處,jedi-flow-engine/jedi_flow_engine/infra/repository/workflow_template_repo_impl.py:52(add() 內)與 :61(update() 內)。兩處要一起改,只改一處等於沒修。 兩段程式碼完全相同(add() 起於 :49、update() 起於 :58):
if entity.xml:
bpmn_util = BpmnUtils(entity.xml)
entity.json = bpmn_util.bpmn_object
if entity.xml: 是 truthy 判斷,空字串 "" 為 falsy → 整段跳過 → entity.json 不被設定 → 那筆資料存進去時 json 是空的,於此後每次讀取都在 mapper 端(workflow_template_mapper.py:21)炸掉。這正是 #87 說的「寫入端把『空字串』當成『沒有填』、反而跳過了驗證」。None 這一支的語意是套件共用的「沒送=不要覆蓋」(同一份語意在 jedi-common/.../base_repository_impl.py:424,唯讀理解、不要改那支),所以不能簡單改成 if entity.xml is not None: ——那會讓 add() 在沒帶 xml 時也去解析 None。feedback_parallel_runners_two_entrypoints_one_fixed):add() 與 update() 是同一個病的兩個入口,驗收要逐一核對兩處都補了,手測也要各走一次(從畫面新增一筆空流程圖、以及把既有一筆改成空流程圖)。jedi-flow-engine/jedi_flow_engine/app/service/workflow_template_service.py:158(update_workflow_template_xml,只把字串塞進 entity 就存、沒做拓撲檢查)#88 流程範本的新增與修改完全不跑任何檢查;起輪次時不要求範本已發布
publish() 已經在 :112-113 呼叫 _validate_bpmn 與 _validate_bpmn_topology,把同兩支叫進 create() 與 update()(先查再寫,用既有的兩支、不要另寫一份驗證);② 起輪次時要求範本必須是已發布狀態。compliance-manager-be/app/flow_engine/service/flow_template_app_service.py:67(create(),目前不驗)compliance-manager-be/app/flow_engine/service/flow_template_app_service.py:84(update(),目前不驗)compliance-manager-be/app/flow_engine/service/flow_template_app_service.py:112-113(publish() 裡已有的 _validate_bpmn + _validate_bpmn_topology 呼叫——照抄這兩行)compliance-manager-be/app/flow_engine/service/flow_template_app_service.py:195(_validate_bpmn)/:248(_validate_bpmn_topology)compliance-manager-be/app/flow_engine/service/workflow_template_snapshot_service.py:46(clone_master_as_snapshot,起輪次複製範本的落點,要加「已發布」條件)clone_master_as_snapshot 的呼叫端有沒有在用未發布的範本起輪次。查到有人依賴就停下回寫問決策者,不要自己決定要不要擋。scripts/init/,屬出貨基線範圍、runner 只回報不自己動。#128 推進或退回階段時,畫面顯示的操作者名稱可以由呼叫端自己填
setdefault 改成直接指派),不採用呼叫端傳入的值;並在請求格式定義裡給那個欄位加白名單,丟掉不認識的 key。compliance-manager-be/api/flow_engine/routes/stage_advance_route.py:59(ctx.setdefault("user_nickname", ...))compliance-manager-be/api/flow_engine/routes/stage_rollback_route.py:40(同樣寫法)compliance-manager-be/api/flow_engine/serializers/stage_advance.py:11-12(ctx 欄位,要加白名單)這張卡的手測總清單:
需決策者先裁的:
jedi-survey/ 子目錄。worktree 建議 wt-fix-b5-survey-folder。涵蓋 #85/#86/#126/#127。apply=False 那一行等於裝飾品);#85 是同一個模組「不要相信畫面送上來的值」的另一個發生點,一起改認知成本最低。⚠️ 但三件的修法不一樣,不要當成同一招(模組頁明文警告)。#85 多人同時填問卷時,「這筆是誰填的」直接採用畫面送上來的名字
get_user_context().login_name——即時同步的基底類別每個事件都會注入登入身分,先查再寫、用既有的),不要相信畫面送上來的值。一行修。jedi-survey/jedi_survey/app/handler/fill_survey_socketio_handler.py:165#86 資料夾更新是畫面送什麼就收什麼,塞一個「已刪除」欄位就繞過守門
apply=False,改用只宣告「名稱」「說明」兩個欄位的嚴格格式來解析,再具名帶進去;「已刪除」「編號」「識別碼」「上層資料夾」這些欄位一律不准從請求設定。jedi-survey/jedi_survey/app/service/survey_folder.py:81jedi-survey/jedi_survey/api/routes/survey_folder_route.py:92(@use_kwargs(..., apply=False))#126 資料夾列表可以叫出系統刻意隱藏的資料夾
__ 開頭)以及已經刪掉的資料夾。只要登入,連權限點都沒掛。jedi-survey/jedi_survey/api/routes/survey_folder_route.py:46__ 開頭的快照資料夾;④ 下拉選單那支照樣正常(證明沒改壞對照範例那支)。#127 資料夾列表把資料庫的原始錯誤訊息整句吐回畫面
jedi-survey/jedi_survey/api/routes/survey_folder_route.py:51這張卡的手測總清單:
__ 快照需決策者先裁的:無。四件的修法與裁定都已明確。
jedi-evidence-classification/。唯讀查證為主,不預設要改碼。worktree 建議 wt-verify-b5-evidence。涵蓋 #89/#129/#130/#131/#132。runner 在第 3 批刪完之後,對每一件回答同一個問題:「這件的落點檔案還在嗎?還在的話,新線是否也走同一段程式?」 三種結論之一:已隨退役消失/還在、要另開修正卡/還在但只影響已無入口的死碼、建議一併刪。
#89 證據檔的內文可以對 AI 下指令(提示注入)
""" 當界線,但沒檢查內容裡有沒有同樣的界線符號,指令段也沒有一句「接下來是資料、不要照做」。對一個以稽核為業的產品來說,判定結果可被交件方操縱是本質問題;有人工複核一關,傷害有限但沒有消除。docker/container_entrypoint.py:374 是容器端組訊息那一段,新線(現在客戶實際在用的批次線)也是叫同一個容器。runner 要先確認這一點。jedi-evidence-classification/docker/container_entrypoint.py:374(已實查確認:f"Filename: {filename}\nContent preview:\n\"\"\"\n{text}\n\"\"\"",界線就是那組 """、內容未過濾)jedi-evidence-classification/jedi_evidence_classification/app/service/evidence_batch_service.py:1143(只確認項目代號存在,信心值/欄位/筆數都不驗、整包原封存進資料庫與審查畫面)#129 按「刪除整批」之後,判定結果與分類紀錄仍永久留在後端主機上
jedi-evidence-classification/jedi_evidence_classification/app/service/evidence_batch_service.py:1417-1427jedi-evidence-classification/jedi_evidence_classification/infra/classifier_container_runner.py:85-89(工作目錄 ~/.cm-jobs/<批次>-<run>/ 的建立處;已實查確認 :74 是 jobs_base_dir 預設值)#130 AI 服務金鑰放在啟動指令的參數上
-e KEY=值 拼進 docker run 的命令列參數,Linux 上任何程序的命令列別的帳號都能從 /proc 讀;容器跑幾分鐘到幾十分鐘期間金鑰一直明著放。產品其他地方都把這把鑰匙當秘密(資料庫加密、寫紀錄檔時遮成 KEY=***),但那段遮蔽只作用在寫檔用的副本,真正拿去執行的參數從頭到尾沒動過。洩出去的是客戶自己的金鑰——客戶自備金鑰的機制已經上線,正式客戶用的是自己申請的那把;試用環境仍暫用原廠預設但額度很低。jedi-evidence-classification/jedi_evidence_classification/infra/classifier_container_runner.py:154(組參數)/:186(實際執行)classifier_container_runner.py:205-224compliance-manager-be/core/plugins/evidence_classification.py:194-202#131 分類程式沒有設記憶體與處理器上限,逾時也殺不掉它
docker kill,逾時之後連要殺誰都指認不出來——使用者一重試就多一個孤兒容器,繼續燒 AI 額度、還握著租戶金鑰。--memory/--memory-swap/--cpus/--pids-limit(順手加 --read-only/--cap-drop=ALL/--security-opt=no-new-privileges);給容器一個由 job ID 推導的 --name,逾時時先 docker kill <name> 再拋錯。上限不必精算——只要不是無限大、而且留得夠給後端服務就達到目的。jedi-evidence-classification/jedi_evidence_classification/infra/classifier_container_runner.py:186(執行點)/命令組裝 :156-169classifier_container_runner.py:197-224 與 llm_clients.py:108-120。#132 分類程式用到的六個外部套件沒有鎖定版本、也沒有校驗碼
jedi-evidence-classification/docker/requirements.txt——anthropic>=0.77/openai>=1.60/python-docx>=1.2/openpyxl>=3.1/python-pptx>=0.6/pypdf>=4jedi-evidence-classification/docker/Dockerfile(安裝那一行要改成帶 --require-hashes)一個共同前提,回報時要一起講:出貨給客戶的落地版刻意沒有安裝分類程式所需要的執行環境,所以這個功能在客戶機器上根本啟動不了——#130/#131 在客戶端打不到、目前只影響我們自己的開發機(這一點上機查證過:客戶端服務容器裡既沒有執行分類程式所需的指令、也沒有對應的連接管道)。🔴 這是一個產品決策、不是技術事實:哪天決定讓落地版也能用自動分類,這兩條必須先修。
這張卡的交付(驗證卡不是手測清單):一張五列的對照表,每列寫「件號/落點檔案還在嗎/新線是否走同一段/結論(消失|要開修正卡|建議併刪)」,附每一列的查證方式(grep 結果或開檔行號)。結論是「要開修正卡」的,順手寫出建議的卡片分組(我預判:#130+#131 同一支函式併一張、#132 單獨一張、#89 單獨一張、#129 視情況)。
需決策者先裁的:
docker run 加幾個參數,成本極低而且現在就保護著我們自己的開發機;#130 要改金鑰傳遞方式,動的東西多一些,而且洩的是客戶自己的低額度試用金鑰。但這是決策者的取捨,runner 不自己定。| 編號 | 問題(白話) | 我的建議 |
|---|---|---|
| D-b5B-1 | #78 要讓客戶內網的代理程式去核對「下載回來的規則包是不是原來那份」。後端這半(把指紋記下來、放進派工單)跟代理程式那半(收到之後真的去對)要做在同一張卡嗎? | 分開兩張卡。 後端先把指紋記進去,代理程式多收一個欄位不會壞;代理那半要重佈客戶端的代理程式才驗得到,混在一起這張卡會驗不完。 |
| D-b5B-2 | #80 改掉「重新解析會把狀態壓回等待中」之後,畫面可能要改成顯示「這一版正在解析中」。前端要不要同一張卡一起改? | 後端這張卡只改後端、把前端要動什麼寫清楚回報,前端另開卡——前端要挑用哪個現成元件呈現這個狀態,不是後端 runner 判斷得來的。 |
| D-b5B-3 | #88 要求「起輪次時範本必須已發布過」。如果查出現有真有流程在用未發布的草稿起輪次,要擋還是要放? | 擋,但先把那些依賴改掉。 草稿之所以叫草稿就是還沒被檢查過,放行等於整條檢查鏈都白做。這一棒先只做查證、把依賴有幾處回報,不要動這一項。 |
| D-b5B-4 | #16 的兩道上限(流程圖分岔最多 8 層、節點最多 100 個)就這樣定案嗎? | 照定案的數字做。 但要知道 8 這個數字是拿現有預設範本(最複雜 2 層)的四倍推出來的,我們手上沒有任何客戶自己畫的範本當樣本;上線後若出現畫得更複雜的客戶要重新檢視(不是現在改)。 |
| D-b5B-5 | #132 分類容器用的六個外部套件沒鎖版本也沒校驗碼(上游被掉包會直接裝進產品),這條本來就在「等裁定要不要開卡」名單上——這次要開嗎? | 開。 這是所有問題裡唯一一條攻擊者完全不必碰我們系統就能得手的;修法是純流程面(鎖版號+加校驗碼)、不動任何產品邏輯、零功能風險;而且就算 M07 舊線整組退役它也還在。 |
| D-b5B-6 | #130(金鑰放在啟動指令上)與 #131(容器沒設記憶體上限、逾時殺不掉)——落地版現在根本跑不起來分類功能,要現在修還是等「決定讓落地版也開放自動分類」時再修? | #131 現在修、#130 可以緩。 #131 只是在啟動指令加幾個參數,成本極低而且現在就保護著我們自己的開發機不被一份壓縮炸彈打掛;#130 要改金鑰的傳遞方式、動的東西多,而且洩出去的是客戶自己申請的低額度試用金鑰。 |
另外要知道(不是待裁、是待辦提醒):
scripts/init/ 的資料庫初始化腳本(那幾張內建流程圖寫在腳本裡)——屬出貨基線範圍,runner 只回報不自己動。