FR-087.T1 盤點報告:租戶隔離破洞逐表對照

FR-087.T1 盤點報告:租戶隔離破洞逐表對照

  • 卡片CM-1663(本卡即母卡,唯一一棒)
  • 範圍:DEV 資料庫(192.168.50.188:25432guidant_ai_dev)唯讀查詢 + BE 主專案與 jedi-* 套件原始碼 grep,沒有修改任何程式碼、沒有套用任何 SQL、沒有真的切租戶測試
  • 版本:BE commit f64f5fb3,branch feature/FR-075(未 push)
  • 日期:2026-09-11
  • 方法:人工查(不用 claude-security 掃描工具——這一棒的答案在資料庫狀態與 SQL,不在程式碼的漏洞模式)

1. 🔴 一句話結論

13 張表、4 支畫面(技術上叫 view)真的把「每個客戶只能看自己資料」這道隔離關掉了,而且已經被首腦實測驗證:拿一把指向不存在客戶的鑰匙去查,待辦任務應該看到 0 筆、實際看到 39 筆;專案應該看到 0 筆、實際看到 213 筆。

好消息:11 張表的修法是純資料庫開關(把已經寫好的規則打開即可),1 張表要先補一段規則再開1 張表要先做決定(要不要對外分公司開放平台版問卷)。壞消息:有一件事必須先查清楚才能動手——待辦任務清單的畫面(vw_user_job_queue)同時是每天凌晨自動清孤兒資料的背景程式在用的同一批底層資料,那支背景程式現在能跑起來,靠的正是「暫時允許背景程式看到全部客戶資料」這條規則(CM-1559 已經記錄過這條規則本身也是一個洞)。先把兩件事一起規劃,否則修好隔離會連帶讓背景清理程式停擺。

另外查到一件工具/前一棒交接檔沒寫清楚、需要更正的事:平台管理員(原廠管理者)看到全部客戶資料,走的是跟「隔離關掉」完全不同的機制——他們用的是「我是最上層客戶」這條合法通道,不是靠隔離沒開才看得到。所以開隔離不會弄壞平台管理後台,這件事可以放心做。


2. 這一棒在檢查什麼(白話)

我們的產品是多家客戶(在系統裡叫「租戶」)共用一套系統的。靠資料庫裡的一道機制把各家的資料隔開,這道機制的技術名字叫 RLS(Row Level Security,白話講就是「資料庫自己會擋,讓每個客戶只查得到自己的資料,不用每支程式各自小心」)。

2026-09-11 決策者實際測試發現:把自己(原廠管理員帳號)切換到一個被指派的「子租戶」身分之後,還是看得到別家客戶的待辦任務與專案。這代表這道隔離機制在某些地方沒有真的生效。

這一棒的工作:把「哪些資料現在該被隔開、實際上有沒有隔開」逐張表盤點清楚,整理成一張對照表,交給決策者決定怎麼修、先修哪個。不修、不改、不套任何 SQL。

產品定的規則(判準,不是本棒發明的):客戶資料分三層——

  • 平台層:像「法規框架」「控制項目錄」這種所有客戶都要用、但只有原廠能編輯的公版資料——全部客戶都看得到,只有原廠能改。
  • 租戶層:客戶自己的資料——上層公司看得到下層分公司的(垂直向下管理),不同分公司之間互相看不到。
  • 平台系統範本:像「內建流程範本」這種公版東西,全租戶都能讀,但是掛在租戶層資料表裡、用一個特殊標記區分(不是像平台層那樣整張表都是公版)。

3. 掃到什麼:13 張表+4 支畫面總覽

3.1 表格總覽(每一列回答五個問題)

