# 出貨基線 RLS 查證（CM-2107）

> 本卡由 FR-116 盤點（CM-2089）帶出，唯讀查證，不改任何環境。

## 結論（白話）

**新客戶裝機時，稽核輪次（`project_audit_rounds`）與審閱標記（`review_marks`）這兩張表，目前實際上是「有」被套上資料庫保護（RLS）的** —— 只要客戶是拿**現在的**安裝包裝機。原因：安裝流程本來就會在建完 schema 之後，自動多跑一輪「套件 migration」，而這一輪就包含幫這兩張表開保護的那支腳本。

但是**已經裝好的兩台機器（STG／POC）目前是「沒有」保護的**，因為它們是在保護規則寫出來**之前**裝的機、後來也沒有升級套用過。這不是安裝流程有洞，而是「舊機器沒有跟著補課」的正常現象——只是目前沒有人去補這一動。

## 逐項證據

### ① 安裝流程有沒有接著跑套件 migration？—— 有，而且新裝／升級都會跑

呼叫鏈：

1. `scripts/installer/install.sh:1715` `init_database()` 先跑 `guidant-db-init`（MODE=init，預設值），套完 `02-schema.sql`（建表結構，`scripts/init/init.sh:100`）等基礎五段。
2. 緊接著同一個函式呼叫 `init_apply_package_content()`（`scripts/installer/install.sh:1729`）。
3. `init_apply_package_content()`（`scripts/installer/install.sh:1770-1793`）用**同一顆 image**、改傳 `GUIDANT_INIT_MODE=migrate MIGRATE_APPLY=1`，再跑一次 `guidant-db-init`。
4. 容器內跑的是 `scripts/init/migrate.sh`，它會讀 `scripts/sql/packages/manifest.tsv`（第二輪套件 migration，見 `migrate.sh:24-31` 註解）逐支套用，其中就包含
   `packages/jedi_compliance_audit/002-compliance-audit-rls.sql`（開 `project_audit_rounds`／`review_marks` 的 RLS，manifest.tsv 第 16 行）。
5. 這一步**新裝與升級走同一條路**（`install.sh:1772` 註解原話：「新裝與升級因此走同一條路」），失敗會直接擋住不讓服務起來（`install.sh:1781-1791`：退出碼非 0 一律 `fail`）。

**結論**：只要客戶是用**含這支套件 migration 的安裝包**（即 CM-1800／09-14 之後出的包）裝機，這兩張表的保護會在裝機當下自動套上，不需要人工介入。

### ② 已裝好的 STG（188）／POC（189）查證結果 —— 兩台都沒有保護

唯讀查詢（`docker exec guidant-db psql -U cmmgr -d guidant_ai`，未做任何寫入）：

| 檢查項 | 188 STG | 189 POC |
|---|---|---|
| `project_audit_rounds` 開 RLS？ | ❌ `relrowsecurity=f` | ❌ `relrowsecurity=f` |
| `review_marks` 開 RLS？ | ❌ `relrowsecurity=f` | ❌ `relrowsecurity=f` |
| 這兩張表有沒有任何 policy？ | 0 筆 | 0 筆 |
| `schema_migrations` 有沒有套件 migration 的紀錄？ | 0 筆（`packages/%` 全空） | 0 筆 |
| baseline marker | `__init_baseline_v1.14.0__` | 同左 |
| `schema_migrations` 總筆數 | 141 | 141（與 188 完全一致，含同一批 2026-08-30 為止的主線 migration） |

兩台的 baseline marker 都是 `v1.14.0`（裝機時間約 2026-08-10 前後），而幫這兩張表開 RLS 的規則（CM-1800）是 **2026-09-14** 才寫出來的 —— **裝機當下這支保護規則根本還不存在**，不是安裝流程漏套，是「機齡早於規則」。裝完之後也沒有任何一支 2026-09 的 migration 紀錄，代表**這兩台機器自 09-14 以來沒有升級套用過任何 migration**，保護規則自然沒有機會補上去。

### ③ 同類問題掃一遍：manifest.tsv 裡所有套件 migration，有沒有哪些「靠攤平補、快照沒重產」

