W4 掃描報告:任務層直接查問卷/設備資料表(CM-2093)

範圍:3 檔,共 1,629 行,分別是 infra/flow_control/repository/flow_control_job_repo_impl.py(1,216 行)、infra/flow_control/repository/job_import_lookup_query.py、infra/readmodel/tasks/job_export_query.py。 掃描工具:Claude Code 官方 claude-security plugin,effort low,只看正式程式碼。 掃描基準 commit:9abe5e64e895。掃描時工作區有其他 session 還沒 commit 的改動。 驗證章:verified。6 條候選全部經過三人面板投票,18 票都投了,6 條都成立,其中 1 條被面板降級。只掃不修。 資料庫隔離現況:2026-09-23 22:34~22:41(台北時間)在 DEV 用唯讀查詢確認,見第 5 節。


1. 一句話結論

卡片擔心的「任務把別家客戶的問卷或設備拼進來」不成立:這批查詢碰到的問卷表、設備表、填答表、問卷指派表都有開資料庫隔離,別家客戶的資料進不來(詳見第 5、6 節)。

真正的問題出在同一家公司內部、不同專案之間。 任務相關的網址都帶「專案編號」和「任務編號」,系統只檢查「你在這個專案裡的身分」,從來沒有核對這筆任務到底屬不屬於這個專案。資料庫隔離只分客戶,不分專案,所以兩層都擋不住。

工具提報 6 條,三人面板全部判定成立。runner 自己開檔又追出 2 條。依總表現況分成三類:

  • 新發現,要進總表(4 條):
    • W4-3:批次匯入任務時,可以順便改掉別的專案的任務。
    • W4-5:任務清單不檢查你是不是這個專案的成員。
    • W4-6:匯出任務 Excel 時,檢查的專案和實際匯出的專案可以是不同的兩個。
    • W4-R2:匯入的「驗證」和「重新驗證」兩步完全沒有檢查身分。
  • 已登記的總表第 63 項,這次補了新細節(3 條):
    • W4-1:修改任務。
    • W4-2:刪除任務。這次另外發現,稽核輪次凍結的保護也一起被繞過。
    • W4-4:查看任務詳細。
  • W4-1 的連鎖後果(W4-R1):跨專案改任務時,會留下一筆假的「這個任務屬於哪個專案」紀錄,之後好幾個判斷會被它帶偏。

2. 這一棒在檢查什麼

「規劃頁」上每個查核項目底下都有若干任務。專案管理者可以在頁面上新增、修改、刪除任務,也可以把任務匯出成 Excel、改好再匯回來。

這三支檔案是這些功能的資料存取層:

  • flow_control_job_repo_impl.py:任務的讀取、新增、修改、刪除。
  • job_import_lookup_query.py:Excel 匯入時,查人員、部門、設備的名稱。
  • job_export_query.py:Excel 匯出時,把任務與指派資料組成一張表。

這三支都不透過問卷套件、設備套件的服務,而是直接查它們的資料表。所以套件自己的權限檢查在這條路上不會生效,能擋的只有兩層:

  1. 資料庫隔離:只擋「不同客戶」。
  2. 呼叫這三支的服務方法(job_service.py、job_import_service.py)裡的權限檢查。

資料存取層本身本來就不該做權限檢查,這一層 19+7+2 個方法都沒有權限檢查是正常的。這一棒要回答的是:守門的那一層有沒有守對東西。


3. 掃到什麼:總覽