# 這是什麼資料(白話) 該是哪一層 現在是哪一層 差什麼 誰在讀、開了會不會弄斷
1 專案清單compliance.projects,213 筆) 租戶層 ❌ 規則寫好了但開關被關掉 打開開關(ENABLE ROW LEVEL SECURITY)即可 一般使用者透過正常網頁功能讀。開了不會弄斷——規則本身完整(4 條)。唯一要先查的是「當初為什麼關」(見 §5.1)
2 工作流程範本compliance.workflow_templates,17,544 筆) 租戶層+平台系統範本混合 ❌ 規則寫好(含公版可讀規則)但開關被關掉 打開開關即可 使用者建立稽核流程時讀。開了不會弄斷——但有 191 筆是歷史留存的凍結副本,沒有標記所屬客戶(詳見 §5.2),這批打開後會消失在所有一般客戶眼前
3 客戶雲端硬碟串接設定public.tenant_drive_integrations,1 筆) 租戶層 ❌ 規則寫好但開關被關掉 打開開關即可 客戶設定 Google Drive 同步時讀寫。開了不會弄斷——目前只有 1 筆資料,規則完整
4 工作流程執行紀錄compliance.workflow_executions,11,016 筆) 租戶層 規則不完整(只有新增/改/刪 3 條,缺「查詢」那條)且開關關掉 先補一條「查詢」規則,再打開開關 見下方「待辦任務清單」——同一批底層資料,兩件事要一起看
5 任務執行紀錄compliance.job_executions,11,065 筆,就是每一個稽核任務的執行記錄) 租戶層 連規則都沒寫,開關也關 照標準做法補 4 條規則(查/改/刪/新增各一條),再開開關 🔴 每天凌晨 3:10 有一支自動清理程式會掃描全部客戶的這張表(找「綁定資料指向的任務已經不存在」的孤兒資料並刪除,core/scheduler.py:386)。這支程式現在能跑,正是因為它沒有客戶身分、被暫時當成最高權限放行(CM-1559 記錄過的那條規則)。如果開了隔離又同時修 CM-1559 那個放行規則,這支清理程式會看不到任何資料、整個失效但不會報錯。兩件事必須一起規劃,見 §6
6 資訊系統清單compliance.information_systems,82 筆,SSP 文件中登記的資訊系統資產) 租戶層 ❌ 連規則都沒寫,開關關掉 補 4 條規則,再開開關 一般使用者透過 SSP 匯入 / 資產管理功能讀寫。開了不會弄斷——沒有查到背景程式或跨租戶統計會用到它
7 缺失改善計畫(POA&M)compliance.poams,92 筆) 租戶層 ❌ 連規則都沒寫,開關關掉 補 4 條規則,再開開關 一般使用者在稽核輪次頁面讀寫(走完整的 app service 授權流程,非背景任務)。開了不會弄斷
8 證據自動分類的執行紀錄compliance.evidence_classification_runs,8 筆) 租戶層 ❌ 連規則都沒寫,開關關掉 補 4 條規則,再開開關 套件內部功能使用,開了不會弄斷(沒有背景程式依賴)
9 證據分類的答案對照表compliance.evidence_classification_ground_truth,1 筆) 租戶層 ❌ 連規則都沒寫,開關關掉 補 4 條規則,再開開關 同上,套件內部功能,開了不會弄斷
10 法規框架解析工作紀錄oscal.framework_parse_jobs,29 筆,匯入法規框架時的暫存工作) 租戶層 ❌ 連規則都沒寫,開關關掉 補 4 條規則,再開開關 🔴 每天凌晨 1:00 有一支自動清理程式會清掉超過 7 天的舊工作紀錄core/scheduler.py:202,走無客戶身分的系統路徑)。這支和上面第 5 項同款——開隔離要跟它一起規劃,但因為只是「清舊資料」不是「找孤兒」,影響範圍較小(清理延遲,不影響任何人看資料)
11 操作紀錄轉發設定config.log_forwarding_settings,1 筆,「系統要把操作紀錄轉送到哪台主機」的設定) 平台層(全系統只有一份設定,不分客戶) ❌ 連規則都沒寫,開關關掉,而且這 1 筆資料的客戶欄位是空的(本來就對,因為它是全系統共用設定) 不是這張表的問題——它本來就該是平台層(只有原廠管理者能設定),現有的權限檢查(能力點守門)已經做了這件事,加 RLS 對它意義不大(詳見 §5.3) 只有原廠管理者能改(既有能力點守門已擋),一般使用者只能讀(也有能力點守門)。這張表建議不動,是產品分類問題不是隔離漏洞
12 使用者角色指派public.user_roles,41 筆,「誰在哪個租戶被指派了什麼角色」) 租戶層 ❌ 連規則都沒寫,開關關掉 補 4 條規則,再開開關 這張表本身就是判斷「使用者現在有什麼權限」的依據,被最核心的授權查詢直接讀(不經過隔離判斷,是隔離判斷的原料之一)。開了不會弄斷一般查詢,但這張表极度敏感、要優先修——它現在完全沒隔離,等於任何客戶能查到別家客戶「誰是誰的管理員」這類名冊
13 使用者-租戶關聯表public.user_tenants,41 筆,「這個使用者隸屬哪些租戶」) 租戶層 ❌ 連規則都沒寫,開關關掉 補 4 條規則,再開開關 🔴 這張表是「切換租戶」功能本身的合法性依據——使用者要切到某個租戶,系統會先查這張表確認「你真的隸屬這個租戶」(jedi-iam 套件 middleware/context.pydefault_membership_check)。這是一張控制access的表,不開隔離反而更危險:任何人現在可以查到全公司「誰能切到哪個租戶」的完整名冊

