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

FR-098 設備與資訊系統資產(jedi-asset)資安檢查

✅ 兩棒掃完並驗收收尾(2026-09-16);發現已全數處理——M19 三件全數已修,隨 1.21.0 出貨(母卡 CM-1813、子卡 CM-1814~1815)。套件本體 60 檔掃完(A1 設備 45 檔+A2 資訊系統 42 檔,共用骨架 27 檔重疊);淨新增 2 條:跨 arc 總表第 80 項(中等,讀取端點不驗權限=產品決策題,設備與資訊系統兩張清冊同一決定)、第 81 項(低,只有修改權限也能達成刪除)。沒有高風險,資料庫隔離做得好。主專案宿主接線 12 支另棒(與 jedi-log 根層 6 支合併,BE repo)。

狀態:✅ 已完成 文件 2 份

🔴 一頁看完

兩棒都掃完、都驗收了。整支套件 3 條實質發現,沒有高風險,資料庫隔離做得好。

# 發現 嚴重度 歸屬
1 設備與資訊系統的讀取端點只驗登入、不驗權限——程式碼裡寫明是刻意取捨 中等 跨 arc 總表第 80 項,產品決策題(後已修,M19,1.21.0 出貨)
2 資訊系統只有「修改」權限的人,送一個「停用」欄位就等於做到「刪除」 低 總表第 81 項(A2 新登記)
3 清單「每頁筆數」沒有上限 低 重複總表第 31 項(共用底層,全站清單都吃到;不另計)

這支套件管兩張清冊:設備(客戶有哪些機器、伺服器)與資訊系統(客戶有哪些系統,例如人事系統、財務系統)。這兩張清冊是稽核工作的對象——稽核任務執行時會指向某台設備、某個系統,合規文件裡也會引用它們。

最大的一條(第 80 項)兩棒的工具都報了、首腦都核實了:

🔴 讀取類端點全部只檢查「有沒有登入」、沒有權限檢查,而寫入類每一個都有。 兩支路由檔都是這個形狀——設備的清單、選單、明細、被引用查詢,以及資訊系統的選單、清單、明細,全部只掛「要登入」;而新增、修改、刪除每一個都多一道權限檢查。

形狀與 FR-096 第 72 項、FR-097 第 75 項相同(讀取漏掉、寫入都有),但性質不同:那兩條是疏漏,這一條是套件契約檔裡寫明的刻意取捨,所以列為產品決策題而不是漏洞——細節見下方 A1 驗收。

§1

🔵 但這支與前幾支的風險形狀不同

資料庫隔離做得比先前幾支好(首腦 2026-09-16 對開發環境唯讀實查):

表 隔離 規則數 客戶欄位 資料量
設備 ✅ 開 4 ✅ 有 3 筆
資訊系統 ✅ 開,且是強制模式 4 ✅ 有 91 筆

所以本系列的重點不在「隔離有沒有開」,而在「程式這一層的權限檢查夠不夠」——與 FR-086/FR-095 那種「資料庫層全空」的情況不同,切棒與重點都照這個判斷排,掃完結果也印證了:兩棒沒有任何一條是資料庫隔離的問題。

八個權限點全部是客戶層級(設備四個、資訊系統四個)。這是正確的——資產清冊本來就該由客戶自己管,不是權限層級標錯。

總進度表

棒 卡 範圍 檔數 狀態 首腦驗收
A1 CM-1814 設備全鏈+套件共用骨架與三支 SQL(jedi 套件) 45 檔 ✅ 掃完,1 條中等(另 1 條候選三票一致駁回)
報告|驗證章 verified
✅ 已驗(2026-09-16)
屬實維持中等,
但首腦改判性質為產品決策題
;runner 兩個判斷皆覆核通過
A2 CM-1815 資訊系統全鏈+共用骨架重疊帶入(jedi 套件) 42 檔 ✅ **掃完,3 條(1 中等 2 低)
報告|驗證章 verified
✅ 已驗(2026-09-16)
F1 併第 80 項(同一產品決策)、F3 重複第 31 項(共用底層)、
F2 新登記第 81 項**(低)