# 發生在哪個畫面、誰做什麼 出事會怎樣 要先有什麼才打得到 該補檢查的位置 嚴重度 與總表的關係
W4-1 規劃頁「編輯任務」。某個專案的管理者送出修改時,網址填自己的專案,任務編號填別的專案的 別的專案的任務被改名、改說明、改類型、換問卷、換部門與設備,還可以把自己加成指派人,然後就能去執行那個任務 同公司內任一專案的管理者(自己開一個專案就是),並且知道目標任務的編號 app/flow_control/service/job_service.py:266(update_job 開頭,第 287 行查完身分後要補「任務屬於此專案」)
api/flow_control/routes/job_route.py:104
中 總表第 63 項後半(已登記「改與刪只驗人、沒驗物」),不另計
W4-2 規劃頁「刪除任務」。同上,網址填自己的專案,任務填別人的 別的專案的任務永久刪除,連同指派、部門、設備、問卷、留言。🔴 新細節:「輪次凍結後不能刪任務」這道保護查的也是網址上的專案,所以對方已經開跑、已凍結的稽核輪次裡的任務也刪得掉 同公司內某個還在規劃期專案的管理者,並且知道目標任務的編號 app/flow_control/service/job_service.py:211(delete_job,凍結檢查要改成查任務真正所屬的專案;project_uid 預設 None 要改成必填)
api/flow_control/routes/job_route.py:147
中 總表第 63 項後半,但凍結被繞過是新的後果,要加進修正卡的驗收項
W4-3 規劃頁「匯入任務 Excel」的確認步驟。管理者上傳的 Excel 裡,任務編號填別的專案的 一次批次改掉別的專案的任務:換指派人、換類型、換部門與設備。程式註解寫著「防止未經驗證的確認」,但那份白名單只確認「任務存在」,不確認「在這個專案裡」 同公司內任一專案的管理者,並且知道目標任務的編號 app/flow_control/service/job_import_service.py:397(confirm_import,第 413 行的白名單要改用這個計畫底下的任務)
infra/flow_control/repository/job_import_lookup_query.py:86
中 新
W4-4 任務詳細頁。任何登入的人打開一筆任務 看得到任何一個專案的任務完整內容:指派人、部門、設備、問卷、檢測工具設定 同一家公司的登入帳號,並且知道任務編號 app/flow_control/service/job_service.py:139-140(get_job,連專案編號這個參數都沒有)
api/flow_control/routes/job_route.py:82
中 總表第 63 項前半,不另計
W4-5 規劃頁的任務清單。任何登入的人帶別的專案的編號來查 列出別的專案每個查核項目底下的任務,包含任務編號。W4-1~W4-3 需要的任務編號就是從這裡來的 同一家公司的登入帳號,並且知道目標專案的編號。查核項目的編號是公開標準的條號(例如 AC.L1-3.1.1_obj.1),可以用猜的 app/flow_control/service/job_service.py:243-244(list_jobs 開頭補成員檢查)
api/flow_control/routes/job_route.py:52
中 新(總表第 63 項只把清單當成「看到編號的地方」,沒有把清單本身列成缺口)
W4-6 規劃頁「匯出任務 Excel」。網址上有兩個編號:專案編號和計畫編號。系統檢查你是不是前者的成員,匯出的卻是後者指向的專案 某個專案的一般成員(連管理者都不必是)可以把別的專案的整張任務表匯出來,內容包括任務編號、指派人的登入帳號、部門、設備 同公司內任一專案的成員,並且知道目標專案的編號 app/flow_control/service/job_import_service.py:103-104(export_excel,要核對計畫編號解析出來的專案等於網址上的專案)
infra/readmodel/tasks/job_export_query.py:79
低(面板 2:1 從中降為低,理由見 4.6) 新
W4-R1 W4-1 的連鎖後果:跨專案加指派人時,系統會寫一筆「這個任務屬於你的專案」的指派紀錄 有好幾個地方用「指派紀錄」反推任務屬於哪個專案,包括凍結判斷、存流程圖時查輪次狀態、「我的任務」清單,都可能被這筆假紀錄帶偏 同 W4-1 修 W4-1 就一起消失。受影響的讀取點:flow_control_job_repo_impl.py:327(_is_round_frozen_for_job,第 339 行取第一筆、沒有排序)、:970(current_round_status_by_workflow_execution 第二條路) 中(與 W4-1 一起修) 與總表 M10 第 6 條是同一種病(「塞一筆假指派,騙過歸屬判斷」),這次在另一個入口再出現一次
W4-R2 匯入任務 Excel 的「驗證」與「重新驗證」兩步。任何登入的人都可以呼叫 可以拿來試探「某個任務編號屬不屬於某個計畫」,也可以試探公司內有哪些帳號、部門、設備名稱。不會改到資料 同一家公司的登入帳號 app/flow_control/service/job_import_service.py:197(validate_import)、:314(revalidate_items)開頭補成員檢查 低 新。與 W4-6 是同一組入口,建議併同一張卡

W4-1~W4-6 經過三人面板投票;W4-R1、W4-R2 是 runner 自己開檔核對的,沒有經過三人面板投票。


4. 每條發現的詳述

4.1 W4-1:管理者可以改掉別的專案的任務

場景:小明在公司裡自己開了一個專案 A,所以他是 A 的管理者。他從任務清單(W4-5)或匯出表(W4-6)拿到專案 B 的某個任務編號。在規劃頁按「編輯任務」送出時,他把網址改成 /grc/project/<A>/job/<B 的任務>,指派人填自己。