3.2 4 支畫面(技術名叫 view)——繞過隔離

view 是什麼:一個事先存好的查詢,用起來像一張表,但背後其實是即時去問好幾張真正的表湊出來的結果。

# 這支畫面是什麼(白話) 問題 誰在讀、開了會不會弄斷
1 待辦任務列表vw_user_job_queue,就是使用者登入後看到的「我的任務」清單) 繞過隔離。PostgreSQL 這個資料庫軟體有個規則:查詢一個「畫面」時,預設是用「建立這個畫面的人」的身分去查底層資料,而不是「正在查詢的人」的身分。建這支畫面的帳號是資料庫最高權限帳號,所以查詢天生就繞過所有客戶的隔離,除非明確關掉這個「用建立者身分查」的預設行為 「我的任務」頁面直接讀(infra/readmodel/tasks/my_grc_jobs_query.py)、稽核儀表板的待辦數量卡片也讀(infra/readmodel/audit/flow_control_dashboard_repo_impl.py:249)。這支畫面底層跨了 12 張表,其中就包含上面第 4、5 項那兩張還沒隔離的表——所以就算只修這支畫面本身,底層那兩張表沒隔離,一樣繞得過去。四項必須一起修(見 §6)
2 使用者能力總覽v_user_capabilities,「這個使用者可以做哪些操作」的完整清單) ❌ 同上(建立者身分繞過隔離),但這張表本身查的資料沒有客戶欄位user_rolesrole_capabilitiescapabilities 三表 JOIN),修法一樣但急迫性較低——因為就算不修,它洩漏的是「這個人有什麼權限」而不是「別家客戶的業務資料」 被授權檢查機制(CapabilityGuard)用來判斷「這個使用者現在有沒有權限做這件事」,也被到期通知功能(找「這個租戶的管理員是誰」)用(app/license/adapter/tenant_admin_directory.py)。開了 security_invoker 不會弄斷——它的資料來源本來就沒有租戶區分
3 使用者路由清單v_user_routes ❌ 同上(建立者身分繞過隔離) 本棒 grep 全 BE 主專案與 jedi-iam 套件,找不到任何 Python 程式碼直接讀這支畫面——選單可見性改走另一條程式路徑計算(jedi_iamUiRouteRepoImpl.get_viewable_by_user_uid,直接查底層表、不經過這支畫面)。這支畫面目前疑似是歷史遺留、沒有實際使用者。修法一樣(加 security_invoker),但建議先確認真的沒人用再排優先序,別花時間修一支沒人讀的畫面
4 角色路由清單v_role_routes ❌ 同上(建立者身分繞過隔離) 同上,本棒 grep 找不到直接的 Python 讀取者;v_user_routes 的定義有引用它(畫面疊畫面),但既然 v_user_routes 本身查無使用者,這支的實際影響也待確認