兩棒各自獨立驗收完成。

⚠️ 兩棒相加是 87,但共用骨架 27 檔兩棒都算,去重後套件實際是 60 個檔案。 兩份檔數都在開卡當下實跑 git ls-files 驗過。

⚠️ 主專案宿主接線 12 支兩棒都不含,會與 jedi-log 的根層 6 支合併成獨立一棒(共 18 檔,BE repo)。跨 repo 必須分棒,工具一次只吃一個掃描根目錄。那一棒是下一個獨立 FR 的範圍,不在本站。

A1 驗收(首腦,2026-09-16)

結論:那條發現屬實,維持中等,但首腦改判它的「性質」——它是產品決策題,不是漏掉。

§2

工具的驗證章

項目 結果 白話
驗證章狀態 verified,無拒收原因 完整跑完
候選 → 去重後 2 → 2 —
投票數 6(2 條 × 3 位檢查員) 1 條 3:0 通過、1 條 3:0 駁回
未審候選 / 中斷 / 被降級 0 / 0 / 0 乾淨
耗時 3,670 秒(1 小時 1 分) —
掃描的程式版本 cdb0d0f4,工作區乾淨 —
範圍 45 檔,與工單逐檔對上 ✅
§3

🔵 F1 屬實,但性質要改判

事實完全正確:設備的四支讀取端點(清單 :37/選單 :56/明細 :68/被引用查詢 :106)只掛「要登入」,而寫入三支(:78/:90/:124)每一支都有權限檢查。

首腦核對了報告引的那句自陳:jedi_asset/plugin/contract.py:27 確實寫著「read 兩項 BE route 不守(守門只在寫入類)」——那句話真的在。

所以它與 FR-096 第 72 項、FR-097 第 75 項不是同一回事:那兩條是「寫入都有、讀取漏掉」的疏漏;這一條是程式碼裡寫明的刻意取捨。

要問的不是「為什麼漏了」,而是「當初這個決定,現在還算數嗎」。

但它仍需要產品面表態,因為現況是:「查看設備」這個權限只在前端隱藏選單、後端根本不擋。一個被刻意拿掉該權限的帳號直接打 API,一樣拿得到整份設備清冊——客戶管理員會誤以為拿掉權限就讀不到。

已登記跨 arc 總表第 80 項,中等。

§4

🔵 runner 的兩個判斷,首腦覆核同意

一、主動駁回工具的另一條候選——正確。 工具說「沒開強制隔離,表擁有者可以繞過」,runner 指出實際連資料庫的角色不是表擁有者,對非擁有者來說一般隔離就已經生效,強制模式只在擁有者自己查詢時才有意義。首腦實查角色權限,結論一致。

二、指出工單本身有一處誤導——也正確。 首腦開卡時寫「設備表沒開強制模式是刻意取捨,代價要查」。runner 查完後指出:那個取捨在這個產品的部署方式下幾乎不影響任何實際行為,真正該關注的是新增規則少了管理員逃生門——而那是維運陷阱(平台管理員能查改刪任意客戶的設備,卻新增不了),不是安全漏洞。

這是首腦開卡時判斷偏了,runner 的更正是對的。

§5

卡片六個重點:工具只碰一個半

工具只實質碰到 ①(讀取端點沒守=F1)與 ③ 的一個角度(那條被駁回的誤判)。②④⑤⑥ 全是人工補上的。兩項值得留著:

  • ⑤ 守門殼是「吵鬧拒絕」設計——必填插槽缺了直接拒絕掛載、錯誤訊息還寫明後果(「掛上去會讓九條資產端點變成公開端點」)。與跨 arc 總表第 13 項那種「零件沒接上就靜默放行」的形狀相反,是正面案例。
  • ⑥ 三支 SQL 都乾淨——授權範圍標準、選單冪等不覆蓋客戶改過的值、外鍵用最保守的選項。
