本卡屬 FR-114 資安修正(母卡 CM-2019),修 CM-2039(2-7)帶進來的回歸。🔴 合回前必做——不修的話合回後規劃頁點開/存檔任何「還沒指派人」的任務都會 404。來源:掃描線 FR-115 分析卡 CM-2108(commit 84eb35bab,分析
docs/analysis/2026-09-24-task-project-ownership.md第 6 節),掃描首腦與派工首腦都在工作區/DEV 唯讀核過屬實。只改 BE 一支查詢,不動套件。
CM-2039 為了擋「網址填甲專案、任務其實是乙專案的」跨專案存取,在任務的讀、改、刪之前先查「這個任務屬於哪個專案」。但它查的是任務指派表(compliance.task_assignees.project_id)——那張表只記「誰被指派」,任務建出來時根本沒有指派列。所以只要任務還沒指派人,就查不到歸屬 → 一律回 404。規劃期大部分任務都還沒指派人,CM-2039 手測只驗了已指派的任務,沒抓到。
派工首腦 DEV 唯讀實查(2026-09-24 10:52):
未軟刪專案下的 USER 任務:25,891 張
其中有指派列的:77 張(0.3%) ← 現行查法只認得這 77 張
走「輪次鏈」查得到專案的:25,891 張(100%)
指派表有值且與輪次鏈不一致:0 張
輪次鏈查出多於一個專案:0 張;兩條路徑互相矛盾:0 張
有指派列但輪次鏈查不到:0 張(換查法不會讓任何現在查得到的任務變查不到)
BE infra/flow_control/repository/flow_control_job_repo_impl.py:1072 resolve_project_id_for_job():現行查 TaskAssignee.project_id(要換掉的實作)
⚠️ 本檔 1,239 行,已超 800 行上限 → 新查詢開新檔,原檔只留一行委派
BE app/flow_control/service/job_service.py:139-151 _assert_job_belongs_to_project()(不動)
呼叫點 :163 get_job、:237 update、:313 delete(不動)
套件 jedi-task-platform/.../task/domain/repository/i_flow_control_job_repo.py:130、domain/service/flow_control_job_domain_service.py:122(port 介面,不動)
參考既有同路查法:flow_control_job_repo_impl.py:986-993(current_round_status_by_workflow_execution 先走 wecm.round_id)、detection_job_notify_query.py:94
新檔放法參考同目錄既有 query 類:infra/flow_control/repository/job_import_lookup_query.py(@property session → get_session())
BE /Users/chouraymond/Projects/Billows/Audit-Manager/compliance-manager-be/.claude/worktrees/wt-fix-security branch fix/security-b1
🔴 主 checkout 給掃描線讀,絕不動。發版卡 CM-2110 可能同時動 BE 工作區 pyproject.toml——你不碰 pyproject/lock,只 add 自己的檔。
infra/flow_control/repository/job_project_ownership_query.py,一個 query 類(@property session)一個方法 resolve_project_id_for_job(job_uid) -> Optional[int],兩段依序查、第一段有值就回:-- 路徑一:任務 → 流程實例 → 控制項對應的輪次 → 專案
SELECT r.project_id
FROM compliance.job_executions je
JOIN public.workflow_execution_control_mapping m ON m.workflow_execution_id = je.workflow_execution_id
JOIN compliance.project_audit_rounds r ON r.id = m.round_id
WHERE je.uid = :job_uid AND je.type = 'USER'
LIMIT 1;
-- 路徑二(路徑一查無才走):任務 → 主流程實例 uid → 輪次 → 專案
SELECT r.project_id
FROM compliance.job_executions je
JOIN compliance.workflow_executions mwe ON mwe.id = COALESCE(je.main_workflow_execution_id, je.workflow_execution_id)
JOIN compliance.project_audit_rounds r ON r.workflow_execution_uid = mwe.uid
WHERE je.uid = :job_uid AND je.type = 'USER'
LIMIT 1;
workflow_execution_control_mapping 在 public,其餘三張在 compliance(首腦實查過)。flow_control_job_repo_impl.py:1072 的方法改成一行委派給新 query(repo 在 init 或 lazy 建一個 query 實例,照同檔對其他 query 類的接法)。docstring 改寫成一兩行說明新來源,拿掉「查 task_assignees」的說明。_assert_job_belongs_to_project、不動三個呼叫點、不動套件。權限比對用途(核心),要寫並做突變:
git log --oneline -- test/ | grep -i 2039 或 grep _assert_job_belongs_to_project),跑一次確認照綠;若它 mock 的是 domain_service 就不受影響。