FR-113 · 需求索引 · 本頁由 build 掃資料夾生成

FR-113 oscal 主專案側接線資安掃描

文件 21 份

FR-113:oscal 主專案側接線資安掃描

這是什麼:jedi-oscal-v2 這支套件本身已經掃過,但主專案這邊接上它的那 167 支檔從來沒被掃過——網址入口、商業邏輯、權限判斷、檔案解析全在這裡。這個需求把它切成多棒,用 Claude Code 官方的資安掃描工具逐棒掃完,只掃不修,修正卡由首腦看完所有報告後統一開(後續已統一派工修完,見下方現況)。

母卡:CM-2000

✅ 2026-10-01 修正現況:本站登記的發現已全數處理——oscal 主專案側(總表第 118~133 項)與 module_frame 批(第 134~153 項)均由 FR-114 修正線修完並隨 1.21.0 出貨(CM-2033/2040/2041/2055/2171/2173/2178~2190 等,對照見 docs/security-report/M11-oscal.md);無裁定不修項。

🔵 2026-09-23 狀態:FR-113 這條線實質結束——十棒中八棒掃完(O9 因超過行數上限拆成 O9a/O9b),O7a/O7b 兩棒從來沒開過卡、已裁移出本批(內容是純解析/格式轉換,檢查重點與本批不同,改跟 jedi-oscal-v2 套件本體那 364 檔一起掃)。本站同時收了 module_frame 那批八棒的報告(母卡 CM-2073,2026-09-23 八棒全部交卷、批次收口),因為檢查重點相同(守門問的問題對不對)。


§1

進度

