§2
結論
- 產品靠資料庫的 RLS(Row Level Security,資料庫自己擋、讓每個客戶只查得到自己的資料)做多租戶隔離。FR-087 盤點(CM-1663)查出 13 張表、4 支 view 的隔離實際上是關的:有的規則寫好但開關沒開、有的連規則都沒寫、view 用「建立者身分」查底層而繞過隔離。決策者 2026-09-11 親測:切到子租戶後仍看到別家待辦 39 筆、專案 213 筆。本案把這些一張一張補齊、打開,並驗證登入、切租戶、我的任務、兩支凌晨排程都還活著。
- 三個定案:D1 公版機制沿用「policy 內加旗標 OR 分支」,不動
app_tenant_allowed_for_session helper(12+ 張表共用),既有 flow_templates.is_builtin/module_frames、detection_profiles 的 scope='SYSTEM' 不回改,新開的表一律用 scope;D2 問卷不加公版旗標(引用是複製進任務),要修的是 survey schema 另 12 張沒 RLS 沒 tenant_id 的裸表;D3 projects 的 RLS 是 2026-06-25 環境對齊順手關的(commit e7709295),非產品決策,補 is_super_admin 分支後重開、DEV 造跨租戶樣本驗。
- 順序硬相依:03:10 孤兒清理與 01:00 框架解析清理兩支排程靠「無身分=super admin」跑,那正是 CM-1559 要收緊的洞。必須 .1a 先給排程具名系統身分 → CM-1559 收緊 → .2c 才開
job_executions/framework_parse_jobs,反過來清理程式會安靜失效不報錯。
- 平台管理員不受影響(
viewer_is_platform_admin() 走 root 路徑,不是靠隔離沒開)。不做:log_forwarding_settings(本質平台層,能力點守門已擋)、任務詳細 API 不驗 project_uid(歸 FR-086)、surveys/survey_folders 公版旗標(D2)。
- 可見度三原則(設計全文見 design.md):① 租戶不跨——一筆資料只屬一個租戶,不跨租戶指派、不跨租戶操作,別租戶要看只能靠公版、母租戶分享給子樹、或複製;② 階層只往下看——母租戶看得到子租戶資料但不在裡面操作,要操作就由該租戶加人給角色、切租戶進去做、紀錄留在該租戶;③ 部門不是牆——部門只記歸屬與寫入範圍,查詢規則一律不加部門條件,可見度靠角色與參與關係。
- 三原則對八張卡的影響:租戶部分的規則形狀不用改(前綴規則就是正解);.2d 與 .2e 開之前要先把
workflow_executions/workflow_templates 改、刪規則裡的部門條件換成租戶條件(DEV 任務指派 68% 跨部門,照現在規則開,跨部門推進任務會更新 0 筆不報錯)。長出兩件另開卡、不擋 1.20.0 的事:指派候選名單擋非本租戶帳號+SPEC 寫明不支援跨租戶指派;母租戶自訂資源(模組框架、檢測方案、範本)scope 加「分享給子樹」一檔。
- 每張開隔離的卡必做「開前 vs 開後」對照(模擬非 super_admin 租戶帳號查筆數);migration 只套 DEV(localhost)+ 188:25432 基線庫並重產 02-schema;帶舊資料升級不破。
§4
需求討論紀錄
- 2026-09-13 決策者裁定 D1–D3 與順序硬相依(見結論 2、3)。同日開卡 runner 在 DEV(localhost)實查校正盤點數字:
projects 221 筆全屬 tenant 102;workflow_templates 18,135 筆、本機 tenant_id IS NULL 為 0 筆(FR-087 於 188 快照查到 191 筆)——.2e 卡要求對 188 快照與基線庫再各查一次,回填 SQL 不論本機是否為 0 都寫進 migration(冪等);information_systems DEV 已由 jedi-asset 002 開 RLS(基線未開),.2c 只做基線對齊;tenant_drive_integrations DEV 在 public schema(盤點寫 compliance),.2a 動手前確認。
- 2026-09-14 決策者裁可見度三原則:討論起點是「母租戶開任務給子租戶帳號,那個帳號會拿不到母租戶的框架、問卷」;分析後決策者判「根本不應該跨租戶指派,頂多上面能看下面,要操作就讓該租戶派權限、用切換進去」,與業界階層式多租戶做法一致。部門則裁「資料共享、部門只管誰能改、特定資料日後逐筆收緊」。連帶查到既有
workflow_executions/workflow_templates 改、刪規則綁部門且無逃生口,.2d/.2e 開前要先修。全文與被排除的選項見 design.md。
- 2026-09-14 決策者裁 B 案:1.20.0 做 .1a(CM-1767)、.2a(CM-1768)、.2d(CM-1771,開前先把
workflow_executions 改、刪規則的部門條件換成租戶條件)、CM-1559 只做第二刀(tenant_id 空值不再算 super admin,含 signed-token 改填真實身分);CM-1559 第一刀(無身分直接拒絕)與 .2b/.2c/.2e/.3a 五張排 1.20.0 之後、SaaS 上線前必做。判斷依據:落地版一台機一個客戶,剩下 11 張表洩漏的是同公司內部子租戶資料,不擋出貨;現在做會撞 FR-093 出貨鏈。決策者原話「那先 B 吧」。
- 2026-09-14 決策者點名核對安裝包 seed:出貨公版只有合規框架(CMMC 2.0 L1/L2)、流程範本 2 支、掃描設定檔 10 套三類(
scripts/init/04-seed-core.sql/05-seed-catalog.sql 實查)。workflow_templates 基線刻意清空(04 尾端有非空即報錯守衛)、module_frames 與 surveys 零筆。DEV 上掛 tenant 1 的那些都是測試資料,前一輪分析被 DEV 資料誤導兩次(問卷公版、模組框架公版),已改正。判斷公版與否一律看 scripts/init/ 不看 DEV。
- 2026-09-14 決策者確認兩件:① 租戶隔離的理解定案——租戶整個隔開;要在某租戶操作,必須先被該租戶授權(加進租戶、給角色)再切換過去;預設母對子只能瀏覽(資料庫層目前只擋讀的範圍、寫入靠程式層角色檢查,「上層對下層只讀不寫」要在 DB 層擋另開卡)。② 問卷沒有公版、不需要公版:出貨基線零筆問卷,DEV 掛 tenant 1 的 8 份是手建測試資料;D2 維持,任務引用時複製。新租戶初始化不複製參考資料(框架、範本、檢測方案走公版共用),只 seed 預設部門、管理員角色、儲存設定。
- 2026-09-14 四項裁決(決策者「都照你建議,也都開」):① CM-1559 拆成 CM-1787(第二刀,1.20.0)與 CM-1788(第一刀,SaaS 前),原卡 Done;② 開 CM-1789(指派候選名單擋非本租戶+SPEC)與 CM-1790(母租戶資源分享給子樹),都排 1.20.0 之後;③ .2d 的
v_user_routes/v_role_routes 零讀者、pg_depend 無依賴,一律 DROP;④ 派工順序:CM-1768 與 CM-1771 現在派、平行(只動主專案 migration);CM-1767 等 FR-093 .4a~.4c 收完再派(動 jedi-common 走 path dependency,避免撞 pyproject);CM-1787 在 1767 後。
- 2026-09-14 範圍討論(B 案的分析過程):首腦建議只做 .1a+CM-1559;範圍分析員指出 CM-1559 第一刀(無身分直接拒絕)回歸面最大、要關的洞落地版目前打不到(X-Tenant-ID:0 已被 FR-069.16 擋、users.tenant_id 無 NULL)、且 Drive 路徑在 DEV 驗不出(表擁有者是 cm_app);而決策者 9/11 親測的兩個症狀對應 .2a 與 .2d,是全案最便宜好驗的兩張。建議 B 案:.1a+.2a+.2d+1559 第二刀,其餘排 1.20.0 之後。等決策者裁。
- 2026-09-13 吸收兩張舊安全債卡:CM-1445(三張授權關聯表無 RLS,FR-069 D9 附帶發現)→ 併 CM-1769;CM-1453(projects RLS 停用是否刻意)→ 查證為環境對齊順手關、非刻意,併 CM-1768。兩張卡已 append 併入說明並改 Done。
- 待裁記帳(三項仍未裁):租戶層設定是否全部繼承最上層(現只複製儲存設定,SMTP/閒置逾時/第三方登入新租戶空白,讀取端各自 fallback);DB 層是否擋「上層對下層寫入」;.4b UI 開關要不要拆 FE 卡。(已裁:.2d 兩支 view DROP;.2e 無主列一律刪。實作期定案兩項:.3a 問卷裸表選 EXISTS 繞 surveys——不加
tenant_id 欄位,因回填會把還在用的作答靜默藏起來;.4b 選 scope 加第三態 SHARED——布林裝不下三態,is_builtin 不回改。)
- 前作:FR-087 盤點報告
../FR-087-2609-tenant-isolation-audit/scan-T1-isolation-gap-audit.md(§3.1 十三張表、§3.2 四支 view、§5.2 無主副本、§6.1 兩支排程、§8 分組);CM-1559 第一步盤點(31 張 RLS 表、8 支排程、引導悖論三類分流)。
§5
座標
- RLS 設計:
docs/system-design/database/RLS_DESIGN.md;helper 定義 scripts/init/02-schema.sql:623;標準 policy 樣板 scripts/sql/2026-04-23-google-drive-oauth-integration.sql:48-79
- 兩支排程:
core/scheduler.py:202(01:00 framework_parse_jobs)、:386(03:10 job_executions)
- fail-open 判定:jedi-common
jedi_common/session/database/db.py:100-119
- 對照帳號:
blsit(tenant 102,非 super_admin)/blsadmin(super_admin,路徑 /1/);DEV 庫 psql -h localhost -p 5432 -U cmmgr -d guidant_ai_dev;基線庫 188:25432 guidant_ai
- branch:BE
feature/FR-075、jedi feature/FR-075(2026-09-13 查)
- skill:
sql-migration、jedi-package-dev
§6
文件
以下全部由 build 掃資料夾產生,新增檔案重 build 即自動出現。標題連結指向渲染後的 HTML,md 連向源檔。
設計
§7
Notion 卡
卡片內容(決策紀錄、驗收條件)以 Notion 為準,本頁只記座標。