FR-162 · Cross-framework Control Mapping — 設計文件 · 2026-10-24

跨框架控制項對應

框架之間的要求對得起來、顧問能調、證據能跨專案沿用;對照存成獨立的 OSCAL Mapping Collection,不塞進任一本 catalog

客戶做完 CMMC 再做 ISO 27001 時,兩套框架有一半的要求其實是同一件事,但現在是兩個獨立專案、證據要重傳一遍。本案建一份跨框架對照庫(控制項層+評估目標層),第一批灌 NIST 官方公布的 CMMC↔︎800-53↔︎ISO 鏈;資源庫有一個對照維護工作台讓顧問調整、匯入匯出;新專案規劃頁有「沿用其他專案的證據」一鍵批次:系統配對全專案評估目標、預覽後一次沿用,沒對應的順便列成差異清單;任務層保留單筆沿用當補救;三個時點主動提醒 user「有符合的證據可以用了」;官方只到控制項層的部分,由 AI 在小範圍內建議評估目標配對、顧問確認。本文件寫定案規則、為什麼這樣定、踩到會怎樣。

設計定案(D1~D8);對照表進 jedi-oscal-v2,決策者已同意 對齊 OSCAL 1.2.2 mapping-collection 雛形:prototype/index.html(三頁) 不做一專案多框架
項目 內容
狀態 已實作(母卡 CM-3057,子卡 25 張皆驗收通過待確認或跳過);第 3 棒依決策者實測直改的規則見下方「控制項關聯管理:現行規則」
日期 2026-10-24
分析 docs/analysis/2026-10-24-cross-framework-control-mapping.md(現況盤點、官方對照資料盤點、被排除方案)
雛形 prototype/index.html(五頁:框架對照工作台、點一顆對應後的對話框、新專案一鍵沿用(舊)、沿用其他專案的證據獨立頁(2026-10-25 定案)、任務層單筆沿用與標籤;雙擊即可開)
前作 FR-038(OSCAL v2 四層架構、source_identifier)、FR-053.1(覆核輪證據承襲)、FR-139/FR-161(資源庫頁與程序書池)
官方資料 決策者桌面 00_Compliance Manager/07_稽核法規文件/Mapping/(OLIR 800-53↔︎ISO 2022、CSF↔︎800-53、ENISA CRA);NIST 800-171/r2/NIST.SP.800-171r2.pdf 附錄 D;主專案 common/util/input/NIST_800-53_to_800-171.xlsx
§1

需求背景與端到端流程

客戶導入合規框架常不只一套:先 CMMC、再 ISO 27001,或加 CRA。系統裡每套框架是一個獨立專案,第二套的證據要重新上傳一遍,user 覺得在重工。NIST 有公布 800-53↔︎ISO 27001 的對照、800-171 附錄 D 有 800-171↔︎800-53 的對照、CMMC 的 practice 編號本身就是 800-171 編號;這些知識現在系統裡沒有落點。

本案要做到:系統知道「框架 A 的這條要求就是框架 B 的那條」,細到評估目標(AO,assessment objective,每條控制項底下要逐一判定的小項);user 做第二套框架時,收證據的畫面直接列出「你在 CMMC 專案對應的評估目標已交過這幾份,要不要沿用」。對照知識有三個來源:官方公布、我們用 AI 分析補、顧問討論後手動調,所以要有工作台能調、能匯入匯出。

%%{init: {'theme':'base','themeVariables':{'primaryColor':'#E2F0F1','primaryTextColor':'#14201F','primaryBorderColor':'#0E7C86','secondaryColor':'#EEF2F3','secondaryTextColor':'#14201F','tertiaryColor':'#FBFCFC','tertiaryTextColor':'#14201F','lineColor':'#4A5A5C','textColor':'#14201F','mainBkg':'#E2F0F1','nodeBorder':'#0E7C86','nodeTextColor':'#14201F','edgeLabelBackground':'#FBFCFC','titleColor':'#14201F','clusterBkg':'#FBFCFC','clusterBorder':'#E4EAEB','actorBkg':'#E2F0F1','actorTextColor':'#14201F','actorBorder':'#0E7C86','actorLineColor':'#C7D1D2','signalColor':'#4A5A5C','signalTextColor':'#14201F','labelBoxBkgColor':'#EEF2F3','labelBoxBorderColor':'#C7D1D2','labelTextColor':'#14201F','loopTextColor':'#14201F','noteBkgColor':'#F6EBD5','noteTextColor':'#14201F','noteBorderColor':'#9C6B12','activationBkgColor':'#EEF2F3','activationBorderColor':'#C7D1D2','sequenceNumberColor':'#FFFFFF'}}}%%
flowchart LR
  subgraph 來源
    O[官方 Excel<br/>OLIR/附錄 D]
    J[OSCAL mapping-collection JSON]
    AI[AI 建議 AO 配對<br/>背景工作]
    C[顧問在工作台調整]
  end
  subgraph 對照庫["對照庫(jedi-oscal-v2,資源庫層跨專案共用)"]
    M[control_mappings<br/>一對框架版本一筆]
    E[control_mapping_entries<br/>控制項層與 AO 層對應邊<br/>官方層/覆寫層分開]
  end
  subgraph 使用
    W[資源庫 對照維護工作台<br/>看、改、匯入匯出]
    T[任務收證據<br/>「沿用其他框架的證據」]
    R[報告引用對照版本]
  end
  O --> M; J --> M; AI --> E; C --> E
  M --> E
  E --> W; E --> T; E --> R
  T -->|勾選沿用| EV[job_evidences 新一筆<br/>同檔、帶來源標記]
圖 1 — 對照怎麼進來、怎麼被用
%%{init: {'theme':'base','themeVariables':{'primaryColor':'#E2F0F1','primaryTextColor':'#14201F','primaryBorderColor':'#0E7C86','secondaryColor':'#EEF2F3','secondaryTextColor':'#14201F','tertiaryColor':'#FBFCFC','tertiaryTextColor':'#14201F','lineColor':'#4A5A5C','textColor':'#14201F','mainBkg':'#E2F0F1','nodeBorder':'#0E7C86','nodeTextColor':'#14201F','edgeLabelBackground':'#FBFCFC','titleColor':'#14201F','clusterBkg':'#FBFCFC','clusterBorder':'#E4EAEB','actorBkg':'#E2F0F1','actorTextColor':'#14201F','actorBorder':'#0E7C86','actorLineColor':'#C7D1D2','signalColor':'#4A5A5C','signalTextColor':'#14201F','labelBoxBkgColor':'#EEF2F3','labelBoxBorderColor':'#C7D1D2','labelTextColor':'#14201F','loopTextColor':'#14201F','noteBkgColor':'#F6EBD5','noteTextColor':'#14201F','noteBorderColor':'#9C6B12','activationBkgColor':'#EEF2F3','activationBorderColor':'#C7D1D2','sequenceNumberColor':'#FFFFFF'}}}%%
sequenceDiagram
  participant U as user(新 ISO 專案,規劃階段)
  participant BE as BE 證據沿用服務
  participant MP as 對照解析(套件)
  participant EV as 其他專案的 job_evidences
  BE-->>U: 規劃頁橫幅:「CMMC L2 專案『鼎盛精密』有 87 個評估目標的證據可沿用」
  U->>BE: 點「查看並沿用」,選來源專案(可多選)
  BE->>MP: 本專案每個 AO 代號 → 對應到來源框架的哪些 AO/控制項?
  MP-->>BE: 每個 AO:目標代號、relationship、confidence、origin、hops
  BE->>EV: 同租戶來源專案中綁到那些代號的任務 → 有效證據
  EV-->>BE: 證據清單
  BE-->>U: 預覽表(每列:本專案 AO/來源/關係/信心/證據數/預設勾選)+「31 個 AO 無對應」
  U->>BE: 調整勾選後按「沿用所選」
  BE->>EV: 每份證據新開一筆 job_evidences:同 file_id、source=REUSED、reused_from_evidence_id、mapping_entry_id
  BE-->>U: 摘要:沿用 142 份到 87 個 AO;專案記 last_evidence_reuse_at
圖 2 — 新專案一鍵沿用(主要入口)
§2

分工概述(30 秒版)

整案一句話:建一份跨框架對照庫並讓顧問可維護,收證據時能把其他框架專案已交的證據帶進來,不再重傳。