棒次 範圍 檔/行 卡片 狀態 發現 面板 耗時 報告
O1 控制項實作與權限判斷核心 6/1,943 CM-2001 ✅ 掃完並驗收 範圍內 2 中(→總表第 118/119 項)+範圍外 3(皆既有項次重現、不另計) ✅ 完整:5 候選 15 票全投、零漏投、全部 3:0;研究員 2 派 2 回零重試 1 小時 22 分 scan-O1.md
O2 資源庫、公版範本、匯出 11/1,772 CM-2002 ✅ 掃完並驗收 範圍內 4 條(1 高 3 中)→ 淨新增 3 項(總表第 120~122 項,含本 arc 第一條高風險)+1 條=CM-1608 同位置重現不另計;範圍外 4(舊憑證,全部既有工單涵蓋:兩把雲端硬碟金鑰=CM-1631、AI 金鑰與登入簽章金鑰=CM-1607,皆不另計) ✅ 完整:8 候選 24 票全投、零漏投、7 條 3:0 + 1 條 2:1;研究員 2 派 2 回零重試 3 小時 0 分 scan-O2.md
O3a Excel 上傳入口與確認流程 5/1,460 CM-2003 ✅ 掃完並驗收 範圍內 1 高 1 中(→總表第 123/124 項;高=覆寫既有資源庫零權限檢查;中=Excel 壓縮炸彈,位置在 O3b 範圍的解析器,只是順資料流撞到、不代表 O3b 掃過了) ✅ 完整:2 候選 6 票全投、零漏投、兩條皆 3:0;研究員 2 派 2 回零重試 1 小時 50 分 scan-O3a.md
O3b Excel 內容解析 7/1,047 CM-2010 ✅ **完成(已驗收登記) 範圍內 2 中**→ 淨新增 2 項(總表第 130/131 項)〔🔵 2026-09-22 首腦驗收改判:壓縮炸彈那條另立第 130 項而非併入第 124 項——O3a 是順資料流從上傳端撞到,O3b 在解析器自己的範圍完整看過,並補上兩件可執行的事實(專案已有可重用的解壓上限、改串流零功能損失);兩項指同一行程式,開卡時三條〔124/127/130〕一張卡做完〕;範圍外 4(憑證外洩,全部既有工單涵蓋:資料庫密碼=CM-1629、登入簽章金鑰=CM-1607、雲端硬碟密鑰與加密金鑰=CM-1631,皆不另計) ✅ 完整:8 候選去重 7、21 票全投、零漏投,存活 6 條(4 條 3:0 + 2 條 2:1),章為 verified;agent 23 派 23 回、研究員 2 派 2 回零重試 1 小時 44 分 scan-O3b.md
O4 Word 上傳解析鏈 9/1,950 CM-2004 ✅ 掃完並驗收 範圍內 1 高 2 中(→總表第 125~127 項;高=覆寫既有範本零權限檢查,與 O3a 同病的 Word 版;中=匯出 Excel 公式注入、Word 壓縮炸彈)+範圍外 1(最高管理員密碼寫死,O1 已報不另計) ✅ 完整:4 候選 12 票全投、零漏投、全部 3:0;研究員 2 派 2 回零重試 2 小時 0 分 scan-O4.md
O5 SSP 六種子物件增刪改查 15/1,808 CM-2005 ✅ 完成(已驗收登記) 🔵 範圍內零發現(六組守門全齊,「複製六份漏一份」沒發生);🔴 越界 1 條→總表第 128 項,工具判中、首腦裁定升 HIGH(SSP 匯出 Excel 範本掛的是「能不能讀資源庫」而非「這份計畫是不是你的」,落在 module_frame、不在本批範圍)+範圍外 4(2 高=POC 資料庫密碼與最高權限帳號密碼散在 250 檔,併 CM-1629;2 中=原廠管理員密碼每套相同〔O1 已報〕、匯出 Excel 公式注入〔O4 已報〕,皆不另計) ✅ 完整:5 候選 15 票全投、零漏投、全部 3:0,章為 verified;agent 17 派 17 回零失敗 3 小時 40 分 scan-O5.md
O6 文件池與匯入比對引擎 6/1,917 CM-2006 ✅ 完成(已驗收登記)⚠️ 面板未跑完 範圍內 1 高 1 中(高=刪程序書時檢查的與刪掉的不是同一份,與 O1 已登記那條同位置,開修正卡請併為一張;中=可把別人的文件掛到自己控制項並讀到檔案代號) ❌ unverified:主研究員完成並交卷,「找寫死密碼」補充掃描逾時未回、決策者額度將盡,手動停止後從磁碟撈回結果;兩條發現 runner 皆已自行開檔核對主專案與套件兩側 1 小時 10 分(至停止) scan-O6.md
O7a Word 內容抽取器 4/1,639 未開卡 🔵 移出本批(併入 jedi-oscal-v2 套件本體那 364 檔一起掃) — — — —
O7b CMMC 轉接器與中介格式 3/1,307 CM-2009 🔵 移出本批(併入 jedi-oscal-v2 套件本體那 364 檔一起掃) — — — —
O8a 合規框架 PDF 解析工作 6/1,343 CM-2007 ✅ 完成(已驗收登記) 🔵 範圍內 1 中→總表第 132 項(解析失敗時把程式當掉的原始訊息存起來並回給前端,洩伺服器路徑與套件結構;需平台管理員身分才打得到。⚠️ 不在工具正式清單裡、未經三人面板,首腦開檔核對屬實);派工預判的「5 支入口只守 3 支」經開檔核對不是缺口——沒守的兩支是讀取、各自有租戶比對擋著;範圍外 4(舊憑證,全部 O1/O2/O5 已報,不另計) ✅ 完整:4 候選 12 票全投、零漏投、全部 3:0,章為 verified;agent 14 派 14 回零失敗;1 條經面板降級(HIGH→MEDIUM) 2 小時 16 分 scan-O8a.md
O8b 框架與版本增刪改查 9/1,479 CM-2008 ✅ 完成(已驗收登記) 🔵 工具範圍內零發現;runner 照卡片重點人工追出 1 中→總表第 133 項(⚠️ 不在工具正式清單裡、未經三人面板,首腦開檔核對屬實)——刪框架/刪版本沒有「還有客戶在用就不能刪」的檢查,而同一批程式的「編輯內容」六支方法每支都有(🔴 本 arc「有人守了一半」第四次現身,但守的是前置條件不是權限,與第 120/123/125 項不合卡)。權限本身 21 處全齊、14 支寫入方法一支不漏,是本 arc 守得最完整的一棒;卡片五條線索四條查證不成立(見報告)。範圍外 5(憑證外洩,4 條已有工單;F3 套件庫管理員帳密對不到既有卡、待首腦裁定) ✅ 完整:7 候選 21 票全投、零漏投,存活 5 條全 3:0、否決 2 條全 0:3,章為 verified;agent 23 派 23 回零失敗。⚠️ 但面板在這棒幾乎不提供保證——工具範圍內沒報東西,面板只驗「報的成不成立」;且本輪未產出覆蓋率自述,無法獨立確認九支檔讀完 2 小時 57 分 scan-O8b.md
O9a Excel/Word 兩線共用的資料轉換接縫 3/916 CM-2069 ✅ **完成(已驗收登記) 範圍內 2 高 1 中**,但三條全落在別棒已登記的位置 → 淨新增 0:兩條高=總表第 123 項(Excel 覆寫範本零權限)與第 125 項(Word 覆寫範本零權限、確認時還能換目標)同位置重現;中=負責人編號使用者自己填、存下去不驗型態,屬第 123/125 項修法涵蓋 ✅ 完整:3 候選 9 票全投、零漏投、全部投成立、無降級;agent 10 派 10 回零失敗 1 小時 37 分 scan-O9a.md
O9b 接線總裝、稽核階段掛鉤、人員對帳 17/1,903 CM-2070 ✅ 完成(已驗收登記)⚠️ 面板未跑完 淨新增 0——唯一那條(推進/退回階段時操作者顯示名稱由前端送什麼就記什麼)與總表第 54 項是同一件事,已在該列追記、不另計;另三條是 O1/O3a/O6/O9a 已報的舊識 ❌ unverified:研究階段 1 小時 52 分零重啟交出 4 條候選,但投票階段 27 位檢查員派出、0 票回收即被決策者因額度中止;⚠️ 範圍內另有兩項查證未及完成 研究 1 小時 52 分(第一次嘗試 12 小時 23 分零產出已廢棄) scan-O9b.md
B1 合規範本主體的增刪改查(入口→資料庫存取+兩支接線設定) 19/1,787 CM-2078 ✅ **完成(已驗收登記) 範圍內 2 中 → 總表第 134/135 項**:改範本只檢查「你有沒有改範本的權限」不檢查「這份範本是不是你家的」(同一支方法裡隔四行,:112 改分享範圍有比對歸屬、:117 改控制項清單沒有);兩個查詢入口只驗登入(同檔四支寫入全掛齊)。🔵 卡片兩項都查了:AI 儀表板旁路接線正確、路由總表無註解掉的入口 ✅ 完整:2 候選 6 票全投、零漏投、兩條皆 3:0;研究員 1 派 1 回 1 小時 34 分 scan-B1.md
B1-item 範本子項增刪改查 4/675 CM-2079 ⚠️ **提前結束(額度告急,驗證投票跑一半被停) 範圍內 1 中(三票確認)=讀範本流程圖零權限檢查;另 1 高+1 待評未經投票**=改流程圖不驗歸屬(檢查被包在 if 裡)、刪/改子項「檢查甲動手改乙」。三條 runner 皆已自行開檔核對 ❌ 未跑完:2 候選應投 6 票、實投 3 票(僅中等那條投完、3:0);研究員 3 派 3 回、研究階段完成;無驗證章(工具未跑到產出階段)。🔵 決策者 2026-09-23 裁③:不補跑——未投票那條(總表第 137 項)比照 O8a/O8b 以「未經三人面板、首腦開檔核實」登記;待評那條(第 138 項)由 B1c runner 推完影響、定為中 約 3 小時(至停止) scan-B1-item.md
B2 範本底下三組清單型資料(控制項預設/稽核目標預設/參考文件池),每組四層 12/1,642 CM-2075 ✅ **完成(已驗收登記) 範圍內 2 中 → 總表第 139/140 項**:列範本程序書清單不驗權限(清單附檔案編號,拿去打下載網址就能把程序書整份抓走);讀稽核目標預設內容兩支入口不驗權限(同檔改與刪都有掛)。🔵 卡片點名的「零命中」紅旗是假警報——參考文件池那層負責的是歸屬比對,八支方法逐支核對全部有做 ✅ 完整:2 候選 6 票全投、零漏投、兩條皆 3:0;agent 7 個 約 50 分鐘 scan-B2.md
B3 SSP 六類附屬資料的範本側鏡射版+範本匯出 15/1,904 CM-2081 ✅ 完成(已驗收登記) 🔴 範圍內 12 條(11 中 1 低)→ 總表第 141~146 項,這批最大收穫:六組讀取入口全漏權限(其中五組跨公司)、六組寫入路徑全無歸屬檢查。專案那側完全相同的六組每一支第一行都有守門(O5 已核,六支全中一支不漏),範本這側同樣位置一支都沒有 ✅ 完整:21 候選去重 13、39 票全投、零漏投;成案 12、一致否決 1、降級 1;agent 41 派 41 回零失敗 約 2 小時 47 分 scan-B3.md
B4 SSP 匯出 Excel 範本三入口 2/1,669 CM-2076 ✅ 完成(已驗收登記) 淨新增 2 條(總表第 148 中/第 149 低)+第 128 項升級為跨客戶。🔴 最重要的產出是把第 128 項從「跨專案」改成「跨客戶」(首腦開 schema 核對:SSP 建表沒有客戶欄位、oscal 只有五張匯入工作紀錄表開隔離);第 148 項是與 O3b 接起來的完整公式注入路徑;第 149 項是 runner 讀程式補的(未經投票)。⚠️ 報告第三條高風險是「找寫死密碼」專項撿回來的既有案,不另計 ✅ 完整:5 候選去重 3、9 票全投、零漏投、三條皆 3:0、無降級 約 91 分鐘 scan-B4.md
B5 範本控制項清單的 Excel 匯出/匯入六個入口 3/1,376 CM-2074 ✅ **完成(已驗收登記) 範圍內 1 中 → 總表第 147 項**:上傳 Excel 存檔那條路只檢查「這個範本存不存在」、不檢查「是不是你們公司的」。🔵 卡片的「零命中」紅旗這次是半真的——六個入口權限都掛齊(卡片說對了),下一層零權限字,但六支方法只有「存檔」那支真的出事,其他五支是讀、被資料庫隔離擋住 ✅ 完整:2 候選 6 票全投、零漏投;成案 1(3:0)、否決 1(3:0)、嚴重度由研究員報的高降為中(三個認定票都投中) 約 65 分鐘 scan-B5.md
B6 產生 Excel 檔案的底層七支(excel_template/ 全目錄) 7/1,656 CM-2077 ✅ **完成(已驗收登記) 範圍內 2 條:1 中 → 總表第 150 項**(任何登入帳號改自己暱稱,就能在全公司下載的每一份範本、含空白範本裡埋公式);另一條=第 148 項的寫入端確認、不另計(補上 B4 沒點到的 generator.py:319)。範圍內零權限發現屬正常(純格式化程式)。runner 另讀出兩處未經投票:說明頁範本名稱(generator.py:247)、帳號模組自己的下拉清單(user_import_template_app_service.py:97) ✅ 完整:2 候選 6 票全投、零漏投、兩條皆 3:0(B6-1 兩中一低維持中;B6-2 三票判低,首腦裁第 148 項維持中);章 verified 約 15 分鐘 scan-B6.md
B1c 用檔案整批匯入合規範本(YAML/Excel 三入口) 8/925 CM-2080 ✅ 完成(已驗收登記) 淨新增 3 條 → 總表第 151/152 項(低)、第 153 項(中):YAML「引用上一段」無上限讓伺服器算不完;Excel 檢核比對寫法平方爆炸;只有「建立」權限就能用 Excel 匯入覆蓋既有範本(⚠️ runner 開檔追出、未經投票、首腦核實)。工具第一條=總表第 134 項第二次獨立命中、不另計。YAML 會不會在伺服器執行程式:不會(yaml.safe_load,結案) ✅ 完整:3 候選 9 票全投全確認、零漏投零降級;章 verified 1 小時 27 分 scan-B1c.md