會發生什麼:系統先確認「小明是不是 A 的管理者」,是,就放行。接著用任務編號把那筆任務撈出來直接改,從頭到尾沒有比對這筆任務是不是 A 的。專案 B 的任務就被改名、換問卷、換設備,小明也變成指派人。

為什麼擋不住:

  • 程式這一關:job_service.py:287 只呼叫 _require_manager(project_uid),查的是網址上的專案。
  • 資料庫這一關:任務表的隔離規則是 app_tenant_allowed_for_session(tenant_id),只分客戶,同一家客戶的任務全部看得到(第 5 節)。

與總表的關係:docs/security-report/M20-tenant-isolation.md 第 3 條已寫明「補上的守門仍不核對這筆任務到底屬不屬於那個專案」,也就是總表第 63 項的後半。這條是既有案,不另計。 修正卡是 FR-114 卡 2-7。

4.2 W4-2:管理者可以刪掉別的專案的任務,而且繞過凍結

場景:同上,小明按「刪除任務」,網址專案填 A,任務填 B 的。

會發生什麼:B 的任務被永久刪除:指派、部門、設備、問卷連結、留言都一起清掉,流程圖上的方塊也被移除(flow_control_job_repo_impl.py:777 起)。

新細節(總表還沒寫到):規劃頁「稽核輪次開跑後不能新增或刪除任務」這道保護(job_service.py:100 _assert_round_in_planning),吃的也是網址上的專案。所以只要小明自己的 A 還在規劃期,就算 B 的稽核已經開跑、任務清單已經凍結,B 的任務還是刪得掉。凍結保護的就是「稽核範圍不能中途改變」,這等於它對跨專案攻擊完全無效。

建議:併入總表第 63 項的修正卡(FR-114 卡 2-7),並在那張卡的驗收項加一條:「凍結檢查要查任務真正所屬的專案」。

4.3 W4-3:批次匯入時順便改掉別的專案的任務

場景:小明是 A 的管理者。他在規劃頁用「匯入任務 Excel」,上傳前把 Excel 的「任務識別碼」欄換成 B 的任務編號,指派人填自己。

會發生什麼:

  1. 「驗證」那一步會報「任務不屬於此稽核計畫」,但**「確認」那一步不重跑那個比對**。
  2. 確認時用的白名單是 get_job_control_uid_map(all_job_uids)(job_import_lookup_query.py:86),它只查「這些任務存不存在、有沒有對應的控制項」,不限定專案,也不限定計畫(第 115 行只有 JobExecution.uid.in_(job_uids))。
  3. 名單上的每一筆都會丟進 update_job(project_uid=A),一次批次改掉 B 的任務。

為什麼是新的:總表第 63 項講的是單筆編輯;批次匯入是另一個入口,而且那段「防止未經驗證的確認」的註解讓人以為已經守住了。這正是「同一件事兩個入口兩套門」的形狀。

建議:白名單改用「這個計畫底下的任務」,也就是驗證步驟用的 get_export_data(ap_uid),並且核對計畫解析出來的專案等於網址上的專案。要和 W4-1 放在同一張卡,否則修好一個入口,另一個還開著(見 memory「平行 runner 兩入口只改一邊」)。

4.4 W4-4:任何人都能開任務詳細頁

即總表第 63 項前半,不另計。本棒只再確認一次現況:job_service.py:140 get_job(self, uid) 的簽名裡仍然沒有專案編號,整支沒有任何權限檢查。

4.5 W4-5:任務清單不檢查成員

場景:同一家公司的任何登入的人,在規劃頁的任務清單 API 帶上別的專案的編號,再把查核項目的編號換成標準條號(例如 AC.L1-3.1.1_obj.1),一條一條查。

會發生什麼:每一條查核項目底下的任務都列出來,包含任務編號、指派人、部門、設備、問卷。W4-1~W4-3 都需要任務編號,這裡就是它們的來源。

為什麼擋不住:job_service.py:244 list_jobs 沒有任何權限檢查。資料存取層(flow_control_job_repo_impl.py:90 那段 SQL)確實用網址上的專案縮小了範圍,所以不會看到別家客戶的,但沒人問「你是不是這個專案的人」。

為什麼是中不是高:要先知道目標專案的編號。專案編號是隨機產生的長串,不能用猜的;不過同一家公司內的人,從其他畫面拿得到的機會不低。