批 卡 做什麼 產出 完成怎麼判定(決策者檢查法)
1 1a 套件對照表與匯入匯出 兩張表、解析服務、OSCAL JSON 與 Excel 匯入匯出、CMMC↔︎800-171 規則生成 套件 migration 021/022、服務、套件測試 套件測試綠;jedi_oscal_v2/migrations/021、022 套進 DEV 兩表存在
1 5 OSCAL 版本標記修正 套件 catalog 預設版本改 1.2.2 + 回填 套件一行+ migration 023 DEV oscal.metadata.oscal_version 全部 1.2.2
2 1b BE 接線與灌官方資料 DI wiring、三份官方檔轉接腳本、DEV 灌資料 di_containers 一處、scripts/import_official_control_mappings.py、DEV 有資料 一句 SQL 看對照筆數(CMMC↔︎171 AO 層、171↔︎53、53↔︎ISO);匯出一份 JSON 打開有 mapping-collection;重跑灌資料筆數不變
3 2a 工作台 BE route、能力點、選單 migration、矩陣端點(主軸×欄一次回含路徑)、寬表匯入匯出、error code BE 端點可打 Swagger 打 matrix(axis=ISO, cols=CMMC,CRA,SEMI)回整張;wide export 再 import 筆數不變
4 3a 證據沿用 BE 共用讀模型、五支 API、job_evidences/projects migration BE 端點可打 對 DEV 既有 CMMC 專案與一個 ISO 專案打 preview 回得到配對;打 reuse 後 DB 多 source='REUSED' 筆數=摘要
5 2b 工作台 FE 矩陣視圖:選主軸、勾並排欄,每格膠囊;點膠囊右側面板看路徑/覆寫/停用/歷史;寬表匯入匯出;AI 建議按欄 頁面 主軸 ISO 並排 CMMC/CRA/SEMI 看到矩陣與空格;點一顆看到三跳路徑;覆寫一條→DB 新 curated 列、官方列 disabled;匯出寬表再匯入一致
6 3b 證據沿用 FE(專案層) 規劃頁橫幅提醒+三步 dialog(選來源→預覽→摘要) 元件 新 ISO 專案進規劃頁看到橫幅;選 CMMC 專案,預覽表每列有對應與證據數;沿用後摘要寫「N 份到 M 個 AO、K 個無對應」
7 3c 證據沿用 FE(任務層) drawer 提示列+單筆沿用、沿用 Tag、AR 頁 Tag 元件 任一任務 drawer 看到提示列與沿用證據的「沿用自 CMMC 專案」標籤;AR 判定頁同標籤
7 4 AI 建議配對 背景工作兩模式:B 控制項層初配(CRA/SEMI 對 ISO/CMMC 的起點)、A 評估目標層配對;A 跑完規則回填控制項層關係;工作台下拉兩項與「確認」 handler+工作台兩處 工作台對 CRA↔︎ISO 按「初配控制項」,幾分鐘後控制項層出現 ai-suggested 列;確認幾條後按「配對評估目標」,AO 層出現列且控制項層關係被回填;抽三條人工看合不合理

依賴一句:1b 等 1a;2a、3a 都等 1b 但不能同批(兩張都改 flow_control_error_code.py 與 manifest.tsv 這類登記型檔);2b/3b/3c 同理不能同批(都改 api.js、locales、error-code.json);卡 4 等 2b;3c 與 4 可同批(不同 repo 不同檔)。卡 5 與 1a 同批(套件不同檔)。決策者功課:驗收卡 1 前把 Mapping/ 三份檔案留在桌面原位,runner 要讀。

§3

決策定案表

# 決策 定案 理由與被排除方案
D1 對照存哪 獨立 mapping 表,放 jedi-oscal-v2(決策者 2026-10-24 裁「這應該是套件功能」) OSCAL Control Layer 三個平行 model:Catalog/Profile/Mapping Collection,對照本來就不在 catalog 裡。排除「塞 catalog_controls.links」:單向、無關係型別、無信心度,框架改版等於重寫 catalog;與 2026-10-10 CRA 裁示衝突。排除「復活 v1 oscal_control_mapping」:只有邊、無關係型別/AO 層/來源。
D2 鍵用什麼 控制項代號 control_id(A.5.1/AC.L2-3.1.1)+ AO part_id(A.5.1_obj.3),不綁數字 id 資源庫 clone、專案 clone 會長新 id(DEV 14 個 catalog 只有 5 個母版),綁 id 每 clone 一次要複製對照。綁代號則母版與副本共用一份。官方長寫法(ISO/IEC 27002:2022 5.1)存 remarks 供報告引用。
D3 粒度 雙粒度:同一張表容納控制項層與 AO 層;解析時 AO 層有就用,沒有退控制項層並降一級信心度 官方全是控制項層;AO 層由 CMMC↔︎800-171 規則生成、AI 建議、顧問補。排除「只做控制項層」:ISO 一條 2~3 個 AO、CMMC 3~6 個,推導出的候選雜訊多一倍。排除「只做 AO 層」:無官方背書、715 個 AO 全人工。
D4 證據怎麼重用 跨專案建議沿用,不做一專案多框架(決策者裁 OK) 一專案多框架要動 resolver、SSP 匯出、AP 草稿、AR 判定、報告包全部,且 ISO 有 SoA、CMMC 沒有。沿用時**新開一筆 job_evidences**同 file_id(檔案本體一份、不複製;證據紀錄各專案一筆),比照覆核輪承襲(inherited_from_round_id)形狀;對 user 是「關聯」——來源換新版不自動跟(已凍結的稽核證據不可變動),要跟就再帶一次。排除「共用同一筆」:凍結/刪除/覆核承襲語意打結。
D5 官方與自家改動怎麼共存 官方層與覆寫層分開:origin 區分 official/third-party/curated/ai-suggested(third-party=OSA、SunSquare 這類二級來源灌入,與 official 一樣是「既有可信起點」,不覆蓋),顧問改官方條時不改原列,另存 curated 列並記 overrides_entry_id;解析時覆寫層優先 官方更新重匯時不沖掉顧問調過的;畫面看得出哪些是自己改的。
D6 匯入語意 更新不覆蓋:同鍵覆蓋、新鍵新增、檔內沒有的不刪;要回到原廠資料用「還原出廠設定」(主軸 × 一欄,二次確認) 比照框架 Excel 匯入既有庫只增不刪。「以檔案取代整欄」不提供:跟匯入重複、又會硬刪紀錄。
D7 800-53 這種我們沒有 catalog 的中繼框架 允許對照的來源/目標指向「只有代號沒有 catalog」的框架版本:在 oscal.frameworks/framework_versions 建 NIST 800-53 R5 與 800-171 r2 兩筆,catalog_id 留 NULL(欄位本來可空) 800-53 是所有官方對照的樞紐。排除另開「參考框架表」:既有表欄位夠用,多一張表多一套 join。畫面上 catalog_id NULL 的框架標「參考用,無目錄」。
D9 沿用的入口與形狀 專案總覽「證據作業」下拉 → 獨立頁;一個來源專案一份沿用設定,隨時調整,不做批次(決策者 2026-10-25 改判,取代「規劃頁三步對話框」) 入口:專案總覽一顆「證據作業」下拉,收「上傳檔案,AI 自動分類」「從雲端資料夾分類」「沿用其他專案的證據」三項(同屬收證據動作,放一起才找得到);規劃頁的常駐按鈕與橫幅移除。頁面比照自動分類:首頁=來源專案列表(一列一個來源:目前沿用檔數→評估目標數、被判定引用數、最後調整、ⓘ 調整紀錄彈窗、「調整」「全部取消」);「選擇來源專案」只列尚未選過的;調整頁=三欄可拖(來源框架導航/該節點證據檔/歸到本專案哪些控制項與評估目標),右欄複用自動分類的 PlacementTable+AOPickerDialog,預設勾「通用」(評估目標層與控制項層一視同仁:控制項層對應=來源控制項全部評估目標都適用;已細分的列不算)、「部分可用」在候選區不勾、可加任何一條;右欄依控制項分組;中欄檔案可預覽;「一鍵勾選通用對應(N)」一次勾好再微調,N=0 時停用並說明原因;「套用變更(+N/-M)」一次存。全部取消=該來源帶進來的全拿掉,被稽核判定引用的擋下不刪並列出。每次調整留唯讀紀錄(誰、何時、+N/-M、擋下幾筆),走既有稽核事件機制,不可復原某一次。決策者原話:「同一個專案不要重複沿用,就是只能取消全部,不要做這麼複雜,就是一直去那個裡面調整對應結果就好」「我們要有記錄就好」。雛形 prototype/project-reuse-page.html。命名(2026-10-24 定):畫面「證據沿用」,程式與 DB 一律 reuse;覆核輪仍叫「承襲」。沿用的是證據檔不是判定結果。
D10 怎麼提醒 user 兩個提醒點:① 證據作業下拉旁一行小字「偵測到〈專案〉有 N 個評估目標的證據可沿用」(含來源新增證據時的「還有 N 個新證據」);② 任務 drawer 證據區提示列+常駐「沿用」按鈕 原規劃頁橫幅(2026-10-24 D10①③)隨入口搬家併成總覽頁一行小字,不再做可關閉橫幅;沿用列表首頁上方也顯示同一句並附「去調整」。drawer 單筆沿用維持。
D11 「其他專案」的範圍 當前使用者看得到的專案(與專案列表同一套可見性:owner 或參與者,admin 全部),再排除封存、已刪除、本專案(決策者 2026-10-25 改判,取代「第一版=同租戶」) 2026-10-25 實查:DEV 租戶 102 的鼎盛 CMMC L2 專案屬另一組織單位(176),使用者專案列表只有 3 個,沿用來源卻列出它且能拉它的證據——同租戶範圍是跨組織單位資料外洩;也解釋 CM-3072 那次 3,599 筆爆量混進鼎盛 204 份檔。RLS 只擋租戶,可見性在應用層:canonical 是 infra/readmodel/audit/flow_control_dashboard_repo_impl.py:_visible_project_ids(owner/_related_project_ids_subq/admin),沿用來源改用它。封存(status='archived')原本也沒排除。資訊系統過濾仍待 ISO 資產設計。
D8 評估目標層與無官方對照的框架怎麼產 AI 兩種模式+規則回填:模式 A(AO 層)對控制項層已對上的組,兩邊 AO prose 兩兩比;模式 B(控制項層初配)給一對框架版本,AI 讀控制項標題+敘述做初配,候選用提示對(ENISA/第三方對照、顧問勾選)或先選 group 兩階段縮小;模式 A 跑完用明確規則回填控制項層關係型別(AO 全等價→等價、全不相關→不相關、其餘部分重疊)。CMMC↔︎800-171 r2 仍規則生成。顧問在工作台確認後才算正式 決策者 2026-10-24 指出:官方資料只讓 CMMC/800-171/ISO 對得起來,CRA 與 SEMI 連控制項層都空,只有模式 A 時它們沒起點,第一版進不了沿用流程;且官方全不給關係型別,D9 的預設勾選對官方鏈幾乎全空。重查後 CRA 有 OSA cra.json(→800-53,CC BY-SA)、SEMI 有 SunSquare 表 2(→IEC 62443)兩份二級來源可灌,初配只補 OSA 未覆蓋的 CRA Article 13/Annex VII 與 62443-4-1 五個 practice(見 docs/analysis/2026-10-24-mapping-sources-for-frameworks-without-official-crosswalk.md,含來源三級分級)。不做 AO 全配(六萬對)。AI 準確率未實測,報告輸出必標來源;AO 的 _obj.N 是我們依匯入順序編的非官方編號,mapping 綁框架版本、改版即新對照。
D12 對照的層級 只能同層:控制項對控制項、評估目標對評估目標(決策者 2026-10-25 裁,10-26 補實作規則)。後端 add_entry 一處把關:主軸評估目標寫到目標控制項的直連自動展開成逐條評估目標,其餘跨層 400 GRC_400189。解析器補一條:排除了某控制項的任一評估目標,同一控制項的控制項層命中也不再成立 矩陣、證據沿用、Excel 共用同一套解析,開「評估目標→控制項」例外,每個讀取端都要各自認。少了解析器那條,逐條標評估目標不可用擋不住多跳退回的控制項層,只能寫跨層列。決策推理見 docs/analysis/2026-10-26-fr162-same-level-mapping-and-evidence-delete-semantics.md
D13 刪除沿用來的證據 只動本專案:同專案多處掛同一份檔可選「只這一處/本專案全部」;沿用來的那筆刪了寫 deleted_reason='NOT_APPLICABLE'=本專案此處不適用,沿用建議不再帶回、調整頁可勾回;來源刪除下游照常可用,標「來源已移除」;刪掉的證據本專案不再給下載(決策者 2026-10-26 裁) 排除「連帶刪除其他專案」:跨過權限與輪次狀態,原上傳者無權替其他專案決定。排除「用 USER_DELETED 判不適用」:調整頁拿掉與歷史清理共用這個值,整輪建議會消失。排除「沿用或刪除時複製檔案」:檔案不可改、軟刪不刪檔、外鍵保護,複製只多儲存空間。job_evidences 不開 RLS(背景與系統脈絡路徑太多)。推理同上份分析
§4