卡號對應以各棒實際卡片為準。B 開頭那八棒屬另一個母案(module_frame 批,母卡 CM-2073),報告放在同一站是因為檢查重點與本批相同(守門問的問題對不對),不是同一張母卡。

🔵 卡號已確認(2026-09-21 首腦驗收):O5=CM-2005、O6=CM-2006,與各卡本文範圍相符。

🔵 O7a/O7b 不必再追卡號了:這兩棒從來沒開過卡(O7a 原表誤標成 CM-2008、與 O8b 重號,只是登記錯誤),決策者已裁移出本批、跟 jedi-oscal-v2 套件本體那 364 檔一起掃——理由是那兩棒的內容(Word 內容抽取器、CMMC 轉接器與中介格式)是純解析/格式轉換,檢查重點是檔案解析安全,與本批「權限守門對不對」不是同一套判斷標準。

🔵 O3b 的卡號不連號是正常的:它是 O3 超過行數上限後補拆出來的,號碼接在 CM-2009 之後(CM-2010)。 其餘九棒為 CM-2001~2009 連號,CM-2005 是 O5。


§2

目前為止找到什麼

O1(2026-09-21):兩個同形狀的權限漏洞——刪除佐證文件與文件庫文件時,檢查的是「你管不管網址上那份計畫」,刪的卻是「你給的文件編號」,兩者沒核對,任何人自己開一個專案就能刪別家公司的文件。另外沿相依關係撿到三條範圍外的:三把還在用的 API 金鑰躺在受版控的對話紀錄裡從沒換過、資料庫密碼寫死在文件腳本裡、每套安裝的最高管理員密碼都一樣。

這棒同時確認了一件事:全系統唯一那支 SSP 權限判斷程式(common/authz/ssp.py)本身沒有被判定有問題——漏洞在呼叫端,不在警衛。後面棒次可以建立在這個前提上,但這是快速檔的一輪結果,不是完整體檢。

O2(2026-09-21):找到目前唯一的新高風險——一家公司的管理員可以改掉、甚至清空另一家公司或原廠公版合規範本的控制項清單,並把底下所有實作記錄整批刪除。系統裡本來就有一支專防這件事的警衛程式(common/authz/sharing.py),但它只站在「改分享身分」那條 if 裡面,只送控制項清單就完全繞過;而被改被刪的三張 oscal.* 表都沒有資料庫層防護,底下沒有第二道關卡。另外三條範圍內的中風險:建資源庫的入口零權限檢查、匯出 Word 沒做跳脫處理(可偽造稽核文件內容)、以及資料庫帳密寫死在文件腳本裡(與 I2/O1 同源)。

這棒也帶出一個值得記的串連:拿寫死在文件腳本裡的資料庫密碼,把客戶存著的雲端硬碟權杖整批撈出來,再用雲端權杖的加密金鑰解開,就能直接讀到客戶存在雲端硬碟裡的檔案——兩條單看都是中等,串起來的後果比任何一條單獨都嚴重,排優先順序時要一起看。⚠️ 一處更正:報告原文寫這兩把金鑰「不在既有的修正工單清單裡」,首腦驗收時復核發現不是——它們在 CM-1631(Google 雲端硬碟應用程式密鑰與加密金鑰外洩,FR-081 I2 開的)裡,報告只查了 CM-1607/CM-1608 兩張沒查到它。這兩把不需要另外開卡,但 CM-1631 的重要性因為上面那條串連而被低估了,已回寫總表。

O3a(2026-09-21):Excel 匯入有三條去路,其中兩條有權限檢查、第三條(覆寫既有資源庫)從頭到尾沒有——任何登入者(連唯讀稽核員都算)拿到別人的範本編號,就能把別家公司或原廠公版的合規範本整份清空重寫,底下每條控制項的實作記錄連鎖刪除。改同一份資料的正規入口是有要求權限的,而隔壁的 Word 匯入已經解過同一題,照抄就好。

這棒同時修正了派工時的兩處預判:①解析編號是隨機碼不是流水號,偷不到也不用偷——攻擊者自己開一張解析單指向別人的範本即可;②「預覽與確認沒綁專案」那個不對稱本身不是缺口,ssp 那條去路的守門 2026-06-16 就刻意下移到商業邏輯層來涵蓋它了,缺口是同一個分派點上的 module_frame 分支沒跟著補。另有一個相鄰陷阱:確認端有一道公司比對,但比的是「解析單是不是我建的」而非「範本是不是我公司的」,看起來有守、實際守不到目標。


O4(2026-09-21):Excel 那條的病,Word 這條原封不動照抄了一份——三條去路裡,寫進專案要求是該專案負責人、建新範本要求「建立範本」權限,唯獨「蓋掉既有範本」那條只確認「它存在嗎」,不問你是誰。任何登入者(含唯讀稽核員)拿到別人的範本編號,就能把別家公司或原廠公版的合規範本整份清空重寫。比 Excel 那條多兩件事:①動手前還能先讀——預覽會把目標範本現有內容一起回傳,等於同時是讀取漏洞;②確認階段還允許再換一次目標編號,所以上傳時只過了「建立新範本」那道較鬆的門,實際做的卻是覆寫別人。另兩條中風險:匯出的 Excel 把使用者寫的字當公式執行(別人打開會跑)、Word 上傳只管壓縮後大小擋不住壓縮炸彈(與 O3a 同源)。

這棒也推翻了派工時的一個預判:原本擔心「只匯入控制項實作」那條獨立入口守門會跟主匯入不一致,開檔核對後反而是全棒最乾淨的一支——匯出/驗證/重驗要求是專案成員、確認匯入要求是負責人,四支公開方法全部有守。那條入口的問題不在權限,在匯出內容沒做跳脫。

O5(2026-09-21):派工時要追的那條線追到第三次,而且洞在隔壁。 原本要查的六組(元件/人員/設備清單/繼承授權/參考資料/系統特性的增刪改查)逐支核對下來全部守齊——六組都是讀用「你是這專案的人」、增改刪用「你是這專案負責人」,子物件編號一律限定在網址那份 SSP 之內查(拿別人的編號會 404),權限鏈起點 ssp_project_resolver.resolve 四條失敗路徑全部拋錯、沒有一條回 None。缺口在這六組餵資料過去的那支「SSP 匯出成 Excel 範本」:只問「登入了嗎」「角色能看資源庫嗎」,不問「這份計畫是不是你們的」,任何登入者知道別人的 SSP 編號就能下載對方整份計畫——人名、email、電話、地址、設備 IP、每條控制項敘述。隔壁做同樣事的 /ssp/<編號>/export 是有守的(ssp_export_app_service.py:74-75 呼叫 require_participant),有反例就代表這裡本來就該有。