4.6 W4-6:匯出時,檢查的專案和匯出的專案可以不同

場景:小華只是專案 A 的一般成員。她在「匯出任務 Excel」的網址 /grc/project/<A>/ap/<X>/jobs/export 裡,把 X 換成專案 B 的編號。

會發生什麼:

  1. 系統檢查「小華是不是 A 的成員」,是。
  2. 接著 get_export_data(ap_uid) 會依序把 X 當成專案編號、輪次編號、計畫編號去解析(job_export_query.py:109,實際在 jedi-compliance-audit 套件的 _resolve_ssp),解析到 B 就匯出 B。
  3. 小華拿到 B 的整張任務表。

面板為什麼降成低:三票裡兩票給低,理由是:匯出內容大多是規劃資訊,不含證據本體或稽核結論;而且只限同一家公司。我同意降級,但要提醒一點:它吐出的任務編號正是 W4-1~W4-3 的攻擊材料,所以修正時要跟那幾條一起排。

附帶:jobs/export 與 jobs/import 的網址是 jedi-task-platform 套件宣告的,套件那邊只掛了「登入+授權模組」,真正的權限檢查交給宿主的服務。宿主的服務只守了四個入口中的兩個(匯出、確認),另外兩個見 W4-R2。這就是卡片說的「套件以為宿主守,宿主只守一半」。

4.7 W4-R1:跨專案加指派人會留下假的歸屬紀錄(runner 自行核對)

場景:接續 W4-1,小明把自己加成 B 的任務的指派人。

會發生什麼:update_job 寫進指派表的那一筆,project_id 用的是網址上的 A(flow_control_job_repo_impl.py:589 用網址專案查 project_id,第 614 行寫入)。於是 B 的任務在指派表裡多了一筆「屬於 A」的紀錄。

為什麼要緊:系統有好幾個地方是用「指派表」反推「這個任務屬於哪個專案」:

  • _is_round_frozen_for_job(:339)取第一筆指派紀錄、沒有排序。拿到的若是 A 那筆,B 的任務能不能「退回」就改看 A 的輪次狀態。
  • current_round_status_by_workflow_execution(:995 起的第二條路)一樣是 JOIN 指派表去找輪次。
  • 「我的任務」清單(資料庫檢視表 vw_user_job_queue)顯示的專案欄位取自指派表,這個任務會以「A 專案的任務」出現在小明的清單上。

結論:這不是獨立的洞,而是 W4-1 讓傷害擴散的方式。修 W4-1 就會一起消失,不另開卡;但修正卡的驗收要檢查 DEV 上有沒有已經存在的這種錯位紀錄。DEV 實查時間 22:38:165 個專案的指派紀錄中,沒有任何一個任務同時掛在兩個專案底下,目前乾淨。

4.8 W4-R2:匯入的驗證兩步沒有任何權限檢查(runner 自行核對)

場景:同一家公司的任何登入的人,直接呼叫匯入的「驗證」或「重新驗證」。

會發生什麼:

  • validate_import(job_import_service.py:197)和 revalidate_items(:314)開頭都沒有成員檢查。
  • 回傳的錯誤訊息可以當成試探工具,例如「任務 X 不屬於此稽核計畫」「使用者不存在:Y」「設備不存在:Z」。
  • 不會寫進資料庫,所以沒辦法改資料。

為什麼是低:只能確認「某個編號或名稱存不存在」,同公司的人本來就從其他畫面看得到大部分這類名稱。建議和 W4-6 併在同一張卡,四個入口一起補成「計畫要屬於網址上的專案+你要是成員」。


5. 資料庫隔離現況(卡片要求的對照表)

查詢方式:2026-09-23 22:34,用 DEV 的 cm_app 帳號(受隔離規則管)唯讀查 pg_class.relrowsecurity 與 pg_policy。22:38 另外開一個「唯讀交易+最高權限變數」查資料分布,結束就回滾,沒有改任何東西。

「有隔離」指資料庫開了隔離,而且有查詢規則;「只分客戶」指規則只比對客戶,不比對專案。