現況接入點盤點

元件 現況 本案動作
jedi_oscal_v2 catalog 層 catalog_controls.source_identifier 是同框架跨版本代號;catalog_control_parts.name='assessment-objective'、part_id 格式 <control_id>_obj.N 不改。對照表鍵對 control_id 與 part_id
jedi_oscal_v2 profile 層 ProfileResolutionService.resolve 已迭代多筆 import 不改
jedi_oscal_v2/app/service/io/oscal_io_service.py 匯入匯出 catalog/profile/SSP/AP/AR/POA&M,無 mapping 加 mapping-collection 匯入匯出(卡 1)
jedi_oscal_v2/migrations/ 到 020 加 021(兩表)、022(800-53/800-171 參考框架版本 seed)、023(oscal_version 回填,卡 5)
jedi_oscal_v2/app/service/catalog/catalog_service.py:90 預設 oscal_version="1.1.2",DEV 53 筆錯標 改 1.2.2(卡 5)
主專案 di_containers/oscal/oscal_containers.py wiring 套件服務(providers.Factory) 加 mapping 相關 service(卡 1)
主專案 api/oscal/routes/resource_library_route.py、能力點 oscal.resource-library.* 四顆 CRUD 加 oscal.control-mapping.read/manage(卡 2);寫入另過 require_tenant_hq(對照全站共用,只開放總部改)
public.ui_routes 側選單走 migration 登記(FR-161 先例 2026-10-23-fr161-pre-engagement-capabilities.sql) 加「控制項關聯管理」入口,掛在「合規稽核」群組(卡 2)
compliance.job_evidences source CHECK 四值;inherited_from_round_id 承襲標記 source 加 REUSED;加 reused_from_evidence_id、mapping_entry_id(卡 3,主線 migration)
public.workflow_execution_control_mapping 任務 ↔︎ control_id+ao_part_id+round_id,證據→AO 的權威鏈 不改,卡 3 讀它找「對應 AO 的任務」
infra/readmodel/audit/ar_evidence_candidate_query.py 既有「某控制候選證據池」讀模型 卡 3 新讀模型照它的 scope 紀律(二次用 wecm scope)
FE src/components/grc/JobExecutionDrawer.vue(2,360 行,超標) 證據區塊在 job-drawer/JobEvidenceLinksSection.vue,承襲標籤 inherited_from_prev_round 新區塊新開 job-drawer/JobEvidenceReuseSection.vue,不往 drawer 塞(卡 3)
FE src/views/module_frame/、src/config/router/index.js、src/config/capabilities.js 資源庫列表頁與範本編輯頁 新頁 src/views/control-mapping/(卡 2)
背景工作 app/background_job/handler_registry.py、AI 閘道 core/plugins/_source_scan_ai.py 範式 @register_job_handler;gateway.complete(req) 經 select_model_for_current_tenant 卡 4 照抄
§5

詳細設計

資料表(套件 jedi_oscal_v2/migrations/021-control-mappings.sql)

-- 一對框架版本一筆(對齊 OSCAL mapping)
CREATE TABLE oscal.control_mappings (
  id                          BIGSERIAL PRIMARY KEY,
  uuid                        UUID NOT NULL UNIQUE,
  source_framework_version_id BIGINT NOT NULL REFERENCES oscal.framework_versions(id),
  target_framework_version_id BIGINT NOT NULL REFERENCES oscal.framework_versions(id),
  title                       TEXT NOT NULL,
  method                      VARCHAR(64),            -- OSCAL provenance.method(manual / tool-assisted / rule)
  matching_rationale          VARCHAR(32),            -- syntactic / semantic / functional(IR 8477)
  status                      VARCHAR(20) NOT NULL DEFAULT 'draft',   -- draft / published / deprecated
  provenance                  JSONB,                  -- 來源文件、頁次、網址、匯入檔名、匯入時間
  remarks                     TEXT,
  created_at / updated_at / created_user / updated_user,
  UNIQUE (source_framework_version_id, target_framework_version_id)
);