範圍外兩條高風險:POC 資料庫密碼寫死在一支已進版控的程式裡(主機/埠號/庫名/帳號/密碼五樣齊全),以及最高權限帳號 cmmgr 的密碼散在 250 個受版控檔案中——與 FR-081 I2 當時數到的 249 是同一件事、同一個根因(查資料庫的指令連同密碼被寫進對話紀錄,對話紀錄依規定進版控),併 CM-1629 不另開卡;但要注意其中一支是程式碼不是文件,清理方式不同(改成從環境變數讀)。

O3b(2026-09-21):Excel 這條線的最後一塊,病的形狀跟前面五棒都不一樣。 解析器是純函式、不碰資料庫也不碰登入狀態,所以這裡沒有「守了一半」可言,問題是「使用者塞多少、系統讀多少」——新找到一條:檔案裡「說明」分頁 B1 那格版本號沒有長度上限,而系統拿去比對的規則沒有綁定開頭,填一百萬個數字(壓縮後幾 KB)就會讓程序算到逾時被砍,四個請求整個網站沒反應。最關鍵的是這一步跑在版本檢查之前,攻擊者連一份合法範本都不需要。同一支檔裡的 version_check.py:35 那份規則是寫對的(有綁開頭結尾)——同一件事寫了兩份、一份對一份錯。另一條是 O3a 順流撞到的壓縮炸彈,本棒在它自己的範圍完整看過,不另計,但補上兩件事實:改成串流讀取不會少任何功能(讀法本來就是逐列),且專案裡已有可直接重用的解壓檢查(core/plugins/detection.py)。

派工時點名要追的四條,兩條證實、兩條否定:公式注入這半邊確實沒擋(進來的 = + - @ 原樣存下),但那只是半條路,另外半邊在匯出線、O2/O4 已各報一次;validators.py 只有 66 行不是缺口(它驗的是必填欄這類業務規則,是使用體驗設計不是安全守門);v2_bundle.py 沒有拿檔案裡的編號當系統內編號用。

這棒也第二次確認了那四把憑證都還沒換(資料庫密碼、登入簽章金鑰、雲端硬碟密鑰與加密金鑰),工具明確查證過現在的 .env 裡仍是同一個值——是現役憑證不是歷史殘留。

O8a(2026-09-21):本 arc 第一棒「範圍內沒有權限漏洞」的線。 派工時點名要追的數字缺口(五支入口只有三次守門)查下來是真的,但那不是缺口——沒守的兩支是讀取(列草稿、看草稿內容),而它們各自有租戶比對擋著,拿別人的編號回 404;寫入三支的守門全在方法第一行、不藏在 if 裡面。躲掉前面六棒那個「守了一半」的病,靠的是三件具體的事:守門位置一致、租戶比對四處全走同一支函式(同一件事只有一份實作就不會有兩份不一致)、同一道守門在上傳與確認各檢查一次且寫明理由。範圍內唯一一條是中風險:解析失敗時把程式當掉的原始訊息整段存進資料庫、再原封不動回給前端,會吐出伺服器路徑與套件內部結構;因為只有平台管理員打得到,所以是中不是高,但那一行的 logger.exception 本來就已經記了技術細節,回前端這一份是多的。

這棒同時更正了派工卡的一個前提:卡片寫「解析是背景執行的」,開檔核對的結果是同步執行——解析在同一個請求裡跑完,所以「背景工作沒有身分脈絡、讀租戶設定時亂挑一個」那個已知前例在這裡不適用。真正在背景跑的只有每天清草稿的排程,而它恰恰是正面教材:明確用 system_context() 宣告身分,註解還寫明省略會靜默掃到 0 筆而不報錯。

O8b(2026-09-22):本 arc 權限守得最齊的一棒,缺口在權限以外。 卡片點名的數字缺口查下來完全對得上——14 支寫入方法全部有「只有平台管理員能動」,加上入口層共 21 處,一處不漏;那支「全批唯一掛 common.authz 的檔」也不是判斷基準,它解的是「瀏覽器開新視窗帶不了登入憑證」這個別人沒有的技術問題,其餘 21 支走正常登入憑證是正規做法。但系統裡還有第二道門——「這份東西還有客戶在用就不准動」,它在「編輯版本內容」那六支方法上每一支都裝了(檔頭註解甚至寫成「雙軌守門」四個字),在「刪掉整個框架」「刪掉整個版本」這兩支上一支都沒裝;刪框架那支的註解還明寫「draft-only 由 FE 把關」——後端知道該檢查,把它交給了前端,而前端只能隱藏按鈕。破壞不可逆:資料庫的連鎖刪除會一路刪到控制項、章節與評估項目,正在稽核中的客戶專案依據整批消失。只有原廠平台管理員打得到,所以是中不是高,但修法已經躺在隔壁檔案(_require_no_references(),六處在用),照叫即可、不要寫第四份。

這棒的方法論教訓有兩個:①只數守門呼叫數會漏掉這種缺口——那兩支刪除方法的權限守門是「有」的,所以數字漂亮,少的是數字量不到的第二道檢查;②工具在小範圍棒次會被密鑰專項帶走注意力——範圍內零發現,報的五條全是範圍外憑證外洩,這一條是 runner 照卡片重點逐支開檔追出來的。面板雖然完整(21 票全投、章 verified),但在這棒幾乎不提供保證,因為面板驗的是「報的成不成立」而不是「有沒有漏報」,不要誤讀成範圍內乾淨。

O9a(2026-09-22):Excel 與 Word 兩條匯入線唯一共用的那支轉換程式,並排看下來最重要的一題是「權限這一層兩邊都沒守,但破法不同」。 Word 那條的確認步驟還額外接受使用者自己指定「要覆寫哪一份範本」——攻擊者可以先用一份自己有權限碰的範本建立工作,等按確認時再把目標換成別人的範本。但這三條全部落在別棒已登記過的位置,淨新增 0(兩條高=總表第 123/125 項同位置重現,中那條屬同一批修法涵蓋)。

這棒把卡片指定的核心題目答完了:其餘 20 支共用函式逐支對照兩線的呼叫方式,沒有發現「一邊驗過、另一邊沒驗」的不對稱缺口——包括原本擔心「去重判太鬆會合併不同人」(不成立,鍵含型態與完整名稱)與「拿檔案裡的標題去對應系統內編號」(不成立,對應範圍只限同一次匯入自己剛產生的元件)。runner 另記一件值得順手處理的:ssp_excel_import_app_service.py:128 那行註解寫「權限由 RBAC middleware 處理」是錯的,實際掛在那條路上的只有登入、記錄、追蹤編號、唯讀授權四樣——留著會讓下一個人以為已經守過了,修第 123 項時順手刪掉。

O9b(2026-09-22,FR-113 最後一棒):這棒是「驗證層」——前面各棒查的是「這支功能有沒有檢查權限」,這棒問的是「檢查權限的那支程式本身,組裝時有沒有漏接零件」(漏接的話程式寫得再完整也不會執行,而且不會報錯)。答案是漏接一個,但不是漏洞,前面九棒的結論不需要重新評估:SspPermissionChecker 宣告需要四個零件、組裝時只接上三個,但漏接之後走的那條路比正常路更嚴格(公用範本被當成專案計畫處理,讀要是成員、改要是負責人),所以是「新路徑沒啟用」不是「守門失效」。淨新增 0——唯一那條(推進/退回階段時操作者顯示名稱由前端送什麼就記什麼)與總表第 54 項是同一件事。

