母卡 CM-1669 | 收口日 2026-09-14 | 六棒 CM-1670~1675,只掃不修 沿革(每棒 commits/決策/教訓)見
../../FR-075-2609-jedi-package-security-audit/handoff/security-scan-LOG.md與git log --grep='FR-088';此檔只寫終局。
流程引擎的套件本體加上主專案接它的那半邊,共 157 支程式檔分六棒掃完,範圍內確認 12 條問題(登記為跨 arc 總表 11 條資安+4 條非資安真 bug,算法見下),最嚴重的三件是:① 任何登入者知道一個任務編號就能讀走別家客戶的稽核證明清單(第 49 條);② 一張惡意流程圖能讓整個系統對所有客戶停止回應(第 55/57 條,兩條同一組病);③ 流程留言不檢查你是不是這個專案的人,可以假冒同事發訊息釣魚(第 51 條)。修正卡本線不開:程式層「這筆是不是你的」那批併進 FR-086 由該側首腦開卡,資料庫層隔離歸 FR-094 八棒接。
「12 條」的算法:以每棒 runner 報的範圍內條數加總(3+4+2+2+1+0),對應跨 arc 總表 §3.1 第 49~59(11 列,H1 的一列是兩位研究員各報一次的同一個洞);非資安 bug 另登記 §3.2 第 13~16(4 條)。各處數字口徑不一之處見本檔末段「數字對帳備註」。
jedi-flow-engine/,72 檔)是「零件供應商」,自己沒有任何對外網址;主專案(宿主,92 檔)接線後才有 19 條真正的 API 入口,所以「有沒有檢查權限」這件事全在宿主那半邊。六棒按業務功能垂直切(任務/階段/流程圖解析/範本管理/執行宿主/執行核心),不按程式層切,讓每棒的問題能一次看完。「面板」=工具派出的三個獨立檢查員對每條候選各投一票(不是儀表板);「verified」=工具自己蓋的章,代表每條候選都投完、沒有漏投;「範圍外」=工具的密鑰專項順手掃全 repo 撈到的,不在該棒檔案清單內。
| 棒 | 卡 | 檔數 | 面板票數與狀態 | 範圍內發現 | 首腦改判 | 報告 |
|---|---|---|---|---|---|---|
| 1 H2 任務完成/回退/留言/證明 | CM-1670 | 23 | 候選 11 去重 10,三人各投一票共 30 票全投出,verified |
3 條(HIGH 1/MEDIUM 2;其中第 51 條後被裁升 HIGH) | 無改判;人工補一條「流程定義 GET 任何登入者可讀」併 P1;證偽卡片一項(is_admin 是平台超管不是租戶管理員) |
../scan-H2-task-comment-evidence.md |
| 2 H1 稽核階段推進與回退 | CM-1671 | 21 | 候選 12 去重 11,33 票全投出,verified |
4 條(MEDIUM 3/LOW 1) | 無改判;卡片兩大疑點一個「讀成立、寫不成立」、一個 0:3 否決 | ../scan-H1-stage-advance-rollback.md |
| 3 P1 流程圖範本與 BPMN 解析 | CM-1672 | 30 | 候選 4 去重 3,9 票全投出且每條 3:0,verified |
2 條(MEDIUM 2;第 55 條後被裁升 HIGH) | runner 報 3 條,F2(套件庫走明文 http)改判範圍外+重複 CM-1634 | ../scan-P1-bpmn-parsing-and-templates.md |
| 4 H3 流程範本管理 | CM-1673 | 13 | 候選 10,30 票全投出且每條 3:0,5 條被檢查員降級,verified |
2 條(MEDIUM 1/LOW 1;第 57 條後被裁回 HIGH) | 無改判;runner 沒交付報告,首腦補寫 | ../scan-H3-flow-template-management.md |
| 5 H4 流程執行宿主服務與接線 | CM-1674 | 21 | 候選 9,27 票全投出(8 條 3:0、1 條 0:3),verified |
runner 報 3 條 → 1 條新(MEDIUM) | 2 條改判重複:留言零守門(=第 51 條且檔案不在 scope)、projects 表隔離關(三處已記) |
../scan-H4-execution-host-service.md |
| 6 P2 流程執行核心 | CM-1675 | 49 | 候選 3 去重 2,6 票全投出(1 條 3:0、1 條 0:3),verified |
0 條新(runner 報 1,自標重複第 51 條) | 同意 runner;最有價值的是「套件公開方法 vs 宿主守門」對照表,帶出 §3.2 第 16 未來陷阱 | ../scan-P2-execution-core.md |
| 合計 | 157 | 135 票 | 12 條 |
每棒驗收的固定步驟(六棒一致):① 核工具蓋的章與票數,對照 runner 自述;② 範圍內每條首腦自己開檔核對行號,需要時到 DEV 資料庫唯讀實查(隔離開關、規則數、筆數);③ 越界檢查——runner 報的每條對照 scope 檔案清單與跨 arc 總表既有條目,越界或重複的改判、不另計;④ 核「交付四件」(報告/commit/Notion 回寫含 hash 與 run ID/狀態);⑤ 跑 git diff <掃描版本> HEAD -- <下一棒 scope> 看平行改動有沒有咬到下一棒。
檔數說明:母卡標題寫「150 檔」是開卡時的數字;P2 開掃前因平行重構(plugin.py 拆成五檔、新長出 api//domain/ports.py)由 42 重算為 49,六棒實際合計 157。
嚴重度以決策者 2026-09-13 裁定後為準;標 裁升 的是檢查員原評 MEDIUM、決策者改 HIGH。「歸屬」是修正卡的落點。行號以跨 arc 總表登記為準(各棒掃描期間程式有漂移,同一支函式在不同報告的行號不同,見末段備註)。完整的「出事會怎樣/怎麼修」在總表與各棒報告,這裡一條一段只寫到能判斷急不急。
第 49 條|HIGH|併 FR-086 程式層那批
app/flow_engine/service/job_evidence_service.py:42-59,修法是 :48 之後補一行既有的 assert_project_participant(...)。第 50 條|MEDIUM|併 FR-086 程式層那批(與 49 必須同卡)
:61-70;route api/flow_engine/routes/job_evidence_route.py:70-71。第 51 條|HIGH(裁升)|併 FR-086 程式層那批
api/flow_engine/routes/flow_engine_route.py:44-64(讀)/:66-121(寫);套件 element_variable_service.py:26/:50。第 52 條|MEDIUM|併 FR-086 程式層那批
api/flow_engine/routes/stage_rollback_route.py:52-67;下游 jedi-compliance-audit audit_round_app_service.py:867-887 只查輪次存不存在。第 53 條|MEDIUM|併 FR-086 程式層那批(附規劃相依)
app/flow_engine/service/stage_advance_service.py:84/:118/:120;連帶 :224 與 stage_rollback_service.py:95。第 54 條|LOW|一行修法,隨 53 同批順手改
api/flow_engine/routes/stage_advance_route.py:59、stage_rollback_route.py:40。第 55 條|HIGH(裁升)|與 57 合一張「BPMN 輸入強固化」卡
jedi_flow_engine/common/utils/bpmn_uilts.py:276/:294/:295/:361。第 56 條|MEDIUM|與 55/57 同批
module-frame.update 權限;runner 誠實標「寫入當下交易能否 commit」未實測,可信度較低。infra/mapper/workflow_template_mapper.py:21;寫入端 workflow_template_repo_impl.py:52/:61;宿主入口 api/module_frame/routes/module_frame_item_route.py:100。第 57 條|HIGH(裁升)|與 55 合一張「BPMN 輸入強固化」卡
api/flow_engine/routes/flow_template_route.py:130-144;schema serializers/flow_template.py:28-29 無長度;驗證器 jedi-flow-engine bpmn_topology_validator.py:168/:271 線性掃描。第 58 條|LOW|一行 decorator,可隨程式層那批順手
flow_template.read 能力點(「能力點」=系統裡「有沒有權限做這件事」的開關),前端選單擋住、API 敞開,同租戶內任何登入者能讀走全部範本的完整流程圖。api/flow_engine/routes/flow_template_route.py:29-45/:58-66;可抄 api/flow_control/routes/project_route.py:41 的做法。第 59 條|MEDIUM|併 FR-086 程式層那批(決策者已裁方向)
app/flow_engine/service/workflow_execution_service.py:655(complete_job)/:833(revert_job);政策自述 common/authz/workflow.py:15-16。第 13 條|非資安 bug|與第 54 條同種寫法一起清
force 旗標有兩個來源(外層參數與 ctx 裡的),守門看外層、執行看 ctx。三個檢查員 0:3 判非漏洞、首腦同意——能跳過的只有兩項「軟提醒」(程式自己註明「軟提醒非硬擋」),真正守門一個都跳不掉;但同一個意思兩個來源是異味。stage_advance_service.py:209/oscal_stage_handlers.py:41。第 14 條|非資安|建議刪
bpmn_generator.py:963/1220/1241。第 15 條|非資安 bug
workflow_execution_service.py:429-441/module_frame_service.py:241。第 16 條|未來陷阱|加陷阱註解或拔掉 provider
JobExecutionService(任務增刪改 6 支公開方法,零守門零租戶檢查)在 DI 容器(程式啟動時把各元件組裝起來的登記簿)建了 provider 但全 codebase 零取用;套件 WorkflowExecutionService(含 complete_job/revert_job)主專案根本沒 import,宿主整支重寫了自己的。今天無害,哪天有人接上就是一組現成的無守門端點。di_containers/flow_engine/workflow_excution_containers.py:113。common/authz/workflow.py:15-16 的政策自述。workflow_executions/job_executions/workflow_templates;element_variables 連欄位都沒有,只能靠程式層(即第 51 條那批)。另有沿用的舊裁定:修正卡一律不派、由 PM 統一安排(早期修正卡 09-21 作廢,後改照內化版問題總表派工,已全數修完)。
每張子卡開卡時首腦讀碼列了「重點看什麼」,runner(或首腦補寫時)逐項回答。證偽與證實同樣有價值——證偽了才知道是什麼擋住、擋的東西哪天會不會消失。
| 棒 | 疑點數 | 成立 | 證偽(不成立) | 機制屬實但無入口/影響低 | 沒問題/只記錄 |
|---|---|---|---|---|---|
| H2 | 8 | 5 | 2 | — | 1 |
| H1 | 8 | 4 + 1 部分成立(讀成立寫不成立) | 1 | — | 2 |
| P1 | 8 | 2 + 錨點 1 | 5 | — | — |
| H3 | 7 | 4 | — | 1 待補查 | 2 |
| H4 | 8 | 全答(兩處修正了卡片的猜測) | 見報告 §5 | ||
| P2 | 9 | 3(含對照表做完) | 3 | 3 | — |
合計 48 項疑點(H4 一棒的分類材料只寫「八項全答」,未細分)。各棒重點:
is_admin 是租戶管理員,追到源頭是平台超級管理員,所以不成立,但註解錯了、它宣稱的兜底也不存在。「反查鏈恆回 None 導致誤擋」屬實但沒引發繞過。ctx 整包往下傳(找到署名可自填)、三張表隔離全關——成立。「專案編號與輪次編號從不核對」讀成立、寫不成立。「force 雙版本」三個檢查員 0:3 否決、首腦同意。eval/exec。validate 端點可卡死、範本讀取漏能力點、宿主 mapper 不解析 XML(壞 XML 讀不炸、發布會擋)、內建範本守門四處無遺漏——四項有答;DTO 無敏感欄位、守門無遺漏——兩項沒問題;「凍結副本不帶租戶」查出機制(租戶靠 jedi-common 在寫入前從登入身分自動填,沒身分就留空),且 DEV 實查修正 FR-087 戊組:191 筆裡只 5 筆快照、186 筆原廠母版,根因待補查。<strong>kwargs 形狀(卡片猜錯成因)但零租戶過濾是真的,併 FR-083「27 支零守門」;啟動流程改寫範本 XML 的對象視呼叫者而定**(記 §3.2 第 15);通知不帶留言原文、收件人從專案往下查;known_project_id 唯一來源是伺服器端解析;輪次凍結檢查三種情況預設放行(fail-open,即「出錯時預設放行」)屬實但影響低於預期。最值得記的三個證偽:
WorkflowExecutionService,套件那 13 支公開方法目前沒有任何外部入口。這一條同時是 §3.2 第 16「接好沒人用的插頭」的來源。eval/exec。首腦驗收時各重跑一次(不是重讀 runner 的敘述),一分鐘就確認。151c5f88 走到 45f14ad9,P1 範圍只動註解與死碼,但 P2 範圍的 plugin.py 拆成五檔,scope 42 → 49、卡片要重寫。往後每棒驗收跑一次 git diff <掃描版本> HEAD -- <下一棒 scope>。element_variables 沒掛租戶 mixin、比姊妹表危險」)被否決的理由是「姊妹三張的隔離開關也全關」——反差不存在,反而坐實四張流程表資料庫層一道隔離都沒有。「這些存在嗎」——高。 六棒面板全 verified、135 票全投出、無漏投;範圍內每一條首腦都自己開檔核對(不信 runner 自述),H2/H1/H3 另在 DEV 資料庫唯讀實查隔離開關、規則數與筆數,P1 五項證偽首腦重跑實測。runner 抓到工具原文一處錯(宣稱 compliance.projects 有隔離、實查關但有 4 條沒生效的規則),首腦複核屬實。
「只有這些嗎」——中等,不可宣稱乾淨。 原因四點:① 工具沒留逐檔閱讀帳本(P2 runner 自陳 coverage.research 空),「49 檔只有這一條」的信心停在中等;② 六棒都以 low 檔次(工具的 effort 等級,子卡標題可見)單輪跑完,沒有第二輪交叉;③ 四棒(H2/H1/H3/H4)票數大半投在範圍外的舊憑證案,真正投在範圍內的票比帳面少(例如 H2 30 票只有 12 票落在 23 檔);④ 總表 §3.4 登記了本 arc「等於沒查」的項目:留言在前端怎麼渲染(儲存型 XSS)、問卷 uid 進 jedi-survey 的租戶檢查、project_audit_rounds/round_stage_transitions 這類無租戶欄的表在 FR-087 之外還有幾張。
feature/FR-075,未 push)依時間順序,git log --oneline --grep='FR-088' 抄出,再補 LOG 第十任 block 列的兩筆跨 arc 裁定 commit;標 ※ 的是不屬本 arc 掃描作業、但與 FR-088 登記或裁定有關的 commit。
| hash | 一句話 |
|---|---|
d9d057e2 |
開卡:讀碼切六棒,母子卡 CM-1669~1675 一次建完 |
e9507d29 |
H2 掃描報告(runner) |
306ef445 |
驗收 H2:三條「只驗身分不驗歸屬」全屬實,登記總表第 49~51 |
ceb455df ※ |
FR-089/FR-090 補母卡登記(README 登記列緊接 FR-088,故 grep 命中) |
1e95823b |
H1 掃描報告(runner) |
60d90cb1 |
驗收 H1:四條屬實、兩大疑點證偽複核,登記第 52~54+§3.2 第 13 |
ebd3b1e1 |
P1 掃描報告(runner) |
12d0d06f |
驗收 P1:兩條屬實、五項證偽重跑實測、F2 改判範圍外;P2 scope 重算 49 檔 |
5b2165b5 |
H3 runner 未交付,首腦補寫報告並驗收;修正 FR-087 戊組 191 筆描述 |
c6c12e68 |
H4 掃描報告(runner) |
0dbe3586 |
驗收 H4:改判 1 新 2 重複、viewer 可完成別人任務登記第 59 條;第九任首腦交棒 |
4a4092f5 ※ |
總表 §6 重整:補 viewer 唯讀待裁項、FR-088 三條升 HIGH 待裁項(跨 arc 動作,含 FR-088 項) |
05e81f6e ※ |
記帳決策者 2026-09-13 五項裁定(viewer 唯讀/問卷不加旗標/程式層併 FR-086/CM-1559 順序歸 FR-094/51、55、57 升 HIGH)——grep 不命中,從 LOG 抄 |
bf14cda8 ※ |
追記 upload_files 加租戶欄+開隔離歸 FR-086 程式層那批——grep 不命中,從 LOG 抄 |
31c0d7f6 |
P2 掃描報告(runner) |
17664009 |
驗收 P2:套件本體 49 檔零新發現、對照表證實宿主沒接套件執行 service;六棒收口 |
本 SUMMARY 自身的 commit 不在上表(寫檔時尚未 commit)。
SUMMARY 之後的後續(現況):
workflow_executions/job_executions/workflow_templates 的隔離開關已接在 FR-094(CM-1770/1771/1772);element_variables、job_evidences、project_audit_rounds、round_stage_transitions 無租戶欄,不在 FR-094,只能靠程式層。security-scan-lead 第一節「≤15 檔」與已裁的 ≤30/40 不一致,未改(收尾類,等令)。座標:
../README.md../scan-H2-task-comment-evidence.md/../scan-H1-stage-advance-rollback.md/../scan-P1-bpmn-parsing-and-templates.md/../scan-H3-flow-template-management.md/../scan-H4-execution-host-service.md/../scan-P2-execution-core.md../../security-scan-consolidated/README.md../../FR-075-2609-jedi-package-security-audit/handoff/security-scan-STATE.md../../FR-075-2609-jedi-package-security-audit/handoff/security-scan-LOG.md.claude/skills/security-scan-lead/SKILL.md以下數字各處說法不一,本檔採用的口徑已在各段標明;未裁前不改任何來源。
complete_job/revert_job 行號:總表第 59 條與 README 寫 :655/:833,H4 報告寫 :655/:805,P2 報告寫 :611/:788——同一支檔在三棒掃描期間有漂移。本檔採總表。job_evidences 782/element_variables 3,011/job_executions 10,783/workflow_templates 17,206;H2 驗收複核寫 848/3,236/11,065,P1 驗收寫 workflow_templates 18,267。同一庫兩組數字,可能是查詢時點不同。修正待驗證(未同步)。17664009)最後一個 block 是第九任首腦交棒;P2 驗收+六棒收口+第十任交棒的 block 在工作區(git status 顯示 LOG/STATE 均為 modified),寫本檔時一併讀了工作區版本。| 詞 | 白話 |
|---|---|
| 套件/宿主 | 套件=jedi-flow-engine 這個「零件供應商」,自己沒有對外網址;宿主=主專案接它的那半邊,所有 API 入口與權限檢查都在這裡 |
| scope/範圍內/範圍外 | 每棒卡片列的檔案清單;工具的密鑰專項會掃全 repo,撈到的東西若不在清單內就是「範圍外」,不算該棒發現 |
| 面板/檢查員/票 | 工具對每條候選發現派三個獨立檢查員各投一票;「3:0」=三人都認為成立,「0:3」=三人都否決 |
verified(stamp) |
工具跑完自己蓋的章,代表每條候選都投完、沒有漏投;不代表「沒有漏掉的問題」 |
| 候選/去重 | 研究員報上來的原始條目;同一個洞被兩位研究員各報一次會合併成一條 |
| 守門/歸屬檢查 | 後端在做事前檢查「你有沒有登入」「你是不是這個專案的人」「這筆資料是不是你的」;本 arc 多數問題是只做了第一項 |
| RLS/隔離開關/規則 | 資料庫層「每個客戶只能看自己資料」的機制;「開關關但有 4 條規則」=規則寫好了但沒啟用,等於沒有 |
| 租戶欄/tenant 欄 | 資料表裡標「這筆是哪個客戶的」的欄位;沒有這個欄位的表,資料庫層想擋也擋不了,只能靠程式 |
| fail-open | 出錯(例如查不到資料、元件沒注入)時預設放行;相反是 fail-closed 預設擋下 |
| JSONB | 資料庫裡可存任意 JSON 結構的欄位型別,沒有形狀限制 |
| DI 容器 | 程式啟動時把各元件組裝、登記起來的登記簿;「建了 provider 沒人取用」=登記了但沒人來拿 |
| 能力點(capability) | 系統裡「有沒有權限做這件事」的細粒度開關,例如 flow_template.read |
| 密鑰專項 | 工具設了 focus 就會額外跑一次「找寫死的密碼與金鑰」的掃描,掃全 repo 不受 scope 限制 |
| 交付四件 | runner 做完一棒必須交的:報告檔、commit、Notion 卡回寫(含 commit hash 與 run ID)、卡片狀態改「修正待驗證」 |