4. 一條應用層的同型洞(決策者裁定歸本棒)

任務詳細 API 收了「這個任務屬於哪個專案」的參數,卻完全沒有用它來檢查

白話說明:一個網址路徑收了「專案編號」與「任務編號」兩個參數(GET /grc/project/<project_uid>/job/<job_uid>),照理應該要確認「這個任務編號真的屬於這個專案編號」才回資料,但實際上程式碼只拿任務編號去查,完全沒理會專案編號。首腦已開檔核對:

# api/flow_control/routes/job_route.py:82-92
def get(self, project_uid: str, job_uid: str, job_service=...):
    dto = job_service.get_job(job_uid)   # ← project_uid 收了但沒用
    ...

本棒繼續往下追,確認同檔的 PUT(改)與 DELETE(刪)也是同款問題——收了 project_uid 但送進 job_service.update_job / delete_job 之後,那兩支方法內部只用 project_uid 檢查「打這支 API 的人是不是這個專案的管理員」,完全沒有檢查「這個任務真的屬於這個專案嗎」

# app/flow_control/service/job_service.py
def delete_job(self, uid: str, project_uid: str = None) -> bool:
    self._require_manager(project_uid)   # 只驗「你是不是這個專案的管理員」
    ...
    return self._domain_service.delete_job(uid)   # 刪除時完全不比對 uid 是否屬於 project_uid

出事會怎樣:一個對「專案 A」有管理權限的人,只要知道「專案 B」裡某個任務的編號(任務編號是 UUID,看起來很難猜,但在任何列出任務的畫面上都看得到),填自己有權限的「專案 A」編號+別人「專案 B」的任務編號,一樣能改掉或刪掉那個任務——因為程式碼從頭到尾沒有檢查兩者是否真的配對。首腦已實測:真編號/全零編號/亂打的字串三種 project_uid 打進去都回 200 且內容相同。

這與 FR-086 B1「只驗身分不驗歸屬」完全同型,修法可併同一張卡。

在哪裡:

api/flow_control/routes/job_route.py:82-92    GET(讀)
api/flow_control/routes/job_route.py:104-140  PUT(改)
api/flow_control/routes/job_route.py:147-167  DELETE(刪)
app/flow_control/service/job_service.py:108-114   get_job(讀,完全不驗)
app/flow_control/service/job_service.py:178-189   delete_job(刪,只驗身分不驗歸屬)
app/flow_control/service/job_service.py:214-238   update_job(改,只驗身分不驗歸屬)

5. 需要先查清楚才能動手的三件事

5.1 專案表的隔離當初為什麼被關掉

交接檔已指出關閉的 migration:2026-06-25-align-poc-to-stg-*.sql 第 124 行,標記 envs=poc,理由只寫「以 STG 為主」。本棒沒有進一步查到更詳細的理由——這件事建議修正卡動手前先找到原始討論脈絡(或直接問當時做這個決定的人),避免修好一個問題又踩到當初關掉它的原因

5.2 工作流程範本有 191 筆「無主」資料