⚠️ 這棒的可信度兩層都有折扣,要照實說:投票階段 27 位檢查員派出、0 票回收就被決策者因額度中止,所以沒有驗證章;範圍內還漏掉兩項查證(人員對帳會不會跨公司比對、三支資料表有沒有公司欄位)。🔵 但它留下一條有用的經驗:第一次嘗試跑了 12 小時 23 分、研究員被砍 3 次、零產出,判定屬「本體小、接縫發散」型——研究員為了判斷守門有沒有接上會跑去全專案搜呼叫鏈、上下文堆爆。解法是開工前先把接線事實查好寫進卡片(誰呼叫誰、守門在哪、租戶怎麼隔離),讓研究員不必出去挖。實證有效:第二次研究階段零重啟一次跑完。

B6(2026-09-23):這棒查的是「下載的那個 Excel 檔是怎麼做出來的」——把資料一格一格寫進 Excel 的那套底層程式,SSP 匯出、範本匯出、帳號模組的使用者匯入範本三邊共用。找到一個比第 148 項門檻低得多的新入口:系統的「修改我的個人資料」功能任何登入的人都能改自己的暱稱,內容完全不檢查;而範本裡「選一位負責人」這類下拉選單,是每次下載時從資料庫撈出全公司使用者、原封不動寫進隱藏分頁的。所以一個人把暱稱改成一段公式,之後全公司任何人下載的任何一份範本——連空白範本也算——都帶著它,同事用 Excel 打開、按「啟用編輯」就執行(總表第 150 項,中)。另一條是第 148 項從寫入端再確認一次,並補上 B4 沒點到的第二個寫入點。這棒範圍內零權限發現是正常的——它是純格式化程式、不碰資料庫,守門責任全在呼叫端;卡片要求標記的「歸屬判斷散落在不該散落的地方」沒有發生。

B6 還修正了一個修法:總表原本寫「公式注入沿用那支加單引號的中和函式」,對會被上傳回來的範本不適用——單引號會被當成內容讀回去,每繞一趟多一撇。要用 CM-2055 已經做好的 set_text_cell(把格子標成純文字、內容一字不改),runner 實際跑過確認讀回來仍是原字串。

B1c(2026-09-23):這棒查「用檔案一次建出整份範本」的兩條路(YAML 匯入、Excel 匯入)。最要緊的是 runner 開檔追出來的一條:Excel 匯入存檔只問「你能不能建新範本」,但只要資料帶了既有範本的編號,就改走「覆蓋」,底下呼叫的正是 B1 與 B1-item 找到的那兩支沒守好的方法——在畫面上一條條改要「修改」權限,走匯入只要「建立」權限(總表第 153 項,中,未經投票、首腦核實)。另兩條是讓伺服器忙到當掉:YAML 檔可以「引用上一段」而且沒有層數上限,幾 KB 的檔就能展開成天文數字個物件(第 151 項);Excel 檢核網頁標籤的比對寫法遇到很多 < 沒有 > 會越跑越慢(第 152 項)。兩條都要先有管理員帳號,所以是低。

B1c 還答完了兩件卡片點名的事:①上傳 YAML 能不能在伺服器上執行程式?不能——用的是安全讀法 yaml.safe_load,範圍內也沒有 yaml.load/unsafe_load/pickle,這條結案;②YAML 匯入今天其實建不出任何東西——第一步呼叫的建範本方法是空殼、固定回空值。但後面步驟是直接呼叫流程套件、完全繞過子項服務、零歸屬檢查,哪天有人把空殼補回來,就是一條沒守的批次寫入(已記在總表 §5「重建 add_module_frame」那件事上)。順帶一提,Excel 匯入的新建分支也呼叫同一支空殼,所以 Excel 匯入今天只有「覆蓋既有」能成功——剛好就是第 153 項。B1c runner 還順手把 B1-item 留下的待評那條(第 138 項)推完,定為中。


§3

✅ 2026-09-23 module_frame 八棒收口

母卡 CM-2073。八棒全部交卷:B1/B1-item/B2/B3/B4/B5(上午登記)、B6/B1c(同日下午登記)。淨新增 20 條(總表第 134~153 項)、零高風險,另把第 128 項升級為跨客戶。⚠️ 唯一打折處:B1-item 面板只投了 3/6 票就因額度中止,決策者裁不補跑,未投票的第 137 項比照 O8a/O8b 以「未經三人面板、首腦開檔核實」登記。

當初「守門看起來完整」的判斷,是怎麼被推翻的

2026-09-21 排 FR-113 時,首腦裁定 module_frame 這塊不納入,理由是「數出 25 支入口有 15 支掛了權限檢查,守門看起來完整」。後來推翻它的過程分三步:

  1. O5 越界撿到第 128 項:SSP 匯出成 Excel 範本那支有掛守門,但掛的是「你能不能讀資源庫」,要保護的卻是「這份計畫是不是你的」——任何登入者知道編號就能把別家公司整份計畫下載走。決策者裁「要掃,先記起來」。
  2. 八棒掃下來,七種「有人守了一半」——而且沒有一種和另一種相同:
棒 這一棒的「守了一半」長什麼樣
B1 同一支方法裡隔四行,改「分享範圍」比對了歸屬、改「內容」沒有
B2 兄弟端點,讀控制項預設清單有掛守門、讀稽核目標預設清單沒掛
B1-item 同一支檔,新增/修改/刪除都檢查了、唯獨「讀流程圖」漏掉
B3 六組一起漏——從專案那側複製過來,補權限時只補寫入那半邊
B4 三個入口,已知那個沒守(而且跨客戶)
B5 六支方法,只有「存檔」那支真的漏
B1c 🔴 第七種:同一件事、兩個入口、兩套門——畫面上一條條改範本要「修改」權限,走 Excel 整批匯入只要「建立」權限
  1. B6 把公式注入整條路收口:使用者寫的字要變成別人電腦上的公式,一路有六個關卡可以擋,實際擋了零個(O3b 查進來、B6 查寫進格子、B4 查出去)。這不是「守了一半」,是所有路都沒守;但 B6 提醒了一件事——修的時候如果只照 B4 的建議改一處,就會自己造出一個守了一半(第 148 項修好、第 150 項那條門檻更低的路還開著),所以開卡要把五個寫入點逐一列成驗收項。

結論:「25 支有 15 支掛了檢查」這個數字是真的,但它回答的是「有沒有掛」,不是「掛的那道問的問題對不對」。判斷一塊地安不安全,不能靠數有幾支掛了守門——這是第 128 項、第 133 項、module_frame 八棒同一個教訓的第三次,已寫進掃描手冊當排序判準。

收口時待決策者裁的三件(總表 §7 第 30~32 項;2026-10-01 同步:已裁並處理)

  • 只有「建立」權限能不能透過匯入覆蓋既有範本?(建議:不能)→ 已裁不能,已修(CM-2178)
  • 第 148 項、第 150 項、帳號模組的使用者匯入範本要不要合成一張修正卡?(建議:合,排在 CM-2055 的 jedi-common 發版之後)→ 第 148/150 項已合併修完(CM-2189)
  • Excel 上傳端要不要加警告日誌、只記不改?(建議:只記日誌)

§4

🔵 2026-09-23 module_frame 那批六棒(B1/B1-item/B2/B3/B4/B5)驗收定案

⚠️ 這六棒屬另一個母案(module_frame 批,母卡 CM-2073),報告放在本站是因為檢查重點與 FR-113 相同。B6(CM-2077)與 B1c(CM-2080)已於同日掃完,八棒收口見上一段。

六棒一起看只有一句話:淨新增 16 條(第 134~149 項)、13 中 2 低 1 待評、零高風險;另把總表第 128 項從「跨專案」升級為「跨客戶」。

