本卡屬 FR-088(母卡見 FR-088 母案),第 1 棒 H2:任務完成/回退/留言/證明的 HTTP 入口與守門政策(BE repo)。只掃不修。
稽核任務跑起來之後,使用者會在畫面上做四件事:把任務標成「完成」、把完成的任務「退回」、在流程上「留言」、替任務上傳「證明文件」。這一棒掃的就是這四件事從網址進來、到判斷「你能不能動這筆」的那段路,共 23 檔。只找問題、不修問題。
這是整個 arc 門檻最低的一組:這些端點只拿到一個流程或任務的編號,沒有專案維度,守門檔自己的檔頭就寫著「原本任何 JWT 持有者猜出 id 即可跨專案/跨租戶操作」(common/authz/workflow.py:3-5)。後來補的守門只補了「寫」的三處,「讀」的四處首腦核對過全沒補。而這幾張表在資料庫層的隔離全是關的(DEV 實查)。若成立,形狀與 FR-086「四層全空」一模一樣。
這些是首腦讀過程式碼後認為最容易出事的地方,行號已開檔核對,是思考起點不是檢查清單:
api/flow_engine/routes/flow_engine_route.py:44-64(讀)與 :66-121(寫)只掛 @jwt_required(),route 直接拿 id 查 workflow_execution 再讀寫 element_variable,沒有任何一行問「你是不是這個專案的人」。留言內容是任意字串(serializers/flow_engine/workflow_execution.py:71-72 只宣告 fields.String()),寫進 JSONB。要驗:猜到別家客戶的流程 uid 能不能讀到/寫進他們的留言;寫進去的內容 FE 怎麼渲染(儲存型 XSS 面,本棒只記錄不追 FE)。job_evidence_route.py:21-30 清單(用 query string job_execution_uid)與 :60-71 單筆,對應的 service app/flow_engine/service/job_evidence_service.py:43-71 兩支方法都沒有 assert_project_participant;同檔 :81(新增)、:145(更新)、:168(刪除)都有。證明資料含檔案 id、雲端硬碟 file id、參考網址、描述。要驗:猜 uid 讀別人的證明清單。job_evidences/element_variables 表:隔離關、零規則、無租戶欄位(DEV 實查:relrowsecurity=f、policy 0、tenant_id 欄 0,782/3,011 筆)。這代表上面兩條若成立,資料庫層零兜底。job_executions 有租戶欄但隔離關、零規則(10,783 筆)。common/authz/workflow.py:34-43——沒有登入身分放行、is_admin 放行(註解說「RLS 仍限租戶」,但上面三張表 RLS 是關的)、role_service 沒注入放行(註解自稱「與 v1 行為一致,背景/未接線安全」)。要驗:is_admin 是租戶管理員還是平台管理員?(若是租戶管理員,租戶 A 的管理員在隔離關閉下能不能動租戶 B 的任務);DI 有沒有可能在某條路徑漏注入 role_service(di_containers/flow_engine/job_evidence_containers.py:31-32 與 workflow_excution_containers.py:151)。app/flow_engine/service/workflow_execution_service.py:137-153(此檔屬 H4,本棒可越界讀這兩支方法)自陳 round 主 workflow 的 USER job 沒有 task_assignees 列,反查回 None,守門一律拒。FR-050 用 known_project_id 繞過。要驗:有沒有其他呼叫者因此「必被擋」而被人拿掉守門、或改傳可控的 known_project_id。這是功能 bug 也是誘發繞過的根源。flow_engine_route.py:124-147/:149-173 用 request.get_json() 直接取 workflow_execution_uid/revert_to_job_id/comment;守門在 service 層 :655/:833(H4 檔,可越界讀)。要驗:comment 是 dict 直接落 JSONB;revert_to_job_id 指到別的流程的 job 會怎樣;get_json() 非 JSON 時是 400 還是 500。flow_engine_route.py:29-37 用 uid 查範本,套件 service 無租戶檢查(P1 範圍),而 workflow_templates 隔離是關的(規則寫好但開關關,FR-087 第 2 項)。要驗:猜 uid 讀別家客戶的流程圖(含自訂階段、角色、任務名稱)。flow_engine_route.py:175-186 走 view vw_user_job_queue 以 user_id 過濾——這條看起來對;順手確認 view 是不是 FR-087 說的「用擁有者身分查底層、繞過隔離」那 4 支之一。session_scope 給最高權限(fail-open)——本棒守門的「無 context 放行」分支與它同源。workflow_executions 缺查詢規則且關、job_executions 零規則且關;每天 03:10 有排程掃 job_executions 全表。common/authz/workflow.py;當時明寫「任一角色的參與者皆通過」——本棒不重審這個政策,只審它有沒有被每條路呼叫到。