-- 一筆對應邊(對齊 OSCAL map)
CREATE TABLE oscal.control_mapping_entries (
  id                   BIGSERIAL PRIMARY KEY,
  uuid                 UUID NOT NULL UNIQUE,
  mapping_id           BIGINT NOT NULL REFERENCES oscal.control_mappings(id) ON DELETE CASCADE,
  source_level         VARCHAR(10) NOT NULL CHECK (source_level IN ('control','objective')),
  source_control_code  VARCHAR(128) NOT NULL,          -- = catalog_controls.control_id
  source_part_id       VARCHAR(255),                   -- objective 時 = catalog_control_parts.part_id
  target_level         VARCHAR(10) NOT NULL CHECK (target_level IN ('control','objective')),
  target_control_code  VARCHAR(128) NOT NULL,
  target_part_id       VARCHAR(255),
  relationship         VARCHAR(20) NOT NULL CHECK (relationship IN
                         ('equivalent-to','subset-of','superset-of','intersects-with','not-related-to')),
  confidence           NUMERIC(3,2) CHECK (confidence BETWEEN 0 AND 1),
  origin               VARCHAR(16) NOT NULL CHECK (origin IN ('official','third-party','curated','ai-suggested')),  -- third-party 由套件 024 補
  status               VARCHAR(16) NOT NULL DEFAULT 'active' CHECK (status IN ('active','disabled')),
  overrides_entry_id   BIGINT REFERENCES oscal.control_mapping_entries(id),  -- curated 蓋 official 時指回原列
  remarks              TEXT,
  created_at / updated_at / created_user / updated_user,
  UNIQUE (mapping_id, source_control_code, source_part_id, target_control_code, target_part_id, origin)
);
CREATE INDEX ON oscal.control_mapping_entries (mapping_id, source_control_code);
CREATE INDEX ON oscal.control_mapping_entries (mapping_id, target_control_code);
-- GRANT ... TO cm_app + sequence 權限;兩表非 tenant-scoped(對照是產品知識,跨租戶共用),不掛 RLS

變更紀錄不另開表:control_mapping_entries 只增不改,顧問改一條=原列 status='disabled' + 新列 overrides_entry_id 指回;誰改、何時在新列的 created_user/created_at。工作台「歷史」就是沿 overrides_entry_id 往回走。

參考框架(022-reference-framework-versions-seed.sql)

在 oscal.frameworks 建 NIST_SP_800_53_R5(authority NIST)、NIST_SP_800_171_R2(authority NIST),各一個 framework_versions,catalog_id NULL。ON CONFLICT (uid) DO NOTHING,uid 寫死在 migration。

各框架怎麼轉到 ISO 27001:轉乘路線

五個產品框架之間沒有任何一組直接對照,全部經參考框架中轉;NIST SP 800-53 R5 是唯一通往 ISO 的匝道(ISO 只與 800-53 有官方對照,即 NIST OLIR)。參考框架只有代號沒有目錄,矩陣預設隱藏,解析結果一律帶 hops 與每跳 origin,顧問看得出一條對應是怎麼來的。

%%{init: {'theme':'base','themeVariables':{'primaryColor':'#E2F0F1','primaryTextColor':'#14201F','primaryBorderColor':'#0E7C86','secondaryColor':'#EEF2F3','secondaryTextColor':'#14201F','tertiaryColor':'#FBFCFC','tertiaryTextColor':'#14201F','lineColor':'#4A5A5C','textColor':'#14201F','mainBkg':'#E2F0F1','nodeBorder':'#0E7C86','nodeTextColor':'#14201F','edgeLabelBackground':'#FBFCFC','clusterBkg':'#FBFCFC','clusterBorder':'#E4EAEB'}}}%%
flowchart LR
  subgraph P["產品框架(有 catalog)"]
    L1[CMMC 2.13 L1]
    L2[CMMC 2.13 L2]
    CRA[CRA 2024/2847]
    SEMI[SEMI E187]
  end
  subgraph R["參考框架(只有代號,無 catalog)"]
    N171[NIST SP 800-171 r2]
    N53[NIST SP 800-53 R5]
    I62[IEC 62443]
  end
  ISO[ISO/IEC 27001:2022]
  L1 -->|"恆等規則<br/>控制項+AO 層"| N171
  L2 -->|"恆等規則<br/>控制項+AO 層"| N171
  N171 -->|"800-171 r2 附錄 D"| N53
  N53 -->|"NIST OLIR 2023-10-12"| ISO
  CRA -.->|"OSA cra.json"| N53
  SEMI -.->|"SunSquare 表 2"| I62
  I62 -.->|"800-82 r3 附錄(官方)<br/>或 OSA(社群)"| N53
圖 3 — 轉乘路線:實線=官方(official),虛線=二級來源(third-party)
起點 到 ISO 幾跳 經過 每跳來源 備註
CMMC L1/L2 3 800-171 → 800-53 恆等規則 → 附錄 D → OLIR,全官方 唯一有 AO 層的一跳在 CMMC↔︎800-171,過了 800-53 退回控制項層
CRA 2 800-53 OSA(二級)→ OLIR(官方) OSA 不涵蓋 Article 13 與 Annex VII 共 17 條,交 AI 初配
SEMI E187 3 62443 → 800-53 SunSquare(二級)→ 800-82 r3 附錄(官方,待取得)或 OSA(二級)→ OLIR(官方) 至少一跳二級,信心最低;三個來源都不含 62443-4-1 五個 practice,交 AI 初配
CMMC ↔︎ CRA 3 800-171 → 800-53 兩邊各自匯到 800-53 再接 不經 ISO

每一跳的資料從哪來、有多可信(決策者 2026-10-24 追問「資料怎麼來的、什麼網站、什麼 PDF、有公信力嗎」):

跳 來源名稱 誰發的 檔案/網址 粒度 公信力 灌入時標
CMMC → 800-171 r2 CMMC 2.13 模型本身 美國國防部(DoD CIO) 《CMMC Model Overview v2.13》《Assessment Guide L2 v2.13》,dodcio.defense.gov/CMMC;桌面 07_稽核法規文件/CMMC 2.0/ 控制項+AO(practice 編號=800-171 編號、AO=800-171A 判定句) 最高:不是對照表,是定義本身 official,規則生成
800-171 r2 → 800-53 R5 800-171 r2 附錄 D NIST 《NIST.SP.800-171r2.pdf》附錄 D 表 D-1~D-14;桌面 NIST 800-171/r2/;表格版 BE common/util/input/NIST_800-53_to_800-171.xlsx 控制項 高:NIST 自發,但 NIST 自註「informational」、ISO 欄是 2013 版不用 official
800-53 R5 → ISO 27001:2022 NIST OLIR Informative Reference #155 NIST(National Online Informative References 計畫) sp800-53r5-to-iso-27001-mapping-2022-OLIR-2023-10-12-UPDATED.xlsx;入口 csrc.nist.gov/pubs/sp/800/53/r5/upd1/final「Supplemental Material」;桌面 Mapping/ 控制項(含 enhancement) 高:NIST 自發、有 IR 8477 方法論與 OLIR 審查流程;但不給關係型別(Strength 欄全空)。ISO 自己沒發對 NIST 的對照 official
CRA → 800-53 R5 OpenSecurityArchitecture osa-data 社群:OSA 是 2008 年起的開源資安架構志工專案(opensecurityarchitecture.org),GitHub 組織 opensecurityarchitecture/osa-data;無署名作者、無標準組織審核 官網 https://www.opensecurityarchitecture.org ;資料庫 https://github.com/opensecurityarchitecture/osa-data ,檔 data/framework-coverage/cra.json(CC BY-SA 4.0);桌面 Mapping/osa-cra-to-sp800-53r5.json;repo scripts/data/osa_cra_to_sp800-53r5.json(與桌面逐 byte 相同) 條→控制項清單,附 coverage % 與 rationale 中偏低:每條有理由可逐條核,但 coverage 是估的、頁面自註「no line-by-line verification recorded」;稽核報告寫「依 OSA」會被問「OSA 是誰」。只當顧問起點,確認後才算 third-party
CRA 官方對照(不灌) ENISA《CRA implementation via EUCC》附錄 ENISA(歐盟網路安全局) ENISA-CRA-implementation-via-EUCC-annex-2025-02.pdf(93 頁,2025-02);桌面 Mapping/ Annex I 每條 → Common Criteria SFR 高,但對的是 ISO/IEC 15408 不是 ISO 27001/800-53,我們無 CC catalog 不灌,留證
CRA 正式對照(未出) CEN/CENELEC prEN 40000-1-4 歐洲標準組織(受 EC 標準化請求 M/606) cencenelec.eu;預計 2027-10 Annex I Part I 2(a)~(m) 技術要求目錄 出來後是唯一正式依據 出來即換
SEMI E187 → IEC 62443 SunSquare Tech 文章表 2 第三方個人:SZ Lin(林上智),SunSquare Tech;同一作者在 SEMI 官網掛過《ISA/IEC 62443 Status Updates》簡報,有 SEMI 資安工作組背景,但文章與簡報都非 SEMI 正式出版 https://sunsquare.tech/2026/07/08/understanding-the-relationship-between-semi-e187-and-isa-iec-62443-in-equipment-security-implementation/ (表格是圖片);桌面 Mapping/SunSquare-image*.webp;抄錄在 analysis §四 E187 12 條 → 62443-4-1 practice/3-3 SR/4-2 CR 中偏低:作者明標「author's interpretation … not an official cross-standard specification」;對應邏輯與 METI 指引、E187 條文一致。SMCC WG4 官方比較分析出來即換 third-party
SEMI 其他(提示用) METI《半導體工廠 OT 安全指引》v1.0 日本經濟產業省 semiconductor_systems_guideline_ver1.0_eng.pdf(132 頁,2025-10-24);桌面 Mapping/ 風險對策 → NIST CSF 2.0 子類別,E187 以章節名引用 高(政府),但對 CSF 非 ISO、E187 非獨立欄 不灌,AI 初配提示
SEMI 官方(確認無對照) SMCC《E187 Compliance Guidance》白皮書 SEMI SMCC WG1/2 Manufacturing_Cyber_Security_White_Paper_FINAL070725.pdf(18 頁,2025-07);桌面 Mapping/;數位產業署《查核規範》v1.0(23 頁)桌面 Semi E187/ 12 條 RQ 的 rationale 與範例 高,但全文零他框架引用 不灌
IEC 62443 → 800-53 R5 ① NIST SP 800-82 r3 附錄 ② ISAGCA 62443↔︎CSF + NIST CSF↔︎800-53 xlsx ③ OSA iec-62443.json ① NIST ② ISA 全球資安聯盟+NIST ③ 社群 ① nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-82r3.pdf(待取得)② isagca.org + 桌面 Mapping/csf-pf-to-sp800-53r5-mappings.xlsx ③ https://github.com/opensecurityarchitecture/osa-data 的 data/framework-coverage/iec-62443.json,repo scripts/data/osa_iec62443_to_sp800-53r5.json(桌面無此檔,runner 直接取自 GitHub) 62443-3-3 SR → 800-53 ① 高(800-82 是 800-53 的 OT overlay)② 高但兩跳 ③ 中偏低 ① official ②③ third-party;沒有 ISA 或 NIST 直接發的官方對照,三者都不含 62443-4-1 practice