🔴 這批真正的價值:每一棒都找到同一個病的不同長相

「有人守了一半」在這六棒出現了六種形狀,而且沒有一種和另一種相同:

棒 這一棒的「守了一半」長什麼樣 在哪
B1 同一支方法裡隔四行——改「分享範圍」那條比對了歸屬,改「內容」那條完全沒有;:117 還順手換掉整份控制項清單 app/module_frame/service/module_frame_service.py(:112 有 assert_scope_writable、:107-109 直接 setattr、:117 換清單)
B2 兄弟端點——讀「控制項預設清單」有掛 require_capability、讀「稽核目標預設清單」沒掛 module_frame_control_default_route.py:51 有/module_frame_control_objective_default_route.py:50-52 沒有
B1-item 同一支檔——新增/修改/刪除三支都檢查了,唯獨「讀流程圖」漏掉 api/module_frame/routes/module_frame_item_route.py:31-33 沒有/:48/:64/:79 有
B3 🔴 六組一起漏——程式是從專案那側複製過來的,補權限時只補了寫入那半邊的角色檢查,讀取整排沒補、歸屬檢查一支都沒問(12 條,這批最大收穫) 六支路由+六支服務,詳見總表第 141~146 項
B4 三個入口——已知那個沒守(而且是跨客戶,即第 128 項) ssp_import_template_app_service.py:280
B5 六支方法——只有「存檔」那支真的漏,其他五支是讀、被資料庫隔離擋住 module_frame_template_import_service.py:329 該補、:159 只驗存在

🔴 這六種漏法本身就是一個結論:當初裁定「module_frame 這批守門看起來完整、不納入 FR-113」是錯的,而單獨掃這批是對的。 當初的理由是「數出 25 支入口有 15 支掛了權限檢查」——判斷一塊地安不安全不能靠數「有幾支掛了守門」,要看「掛的那道問的問題對不對」。 這與第 128 項推翻那次、第 133 項那次,是同一個教訓的第三次。

🔴 B3 是這批證據最強的一棒

專案那一側完全相同的六組(FR-113 O5 掃過)每一支的每一個對外方法第一行都有守門——讀是「你是這個專案的人」、寫是「你是這個專案的負責人」,六支全中、一支不漏。範本這一側六支服務,同樣位置一支都沒有,只有 _require_mf() 的「查得到就過」。

兩邊放在一起看,這不是「可能有風險」,是同一套邏輯在一側做了、在另一側整排沒做。而且五張存放實際資料的 oscal.* 表一張都沒有客戶隔離設定(逐張實查零命中),程式這層是唯一關卡。

🔴 第 128 項升級為跨客戶(首腦 2026-09-23 實查裁定)

B4 獨立複查了這條,首腦親自開 scripts/init/02-schema.sql 核對:

  • oscal.system_security_plans 建表只有九個欄位(id/uuid/metadata_id/status/import_profile_id/created_at/updated_at/created_user/updated_user),沒有任何客戶欄位
  • 整個 oscal 區域只有五張表開了資料庫層隔離,全部是匯入工作紀錄表(ssp_docx_parse_jobs/ssp_excel_parse_jobs/ap_docx_parse_jobs/ar_xlsx_parse_jobs/framework_parse_jobs)——SSP 本體與它的附屬資料一張都沒開

結論:程式這一關沒擋、資料庫那一關也擋不住,兩關皆空。任何登入者只要知道編號,就能把別家公司整份系統安全計畫下載走——裡面有人名、email、電話、設備清單、IP 位址、每一條控制項的實施狀況。嚴重度維持高風險,但影響範圍從「同一家公司的別的專案」改成「跨客戶」。

🔴 跨批接力接上了:公式注入的完整路徑(第 148 項)

這條是 O3b 與 B4 各查到一半、接起來才完整的:

  • 上半(O3b):使用者 Excel 裡以 =、+、-、@ 開頭的儲存格原樣存進資料庫(app/oscal/service/excel_parser/sheet_handlers.py:64)
  • 下半(B4):匯出端不但沒擋,app/module_frame/excel_template/generator.py:578 把資料庫裡的文字原封不動塞進儲存格,而同一支檔 :137 還把工作簿設成 fullCalcOnLoad = True(開啟時重算全部公式)

接起來就是:甲客戶填一段以 = 開頭的特製文字,乙顧問下載這份 Excel 打開,那段文字就被 Excel 執行——可以把隔壁儲存格的內容串進網址送到攻擊者的伺服器,某些寫法在使用者點掉信任提示後還能執行外部指令。注意執行發生在對方的電腦上,我們的防護管不到。

🔴 修法:在寫入格子的共用函式處理,不要每個呼叫端各修一次(否則下次新增欄位又會漏),而且這份範本會被上傳回來,不能用加單引號的中和函式(每繞一趟多一撇)——要用 CM-2055 在 jedi-common 新建的 set_text_cell(把格子標成純文字、內容一字不改)。要改的五處(六行)與排程依賴見總表第 148 項與 §4 🅶 組;B6 同時找到門檻更低的第 150 項,兩條同一張卡。

⚠️ B1-item 那一棒的打折處

B1-item 驗證投票跑到一半被停(應投 6 票實投 3 票),三條發現裡只有一條有三票確認。決策者 2026-09-23 裁不補跑:未投票的第 137 項比照 O8a/O8b 以「未經三人面板、首腦開檔核實」登記,第 138 項由 B1c runner 推完影響定為中。第 137 項跨公司打不打得到,兩位 runner 推論相反、都沒實測,開修正卡時要實測。

🔵 這六棒另外排除掉的懷疑(排除也是結論,下次不用重查)

  • B1:AI 儀表板那條旁路接線正確——它註冊的是專門為儀表板寫的變體,會主動把 profile 資料清空再回傳,儀表板拿到的資料比主線更少,不是繞過守門拿更多;路由總表 252 行沒有被註解掉的入口、也沒有孤兒入口。
  • B2:卡片點名的紅旗「參考文件池商業邏輯整支檔 grep 權限關鍵字零命中」是假警報——該層負責的是歸屬比對,八支方法逐支核對全部有做,update_meta(:133)與 delete_from_pool(:151)還明文比對 context_id != ssp_id 不符就 404。卡片擔心的「檢查 A、動手改 B」在這支檔沒有發生。
  • B3:卡片要比對的「檢查 A、動手改 B」在這批也沒有發生——各服務的查找函式形狀其實是對的(先解出範本的 SSP 編號、再在那個範圍內比對子物件編號),病因是「解出範本」那一步根本沒問歸屬。
  • B4:另一支被點名的 generate()(從範本匯出那支)沒有同類問題——但它靠資料庫隔離在守、不是靠程式(compliance.module_frames:24303 有開隔離),範本表規則一被改動它就跟著破,修第 128 項時順手把這支也補上程式層檢查。
  • B5:卡片的「檢查 A、動手改 B」在這支檔也沒有發生(寫入用的編號一律來自網址解析,items[] 不含任何資源編號,還有兩層防呆)。被否決的那條候選是「上傳檔案表的公司隔離」,研究員引的是 2026-06-28 的舊註解,而 2026-09-16 的 migration 已經補上——這也是「面板有在做事」的證據。

🔵 一個訊號的兩次相反結局,值得記下來

「整支服務檔 grep 權限關鍵字零命中」這個紅旗,在這批出現兩次、結局相反:B2 是假警報(權限守在入口層,這層做的是歸屬比對,而且做對了),B5 是真洞(六支方法裡存檔那支真的沒守)。

結論:它是值得查的訊號,但不是結論。 看到零命中要往下追一層看「這層本來該負責什麼」,不能直接當成缺口,也不能直接放行。