`scripts/sql/packages/` 整個目錄是 **git 忽略**的（`.gitignore:22`）——它是 build 期由 `flatten_pkg_migrations.sh` 從各 jedi 套件現烤出來的，本來就不是「寫進基線快照」的東西，設計上就該靠 migrate 模式在裝機／升級當下補，**不是**遺漏。

實際比對 `scripts/init/02-schema.sql`（出貨基線快照）與 11 支帶 RLS 的套件 migration 動到的 26 張表，看基線快照裡這些表**當下**有沒有已經開 RLS（即使套件 migration 本身不寫進快照，只要另一支主線 migration 已經把同樣的保護收斂進基線，就不算缺口）：

| 表 | 基線快照已開 RLS？ |
|---|---|
| `compliance.information_systems` / `public.devices`（asset） | ✅ 已開 |
| `public.bulletins` | ✅ 已開 |
| `compliance.project_audit_rounds` / `compliance.review_marks`（**本卡查的這兩張**） | ❌ **沒開** |
| `compliance.detection_execution_groups` 等 7 張（detection） | ✅ 已開 |
| `public.upload_files` | ❌ **沒開** |
| `public.feedback_issues` | ✅ 已開 |
| `config.tenant_license_events` / `config.tenant_licenses` | ✅ 已開 |
| `compliance.agent_tasks` 等 3 張（remote_agent） | ✅ 已開 |
| `public.system_configs` | ✅ 已開 |
| `compliance.control_group_participants` 等 6 張（task_platform participants） | ❌ **沒開**（6 張全沒開） |

**發現同類缺口共 2 組、8 張表**：`review_marks`／`project_audit_rounds`（本卡）以及 `upload_files`、`control_group_participants`／`process_participants`／`project_control_participants`／`project_group_participants`／`project_participants`／`task_assignees`（task_platform 六張參與者表）。

這些**不是**「攤平沒補、快照該重產卻沒重產」的問題——它們本來就該靠**裝機當下自動跑的 migrate 輪**（見①）補上，設計上是正確的；問題只出在**已經裝好、又沒升級**的舊機器上（如 188／189），這一動沒人做。是否要幫舊機器補這一動（跑升級流程 / 手動套用），**屬於決策者裁示範圍，本卡不建議也不動手**。

## 建議（僅供參考，不動手）

- 若 188／189 短期內不會走正式 `install.sh --upgrade` 升級流程，且這兩張表的資料有跨租戶外洩風險疑慮，可考慮由決策者裁示是否手動對這兩台跑一次 `GUIDANT_INIT_MODE=migrate MIGRATE_APPLY=1`（即安裝流程本來就會做的那一步，補做而已，不是新動作）。
- 長期看，「裝機時間早於某條規則」這類舊機器是否要有主動巡檢機制（例如定期跑 `MIGRATE_APPLY=0` dry-run 檢查待套清單），是否要另外收斂進出貨流程，同樣留給決策者裁示。

## 查證方式（供覆核）

```bash
# 讀 code：呼叫鏈
grep -n "init_apply_package_content\|GUIDANT_INIT_MODE=migrate" scripts/installer/install.sh
sed -n '1,40p' scripts/init/migrate.sh

# 查 188 / 189（唯讀，未寫入）
ssh jedi@192.168.50.188 "docker exec guidant-db psql -U cmmgr -d guidant_ai -tAc \
  \"SELECT relname, relrowsecurity FROM pg_class WHERE relname IN ('project_audit_rounds','review_marks');\""
ssh jedi@192.168.50.188 "docker exec guidant-db psql -U cmmgr -d guidant_ai -tAc \
  \"SELECT tablename, policyname FROM pg_policies WHERE tablename IN ('project_audit_rounds','review_marks');\""
ssh jedi@192.168.50.188 "docker exec guidant-db psql -U cmmgr -d guidant_ai -tAc \
  \"SELECT filename FROM public.schema_migrations WHERE filename LIKE '%compliance_audit%';\""
# 189 同上，主機改 192.168.50.189
```