表 有沒有隔離 隔離依據 這批哪裡碰到
survey.surveys(問卷) ✅ 有 客戶 repo :190、:193、:269、:869、:916
survey.task_surveys(問卷指派) ✅ 有 跟隨流程實例(流程實例再依客戶) repo :1094
survey.question_answers(填答) ✅ 有 跟隨題目(題目再跟隨問卷頁) repo :920
public.devices(設備) ✅ 有 客戶 repo :166、:289、:639、:770;匯入 :60;匯出
public.org_units(部門) ✅ 有 客戶 repo :154、:281、:630;匯入;匯出
public.users(使用者) ✅ 有 客戶+本人 repo :595;匯入 :21、:120
compliance.job_executions(任務) ✅ 有 只分客戶 幾乎每個方法
compliance.workflow_executions(流程實例) ✅ 有 只分客戶 repo :210、:251、:373
compliance.workflow_templates(流程範本) ✅ 有 客戶;讀取另外放行系統範本與上層共享範本 repo :379、:436、:962
compliance.projects(專案) ✅ 有 客戶 repo :90、:589、:677
compliance.task_assignees(任務指派) ✅ 有 跟隨專案(專案再依客戶) repo :137、:301、:339、:600
compliance.project_audit_rounds(稽核輪次) ✅ 有 跟隨專案 repo :346、:986
config.job_execution_detection_tools/_agents(任務綁的檢測工具) ✅ 有 客戶 repo :1159、:1174
compliance.job_evidences(任務佐證連結) ❌ 沒有 — repo :181、:259、:857、:885、:900
compliance.job_execution_devices/_org_units/_surveys/_comments(任務關聯表) ❌ 沒有 — repo :154、:166、:626~:836
compliance.workflow_templates_trans(範本翻譯) ❌ 沒有 — repo :524、:532(寫入)
public.workflow_execution_control_mapping(流程↔︎控制項對位) ❌ 沒有 — repo :986;匯入 :111;匯出
oscal.catalog_control_parts 等 oscal 目錄表 ❌ 沒有 — repo :90、:106、:677
config.detection_tools(工具清單,全系統共用) ❌ 沒有 — repo :1159

讀這張表的方法:沒有隔離的表,只要每一條查詢都 JOIN 到一張有隔離的表,或它的鍵值是從有隔離的查詢拿來的,就不會漏。第 6 節逐條確認過這個前提。


6. 卡片點名要追的問題

6.1 第 190 行 session.query(Survey).filter(Survey.uid.in_(ref_ids)):ref_ids 從哪來?:排除

  • ref_ids 取自任務佐證連結表的 ref_id。
  • 這個欄位全專案只有一個寫入點:add_survey_evidence(:890),寫入的值是 create_snapshot() 在伺服器端新產生的問卷快照編號(survey_handler.py:191-195)。
  • 前端傳的是「來源問卷」的編號,會先經過問卷套件的 get_by_uid 讀一次(受問卷表的客戶隔離),再複製一份快照。
  • 所以前端沒有辦法把任意問卷編號直接塞進 ref_id,「把別家問卷拼進自己的任務」這個形狀不成立。
  • 就算佐證連結表沒有隔離,第 190 行查的是問卷表,也會被客戶隔離擋掉。

同一家公司內,拿別的專案的問卷來建快照是可以的,但問卷本來就是客戶層級的共用題庫,這是設計,不是漏洞。

6.2 第 912、1090 行讀填答結果:有沒有綁定「這個任務」?:排除

  • _batch_fetch_task_surveys(:1072)用 TaskSurvey.task_id.in_(job_ids) 過濾,有綁定任務。
  • has_survey_answers(:911)用快照查所有填答。快照是每個任務各自複製一份,而且傳進來的快照編號是從 get_job_survey_evidences(job_uid) 拿到的(survey_handler.py:167),也綁定在這個任務上。
  • 兩張表都有隔離(第 5 節)。

6.3 沒有隔離的表,有沒有哪條查詢「只查那張表」而漏掉?:排除,但記一個前提

逐條看過,每一條都 JOIN 到有隔離的表,或者鍵值是從有隔離的查詢拿來的:

  • list_jobs 在沒帶專案編號時,會退回從 oscal 目錄表開始查(:106),但最後一定 JOIN 流程實例(有隔離)。網址一定帶專案編號,所以這條舊路其實走不到。
  • 🔶 前提要記住:範本翻譯表沒有隔離,而 _write_template_xml(:502)會直接寫入它,只靠 WHERE workflow_template_id = :tid。目前所有呼叫端的 tid 都來自有隔離的查詢(任務本身的 template_id,或 get_template_context_by_uid 的結果)。DEV 22:38 實查:41,519 筆任務的範本編號與流程實例的範本全部一致;範本全部是「客戶自有」,而且與流程實例屬於同一客戶。所以現在安全。但哪天有人新增一個「前端直接帶範本編號」的入口,翻譯表就擋不住了。這一條不開卡,只記在這裡給之後改這支的人。