公信力的實務判準:「稽核報告引用這條時,客戶或驗證機構會不會問『這是誰說的』」。NIST、DoD、ENISA、METI 不會被問;OSA、SunSquare 會,所以它們只能是顧問看哪幾條的起點,顧問在工作台確認過轉 curated 才進報告,報告寫「顧問確認,參考來源 X」。三級分級的完整定義見 analysis §二。

DEV 實際灌了哪幾冊(2026-10-24 實查 oscal.control_mappings;origin 四值:official/third-party/curated/ai-suggested,二級來源自 CM-3068 起標 third-party,curated 保留給顧問覆寫與確認):

mapping 來源 → 目標 控制項層 AO 層 origin provenance 現況
90 CMMC L2 → 800-171 r2 110 320 official(規則生成) 記規則文字
129 CMMC L1 → 800-171 r2 17 59 official(規則生成) 記規則文字;L1 的 AO 以文字比對對應 L2 practice 序號
128 800-171 r2 → 800-53 R5 198 0 official 記「附錄 D」,無網址
127 800-53 R5 → ISO 27001:2022 643 0 official 記 OLIR #155 檔名與日期,無網址;Clause 4~10 列 remarks='clause'、status='disabled'
130 CRA → 800-53 R5 179 17 third-party 記 OSA 檔名、GitHub 網址、CC BY-SA 4.0
131 SEMI E187 → IEC 62443 26 0 third-party 記「SunSquare 2026-07-08 Table 2」與 CSV 檔名,無文章網址
132 IEC 62443 → 800-53 R5 65 0 third-party 記 OSA 檔名、GitHub 網址、CC BY-SA 4.0
267 CMMC L2 → ISO 27001:2022 229 684 ai-suggested+curated AI 初配(CM-3066 跑出);這冊是直達路線,有了它 CMMC→ISO 不必經 800-53
268 CRA → ISO 27001:2022 64 0 ai-suggested AI 初配

三冊 official/third-party 的 provenance 缺網址(128、127、131),報告引用時無法一鍵指路;補法是在灌資料腳本的 provenance 加 url 後重跑(冪等,只更新不新增),屬小修,要不要開卡由決策者裁。

踩到會怎樣:跳得越多對得越鬆。ISO 一條常對 800-53 多條、800-53 一條又對 800-171 多條,三跳後一條 CMMC 控制項可能對到 ISO 四五條,含雜訊;因此官方鏈第一版關係一律 intersects-with、信心留空,靠顧問在工作台確認或 AI 補關係型別。路線不是寫死的:顧問或 AI 直接建一冊「CRA ↔︎ ISO」後,解析服務會走一跳直達,800-53 自動退為備援。來源分級與二級來源的可信度判準見 docs/analysis/2026-10-24-mapping-sources-for-frameworks-without-official-crosswalk.md §二。

解析規則(套件 ControlMappingResolutionService)

輸入:來源框架版本 id、來源 control_id、來源 part_id(可空)、目標框架版本 id 清單(空=全部)。輸出:[(目標框架版本, 目標 control_id, 目標 part_id 或 None, relationship, confidence, origin, 幾跳)]。

  1. 只看 status='active' 且沒被 overrides_entry_id 指到的列(被蓋的官方列自動退場)。
  2. 直連優先:source→target 或 target→source 反向都算(對照雙向等價)。
  3. 先找 AO 層(source_part_id 相符);沒有才找控制項層,控制項層命中時回傳 target_part_id=None 並把 confidence 乘 0.7(常數放 common/constant,可調)。
  4. 多跳(CMMC→800-171→800-53→ISO):最多三跳,每跳 confidence 連乘,relationship 取最弱(equivalent-to > superset/subset > intersects-with),回傳 hops。
  5. not-related-to 是明示排除,命中即從結果拿掉,不往下跳。控制項層的排除涵蓋該控制項全部評估目標;反過來,排除了某控制項的任一評估目標,同一控制項的控制項層命中(「全部評估目標適用」)也不再成立。少了後半條,評估目標列逐條標不可用擋不住多跳的控制項層退路,只能寫跨層排除列。

匯入匯出(套件 OscalIoService 擴充+ControlMappingExcelService)

  • OSCAL JSON:mapping-collection 1.2.2 形狀;mapping-resource-reference.href 寫框架版本 uid;mapping-item.type 用 control 或 part;provenance 必填照 D1。匯入時框架版本 uid 對不到 → 400 列出缺哪個。
  • Excel/CSV 固定範本(下載空白範本):欄位 source_framework_code, source_version, source_control_code, source_part_id, target_framework_code, target_version, target_control_code, target_part_id, relationship, confidence, remarks。匯入時 control_code 不存在於該框架 catalog(參考框架除外)→ 整檔退回列出錯行,不半寫。
  • 官方檔轉接器(卡 1 一次性,放主專案 scripts/):OLIR xlsx(20 sheet、A 欄 800-53、D 欄 ISO,Clause 條文列先存著標 remarks='clause')、NIST_800-53_to_800-171.xlsx(B 欄逗號多值拆列)、CMMC↔︎800-171 規則生成(practice XX.Ln-a.b.c → a.b.c,AO _obj.N 同序號 equivalent-to)。三者 origin='official'、relationship='intersects-with'(官方不給型別,CMMC↔︎171 例外為 equivalent-to)、confidence NULL、method='manual'、provenance 記檔名與日期。
  • 匯入語意照 D6;「還原出廠設定」是獨立 API POST /oscal/control-mappings/factory-reset 帶 confirm=true。

證據沿用(主專案,卡 3a/3b/3c)

共用讀模型 infra/readmodel/audit/evidence_reuse_candidate_query.py:

  • 專案 → module_frames.oscal_framework_version_uid → 來源框架版本(專案 clone 的 catalog framework_version_id 是 NULL,必須走 module_frame 回溯,2026-10-24 實查五個專案皆可回溯)。
  • 本專案每個 AO(workflow_execution_control_mapping 的 control_id/ao_part_id,取當前輪)→ 套件解析 → 目標代號清單(含 relationship/confidence/origin/hops)。
  • 同租戶(D11)、user 選定的來源專案:其 workflow_execution_control_mapping 命中目標 control_id(AO 層命中再縮 ao_part_id)→ job_evidences(is_deleted=false、排除 source='REUSED' 避免循環、排除本專案已有同 file_id 的)。
  • 輸出每列:本專案 AO、來源專案、來源控制項/AO、relationship、confidence、origin、hops、證據清單(uid、檔名、描述、上傳時間、來源專案該 AO 的最新判定供參考)、default_checked(D9 規則)。
  • 單一 job_execution_id 版本=同一支加 filter,供 drawer。