§5

🔵 2026-09-22 三棒(O3b/O8a/O8b)驗收定案

這三棒一起看只有一句話:淨新增 4 條中風險,沒有任何新的高風險。

🔴 這個數字是修正過的。 首腦第一輪驗收時只讀了工具的正式產出(CLAUDE-SECURITY-RESULTS.jsonl),那份清單裡 O8a/O8b 範圍內確實是零發現,於是判成「淨新增只有 2 條」。但 runner 依派工卡逐支開檔查出來的發現不會進那份清單——它沒經過三人面板,只寫在 runner 的手寫報告本文裡。登記時把落差交回首腦,首腦自行開檔核對後裁定:那兩條要補編進總表,成為第 132/133 項。🔴 往後驗收一律要讀兩層:工具的 jsonl + runner 的手寫報告本文。

三份報告表面上加起來有 6 條「高風險」,一條都不是這三棒查出來的。那是掃描工具附帶的「找寫死密碼」專項——只要設了 focus,它就會掃全部程式庫,跟這一棒要看哪幾支檔完全無關,所以同一批舊東西被三棒各撿了一次。內容是:資料庫密碼散落在約 250 個受版控檔案、對話紀錄檔裡整份 .env(OpenAI/Anthropic/Google 金鑰、Google 登入用的應用程式密鑰、雲端硬碟權杖的加密金鑰)、內部套件庫與物件儲存的帳密、登入簽章金鑰散落 30 個檔案、安裝版每一套共用同一組原廠管理員密碼。這些總表全部早就登記了(§3.1 第 2 項、CM-1607、CM-1629、FR-079 B2 F1/F2/F3/F14、FR-088 H4),🔴 不另計項次、不加進總表數字。

唯一值得補進既有案的新資訊在 O8a:有一位驗證員實際拿雜湊值比對過,GOOGLE_API_KEY 與 GOOGLE_DRIVE_OAUTH_CLIENT_SECRET 與現行 .env 仍然相同、是還活著的憑證,不是已輪替的歷史殘留。已追記進總表 CM-1607 那一列(用追加、不改原文)。

真正的淨新增:4 條(O3b 兩條、O8a 一條、O8b 一條)

總表項次 嚴重度 白話一句 在哪裡
130 🟡 中 上傳一個「試算表炸彈」就能讓整個產品停擺——讀檔那行把整份 Excel 建成記憶體物件,而上傳端只量壓縮後大小(10MB),壓縮後 2MB 的檔案可以膨脹到好幾 GB。工人被佔住到 120 秒逾時、記憶體無上限成長;落地版是單一容器又沒設記憶體上限,直接被系統殺掉、整個產品下線,還可以一直重打 app/oscal/service/excel_parser/parser.py:42
131 🟡 中 版本號的比對規則可以被卡死——規則沒綁開頭結尾,而送進去比對的是使用者 Excel「說明」分頁 B1 那格、沒有長度限制。塞一百萬個數字(壓縮後幾 KB),比對引擎從每個起點各重試一次,一個請求佔住一個工人到逾時;預設四個工人,四個便宜的請求就讓整個系統沒有回應 app/oscal/service/excel_parser/parser.py:148(規則)/:151-153(比對)
132 🟡 中 解析 PDF 失敗時,把程式當掉的原始訊息整段存起來、再原封不動送回前端——那段訊息是給工程師除錯用的,裡面有伺服器上的完整檔案路徑、第三方套件與版本。攻擊者不必猜系統長什麼樣子,餵一份壞掉的 PDF 就能讓系統自己說出來,之後拿套件版本去查公開的已知漏洞、拿路徑在別的漏洞裡填對位置。需平台管理員才打得到,所以是中不是高 app/oscal/service/framework_parse_job_service.py:228(存)/:667(回傳)
133 🟡 中 刪掉整個框架、刪掉整個版本,後端完全不檢查「還有沒有客戶專案在用」,把關交給了前端——合規框架是全平台共用的資料,資料庫的連鎖刪除會一路刪到版本、控制項、章節與評估項目,正在稽核中的客戶專案,依據的整份標準憑空消失,而且不可逆。需原廠平台管理員才打得到,是「誤操作釀災」不是「外人打得進來」,所以是中不是高 app/oscal/service/framework_app_service.py:185(刪框架)/app/oscal/service/framework_version_app_service.py:224(刪版本)

⚠️ 第 132/133 項沒有經過三位檢查員投票——它們不在工具的正式清單裡,是 runner 照派工卡重點逐支開檔追出來、寫在手寫報告本文裡的,由首腦自行開檔核對屬實。

O3b 那兩條(第 130/131 項)的門檻都只有「有一個能登入的帳號」——不需要任何功能權限、不需要是任何專案的成員。第 130 項用 source_type=module_frame 連範本權限都不用;第 131 項發生在版本檢查之前,連把範本版本號填對都不需要。

修法都不大,而且不要自己造輪子:第 130 項在 load_workbook 前先用 zipfile.ZipFile 檢查解壓後總大小、檔案筆數、壓縮比——🔴 上限數字參考 config/config.py:194-200(2026-09-24 更正:原寫「沿用 core/plugins/detection.py:338-340」,FR-115 W3 查出那幾行是死設定、套件從不讀;而 config.py 那組數字是照一份檢測規則包的大小訂的,拿來管 Excel 合不合適要由開卡的人判斷);順手改 read_only=True(讀法本來就逐列走,零功能損失)。第 131 項比對前截斷字串、規則綁上 ^$——更好的是直接刪掉這份、改用既有寫對的 version_check.parse_semver(同一件事現在有兩份實作、一份對一份錯)。

還要記住的兩件「查過」

  1. 公式注入:進來這半沒擋,但只算半條路(待查項,不編項次)——app/oscal/service/excel_parser/sheet_handlers.py:64 的 _coerce_cell 只把值正規化成字串/None/布林,=、+、-、@ 開頭的內容原樣存進去。會不會真的出事取決於匯出那一端有沒有中和,🔴 而匯出那一端就是總表第 122 項(O2,匯出 Word 沒跳脫)與第 126 項(O4,匯出 Excel 把使用者寫的字當公式)。修那兩條的中和函式時,要一併確認進來這條路要不要也擋一次——兩端各擋一次,才不會「改了輸出、換個入口又存進來」。已收進總表 §3.4。
  2. O3b 排除掉一條——派工卡懷疑「Excel 裡的編號會不會被當成系統編號直接用」,v2_bundle.py:188 與 :214 都把 matched_party_uuid 寫成 None 交給後面的對帳程序填,不成立。排除也是結論。

🔴 第 133 項:本 arc「有人守了一半」第四次現身

前三條(總表第 120/123/125 項)都在資源庫那塊地的三個入口——編輯控制項清單、Excel 匯入、Word 匯入。第 133 項在合規框架這條線,形狀完全一致:

framework_version_edit_service.py 的六支編輯方法,每一支都呼叫 _require_no_references()(:208/:219/:232,定義在 :266),檔頭註解甚至把它寫成「雙軌守門」四個字。而 framework_app_service.py:185 刪框架、framework_version_app_service.py:224 刪版本,一支都沒有;刪框架那支的註解還明寫「draft-only 由 FE 把關;此處只保 NotFound 防呆」——後端知道該檢查,把它交給了前端,而前端只能隱藏按鈕。

🔴 守的那一半寫得這麼仔細,正好證明開發者知道該守,只是沒有把同一道門套到刪除這條路。