§6

這份結果可信到什麼程度

  • 「這條是真的嗎」→ ✅ 可信。 三位檢查員一致通過;首腦開檔核對那句自陳確實存在。
  • 「是不是只有這條」→ ❌ 不可信。 快篩模式,工具六個重點只碰一個半;其餘靠人工補查。
§7

交付狀況

項目 狀況
報告 ✅ runner 自行交付(scan-A1-device.md)
commit ✅ runner 自行 commit(0ea83527)
Notion 子卡回寫 + 狀態改「修正待驗證」 ✅ runner 自行完成
修正卡 ✅ 已處理——第 80 項後由 FR-114 修正線處理(M19-1~3 全數已修,1.21.0 出貨)

✅ 交付四件齊全——本計畫第十一次做滿。

A2 驗收(首腦,2026-09-16)

結論:三條發現全部屬實,但淨新增只有一條(第 81 項,低)。另外兩條一條併進第 80 項、一條是總表第 31 項的重複。沒有高風險。

A2 與 A1 掃的是同一個程式版本(cdb0d0f4),兩棒之間套件零 commit,所以兩份結果可以直接對照。

§8

工具的驗證章

項目 結果 白話
驗證章狀態 verified,無拒收原因 完整跑完
候選 → 去重後 3 → 3 —
投票數 9(3 條 × 3 位檢查員) F1、F2 各 3:0 通過;F3 2:1 通過(異議在影響程度,不在路徑)
未審候選 / 中斷 / 被降級 0 / 0 / 0 乾淨
耗時 3,374 秒(56 分) —
掃描的程式版本 cdb0d0f4,工作區乾淨 與 A1 同版
範圍 42 檔,與工單逐檔對上 ✅
§9

三條判定

F1(工具評中等)資訊系統三支讀取端點只驗登入、不驗權限 → 併第 80 項,不另計

事實正確:資訊系統的選單(information_system_route.py:45)、清單(:69)、明細(:105)只掛「要登入」;新增(:82)、修改(:114)、刪除(:135)每一支都多一道權限檢查。拿得到的是系統名稱、描述、機密性/完整性/可用性等級、狀態、部署模式、授權邊界、系統負責人。跨客戶仍被資料庫隔離擋住,只是同一客戶內外洩。

首腦判定:與 A1 第 80 項是同一個產品決策。 套件契約檔 plugin/contract.py:27 那句「讀取類端點不守、守門只在寫入類」一句話涵蓋設備與資訊系統兩邊,不是兩個獨立的漏洞。處置:第 80 項的描述擴為「設備與資訊系統兩張清冊」,補上資訊系統三支行號。

F2(工具評低)只有「修改」權限的人也能達成「刪除」 → 新登記第 81 項

事實正確:修改端點(:124)的請求格式 InformationSystemUpdateSchema(api/serializers/information_system.py:59)允許送 is_active(是否啟用)欄位,資料存取層(information_system_repo_impl.py:127)照單寫入;而刪除端點(:136)做的事也只是把 is_active 設成 false。所以有修改權限、沒刪除權限的人,送一個 is_active: false 就等於刪掉了。

首腦判定:屬實,是新發現,維持低。 前端修改頁根本沒送這個欄位(前端只在清單篩選用到 is_active: true),所以這是純後端旁路;可逆(資料列還在),且只在自己客戶內。設備那半的修改格式沒有 is_active 欄位,不同病。

修法(二擇一):從 InformationSystemUpdateSchema 拿掉 is_active;或修改端點收到 is_active=false 時改要求刪除權限。

F3(工具評低)每頁筆數沒有上限 → 重複總表第 31 項,不另計

事實正確:資料存取層 information_system_repo_impl.py:60 直接 .limit(page_size),而 page_size 的來源是共用底層 jedi-common/jedi_common/interfaces/schema/common.py:13 的 PagerSchema.page_size,沒有範圍檢查(同檔的 page 有)。負值會一路到資料庫變 500。