API(api/flow_control/routes/evidence_reuse_route.py;2026-10-25 改判後的形狀,舊的 sources/preview/reuse/new-since-last 由新端點取代或改語意):

  • GET /projects/<uid>/evidence-reuse/sources:可見(D11)、未封存、未刪、非本專案的來源,每筆帶框架、可對應 AO 數、證據檔數、authorization_boundary_description(living SSP 的 ssp_system_characteristics)、reused_count(已沿用檔數/AO 數)、cited_count(已被判定引用)、last_adjusted_at/by、can_pick(沒有 AO 層對照者 false 並附原因)。首頁列表與選擇頁共用。
  • GET /projects/<uid>/evidence-reuse/<source_uid>/tree:來源框架導航(群組→控制項→AO,各節點證據檔數)與每檔的「本專案預設歸檔」(對照表配到的 AO 層通用列)與「其他可能」(部分可用)。
  • PUT /projects/<uid>/evidence-reuse/<source_uid> body=期望的最終集合 [{evidence_uid, job_execution_uid, mapping_entry_id|null}];BE 算差集:新增寫、拿掉軟刪、被判定引用的拿不掉回在 blocked;回 {added, removed, blocked} 並寫一筆紀錄事件。
  • DELETE /projects/<uid>/evidence-reuse/<source_uid>:全部取消,同樣跳過被引用並回報;寫紀錄事件。
  • GET /projects/<uid>/evidence-reuse/<source_uid>/history:該來源的調整紀錄(稽核事件 EVIDENCE_REUSE_ADJUSTED/EVIDENCE_REUSE_CLEARED,payload 帶數字)。
  • GET /jobs/<uid>/evidence-reuse、POST /jobs/<uid>/evidence-reuse/reuse:drawer 單筆,維持。
  • 權限:專案 manager 或任務指派人(既有 project-role 守門);頁面入口與自動分類同 PROJ.EVIDENCE_MANAGE。

寫入:每筆新開 job_evidences:同 file_id、source='REUSED'、reused_from_evidence_id、mapping_entry_id、description 帶「沿用自〈專案〉〈控制項 AO〉」。同任務同 file_id 已存在(既有唯一索引)→ 跳過並計入摘要。檔案本體不複製:沿用列直接指向同一份檔,不經過來源那筆證據。

刪除與來源移除(決策者 2026-10-26 裁):

  • 刪除只動本專案,從不連帶其他專案。 任務刪除前先打 GET /job-evidence/<uid>/delete-impact,回本專案同一輪同一份檔(只比 file_id,不擴到同內容另存的檔)另掛的評估目標,以及其他專案名。其他專案只告知、不動。
  • 本專案多處掛同一份檔時,使用者選範圍:DELETE /job-evidence/<uid>?scope=this|project。project=本專案當前輪同一份檔的全部有效證據,逐筆照現行守門(被指派人/專案 manager、輪次未結束),擋下的留著並在 kept 回報。前端 job-drawer/JobEvidenceDeleteDialog.vue,只有本專案另有掛載時才出兩個選項。
  • 沿用來的那筆被任務刪除=本專案此處不適用:寫 deleted_reason='NOT_APPLICABLE'(自己上傳的照舊 USER_DELETED)。任務層候選、一鍵沿用、橫幅「有新證據」與寫入前比對都排除這組(任務, 檔案);調整頁不排除,照常列出、目標標 dismissed、不預設勾,勾回照常寫入。陷阱:調整頁拿掉、全部取消、歷史清理也都是 USER_DELETED(DEV 當前輪有 3,738 筆),拿它當不適用會讓整輪沿用建議一起消失,所以不適用必須是獨立的原因值。
  • 來源被刪,下游照常可用:檔案從不因刪證據被刪(軟刪;job_evidences.file_id 對 upload_files 有外鍵,還有人指著就刪不掉);硬刪來源專案只刪該專案自己的證據列。下游標籤改「沿用自〈專案〉(來源已移除)」,來源那筆軟刪、硬刪或來源專案已刪都算(reused_source_removed,任務抽屜與判定頁共用同一支純量 SQL)。
  • 檔案只有一份、不複製:上傳後內容不可改,複製與關聯只差儲存空間;複製會打掉 sha256 去重與「也掛在 N 處」,所以不做。唯一會改判的情況是客戶要求「刪專案時連檔案一起徹底清掉」,那時要在沿用當下複製,不是刪除時才複製。
  • 跨租戶:來源只從「使用者看得到的專案」取(RLS 擋跨租戶,程式層擋同租戶看不到的組織單位);寫入前後端重算候選,前端偽造的證據 uid 寫不進去。job_evidences 本身沒開 RLS,靠 join 有 RLS 的表擋;評估過開 RLS,背景與系統脈絡路徑太多,沒做。

Migration(主線 phase=active,回寫時明講出貨基線待重產):job_evidences.source CHECK 加 REUSED;加 reused_from_evidence_id INT、mapping_entry_id BIGINT(soft-ref);compliance.projects 加 last_evidence_reuse_at TIMESTAMPTZ;job_evidences.deleted_reason CHECK 加 NOT_APPLICABLE(2026-10-26-fr162-job-evidences-not-applicable.sql)。

FE 專案層(2026-10-25 改判,取代 3b 的規劃頁對話框):路由比照自動分類:…/round/:roundUid/evidence-reuse(首頁列表)、…/evidence-reuse/new(選擇來源專案)、…/evidence-reuse/:sourceUid(三欄調整頁,新增與調整同一頁)。新目錄 src/views/evidence-reuse/;三欄用 PrimeVue Splitter(state-key 存欄寬);右欄複用 evidence-classification/components/PlacementTable.vue 與 AOPickerDialog.vue。總覽工具列的「自動分類證據」「證據雲端資料夾」兩顆與新項合併成「證據作業」下拉(ProjectAuditorOverview.vue),各項顯示條件維持原本。規劃頁 EvidenceReuseBanner.vue/EvidenceReuseEntryButton.vue/EvidenceReuseDialog.vue 移除。授權邊界預設三行、展開全文,畫面不標「SSP」。雛形 prototype/project-reuse-page.html。 FE 3c 任務層:job-drawer/JobEvidenceReuseSection.vue,證據區動作列加常駐按鈕「沿用」(與上傳、加連結並排,正式入口、永遠在)+提示列(只是提醒,有可沿用才出現)+展開清單勾選沿用;全部沿用完或沒有對應時按鈕仍可按,清單處顯示原因並指路框架對照頁;已沿用證據在既有清單 Tag lang.grc_shared.reused_from_project(比照 inherited_from_prev_round),點 Tag 顯示來源專案與該處判定;AR 判定頁證據清單同 Tag。

控制項關聯管理:現行規則(頁面名稱已改「控制項關聯管理」)