但它缺的東西跟前三條不一樣,所以不要合成同一張卡:前三條缺的是「這筆資料是不是你的」(歸屬檢查),第 133 項缺的是「還有沒有人在用」(前置條件檢查)。修法也不同——第 133 項的修法已經躺在隔壁檔案,兩支刪除方法比照編輯路徑呼叫 _require_no_references() 即可,照叫就好、不要寫第四份。

這也解釋了為什麼工具沒報而人工追到:那兩支刪除方法的權限守門是「有」的(全批 21 處、14 支寫入方法一支不漏,數字很漂亮),少的是數字量不到的第二道前置條件檢查。🔴 只數「有幾支掛了守門」這個盤點方法本身就不安全——與第 128 項推翻 module_frame 那次是同一個教訓。

O8a/O8b:工具零發現,人工各追出一條

兩棒在工具的正式產出裡範圍內都是零發現,各自那一條(第 132/133 項)是 runner 依派工卡人工追出來的。除此之外沒有其他存活發現,而且派工時各自點名要追的疑點都查清楚了:

  • O8a:卡片擔心的「framework_parse_job_service.py 五支入口只有三次 require_platform_admin」——查下去不是缺口,沒守的那兩支是讀取(列草稿、看草稿內容),各自有租戶比對擋著,拿別人的編號會 404。
  • O8b:卡片點名的「oscal_framework_version_route.py 是全批 22 支入口檔中唯一掛 common.authz 的一支」——查清楚了,它解的是「瀏覽器開新視窗帶不了登入憑證」這個別人沒有的技術問題,其餘 21 支走正常登入憑證、權限守在下一層,屬規範明文允許的正規做法。
§6

🔴 寫法上要分清楚:這是「這條線的疑點已經清掉」,不是「掃空了」。 照既有紀律,可信度分兩層:「這幾條存在嗎」——已答(工具那幾條:O8a 4 候選 12 票全投、O8b 7 候選 21 票全投,章皆 verified;人工追出的第 132/133 項由首腦開檔核對);「只有這幾條嗎」——面板完整跑完,該範圍可以宣稱已掃到。但要留意一個結構性限制:這兩棒工具報出來的每一條都落在範圍外,而面板驗的是「報的成不成立」不是「有沒有漏報」,所以面板票數在這兩棒對「範圍內乾不乾淨」幾乎不提供保證——撐住這個結論的是 runner 照卡片重點逐支開檔核對+首腦復核,不是工具。

§7

🔴 O2/O3a/O4/O5 串起來的結論:同一塊地被四條路打穿

「合規範本」(原廠公版與各公司自建的控制項範本)的寫入路徑,到目前為止被三條完全不同的入口各打穿一次,三條都是本 arc 的高風險:

棒 打進來的路 病因
O2(總表第 120 項) 編輯範本的控制項清單 程式裡有一支專防此事的警衛(common/authz/sharing.py 的 assert_scope_writable),但它只站在「改分享身分」那個 if 裡,只送控制項清單就繞過
O3a(總表第 123 項) Excel 匯入覆寫範本 同一支方法兩條路,一守一不守——寫進專案那條要求負責人(註解還寫明為什麼守在這一層),覆寫範本那條只驗存在
O4(總表第 125 項) Word 匯入覆寫範本 同一個判斷式三條路,守了兩條——寫進專案要求負責人、建新範本要求建立權限(旁邊十幾行註解解釋為什麼),覆寫既有範本那條只有一行「驗存在」
O5(總表第 128 項) 匯出:SSP 倒成 Excel 範本 隔壁有守、這支沒守——/ssp/<編號>/export 呼叫 require_participant,/ssp/<編號>/excel-template 整支檔連 require_ 這幾個字都沒出現

四條都不是「沒人想到要守」,而是「有人守了一半」——每一次都有人在旁邊那條路上仔細設計過守門、寫了註解,就是沒有把同一道門套到這一條。

O5 補上的是「讀」這一面:前三條打的是寫入(清空、覆寫),O5 打的是讀出(整份下載)。同一塊地的進出兩個方向都有缺口,修的時候不要只看寫入。

🔴 修的時候必須三條一起修——只補其中一條,另外兩條照樣進得去。 三條寫的是同一批資料、造成的破壞相同(範本整份清空、底下每條控制項的實作記錄連鎖刪除、別家公司與原廠公版都打得到),而且底層那批 oscal.* 表一張都沒有資料庫層的客戶隔離,程式這一層是唯一關卡。開卡時三條寫成同一張卡,驗收要三個入口各打一次。

另外一組較小的:O4 的「匯出 Excel 公式注入」(第 126 項)與 O2 的「匯出 Word 沒跳脫」(第 122 項)是同一病兩個出口,同一組修法;而總表 §4 🅶 組早就為第 78/82 項抽過一支中和函式,三個出口沿用同一支即可、不要各寫一份。


§8

相關文件

  • scan-inventory.md|切棒盤點:範圍界定、每棒推導、每棒重點的原始依據
§9

文件

以下全部由 build 掃資料夾產生,新增檔案重 build 即自動出現。標題連結指向渲染後的 HTML,md 連向源檔。

證據與盤點

文件 類型 標題 最後更新
module-frame-scan-inventory / md 盤點證據 module_frame 掃描盤點——範圍界定與切棒 2026-09-22
scan-B1-item / md 盤點證據 B1-item 掃描報告:範本子項的增刪改查(4 檔/675 行) 2026-09-22
scan-B1 / md 盤點證據 B1 檢查結果:合規範本主體的增刪改查 2026-10-01
scan-B1c / md 盤點證據 B1c 檢查結果:用檔案整批匯入合規範本 2026-10-01
scan-B2 / md 盤點證據 B2 掃描報告:範本底下三組清單型資料(控制項預設清單/稽核目標預設清單/參考文件池) 2026-09-22
scan-B3 / md 盤點證據 B3 掃描報告:SSP 六類附屬資料的範本側鏡射版 + 範本匯出 2026-09-22
scan-B4 / md 盤點證據 B4 掃描報告 — SSP 匯出 Excel 範本(CM-2076) 2026-10-01
scan-B5 / md 盤點證據 B5 掃描報告:範本控制項清單的 Excel 匯出/匯入 2026-09-22
scan-B6 / md 盤點證據 B6 掃描報告 — Excel 範本的底層產生機制(CM-2077) 2026-10-01
scan-O1 / md 盤點證據 O1 檢查結果:控制項實作與 SSP 權限判斷核心 2026-09-21
scan-O2 / md 盤點證據 O2 檢查結果:資源庫、公版範本、匯出 2026-09-26
scan-O3a / md 盤點證據 O3a 檢查結果:Excel 上傳入口與確認流程 2026-09-21
scan-O3b / md 盤點證據 O3b 檢查結果:Excel 內容解析 2026-09-22
scan-O4 / md 盤點證據 O4 檢查結果:Word 上傳與解析全鏈 2026-09-21
scan-O5 / md 盤點證據 O5 檢查結果:系統安全計畫的六類附屬資料 2026-09-21
scan-O6 / md 盤點證據 FR-113.O6 掃描報告——程序書文件池與匯入比對引擎 2026-09-21
scan-O8a / md 盤點證據 O8a 掃描報告——合規框架 PDF 解析工作 2026-09-22
scan-O8b / md 盤點證據 O8b 掃描報告——合規框架與版本的增刪改查 2026-09-22
scan-O9a / md 盤點證據 O9a 檢查結果:Excel/Word 兩線共用的資料轉換接縫 2026-09-22
scan-O9b / md 盤點證據 O9b 檢查結果:接線總裝、稽核階段掛鉤、人員對帳 2026-09-22
scan-inventory / md 盤點證據 jedi-oscal-v2 主專案側接線程式 — 切棒盤點 2026-09-21