compliance.workflow_templates 有 191 筆(佔 17,544 筆中的一小部分)的客戶欄位是空的,時間集中在 2026-07-27 到 2026-08-09 之間,其中 132 筆現在還連著實際的工作流程執行紀錄(也就是有客戶正在用)。查了程式碼註解,這批是「凍結副本」(app/flow_engine/service/workflow_template_snapshot_service.py,每次有人正式啟動一個稽核流程時,系統會把當時的範本複製一份凍結起來,之後管理員改範本不會影響已經在跑的流程)。這批複製品目前沒有標記屬於哪個客戶——若直接打開隔離,這 191 筆會從所有客戶眼前消失(因為隔離規則的邏輯是「客戶欄位配對不上就看不到」),而其中 132 筆還連著正在使用中的流程執行紀錄,會造成使用者打開自己的稽核流程時看不到當初凍結的範本內容這批需要先補上客戶欄位(回填正確的客戶歸屬)才能安全開啟隔離,不能跟其他表一樣直接開開關了事。

5.3 操作紀錄轉發設定可能不需要 RLS

config.log_forwarding_settings 只有 1 筆資料,而且設計上本來就是全系統共用、不分客戶的一份設定(「系統要把操作紀錄送到哪台外部主機」是原廠層級的維運決定,不是租戶概念)。查證了套件的守門機制(jedi_api_log.forwarding),讀寫已經各自有獨立的權限檢查(能力點 log-forwarding.read / log-forwarding.update)。這張表沒有隔離不是洞,是因為它本來就不該有「客戶」這個維度——交接檔把它跟其他 12 張表放在同一組建議「照標準模式補 4 條」,本棒認為這張需要另外處理:要嘛從盤點清單移除(承認它是平台層資源),要嘛決策者仍希望它形式上符合「有 tenant_id 欄位的表都要有 RLS」的規範就補(但功能上不會有實際差異,因為守門已經做了該做的事)。留給決策者判斷,本棒不擅自排除。


6. 🔴 本棒最重要的一項:開了隔離會不會弄斷現在的功能

這一項是本棒存在的理由,不是列完清單就結束。

6.1 背景清理程式——會受影響的兩支

系統有兩支「不需要任何人登入、自動在背景跑」的排程工作,跑的時候用的是最高權限身分(因為沒有登入者,系統暫時假裝是「看得到全部」的身分,這條放行規則本身也是另一個已知問題,記錄在 CM-1559):

排程 頻率 讀的表 開隔離的影響
孤兒綁定資料清理core/scheduler.py:386 每天凌晨 3:10 compliance.job_executions(本棒第 5 項) 🔴 這支現在能掃全部客戶的任務,是靠它有「暫時最高權限」身分。若之後 CM-1559 那個「暫時最高權限」的放行規則被收緊(那件事遲早要做,本身就是一個已知風險),這支清理程式會突然什麼都掃不到——它不會報錯,只是安靜地不再做事,孤兒資料會一直堆積沒人發現。兩件事要放在同一張規劃裡處理,不能各修各的
法規框架解析工作清理core/scheduler.py:202 每天凌晨 1:00 oscal.framework_parse_jobs(本棒第 10 項) 同款成因,但影響較小——最壞情況是舊的暫存工作記錄清得比較慢,不影響任何人看得到的資料

這兩支排程本身走的是「背景任務,無使用者身分」這條路,資料庫層級開不開隔離不會讓它們「看不到自己的東西」(因為它們本來就沒有「自己」),會出問題的是未來如果連「背景任務暫時給最高權限」這條規則本身也被收緊,兩件事疊在一起才會炸。本棒的結論是:修隔離的人必須知道這兩支排程的存在,安排修復順序時把它們排進同一輪驗證,而不是修完隔離就直接上生產環境。

6.2 平台管理員——查證結果:不會受影響(更正前一棒交接檔的推測)

交接檔原本列了「原廠管理者要看全部租戶,要確認走的是哪條路」這一項待查。本棒已查清楚:平台管理員(原廠層級的帳號)判斷「你是不是平台管理員」,用的函式是 viewer_is_platform_admin()(在 jedi-iam 套件的 authz/platform.py),這個函式呼叫的正是資料庫隔離機制用來判斷「這個人是不是最上層客戶」的同一段邏輯_session_paths_have_root)。也就是說:平台管理員能看到全部客戶資料,走的是「我本來就是最上層客戶,隔離規則本身允許最上層看下層」這條合法的路,不是靠某個地方隔離沒開才漏看得到