本段是目前的規則,與下方「矩陣視圖」原設計衝突時以本段為準。

  • 只能同層對:控制項對控制項、評估目標對評估目標。拖曳只能拖到同一欄、同一層的另一列,放下就是「搬」(新格寫直連、原格標不可用),沒有複製。後端在寫入一處把關(add_entry):主軸評估目標寫到目標控制項的直連(清空、詳情對話框的確認/改狀態/標不可用都走這條)一律展開成對該控制項每個評估目標各一條;其餘跨層寫入回 GRC_400189。前端各入口不必各自處理層級。
  • 控制項層有對應、評估目標層沒細分=目標控制項的全部評估目標都適用(證據沿用同規則)。評估目標列直接把它們全部列出(虛線膠囊,來源/狀態/信心照控制項層那條),只是顯示、不寫資料。該列一旦細分到某目標控制項的部分評估目標,那個控制項的控制項層命中就不再算全部適用(矩陣與沿用共用 refined_targets)。
  • 陷阱:解析器只要該評估目標有任何評估目標層列,就不再退回控制項層,帶出的那組會一起消失。所以在帶出的膠囊上刪除、搬移,或在該列新增,前端都先呼叫 POST /oscal/control-mappings/entries/expand 把整組寫成逐條 curated(關係與信心照搬),再做原本的動作。
  • 人工新增一律「通用」(equivalent-to)、信心 1,不讓使用者填。新增走共用挑選器(見下),可多選。
  • 每格:膠囊 ×=移除(寫一條主軸↔︎目標「不可用」直連,多跳的中繼不動);格尾「+」新增、垃圾桶「清空這格」;都靠右。清空在控制項列=對每個目標控制項寫控制項層不可用;在評估目標列=真的評估目標層膠囊照它寫、承繼膠囊送目標控制項由後端展開,寫出來全是評估目標對評估目標。
  • 還原出廠設定(維護選單):主軸 × 選一欄,只留隨套件出貨的官方與第三方;顧問與 AI 列停用不刪,並清掉 overrides_entry_id(解析器把被任何列指到的列當退場,只停用不會讓官方列回來)。
  • 暫時隱藏:「產生 AI 建議」對所有人隱藏(含超級管理員)、OSCAL 匯入匯出與「一對框架檔」匯入隱藏;開關在 FE controlMappingUtil.js(AI_SUGGEST_ENABLED/OSCAL_IO_ENABLED),後端端點與能力點守門照舊。
  • 工具列兩列:上列搜尋、篩選(只在目前那段顯示列數)、排序;下列全部展開、欄寬(自動符合=依內容、平均分配=框架欄等寬,主軸欄都不吃剩餘寬)、全螢幕|批次作業(下載當前主軸框架設定、空白範本)、維護(匯入關聯表、還原出廠設定;整顆按 v-can manage)。分段按鈕一律共用元件 SegmentButtons(照待辦任務頁、small)。
  • 關聯表 Excel:匯出主軸對全部其他框架;第 1~3 列標題與說明、第 4 列中文表頭、第 5 列(隱藏)是匯入用機器鍵、第 6 列起資料;狀態寫「通用/部分可用」、來源寫中文。匯入找第 5 列的鍵(舊版第 1 列相容),比對用畫面狀態(避免把 subset/superset 改寫成等價)。匯出再匯入零變動(DEV 實測 1,490 筆全略過)。
  • 共用挑選器 AOPickerDialog(控制項關聯管理新增對應、證據沿用加入評估項目、AI 自動分類三處共用):左欄群組 → 控制項(點已展開的群組收合)、右欄勾選(控制項標題三態框)、上方搜尋(搜全部)與「全部/只看已勾」、底列已勾膠囊。群組代號 ISO27001-A5 而名稱已是「A.5 …」就不顯示代號(挑選器內統一套)。標題「從 〈框架〉 挑選,對應到 〈這一條〉」;評估目標字母從文字開頭 [x] 取(_obj.N 不是字母序)。

對照維護工作台:矩陣視圖(主專案,卡 2)