首腦判定:根因在共用底層,不是資訊系統專屬——全站所有繼承 RequestMetaSchema 的清單端點都吃到;設備那半走另一條路徑(device_repo_impl.py:93/114)同樣沒上限。這正是總表 §3.1 第 31 項(FR-085 C3-2,同一行程式碼,中等,已登記)。A2 報告標「重複第 31 項,不另計」,並補一句:資訊系統與設備兩條分頁路徑都吃到,佐證第 31 項影響全站。修法一處修全站:PagerSchema.page_size 加 validate.Range(min=1, max=<上限>)。

§10

卡片五個重點:工具只碰一個

工具只實質碰到 ①(讀取端點沒守=F1)。②③④⑤ 全是人工補上的:

  • ② 兩張表隔離差別(人工查證):資訊系統表開了強制模式、設備表沒開,兩表各 4 條規則(查/增/改/刪),全部是「平台管理員逃生門 或 本客戶」,只看客戶、沒有部門維度。強制模式的修補沒有副作用;設備表的現況見 A1 ③(實際連線角色不是表擁有者,一般模式已生效)。
  • ③ 新增規則少部門維度(人工查證):新增/修改的請求格式沒有客戶/部門欄位,客戶 id 由底層從連線身分填入;新增規則檢查的也是「本客戶」。所以新增範圍=讀取範圍=本客戶,沒有「寫到別人部門」的路,不成立。
  • ④ 合規文件引用路徑(人工查證):主專案 app/oscal/service/ssp_resources_context_service.py:148-156 呼叫套件的正規查詢服務,走同一個資料存取層、同一套隔離規則,沒有繞過。
  • ⑤ 送空條件(人工查證):查詢條件十個欄位全選填,清單端點送空條件會回本客戶整表(分頁內)——歸總表第 70 項,不另計。
  • 附:共用守門殼(plugin/assembly.py:74-90)缺任一必填插槽就拒絕掛載,資訊系統這半與設備共用同一個殼,A1 的正面結論適用。

A1 已查過的四件(讀取不驗權限=同一決策/沒開強制隔離不是漏洞/空條件歸第 70/隔離與權限點層級正確),工具只重報了第一件(F1),已按規則併入第 80 項。

§11

這份結果可信到什麼程度

  • 「這三條是真的嗎」→ ✅ 可信。 9 票全投,F1/F2 3:0、F3 2:1(異議在影響程度不在路徑);首腦逐條開檔核對。
  • 「是不是只有這三條」→ ❌ 不可信。 快篩模式、一位研究員,五個重點只碰一個;其餘靠人工補查。
§12

交付狀況

項目 狀況
報告 ❌ runner 未交,首腦補寫(scan-A2-information-system.md)
commit ❌ runner 未 commit,首腦補
Notion 子卡回寫 + 狀態改「修正待驗證」 ✅ 首腦補回寫
修正卡 ✅ 已處理——第 81 項隨 M19 一併修完(1.21.0 出貨)

🔴 runner 四件全未交——本計畫第八次,全部由首腦補寫。掃描本身是完整跑完的(驗證章 verified),沒交的是「掃完之後」那四件。

§13

兩棒收尾:總表怎麼變

計數 變化
總表 §3.1 80 → 81(新增第 81 項)
總表 §3 97 → 98
risk-overview 低 37 → 38(資安類 20 → 21)
全部 112 → 113

第 80 項描述擴為「設備與資訊系統兩張清冊」;第 31 項補一句「資訊系統與設備兩條分頁路徑都吃到」。

需求討論紀錄

§14

為什麼這樣切

兩棒的分界是「設備/資訊系統」,共用骨架刻意重疊:

  • A1(設備,45 檔)——設備那半的完整路徑(14 檔)+ 套件共用骨架與三支 SQL(31 檔)。共用骨架放第 1 棒是因為它要先被讀過、第 2 棒才有對照。風險較高,先跑(見下)。
  • A2(資訊系統,42 檔)——資訊系統那半(15 檔)+ 共用骨架重疊帶入(27 檔)。