結論:開啟本棒盤點的這 13 張表+4 支畫面的隔離,不會影響平台管理員後台的任何功能。 這件事原本被列為風險,查證後可以放心排除。

6.3 統計/儀表板類——查證結果:本棒範圍內沒有找到會被弄斷的

本棒 grep 了所有讀取這 13 張表的程式碼位置(§3.1「誰在讀」欄已逐項列出),沒有找到任何一支「刻意讀取全部客戶資料做統計」的功能——目前找到的統計類查詢(如稽核儀表板 flow_control_dashboard_repo_impl.py)都是先算出「這個使用者參與的專案」清單,再用那個清單去查任務與流程資料,本來就是照使用者身分過濾的,隔離開啟後行為一致。

唯一的例外要另案追蹤:交接檔提到 FR-083 已查出「AI 儀表板有 27 支查詢功能完全不檢查權限」,本棒 grep 了那 27 支查詢的清單(見 FR-083 報告),它們讀的資料範圍與本棒盤點的 13 張表大致重疊但不是同一批程式碼路徑(AI 儀表板是動態組裝任意查詢方法,本棒盤點的是 RLS 資料庫層),兩者是各自獨立的洞,不是同一個機制,修一邊不會自動修好另一邊,也不會互相弄斷。AI 儀表板那 27 支的權限問題請照 FR-083 報告另案處理。


7. 問卷平台旗標——決策者要裁的一項,本棒只整理選項

survey.surveyssurvey_folders 兩張表沒有「這是平台公版問卷」的標記欄位。本棒查了 DEV 資料庫實際資料:

tenant_id=1(原廠自己)的問卷:8 份
tenant_id=102(某客戶)的問卷:44 份

原廠帳號建的 8 份問卷,因為沒有平台公版標記,子租戶完全看不到。這跟另外兩個「公版資料」的做法不一樣:

資料類型 怎麼標記公版
flow_templates(流程範本) is_builtin 欄位
module_framesdetection_profiles(模組框架/偵測方案) scope=SYSTEM 欄位
surveyssurvey_folders(問卷) 沒有任何標記欄位

要決策者裁的問題是:原廠的 8 份問卷,到底該不該讓所有客戶都看得到?

  • 選項 A:加旗標比照 flow_templates——加一個 is_builtin 或類似欄位,原廠建的問卷全客戶可讀。優點是跟其他公版資料的做法一致;缺點是要改 schema、改讀取邏輯、也要決定「哪些既有問卷該回溯標成公版」。
  • 選項 B:不加旗標,維持現狀——原廠的問卷就是原廠自己用的,不是公版庫。優點是不用動任何東西;缺點是如果原廠原本的用意是「建立範例問卷給客戶參考」,這個用意目前沒有實現。
  • 選項 C:另開專屬機制——問卷公版化走跟其他三種不一樣的設計(例如「複製一份到客戶自己名下」而非「共讀原廠那份」)。優點是問卷可能本來就需要客戶各自修改,不適合共讀;缺點是又是第四套做法,long-term 一致性更差。

本棒不建議,交給決策者選。


8. 修法分組與順序建議(本棒不寫 migration,只給分組建議)