為什麼是矩陣不是一對一(決策者 2026-10-24 裁):一對一配對數隨框架數平方成長(5 框架 10 對、10 框架 45 對、加 3 個參考框架 78 對),顧問被迫同一條控制項對不同框架看好幾次。資料層不變(對照庫仍存邊,任意一對都能解析),只改畫面組織。

  • 路由 control-mapping,側選單掛「合規稽核」群組(ui_routes migration),能力點 oscal.control-mapping.read/manage。
  • 主軸 × 並排欄:頂部選一個主軸框架(任選,記上次)、勾要並排看的框架(每個一欄);參考框架(800-53、800-171、62443)預設隱藏,系統背後用它們算多跳。主表一列一條主軸控制項(左欄 sticky),可 ▸ 展開到評估目標(只在主軸側展開,目標側不展開);每格列出對到的目標代號膠囊(關係色點+來源小標),超過兩顆收成「+N」;空格顯示「—+」,一眼看出主軸比該框架多要什麼。
  • 編輯在膠囊上:點膠囊 → 置中對話框(PrimeVue Dialog modal,寬約 60rem;決策者 2026-10-25 裁捨棄抽屜,雛形 prototype/mapping-side-panel.html)。由上到下七塊:①標題(控制項對應/評估目標對應)②兩端並排——左是點的那列、右是對到的,各顯示框架、代號、標題,可展開條文全文與評估目標清單(AO 層對應預設展開命中那條並標「評估目標 N/M」)③狀態:只有「通用」「部分可用」兩種顯示值+一句補充(見下)④來源:官方寫文件名(NIST OLIR #155/800-171 附錄 D/DoD CMMC 模型)、第三方寫機構與檔名附連結、AI 寫日期與理由、顧問寫人名時間備註;信心分數放在來源行一起看:第三方(OSA coverage 換算、SunSquare 0.6)、AI(附理由)、顧問(覆寫時可填)顯示;官方不顯示(NIST/DoD 不給、DB 為 NULL,不顯示成「未給」) ⑤路徑:每跳一列(代號、框架、該跳來源),直連一跳;中繼框架只在這裡出現 ⑥動作:確認這條對應/改為通用(或部分可用)/標為不可用;被顧問改過的多「撤銷,回到官方」⑦變更紀錄。不出現「間接」「無法修改」字樣,每顆都能改(改=在兩端建 curated 直連,中繼不動,9a 端點)。空格「+」新增(目標框架已定,只選目標控制項/AO)。
  • 狀態顯示值與資料值的換算(決策者 2026-10-25 定名):畫面只顯示通用(綠點:equivalent-to,以及 superset-of/subset-of 從寬往窄沿用的方向)與部分可用(橘點:intersects-with、涵蓋從窄往寬的方向、官方未給強度的全部)。不可用不是顯示狀態(not-related-to 的格子不會出現東西),只當對話框的動作名。顧問改狀態時選項只有「通用」「部分可用」「不可用」三個,分別存 equivalent-to/intersects-with/not-related-to;涵蓋兩值只從官方資料進來,顧問不手選。等價/部分重疊/涵蓋/不相關這些術語只留在資料庫、Excel 與 OSCAL 匯出,畫面不出現。證據沿用預覽只對「AO 層通用」預設勾選。
  • 篩選:只看「無對應的列」「有 AI 建議待確認」;搜尋主軸代號/標題。
  • 工具列按「主軸 × 選定欄」:匯入/匯出寬表(Excel 一個 sheet:主軸一欄+每個框架一欄,格內多值以分號分隔)、空白範本、產生 AI 建議(選哪一欄或全部已勾的欄;初配控制項/配對評估目標)。OSCAL 匯出系統自動拆成每對框架一份 mapping-collection;OSCAL 匯入一次一份。「重建」改為對「主軸 × 一欄」做,二次確認。
  • BE 端點:GET /oscal/control-mappings/matrix?axis_fv=&cols_fv=&expand_ao=&filter=(一次回整張矩陣:主軸控制項與 AO、每格的生效對應含路徑;93×3 欄量級一次查得完,解析走套件 resolve 批次版);寬表匯入匯出 GET|POST /oscal/control-mappings/wide?axis_fv=&cols_fv=;其餘端點(override/disable/history/ai-suggest)不變。
  • 主軸欄凍結在左(sticky)、右側欄固定寬可橫向捲動:欄數再多也不擠扁,主軸永遠看得到;FE 用 PrimeVue DataTable frozen 欄或自訂 sticky。禁左側粗色邊框;dark-card、全站字級。雛形 prototype/mapping-workbench.html。

AI 建議配對(主專案,卡 4)

兩種模式共用一支 plugin core/plugins/_control_mapping_ai_suggest.py,皆走 @register_job_handler、gateway.complete(select_model_for_current_tenant)、_ai_quota_adapter 配額、prompt 由 jedi_ai_gateway.get_prompt("control-mapping", <key>, BUILTIN) 取;worker 另起。

  • 模式 B 控制項層初配 control_mapping_ai_suggest_controls,payload {source_framework_version_id, target_framework_version_id, hint_pairs?}:對來源每條控制項(標題+statement prose+繁中)與目標候選控制項兩兩比,回 relationship(五值)+confidence+reason,寫控制項層 origin='ai-suggested';mapping 不存在先建(method='tool-assisted')。候選縮小優先序:① hint_pairs(顧問在工作台勾「大概相關」,或由 ENISA CRA 報告/第三方 E187↔︎62443 對照整理成提示);② 無提示時兩階段:先讓 AI 從目標框架 group 選相關 group,再比 group 內控制項;③ 總對數 ≤ 5,000 直接全配。第一版要跑:CRA 的 Article 13/Annex VII 17 條↔︎ISO、62443-4-1 五個 practice↔︎ISO/800-53(其餘由 1b 灌的 OSA 與 SunSquare 二級來源接)。
  • 模式 A 評估目標層 control_mapping_ai_suggest_objectives,payload {mapping_id, only_missing}:只對 origin 為 official/third-party/curated 的 active 控制項層列跑(ai-suggested 的要顧問確認後才跑);每列兩邊 AO prose 一次 call,要求每對都回一筆(含 not-related-to);寫 AO 層 origin='ai-suggested',同鍵已有 curated 跳過。
  • 規則回填控制項層關係(模式 A 每列跑完):來源控制項 AO 全部各有一個 equivalent-to → 控制項層 equivalent-to、confidence 取平均;全部 not-related-to → not-related-to;其餘 intersects-with、confidence 取有對應者平均。寫成新 ai-suggested 列 overrides 原 official 列,不改官方列。
  • AI 回非 JSON 或代號不在範圍 → 該列記 error 繼續不中斷;progress 每列回報。
  • 工作台:「產生 AI 建議」下拉兩項(初配控制項/配對評估目標);「AI 建議」篩選;「確認」轉 curated(新列 overrides 指回 AI 列;2a 的 override_entry 若不接受不改內容只確認,加 confirm_entry)。

AO 配對實例(DEV 真實資料):CMMC AC.L2-3.1.1 6 個 AO ↔︎ ISO A.5.15/5.16/5.18 共 7 個 AO,42 對一次給 AI。期待:_obj.1「authorized users are identified」↔︎ A.5.16_obj.3「each identity is unique and linked to an individual」等價 ~0.9;_obj.4「access limited to authorized users」↔︎ A.5.15_obj.4「access control rules are implemented」等價 ~0.85;_obj.2/_obj.5 程序身分 → 不相關(ISO 無此概念);_obj.3/_obj.6 設備 ↔︎ A.5.15_obj.4 部分重疊 ~0.6。存的是代號對代號,prose 只是配對輸入。

Error code(主專案 common/code/flow_control_error_code.py,GRC_ 前綴照序)

碼 情境
GRC_400xxx CONTROL_MAPPING_IMPORT_INVALID 匯入檔欄位/代號對不到,msg 帶錯行清單
GRC_404xxx CONTROL_MAPPING_NOT_FOUND mapping 或 entry 不存在
GRC_409xxx CONTROL_MAPPING_PAIR_EXISTS 同一對框架版本已有 mapping
GRC_412xxx CONTROL_MAPPING_REBUILD_NOT_CONFIRMED 重建未帶 confirm
GRC_409xxx EVIDENCE_ALREADY_IN_JOB 沿用時同檔已在該任務
GRC_400189 CONTROL_MAPPING_CROSS_LEVEL 對照跨層寫入(評估目標→控制項的直連會自動展開,不會走到這裡)
JOB_EVIDENCE_400001 JOB_EVIDENCE_DELETE_SCOPE_INVALID(common/code/error_code.py) 刪除範圍不是 this/project

FE error-code.json 中英同步。

權限

工作台屬系統層(沒有「專案」對象,FR-160 判準),兩顆能力點 is_platform=false、scope {tenant,org_unit},license_module='module-frame'。收證據沿用沿用任務既有守門。

§6

拆分表

卡 批 Repo 做什麼 改哪些檔 驗收 依賴
1 套件對照表與匯入匯出 1/2 jedi-python-package + BE(DI、轉接腳本) 021/022 migration、entity/repo/mapper/service、解析服務、OSCAL 與 Excel 匯入匯出、三份官方檔轉接、DEV 灌資料 套件 jedi_oscal_v2/{migrations,domain,infra,app}/...mapping...;BE di_containers/oscal/oscal_containers.py、scripts/import_official_control_mappings.py DEV 兩表有資料(CMMC↔︎171 AO 層、171↔︎53、53↔︎ISO);匯出 JSON 符合 1.2.2;重匯不重複;解析 A.5.15 回得到 CMMC AO 無
5 OSCAL 版本標記 1 jedi-python-package catalog_service.py:90 改 1.2.2;023 回填 migration 套件兩檔 DEV metadata 全 1.2.2 無(與卡 1 不同檔)
2 對照維護工作台 3/5 BE 一張、FE 一張(Repo 不同拆兩卡:2a BE、2b FE) 2a:route、能力點、ui_routes migration、匯入匯出端點、error code;2b:頁面、匯入匯出 dialog、i18n、capabilities.js BE api/oscal/routes/control_mapping_route.py、app/oscal/service/control_mapping_app_service.py、scripts/sql/2026-10-xx-fr162-control-mapping-capabilities.sql;FE src/views/control-mapping/*、router/index.js、capabilities.js、locales 選 CMMC L2↔︎ISO 看到列與 AO;改一條→DB 新 curated 列、官方列 disabled;匯出再匯入一致 卡 1
3 證據沿用 4/6/7 3a BE;3b FE 專案層;3c FE 任務層 3a:共用讀模型、五支 API、migration(job_evidences 三處+projects 一欄)、error code;3b:規劃頁橫幅+三步 dialog;3c:drawer 區塊、Tag、AR 頁 Tag BE infra/readmodel/audit/evidence_reuse_candidate_query.py、app/flow_control/service/evidence_reuse_app_service.py、api/flow_control/routes/evidence_reuse_route.py、scripts/sql/2026-10-xx-fr162-job-evidences-mapped.sql;FE views/project/planning/EvidenceReuseBanner.vue、EvidenceReuseDialog.vue、job-drawer/JobEvidenceReuseSection.vue、evidenceMapper.js、AR 證據清單元件 見分工概述卡 3 列;DB source='REUSED' 筆數=摘要沿用數;來源專案刪一份檔,新專案那筆仍可預覽 卡 1;3b、3c 等 3a
4 AI 建議配對 7 BE + FE 小改(同卡,FE 只加下拉與篩選) 兩種模式 handler、prompt、入列端點、規則回填;工作台「產生 AI 建議」下拉、origin 篩選、「確認」 BE core/plugins/_control_mapping_ai_suggest.py、route;FE 工作台頁兩處 CRA↔︎ISO 初配有列;確認後 AO 層有列;控制項層關係被回填;只跑一次 only_missing 新增 0 卡 2b

七批(以 Notion 子卡 Stage 為準):批 1 = 1a、5(套件不同檔)。批 2 = 1b。批 3 = 2a。批 4 = 3a。批 5 = 2b(順便建 mappingTags.js 共用 Tag 元件)。批 6 = 3b。批 7 = 3c、4。原規劃把 2a/3a 同批、2b/3b/3c 同批,重查發現它們各自要在同一個登記型檔(error code 表、migration manifest、FE api.js/locales/error-code.json)加列,同批合回必衝突,故拆開;工作台先、沿用後,決策者最早在批 5 看到畫面。工作坊規範「一張卡只改一個 repo」,所以卡 1 拆 1a(套件)/1b(BE),卡 2 拆 2a(BE)/2b(FE),卡 3 拆 3a(BE)/3b(FE 專案層)/3c(FE 任務層)。

§7

端到端驗收

  1. DEV 有 CMMC L2 專案 A(已交若干證據)與 ISO 專案 B,同資訊系統。
  2. 工作台選 CMMC L2 → ISO 2022,看到 AC.L2-3.1.1 對 A.5.15/A.5.16/A.5.18(經 800-171、800-53 三跳,hops=3),展開 AO 層有 AI 建議或顧問確認的配對。
  3. 建好 B 進規劃頁,頂部橫幅「偵測到 CMMC L2 專案 A 有 N 個評估目標的證據可沿用」;點進去選 A,預覽表每列有對應、關係、信心、證據數,等價且信心高的預設勾;按沿用,摘要「沿用 N 份到 M 個 AO,K 個無對應」,無對應清單可下載。 3b. 之後在 A 的 AC.L2-3.1.1 [d] 再上傳一份新證據,B 規劃頁橫幅「有 1 份新證據可沿用」;B 的 A.5.15 [b] drawer 證據區提示列「其他專案有 1 份符合的證據」,單筆沿用成功、清單帶「沿用自 CMMC 專案」。
  4. B 送審、AR 判定頁該證據帶同樣標籤。
  5. 工作台把 A.5.15 [b]↔︎AC.L2-3.1.1 [d] 改成 intersects-with,匯出 Excel 看到 curated 列;重匯官方 OLIR 檔,curated 列仍在、官方列數不變。
§8

不做

  • 一專案多框架(D4)。
  • CRA、SEMI E187 到 ISO 的「官方」等級對照(CRA 官方只對 Common Criteria、SEMI 沒有);第一版靠二級來源(origin='third-party',報告標來源)+AI 補洞,不會有「官方」等級。
  • ISO Clauses 4~10 建模(OLIR 的 204 列條文對應先存著標 clause,等 ISO 導入設計拍板)。
  • 自動從 NIST/ENISA 網站抓更新。