為什麼共用骨架要重疊:套件的守門殼、插件契約、錯誤碼、資料存取底層是兩半共用的,切在接縫上會讓兩棒都看不到全貌。重複報由首腦驗收時挑掉——接縫沒人看才是真的損失。

為什麼設備排第一:設備那張表刻意只開一般隔離、沒開「強制」模式,資訊系統那張開了。腳本檔頭說明設備不開強制的理由是「新增規則沒有留管理員逃生門且要求部門相符,開了會擋掉沒有部門歸屬的使用者」——這是刻意取捨,但取捨的代價要查。

§15

兩張表的隔離設定刻意不一樣

這件事開卡時已讀過腳本檔頭,是本系列最值得追的技術點:

  • 資訊系統開了強制模式,起因寫在檔頭:開發環境這張表的擁有者正好是應用程式連線帳號本人,只開一般模式等於沒開——實測「擋全部」的規則仍讀得到全部 83 筆,所以補了強制模式。
  • 設備刻意只開一般模式。那麼它現在是不是正處於檔頭描述的那個「等於沒開」的狀態,是 A1 要回答的問題。
§16

這支被三個地方引用,是交叉路口

稽核流程的任務執行(三支查詢直接 import 設備的資料模型)、問卷、合規文件的資源庫。跨模組引用時,原本的隔離與權限檢查會不會被繞過,是本系列要看的接縫。

§17

共同設定

項目 值
主 session 模型 Opus 5 (1M context),不要用 Sonnet
effort low(單一研究員掃全範圍+三位檢查員投票)
focus attack-surface(跳過測試碼、打包產物、文件)
派工節奏 一次只派一棒,驗收完才派下一棒
§18

🔴 前兩個 arc 換來的紀律(本系列必照做)

掃完要逐項回頭核工單的「重點看什麼」。 快篩模式下工具常常只咬住一個最顯眼的攻擊模式,工單列的重點大半沒被碰到:

  • FR-097 L1:工單標為「本棒最重要」的疑點工具完全沒碰,首腦事後補查才撈到,而它是那一棒最嚴重的一條。
  • FR-097 L2:runner 掃描前就知道要補查,結果工具只碰到五個重點裡的一個、且只碰到那一個的一個角度,其餘全靠人工補上。

工具沒答的自己開檔查了再寫,標明「(工具未報,人工查證)」,並在報告可信度段誠實寫出哪幾點工具沒碰。報告格式抄 FR-097 L2,那一份的「卡片重點逐項人工查證」段是範本。

§19

相關座標

  • 首腦手冊:.claude/skills/security-scan-lead/SKILL.md
  • 跨 arc 總表:docs/features/security-scan-consolidated/
  • 套件路徑:~/Projects/Jedicogy/module/jedi-python-package/jedi-asset/
  • branch:jedi 套件 repo main、BE repo main(2026-09-16 開卡時實查)
§20

文件

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

證據與盤點

文件 類型 標題 最後更新
scan-A1-device / md 盤點證據 A1 檢查結果:設備全鏈+套件共用骨架(jedi-asset) 2026-09-16
scan-A2-information-system / md 盤點證據 A2 檢查結果:資訊系統全鏈(jedi-asset) 2026-09-16
§21

Notion 卡

卡片內容(決策紀錄、驗收條件)以 Notion 為準,本頁只記座標。

關係 卡號 標題 狀態
母案 CM-1813 FR-098 掃 jedi-asset(設備與資訊系統資產)資安掃描(兩棒 60 檔,只掃不修) —
子卡 CM-1814 A1 掃設備全鏈+套件共用骨架(45 檔,jedi 套件) 修正待驗證
子卡 CM-1815 A2 掃資訊系統全鏈(42 檔,jedi 套件) 修正待驗證