內容 風險 備註
甲:純開關(可一支 migration 一起做) projectstenant_drive_integrations 開開關 規則現成、無背景依賴、無孤兒資料問題
乙:補規則再開開關 job_executionsinformation_systemspoamsevidence_classification_runsevidence_classification_ground_truthframework_parse_jobsuser_rolesuser_tenants 照標準模式各補 4 條規則 job_executionsframework_parse_jobs 要跟背景排程(§6.1)一起驗證;user_rolesuser_tenants 敏感度最高建議優先
丙:先查後開 workflow_executions(補 1 條「查詢」規則後開) 與待辦清單畫面(丁組)同一批底層資料,要一起修
丁:畫面補一行設定 4 支畫面加 security_invoker vw_user_job_queue 跨 12 張表,其中含 job_executionsworkflow_executions丙丁必須同一輪一起驗證v_user_routesv_role_routes 找不到實際讀者,建議先確認用途再排優先序
戊:需要先處理資料才能開 workflow_templates 中高 191 筆凍結副本無客戶歸屬,要先回填客戶欄位
己:要先查清楚原因 projects(併入甲組前,先查當初關閉的完整脈絡) 不查清楚可能重蹈覆轍
庚:產品決策後再動 surveyssurvey_folders 平台旗標 等決策者裁 §7 選項
辛:建議重新分類,不當隔離缺口修 log_forwarding_settings 見 §5.3,這張本質是平台層資源
壬:應用層洞,與 FR-086 併 任務詳細 API 不驗 project_uid(§4) GET/PUT/DELETE 三支一起改

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

表格與規則現況(§3 的「現在是哪一層」欄):可信度高。 每一項都是本棒直接對 DEV 資料庫下唯讀 SQL 查出來的(pg_classpg_policyinformation_schema),SQL 語句與交接檔給的一致,逐項比對與交接檔一字不差(13 張表、4 支畫面,數字、規則條數全部吻合)。

「誰在讀、開了會不會弄斷」欄:這是本棒的核心產出,方法是 grep BE 主專案與 jedi- 套件原始碼。* 有把握的地方:

  • 背景排程的存在與讀取範圍(直接讀 core/scheduler.py 原始碼確認)
  • 平台管理員走哪條路(追到 viewer_is_platform_admin() 與 RLS 用同一段判斷邏輯,這是本棒本身查證出來、更正了交接檔待查項目的部分)
  • 任務詳細 API 的驗證缺口(開檔逐行核對,含 GET/PUT/DELETE 三支)

不保證的地方

  1. grep 的天生限制——動態組出來的 SQL(字串拼接、ORM 產生的查詢)grep 可能漏掉;本棒對關鍵表(job_executionsworkflow_executionsuser_roles)額外用「誰 import 這個 ORM model」交叉確認,但沒有對全部 13 張表逐一做這層交叉確認。
  2. FE(前端)程式碼完全沒有查——本棒只查了 BE 主專案與 jedi-* 套件,前端若有直接組 SQL 或呼叫未涵蓋在本棒範圍內的 API 路徑,不在本次盤點內。
  3. 沒有做任何實際測試——沒有真的開過任何一個開關、沒有真的切過租戶、沒有真的跑過任何一支修法。全部是讀程式碼、讀規則定義、查資料庫結構與統計數字推論出來的。
  4. STG/POC 環境完全沒有查——依卡片紀律只查 DEV,若 STG/POC 的隔離狀態與 DEV 不同,那是另一件需要單獨盤點的事。

合起來的建議:甲組(純開關)與壬組(應用層洞)證據最扎實,可以直接排修正卡。乙丙丁戊組修之前,建議修正卡的人自己再對照一次程式碼(尤其 §6.1 提到的兩支背景排程),因為「開了隔離不會弄斷什麼」這件事的代價是「弄斷了才會發現」。


10. 座標

  • 來源交接檔FR-080/handoff/fr080-to-fr075-HANDOFF.md §二
  • 三層模型原文:docs/changelog/2026-06-29-feat-tenant-scope-data-model-b2.md2026-06-30-feat-compliance-framework-platform-write.md
  • 標準規則寫法:docs/system-design/database/RLS_DESIGN.md
  • 同型前案:FR-086(四層全空,§4 任務詳細 API 建議併卡)、CM-1559(無身分時隔離整個關掉,§6.1 兩支背景排程受影響)、FR-083(AI 儀表板 27 支查詢不經權限檢查,§6.3 已確認是獨立的洞)
  • 跨 arc 總表:security-scan-consolidated/
  • 首腦手冊:.claude/skills/security-scan-lead/SKILL.md