6.4 開工前四件事

項目 結果
① 權限檢查次數 vs 對外方法數 三支檔的權限檢查次數都是 0,方法數分別是 19/7/2。這是資料存取層,本來就該是 0。真正的檢查在 job_service.py(改、刪、新增有;看詳細、看清單沒有)與 job_import_service.py(匯出、確認有;驗證、重新驗證沒有)
② 同一套接線複製多份、某一份漏了 同一個「任務」有兩組入口:規劃頁單筆(job_route.py)和 Excel 批次(套件的 job_import_route.py)。兩組都漏了同一件事:沒核對任務或計畫屬於網址上的專案。另外,兩組裡的「讀」都沒守
③ 套件以為宿主守、宿主以為套件守 命中一處:jedi-task-platform 的匯入匯出網址只掛「登入+授權模組」,把權限交給宿主;宿主只守了四個入口中的兩個(W4-R2)。另外,DI 有把守門零件注入進去(flow_control_containers.py:422、:439),所以 _require_manager 開頭那條「零件沒給就放行」在正式環境走不到,排除
④ AI 儀表板那條路 di_containers/dashboard_apis/ 沒有申報這三支或任務服務的任何查詢,不適用

7. 這份結果可信到什麼程度

「這幾條存在嗎」:W4-1~W4-6 很可信。

  • 三人面板的 18 票全部判定成立。
  • W4-1~W4-4 三票一致。
  • W4-5、W4-6 有一票給比較低的嚴重度。
  • runner 開檔逐條核對過,行號都對得上。

但全部是讀程式推論,沒有實際打 API 驗證。 第 5 節的資料庫隔離狀態,以及 4.7、6.3 的資料分布,是在 DEV 唯讀實查的(附時間),沒有寫入任何資料。

「只有這幾條嗎」:不保證。 這次用的是最快的檔位(effort low)。W4-R1、W4-R2 是工具沒報、runner 自己追出來的。任務平台其他還沒掃的檔(job_batch_complete_service.py 等,見盤點檔第 131 行)不在本棒範圍內。


8. 執行概況(數字,給工程師看)

項目 數值
掃描範圍 3 檔/1,629 行
基準 commit 9abe5e64e895(工作區有平行 session 的改動)
檔位 effort low,只看正式程式碼(因此多跑一輪密鑰專項)
研究員 派 2 支,回 2 支
原始候選 6 條,去重後 6 條
投票 三位檢查員 × 6 條 = 18 票,全部投出,沒有漏投
票型 6 條都是 3:0 成立;W4-6 的嚴重度由面板從中降為低(低、低、中)
密鑰專項 零發現
驗證章 verified(CLAUDE-SECURITY-REVISION-9abe5e64e895-dirty.json)
工具 run ID wf_cc9b4a46-2c3
耗時 約 22 分鐘(20 個 agent,零失敗;其中 1 個回傳空結果,不影響投票)
工具產出的原始報告 CLAUDE-SECURITY-20260923-143314/(沒有進版控)
報告編號對照 工具 F1~F6 對應本報告 W4-1~W4-6

9. 待首腦裁決

  1. W4-2 的「凍結被繞過」要不要加進 FR-114 卡 2-7 的驗收項? 建議要。卡 2-7 目前寫的是「PUT/DELETE 補核對」,沒提到凍結檢查也要改查任務真正所屬的專案。只補核對其實會一起修好,但驗收時要有人刻意測「對方專案已凍結」這個情境。
  2. W4-3(批次匯入確認)要不要併進卡 2-7? 建議併。同一件事有兩個入口,分開修容易只修好一邊。
  3. W4-5(清單)、W4-6+W4-R2(匯出與匯入驗證)要開新卡,還是併進卡 2-7? 這幾條是同一種病(只看網址上的專案、不核對資源歸屬),但入口在另外兩支檔。建議開一張「任務匯入匯出四入口+清單補檢查」的新卡,和卡 2-7 放在同一批。
  4. 總表登記:建議新增 4 項,分別是 W4-3、W4-5、W4-6、W4-R2;第 63 項的說明補上「凍結被繞過」和「批次匯入是第二個入口」;W4-R1 記進第 63 項的附註,不另計。