A.1 盤點:檢測、證據、問卷三模組(加資產的設備)端點 × 三段式能力點

卡片 CM-2513(FR-132 T-1.3)。唯讀盤點,不改程式、不改 DB。

§1

範圍與數量

  • 範圍:設備(device,歸 asset)、遠端代理(remote-agent-manage)、檢測工具外掛與執行(plugin)、檢測基準(detection-profile)、授權掃描網段(scan-zone)、雲端整合(cloud_integration)、AI 證據分類(jedi-evidence-classification)、儲存設定(storage-config)與檔案上傳下載(jedi-file-upload)、問卷(survey)。
  • 主專案 route 檔 6 支、17 個 method:api/cloud_integration/routes/ 4 支(13)、api/remote_agent/routes/agent_file_route.py(1)、api/system_config/routes/system_config_route.py 中屬 storage-config 的 2 個 Resource(3;該檔其餘端點屬 T-1.4)。
  • 外部套件 route 檔 20 支、150 個 method:jedi-detection 4 支(46:工具外掛與執行 18、基準 18、分類字典 5、掃描網段 5)、jedi-remote-agent 1 支(19)、jedi-asset 設備 2 支(11)、jedi-evidence-classification 3 支(21)、jedi-file-upload 1 支(6)、jedi-survey 9 支(47:設計 29、填答 18)。讀的是 .venv/lib/python3.11/site-packages/ 內實際執行的版本(jedi-detection 1.2.7、jedi-remote-agent 1.4.0、jedi-asset 1.2.2、jedi-evidence-classification 1.6.2、jedi-file-upload 1.3.4、jedi-survey 1.2.3)。
  • 合計 26 支檔、167 個 method。泛用系統設定端點 /system/config/*(jedi-system-core)雖會依 GROUP 守 storage-config.*,屬 T-1.4 範圍,本卡只記它的分流規則不逐支列。
  • DEV 查詢時間:2026-10-06 17:32(本機 localhost:5432/guidant_ai_dev)。範圍內舊能力點 30 個(8 種 resource_type)。
§2

先看這幾點

  1. 檢測三合一、雲端整合、問卷三個加值包都能各自整包落在一個模組,對照驗證表在文末。唯一跨界的是 AI 證據分類設定借用了 storage-config.update(見第 5 點)。
  2. 檢測執行(開始、取消、重跑、刪除紀錄)9 個 method 沒有能力點,只有授權照+專案任務操作人檢查。它是專案任務的一部分,建議歸 grc.job,不放 detection——放 detection 會讓「買了檢測包、但公司角色沒勾 detection」的稽核員在任務頁按開始掃描時 403。見文末定案結論。
  3. 遠端代理管理 15 個 method 全部守同一顆 remote-agent-manage.create,連列表都是。原因是套件只給一個 admin_required 插槽、不分讀寫(core/plugins/remote_agent.py:147-168)。改名時要麼請套件拆成讀寫兩欄,要麼宿主自掛 route;不拆的話新名四個動詞只有一個有作用。
  4. 問卷填答端 18 個 method 有 16 個沒有能力點,只有授權照+任務指派人/專案成員檢查;另 2 個(改、刪留言)反而要問卷設計權。定案:不另開 survey.response,歸 grc.job.read/update(見文末定案 2)。
  5. storage-config.* 一顆鑰匙開了五扇門:儲存設定頁、Google 應用程式憑證頁、AI 服務設定頁、支援頁、AI 證據分類設定(套件寫入端借 storage-config.update)。拆不拆、歸哪個模組都見文末定案結論,建議見「storage-config 歸屬建議」。
  6. 證據分類套件 21 個 method 沒有任何能力點,全靠專案成員角色(manager/參與者);它也沒有授權照守門。
§3

新名總覽(定案)

新資源 read create update delete 獨立動詞 取代的舊名
asset.device ✓ ✓ ✓ ✓ import、export device.*
detection.remote-agent ✓ ✓ ✓ ✓ — remote-agent-manage.*
detection.plugin ✓ ✓ ✓ ✓ — plugin.*
detection.profile ✓ ✓ ✓ ✓ — detection-profile.*
detection.scan-zone ✓ ✓ ✓ ✓ — scan-zone.read/update
evidence.cloud-integration ✓ ✓ ✓ ✓ — cloud_integration.*
evidence.classification ✓ ✓ ✓ ✓ — 無(新開)
system.storage-config ✓ ✓ ✓ ✓ — storage-config.*
evidence.classification-profile ✓ ✓ ✓ ✓ — 借用 storage-config.update(拆出)
ai.ai-provider-config ✓ ✓ ✓ ✓ — 借用 storage-config.*(拆出)
survey.survey ✓ ✓ ✓ ✓ import、publish survey.*
  • detection.profile 不叫 detection.detection-profile:模組名已是 detection,資源段重複前綴只是噪音。detection.scan-zone、detection.remote-agent 同理把 -manage 拿掉(manage 是頁名,不是資源)。
  • evidence.cloud-integration 用連字號:舊名 cloud_integration 是底線,三段式規定資源段內用連字號。license 鍵仍是 cloud_integration(D10 的 license_module 欄存原值),授權照不受影響。
  • survey.survey 兩段同名,但資源段要有值;若 T-1.6 合併時傾向 survey.questionnaire,主表照換即可。
  • 不開 survey.response:填答端歸 grc.job.read/update(通則 A,見文末定案 2)。
  • detection.scan-zone 補 create/delete:今天只有 read/update 兩顆,新增與刪除都掛 update。依「固定四個」規則補齊,route 改守對應動詞。
§4

主表

欄位:route 路徑 | HTTP | 程式位置 | 現有守門 | 新三段名 | 現有能力點 | 對應 UI | 可指派範圍層 | 備註。

守門縮寫:J=jwt_required;L(x)=授權照守門 require_license("x");C(x)=能力點守門;PA=平台管理員;成員:=專案層角色檢查(參與者=任何成員)。所有路徑前綴 /api/1.0。套件 route 檔的路徑以 jedi_xxx/api/routes/ 起算。

設備(asset.device)

jedi-asset 的 route 守門:auth_required(宿主注入的 jwt)+capability_required(rt.config.device_*_capability),能力點名預設值在 jedi_asset/plugin/contract.py:77-80。無授權照守門(設備屬基礎包)。

route 路徑 HTTP 程式位置 現有守門 新三段名 現有能力點 對應 UI 範圍層 備註
/devices POST jedi_asset/api/routes/device_route.py:39 J+C(device.read) asset.device.read device.read DeviceManage(/device/device-manage) org_unit 列表
/devices/menu GET device_route.py:59 J+C(device.read) 跟呼叫頁面的權限(通則 B) device.read ModuleFrameSspDevicesPanel、JobExecutionDrawer(useDeviceTargetOptions) org_unit 輔助端點
/device POST device_route.py:129 J+C(device.create) asset.device.create device.create DeviceManage org_unit
/device/<uid> GET device_route.py:72 J+C(device.read) asset.device.read device.read DeviceManage org_unit
/device/<uid> PUT device_route.py:82 J+C(device.update) asset.device.update device.update DeviceManage org_unit
/device/<uid> DELETE device_route.py:94 J+C(device.delete) asset.device.delete device.delete DeviceManage org_unit
/device/<uid>/references GET device_route.py:111 J+C(device.read) asset.device.read device.read DeviceManage(刪除前警示) org_unit 輔助端點
/devices/import-template GET jedi_asset/api/routes/device_import_route.py:50 J+C(device.create) asset.device.import device.create DeviceImportDialog org_unit 匯入類獨立動詞
/devices/import/verify POST device_import_route.py:65 J+C(device.create) asset.device.import device.create DeviceImportDialog org_unit
/devices/import/confirm POST device_import_route.py:82 J+C(device.create) asset.device.import device.create DeviceImportDialog org_unit FE 該元件另判 hasCap('device.update'),與 BE 守的 create 不一致
/devices/export POST device_import_route.py:102 J+C(device.read) asset.device.export device.read DeviceExportMenu org_unit 對外送出類獨立動詞;今天有 read 就能匯出

主專案另有兩處拿 device.read 判斷:app/module_frame/service/template_lookup_scope.py:16(沒有就不給設備 IP)、di_containers/dashboard_apis/device.py:19(儀表板資料源)。

遠端代理(detection.remote-agent)

套件 jedi_remote_agent/api/routing.py:15-62 的路由表第三欄宣告每支的認證等級;管理端點(AUTH_ADMIN)由宿主的 admin_required 套上,內容是 J+L(remote-agent-manage)+C(remote-agent-manage.create)(core/plugins/remote_agent.py:147-168)。代理呼入的控制面端點驗代理通行證,不帶使用者身分。

route 路徑 HTTP 程式位置 現有守門 新三段名 現有能力點 對應 UI 範圍層 備註
/remote-agents GET jedi_remote_agent/api/routes/remote_agent_route.py:58 J+L(remote-agent-manage)+C(remote-agent-manage.create) detection.remote-agent.read remote-agent-manage.create ToolPluginManage、StorageConfigForm、ProjectPlanningView(代理下拉) tenant 輔助端點(啟用中的代理下拉);見下方「跨頁下拉」
/remote-agents POST remote_agent_route.py:66 同上 detection.remote-agent.create 同上 RemoteAgentManage(/system/remote-agent-manage) tenant
/remote-agents/all GET remote_agent_route.py:90 同上 detection.remote-agent.read 同上 RemoteAgentManage tenant 管理頁列表(含停用)
/remote-agents/health-check POST remote_agent_route.py:75 同上 detection.remote-agent.read 同上 RemoteAgentManage tenant 只讀狀態,不改資料
/remote-agents/<uid>/reconcile POST remote_agent_route.py:82 同上 detection.remote-agent.update 同上 RemoteAgentManage tenant 對帳會改雲端錨點狀態
/remote-agents/<uid> GET remote_agent_route.py:98 同上 detection.remote-agent.read 同上 RemoteAgentManage tenant
/remote-agents/<uid> PUT remote_agent_route.py:105 同上 detection.remote-agent.update 同上 RemoteAgentManage tenant 含啟用/停用
/remote-agents/<uid> DELETE remote_agent_route.py:112 同上 detection.remote-agent.delete 同上 RemoteAgentManage tenant
/remote-agents/<uid>/revoke POST remote_agent_route.py:119 同上 detection.remote-agent.update 同上 RemoteAgentManage tenant 狀態轉換但非四類獨立動詞之一
/remote-agents/<uid>/unrevoke POST remote_agent_route.py:127 同上 detection.remote-agent.update 同上 RemoteAgentManage tenant
/remote-agents/<uid>/approve POST remote_agent_route.py:135 同上 detection.remote-agent.update 同上 RemoteAgentManage tenant 見文末定案結論:核准新報到代理是否開 approve 獨立動詞(見文末定案結論)
/remote-agents/<uid>/rebind POST remote_agent_route.py:144 同上 detection.remote-agent.update 同上 RemoteAgentManage tenant
/agents/enroll-token GET remote_agent_route.py:154 同上 detection.remote-agent.read 同上 RemoteAgentManage tenant 只回摘要
/agents/enroll-token POST remote_agent_route.py:160 同上 detection.remote-agent.create 同上 RemoteAgentManage tenant 產生註冊 token,明文只回一次
/agents/enroll-token/revoke POST remote_agent_route.py:168 同上 detection.remote-agent.delete 同上 RemoteAgentManage tenant 停用所有註冊 token
/agents/register POST remote_agent_route.py:177 註冊 token(AUTH_ENROLL) 豁免 無 無 UI(代理呼入) — 代理用註冊 token 換通行證,刻意不設使用者守門
/agents/heartbeat POST remote_agent_route.py:187 代理通行證 豁免 無 無 UI — 代理呼入
/agents/tasks/<uid>/ack POST remote_agent_route.py:194 代理通行證 豁免 無 無 UI — 代理呼入
/agents/tasks/<uid>/result POST remote_agent_route.py:200 代理通行證 豁免 無 無 UI — 代理呼入
/agents/files/<uid> GET 主專案 api/remote_agent/routes/agent_file_route.py:40 代理通行證+任務綁定驗證 豁免 無 無 UI — 宿主自掛(core/plugins/remote_agent.py:224-233),代理下載任務源碼包

套件路由表 15 條路徑、19 個 method(管理 15、代理呼入 4),加主專案宿主自掛 1 支。

跨頁下拉:GET /remote-agents 被三個非代理管理頁拿來當下拉(工具外掛設定、儲存設定、專案規劃的檢測任務)。今天要 remote-agent-manage.create 才拿得到,等於「會設儲存的人一定要有代理建立權」。新名掛 detection.remote-agent.read 後,出貨角色範本要確認儲存設定管理人與專案規劃者都帶這顆 read。

檢測工具外掛與執行(detection.plugin)

jedi-detection 所有 route 都由 jedi_detection/api/routing.py:175-192 的動態子類套上宿主的 jwt_required(core/plugins/detection.py:388),再加各自的 L 與 C。能力點名預設在 jedi_detection/plugin/contract.py:32-54。

route 路徑 HTTP 程式位置 現有守門 新三段名 現有能力點 對應 UI 範圍層 備註
/detection-tools GET jedi_detection/api/routes/detection_tool_route.py:31 J+L(plugin) detection.plugin.read ∣ grc.job.read 無 ToolPluginManage(/plugin/tool-plugin-manage)、DetectionProfileManageView、JobExecutionDrawer(只在任務綁工具時) tenant 工具目錄(全域字典、不含憑證)
/detection-tools/<uid> GET detection_tool_route.py:43 J+L(plugin) detection.plugin.read 無 同上 tenant
/detection-tools/configs GET detection_tool_route.py:56 J+L(plugin) detection.plugin.read 無 ToolPluginManage、ProjectPlanningView tenant 租戶工具設定列表
/detection-tools/configs POST detection_tool_route.py:70 J+L(plugin)+C(plugin.update) detection.plugin.create plugin.update ToolPluginManage tenant 今天建立守 update
/detection-tools/configs/<uid> PUT detection_tool_route.py:91 J+L(plugin)+C(plugin.update) detection.plugin.update plugin.update ToolPluginManage tenant
/detection-tools/configs/<uid>/reset POST detection_tool_route.py:108 J+L(plugin)+C(plugin.update) detection.plugin.update plugin.update ToolPluginManage tenant 清空憑證
/detection-tools/configs/<uid>/test-connection POST detection_tool_route.py:132 J+L(plugin)+C(plugin.update) detection.plugin.update plugin.update ToolPluginManage tenant 會解密憑證送往主機,必須維持寫入級
/detection-tools/configs/<uid>/referencing-tasks-count GET detection_tool_route.py:152 J+L(plugin) detection.plugin.read 無 ToolPluginManage tenant 輔助端點
/detection-tools/<uid>/param-schema GET detection_tool_route.py:165 J+L(plugin) grc.job.read 無 JobExecutionDrawer tenant 輔助端點;任務設置畫面用
/detection-tools/jobs/<job_uid>/execute POST detection_tool_route.py:186 J+L(plugin);成員:任務操作人 grc.job.update 無 JobExecutionDrawer org_unit 見文末定案結論:歸 grc.job(見文末定案結論)
/detection-tools/jobs/<job_uid>/cancel POST detection_tool_route.py:218 同上 grc.job.update 無 JobExecutionDrawer org_unit 同上
/detection-tools/jobs/<job_uid>/executions GET detection_tool_route.py:354 J+L(plugin);成員:參與者 grc.job.read|grc.job-evidence.read 無 JobExecutionDrawer org_unit 同上
/detection-tools/execution-groups/<g>/assignments/<a>/rerun POST detection_tool_route.py:237 J+L(plugin);成員:任務操作人 grc.job.update 無 JobExecutionDrawer org_unit 同上
/detection-tools/execution-groups/<g>/assignments/<a>/cancel POST detection_tool_route.py:258 同上 grc.job.update 無 JobExecutionDrawer org_unit 同上
/detection-tools/execution-groups/<g>/assignments/<a>/start-now POST detection_tool_route.py:279 同上 grc.job.update 無 JobExecutionDrawer org_unit 同上
/detection-tools/execution-groups/<g>/cancel POST detection_tool_route.py:299 同上 grc.job.update 無 JobExecutionDrawer org_unit 同上
/detection-tools/execution-groups/<g> DELETE detection_tool_route.py:335 同上 grc.job.update 無 JobExecutionDrawer org_unit 只能刪全失敗的群組;清理執行紀錄,不是刪任務
/detection-tools/executions/<uid> DELETE detection_tool_route.py:317 同上 grc.job.update 無 JobExecutionDrawer org_unit 同上

「任務操作人」=assert_job_operator(jedi_detection/app/service/detection_orchestration_service.py:209,913,969,1061,1090,1159,1189,1532),列表走 assert_project_participant(:1736),實作由宿主的流程執行 service 注入。

檢測基準(detection.profile)

守門 _profile_read/_create/_update/_delete 定義在 jedi_detection/api/routes/detection_profile_route.py:58-61。分類字典 4 個寫入端點走平台管理員(全域字典表)。

route 路徑 HTTP 程式位置 現有守門 新三段名 現有能力點 對應 UI 範圍層 備註
/detection-tool-profiles/list POST detection_profile_route.py:141 J+L(detection-profile)+C(read) detection.profile.read detection-profile.read DetectionProfileManageView(/plugin/detection-profile-manage) tenant
/detection-tool-profiles/menu GET detection_profile_route.py:170 同上(read) detection.profile.read detection-profile.read JobExecutionDrawer(useDetectionProfileOptions) tenant 輔助端點
/detection-tool-profiles POST detection_profile_route.py:191 同上(create) detection.profile.create detection-profile.create DetectionProfileManageView、ProfileFormDialog tenant
/detection-tool-profiles/<uid> GET detection_profile_route.py:219 同上(read) detection.profile.read detection-profile.read 同上 tenant
/detection-tool-profiles/<uid> PUT detection_profile_route.py:238 同上(update) detection.profile.update detection-profile.update 同上 tenant
/detection-tool-profiles/<uid> DELETE detection_profile_route.py:266 同上(delete) detection.profile.delete detection-profile.delete 同上 tenant
/detection-tool-profiles/<uid>/versions GET detection_profile_route.py:283 同上(read) detection.profile.read detection-profile.read ProfileVersionTable tenant
/detection-tool-profiles/<uid>/referencing-usage GET detection_profile_route.py:313 同上(read) detection.profile.read detection-profile.read useProfileUsage tenant 輔助端點
/detection-tool-profiles/<uid>/new-version POST detection_profile_route.py:421 同上(create) detection.profile.create detection-profile.create ProfileVersionTable tenant 版更算建立新一版
/detection-tool-profiles/<uid>/fork POST detection_profile_route.py:450 同上(create) detection.profile.create detection-profile.create ProfileCopyDialog tenant 跨資源動作歸被產出的資源
/detection-tool-profiles/<uid>/copy-to-tool POST detection_profile_route.py:471 同上(create) detection.profile.create detection-profile.create ProfileCopyDialog tenant 同上
/detection-tool-profiles/<uid>/deactivate PUT detection_profile_route.py:495 同上(delete) detection.profile.delete detection-profile.delete DetectionProfileManageView tenant 停用守 delete(套件設計:停用即「下架」)
/detection-tool-profile-versions/<uid>/controls GET detection_profile_route.py:519 同上(read) detection.profile.read detection-profile.read ProfileVersionTable tenant
/detection-tool-profile-versions/<uid>/extraction GET detection_profile_route.py:540 同上(read) detection.profile.read detection-profile.read 同上 tenant
/detection-tool-profile-versions/<uid>/extraction POST detection_profile_route.py:555 同上(create) detection.profile.create detection-profile.create 同上 tenant 手動重抽
/detection-tool-profile-versions/<uid>/referencing-usage GET detection_profile_route.py:339 同上(read) detection.profile.read detection-profile.read useProfileUsage tenant 輔助端點
/detection-tool-profile-versions/<uid>/source PATCH detection_profile_route.py:370 同上(create) detection.profile.create detection-profile.create ProfileSourceDialog tenant 補來源檔
/detection-tool-profile-versions/<uid> DELETE detection_profile_route.py:403 同上(delete) detection.profile.delete detection-profile.delete ProfileVersionTable tenant
/detection-profile-taxonomies GET jedi_detection/api/routes/detection_profile_taxonomy_route.py:62 J+L(detection-profile) detection.profile.read 無 useProfileTaxonomy(基準表單下拉) tenant 輔助端點
/detection-profile-taxonomies POST detection_profile_taxonomy_route.py:84 J+L+PA 平台層(T-1.5) 無 useProfileTaxonomy — 全域字典,平台管理員維護
/detection-profile-taxonomies/<uid> PUT detection_profile_taxonomy_route.py:111 J+L+PA 平台層 無 同上 —
/detection-profile-taxonomies/<uid> DELETE detection_profile_taxonomy_route.py:135 J+L+PA 平台層 無 同上 —
/detection-profile-taxonomies/<uid>/referencing-profiles-count GET detection_profile_taxonomy_route.py:150 J+L(detection-profile) detection.profile.read 無 同上 tenant 輔助端點

detection_profile_route.py 18 個、detection_profile_taxonomy_route.py 5 個。另外 core/plugins/_file_ownership.py:224-225 用 detection-profile.read/delete 判基準來源檔的下載與刪除權,改名要一起動。

授權掃描網段(detection.scan-zone)

基礎設施豁免:scan-zone 在 common/authz/license.py:107-121 的 INFRASTRUCTURE_MODULES,授權照守門恆放行。仍要歸模組:它是檢測的安全邊界,只有買了檢測包才有意義,歸 detection。

route 路徑 HTTP 程式位置 現有守門 新三段名 現有能力點 對應 UI 範圍層 備註
/scan-zones GET jedi_detection/api/routes/scan_zone_route.py:38 J+L(scan-zone,豁免)+C(scan-zone.read) detection.scan-zone.read scan-zone.read ScanZoneManage(/system/scan-zone) tenant
/scan-zones POST scan_zone_route.py:47 同上+C(scan-zone.update) detection.scan-zone.create scan-zone.update 同上 tenant 補四動詞後改守 create
/scan-zones/<uid> PUT scan_zone_route.py:60 同上+C(update) detection.scan-zone.update scan-zone.update 同上 tenant 含確認 pending 建議
/scan-zones/<uid> DELETE scan_zone_route.py:70 同上+C(update) detection.scan-zone.delete scan-zone.update 同上 tenant 補四動詞後改守 delete
/scan-zones/suggest-from-agents POST scan_zone_route.py:81 同上+C(update) detection.scan-zone.create scan-zone.update 同上 tenant 產生 pending 建議列

FE 常數在 src/config/scanZone.js(SCAN_ZONE_READ/SCAN_ZONE_UPDATE),改名要一起動。

雲端整合(evidence.cloud-integration)

主專案 api/cloud_integration/__init__.py 掛載,前綴 /api/1.0。

route 路徑 HTTP 程式位置 現有守門 新三段名 現有能力點 對應 UI 範圍層 備註
/integrations/google-drive GET api/cloud_integration/routes/google_drive_integration_route.py:56 J+L(cloud_integration) evidence.cloud-integration.read 無 GoogleDriveIntegrationCard(/settings/cloud-integrations)、ProjectListView、ProjectPlanningView、ProjectAuditorOverview tenant 刻意不掛 read(:49 註解:專案頁的一般成員要看連線狀態);見文末定案結論
/integrations/google-drive DELETE google_drive_integration_route.py:71 J+L+C(cloud_integration.delete) evidence.cloud-integration.delete cloud_integration.delete GoogleDriveIntegrationCard tenant 中斷連線
/integrations/google-drive/auth-url POST google_drive_integration_route.py:90 J+L+C(cloud_integration.create) evidence.cloud-integration.create cloud_integration.create GoogleDriveIntegrationCard tenant
/integrations/google-drive/callback(DRIVE_CALLBACK_ROUTE_PATH) GET google_drive_integration_route.py:110 無 JWT;OAuth state+瀏覽器 nonce cookie 驗證 豁免 無 無 UI(Google 導回) — 外部導回端點,刻意不設使用者守門
/integrations/google-drive/verify-credentials POST api/cloud_integration/routes/drive_app_credential_verify_route.py:32 J;service 層 PA+storage-config.read(app/cloud_integration/service/drive_app_credential_verify_service.py:74-78) 隨 storage-config 新名的 read(見文末定案結論) storage-config.read GoogleDriveAppConfigForm(/system/drive-app-config) tenant 平台憑證設定頁,見 storage-config 段
/integrations/google-drive/sync-jobs GET api/cloud_integration/routes/google_drive_sync_route.py:43 J+L+C(cloud_integration.read) evidence.cloud-integration.read cloud_integration.read SyncJobHistoryDialog、GoogleDriveIntegrationCard tenant
/integrations/google-drive/sync-jobs/<uid>/retry POST google_drive_sync_route.py:90 J+L+C(cloud_integration.update) evidence.cloud-integration.update cloud_integration.update SyncJobHistoryDialog tenant
/integrations/google-drive/projects/<p>/init-folders POST google_drive_sync_route.py:111 J+L;成員:manager(app/cloud_integration/service/drive_sync_admin_service.py:136) grc.project.update 無 ProjectCloudIntegrationsPanel org_unit 專案 manager 觸發,非租戶管理員;見文末定案結論
/integrations/google-drive/projects/<p>/verify-and-repair-folders POST google_drive_sync_route.py:143 J+L;成員:manager grc.project.update 無 ProjectCloudIntegrationsPanel org_unit 同上
/integrations/google-drive/projects/<p>/folders GET google_drive_sync_route.py:177 J+L+C(cloud_integration.read) evidence.cloud-integration.read cloud_integration.read ProjectCloudIntegrationsPanel、ProjectAuditorOverview org_unit 專案頁用,要求租戶級 read——與上兩支的「專案 manager 即可」不一致,見文末定案結論
/integrations/google-drive/tenant/rebuild-all POST google_drive_sync_route.py:209 J+L+C(cloud_integration.update) evidence.cloud-integration.update cloud_integration.update GoogleDriveIntegrationCard tenant 破壞性操作
/integrations/google-drive/webhook/register POST google_drive_sync_route.py:239 J+L+C(cloud_integration.update) evidence.cloud-integration.update cloud_integration.update GoogleDriveIntegrationCard tenant
/webhooks/google-drive/<tenant_id> POST api/cloud_integration/routes/google_drive_webhook_route.py:39 無 JWT;X-Goog-Channel-ID+X-Goog-Channel-Token 比對 豁免 無 無 UI(Google 推送) — 確認刻意不設守門:Google 呼入、恆回 200;身分靠 channel token 比對

google_drive_integration_route.py 4 個、google_drive_sync_route.py 7 個、drive_app_credential_verify_route.py 1 個、google_drive_webhook_route.py 1 個。

cloud_integration.update 不是孤兒:有 3 支 route 守它(retry、rebuild-all、webhook register),FE GoogleDriveIntegrationCard.vue:219、SyncJobHistoryDialog.vue:18 也讀它,10 個角色持有。它沒出現在 /settings/cloud-integrations 選單綁定(該頁只綁 create/delete/read),所以「選單反查」會把它誤判成沒人用。

AI 證據分類(evidence.classification)

jedi-evidence-classification 所有 route 由動態子類套上宿主 jwt_required(core/plugins/evidence_classification.py:608),沒有授權照守門、沒有能力點(套件 CAPABILITIES 為空,jedi_evidence_classification/plugin/contract.py:25)。專案層檢查在 service:_require_participant/_require_manager(jedi_evidence_classification/app/service/evidence_batch_service.py:1472-1481),宿主實作為精確查 participant.role == "manager"(core/plugins/evidence_classification.py:72-114)。

route 路徑 HTTP 程式位置 現有守門 新三段名 現有能力點 對應 UI 範圍層 備註
/evidence-batches POST jedi_evidence_classification/api/routes/evidence_batch_route.py:51 J;成員:manager(service :412) evidence.classification.create 無 AutoClassifyBatch org_unit 建批次
/evidence-batches GET evidence_batch_route.py:75 J;成員:參與者(:436,440) evidence.classification.read 無 AutoClassifyList org_unit
/evidence-batches/<b> GET evidence_batch_route.py:101 J;成員:參與者(:462) evidence.classification.read 無 AutoClassifyBatch org_unit
/evidence-batches/<b> DELETE evidence_batch_route.py:115 J;成員:manager(:732) evidence.classification.delete 無 AutoClassifyList org_unit
/evidence-batches/<b>/files POST evidence_batch_route.py:122 J;成員:manager(:513) evidence.classification.update 無 AutoClassifyBatch org_unit 上傳檔進批次
/evidence-batches/<b>/files/<f> DELETE evidence_batch_route.py:137 J;成員:manager(:542) evidence.classification.update 無 AutoClassifyBatch org_unit 從批次移出檔案
/evidence-batches/<b>/seal POST evidence_batch_route.py:145 J;成員:manager(:552) evidence.classification.update 無 AutoClassifyBatch org_unit 封存批次
/evidence-batches/<b>/classify POST evidence_batch_route.py:153 J;成員:manager(:996) evidence.classification.update 無 AutoClassifyBatch org_unit 送 AI 分類(花額度)
/evidence-batches/<b>/archive POST evidence_batch_route.py:176 J;成員:manager(:577) evidence.classification.update 無 AutoClassifyBatch org_unit 把分類結果寫進任務佐證
/evidence-batches/<b>/purge POST evidence_batch_route.py:183 J;成員:manager(:669) evidence.classification.delete 無 AutoClassifyBatch org_unit 清掉暫存檔
/classification-run/<r>/state GET evidence_batch_route.py:197 J;成員:參與者(:824) evidence.classification.read 無 useEvidenceClassification org_unit
/classification-run/<r>/state PUT evidence_batch_route.py:202 J;成員:manager(:839) evidence.classification.update 無 同上 org_unit 人工審閱結果
/classification-run/<r>/file/<f>/preview GET evidence_batch_route.py:212 J;成員:參與者(:885) evidence.classification.read 無 EvidenceFilePreviewDialog org_unit
/classification-run/<r>/report/validation GET evidence_batch_route.py:230 J;成員:參與者(:909) evidence.classification.read 無 useEvidenceClassification org_unit
/classification-run/<r>/report/adjudication GET evidence_batch_route.py:236 J;成員:參與者(:920) evidence.classification.read 無 同上 org_unit
/classification-ground-truth POST jedi_evidence_classification/api/routes/evidence_classification_route.py:19 J;任一專案的 manager(evidence_classification_service.py:58) evidence.classification.update 無 無 UI(FE 無呼叫) tenant 評估用正解匯入,FE 沒有入口;見文末定案結論去留
/classification-profiles GET jedi_evidence_classification/api/routes/classification_profile_route.py:51 J;service:storage-config.update(classification_profile_service.py:165,宿主 core/plugins/evidence_classification.py:137) evidence.classification.read storage-config.update AiServiceConfigForm(/system/ai-service-config) tenant 讀也要 update——今天的設計
/classification-profiles POST classification_profile_route.py:65 同上 evidence.classification.create storage-config.update 同上 tenant
/classification-profiles/<p> PUT classification_profile_route.py:72 同上 evidence.classification.update storage-config.update 同上 tenant
/classification-profiles/<p> DELETE classification_profile_route.py:77 同上 evidence.classification.delete storage-config.update 同上 tenant
/classification/available-models GET classification_profile_route.py:84 J;無其他檢查(service :120-126 只取身分) evidence.classification.read 無 AutoClassifyList、AutoClassifyBatch tenant 輔助端點

evidence_batch_route.py 15 個、classification_profile_route.py 5 個、evidence_classification_route.py 1 個。

「分類設定」該不該跟批次同一資源:批次的寫入看專案成員角色、分類設定看租戶管理員,兩者授權對象不同。建議拆成 evidence.classification(批次與結果)與 evidence.classification-profile(AI 分類設定),列入見文末定案結論。主表暫填前者。

儲存設定與檔案(storage-config)

storage-config.* 四顆宣告在 jedi_file_upload/plugin/contract.py:33-37。route 不直接守它,而是三條路徑間接用:

  1. 泛用系統設定端點(jedi_system_core,T-1.4 範圍)依 GROUP 分流:STORAGE_CONFIG 與 AI_PROVIDER_CONFIG 兩組映到 storage-config.*(core/plugins/system_core.py:64,78)。
  2. 主專案兩支專屬端點(下表)。
  3. 套件與 service 層借用:AI 分類設定(上一段)、Google 應用程式憑證驗證(雲端整合段)。
route 路徑 HTTP 程式位置 現有守門 新三段名 現有能力點 對應 UI 範圍層 備註
/system/config/storage/restore-default GET api/system_config/routes/system_config_route.py:119 J;assert_config_capability("STORAGE_CONFIG","read") <storage-config>.read storage-config.read StorageConfigForm(/system/storage-config) tenant 只回出廠快照有無
/system/config/storage/restore-default POST system_config_route.py:127 J;assert_config_capability("STORAGE_CONFIG","update") <storage-config>.update storage-config.update 同上 tenant 恢復內建儲存
/system/config/ai-provider/model-options GET system_config_route.py:100 J;assert_config_capability("AI_PROVIDER_CONFIG","read") 見文末定案結論(AI 服務設定歸屬) storage-config.read AiServiceConfigForm tenant AI 服務設定頁借用 storage-config

jedi-file-upload 的檔案端點(不開能力點,歸屬由檔案登記表決定):

route 路徑 HTTP 程式位置 現有守門 新三段名 現有能力點 對應 UI 範圍層 備註
/file/upload POST jedi_file_upload/api/routes/upload_file_route.py:88 J 不開能力點 無 全站上傳元件 — 上傳本身不判功能權;檔案掛到哪個功能時由該功能守
/file/upload/<uid> DELETE upload_file_route.py:113 J+檔案歸屬(寫) 不開能力點 無 同上 — 歸屬判斷委派各功能(core/plugins/_file_ownership.py、core/plugins/file_upload.py)
/file/uploads POST upload_file_route.py:124 J 不開能力點 無 同上 — 批次上傳
/file/access-token/<uid> GET upload_file_route.py:153 J+檔案歸屬(讀) 不開能力點 無 全站下載/預覽 — 換短期下載憑證
/file/download/<uid> GET upload_file_route.py:165 短期憑證或 JWT+檔案歸屬(讀) 不開能力點 無 同上 — 瀏覽器導頁
/file/pdf-preview/<uid> GET upload_file_route.py:200 同上 不開能力點 無 同上 —

檔案端點不開能力點的理由:一個檔案屬於哪個功能是登記表查出來的,同一支端點服務十幾種檔案,掛任何單一能力點都會讓其他功能的檔案打不開。權限已由「該檔案所屬功能的原本守門」代判。

問卷設計(survey.survey)

jedi-survey 的守門 decorator 是宿主注入的 port(jedi_survey/api/guards.py);授權照 require_license("survey") 全端點都有,能力點名預設在 jedi_survey/plugin/contract.py:32-35,89-91。讀取端點全部沒有 survey.read,只有授權照。

route 路徑 HTTP 程式位置 現有守門 新三段名 現有能力點 對應 UI 範圍層 備註
/survey-folders/menu GET jedi_survey/api/routes/survey_folder_route.py:33 J+L(survey) survey.survey.read 無 survey-v2(/survey/manage) org_unit 輔助端點
/survey-folders POST survey_folder_route.py:45 J+L survey.survey.read 無 同上 org_unit 列表
/survey-folder/<uid> GET survey_folder_route.py:60 J+L survey.survey.read 無 同上 org_unit
/survey-folder POST survey_folder_route.py:73 J+L+C(survey.create) survey.survey.create survey.create 同上 org_unit 資料夾不另開資源
/survey-folder/<uid> PUT survey_folder_route.py:90 J+L+C(survey.update) survey.survey.update survey.update 同上 org_unit
/survey-folder/<uid> DELETE survey_folder_route.py:109 J+L+C(survey.delete) survey.survey.delete survey.delete 同上 org_unit
/surveys/menu GET jedi_survey/api/routes/survey_route.py:34 J+L survey.survey.read 無 SurveyPickerDialog(任務設置挑問卷) org_unit 輔助端點
/surveys POST survey_route.py:59 J+L survey.survey.read 無 survey-v2 org_unit 列表
/survey/<uid> GET survey_route.py:88 J+L survey.survey.read 無 survey-v2、SurveyPreview、AuditControlRef、RoundAuditReviewView、RoundPoamView org_unit 稽核頁也讀問卷本體
/survey POST survey_route.py:105 J+L+C(create) survey.survey.create survey.create survey-v2 org_unit
/survey/<uid> PUT survey_route.py:133 J+L+C(update) survey.survey.update survey.update 同上 org_unit
/survey/<uid> DELETE survey_route.py:156 J+L+C(delete) survey.survey.delete survey.delete 同上 org_unit
/survey/excel-import POST survey_route.py:175 J+L+C(create) survey.survey.import survey.create BatchImportDialog org_unit 匯入類獨立動詞
/survey/import-template/token GET survey_route.py:201 J+L survey.survey.import 無 signedDownloadUtil org_unit 換範本下載憑證
/survey/import-template GET survey_route.py:209 短期憑證(signed_token_required) 豁免(憑證端點) 無 同上 — 憑證由上一支發
/survey/move POST survey_route.py:221 J+L+C(create) survey.survey.update survey.create survey-v2 org_unit 移資料夾是改屬性,今天守 create
/survey/<uid>/clone POST survey_route.py:242 J+L+C(create) survey.survey.create survey.create 同上 org_unit
/survey/<uid>/release PUT survey_route.py:265 J+L+C(update) survey.survey.publish survey.update 同上 org_unit 發布=狀態轉換獨立動詞
/survey/<uid>/release DELETE survey_route.py:279 J+L+C(delete) survey.survey.publish survey.delete 同上 org_unit 取消發布;今天守 delete
/survey-question POST jedi_survey/api/routes/survey_question_route.py:33 J+L+C(create) survey.survey.update survey.create survey-v2 org_unit 題目、頁面是問卷內容,改它們=改問卷
/survey-question/<uid> PUT survey_question_route.py:62 J+L+C(update) survey.survey.update survey.update 同上 org_unit
/survey-question/<uid> DELETE survey_question_route.py:98 J+L+C(delete) survey.survey.update survey.delete 同上 org_unit
/survey-questions/reorder POST survey_question_route.py:113 J+L+C(create) survey.survey.update survey.create 同上 org_unit
/survey-question/move POST survey_question_route.py:134 J+L+C(create) survey.survey.update survey.create 同上 org_unit
/survey-page POST jedi_survey/api/routes/survey_page_route.py:33 J+L+C(create) survey.survey.update survey.create 同上 org_unit
/survey-page/<uid> PUT survey_page_route.py:61 J+L+C(update) survey.survey.update survey.update 同上 org_unit
/survey-page/<uid> DELETE survey_page_route.py:88 J+L+C(delete) survey.survey.update survey.delete 同上 org_unit
/survey-pages/reorder POST survey_page_route.py:102 J+L+C(create) survey.survey.update survey.create 同上 org_unit
/survey-page/move POST survey_page_route.py:123 J+L+C(create) survey.survey.update survey.create 同上 org_unit

題目與頁面的增刪歸問卷 update 是依「其他動作歸 update」規則:使用者在問卷編輯器裡加一題,語意是「編輯這份問卷」,不是「建立一個題目資源」。今天的守法(加題守 create、刪題守 delete)讓「能改問卷但不能刪問卷」的角色連題目都刪不掉,改名時順手校正。

問卷填答與任務問卷(歸 grc.job)

填答端(mount_answer_routes,jedi_survey/api/routing.py:73-109)全部 J+L(survey),沒有能力點。專案層檢查在 service:assert_task_survey_reader(專案參與者)、assert_task_survey_writer(被指派人本人或專案 manager)、assert_task_survey_manager(專案 manager),定義在 jedi_survey/app/common/task_survey_guard.py。

route 路徑 HTTP 程式位置 現有守門 新三段名 現有能力點 對應 UI 範圍層 備註
/task-surveys POST jedi_survey/api/routes/task_survey_route.py:28 J+L;成員:參與者(task_survey_service.py:79) grc.job.read|grc.job-evidence.read(後者限已完成問卷) 無 JobExecutionDrawer org_unit 列表(必帶 task)
/task-survey/<uid> GET task_survey_route.py:40 J+L;成員:參與者(:94) grc.job.read|grc.job-evidence.read(後者限已完成問卷) 無 SurveyPreview org_unit
/task-survey/<uid> PUT task_survey_route.py:49 J+L;成員:manager(:112) grc.job.update 無 JobExecutionDrawer org_unit 管理者改任務問卷狀態(含審核轉換);見文末定案結論
/task-survey/configured/<uid> PUT task_survey_route.py:155 J+L;成員:manager(:613) grc.job.update 無 JobExecutionDrawer org_unit 設定任務要用哪份問卷、要不要審核;屬任務設置
/task-survey/import-template/<task_uid> GET task_survey_route.py:65 J+L;無成員檢查 grc.job.read 無 SurveyPreview org_unit 守門缺口:service generate_task_survey_question_import_excel_template_data(task_survey_service.py:206)不驗身分,見缺口清單
/task-survey/import-answers POST task_survey_route.py:134 J+L;成員:manager(:296) grc.job.update 無 SurveyPreview org_unit Excel 批次匯入答案;填答端一律歸 grc.job.update,不開 import
/project-survey/answers/<uid> GET jedi_survey/api/routes/question_answer_route.py:30 J+L;成員:參與者(question_answer_service.py:102) grc.job.read|grc.job-evidence.read(後者限已完成問卷) 無 SurveyPreview org_unit
/project-survey/answers/<uid> PUT question_answer_route.py:40 J+L;成員:被指派人或 manager(:120) grc.job.update 無 同上 org_unit
/project-survey/answers/<uid>/patch POST question_answer_route.py:59 同上(:255) grc.job.update 無 同上 org_unit
/project-survey/answers/<uid>/checkpoint POST question_answer_route.py:89 同上(:407) grc.job.update 無 同上 org_unit 暫存/提交
/project-survey/histories/menu/<uid> GET jedi_survey/api/routes/question_answer_history_route.py:24 J+L;成員:參與者(question_answer_history_service.py:58) grc.job.read|grc.job-evidence.read(後者限已完成問卷) 無 SurveyPreview org_unit 輔助端點
/project-survey/histories POST question_answer_history_route.py:35 同上(:71) grc.job.read|grc.job-evidence.read(後者限已完成問卷) 無 同上 org_unit
/project-survey/history/revert POST question_answer_history_route.py:47 J+L;成員:被指派人或 manager(:82) grc.job.update 無 同上 org_unit
/project-survey/history-details POST jedi_survey/api/routes/question_answer_history_detail_route.py:20 J+L;成員:參與者(question_answer_history_detail_service.py:35) grc.job.read|grc.job-evidence.read(後者限已完成問卷) 無 同上 org_unit
/survey-discussions POST jedi_survey/api/routes/survey_discussion_route.py:25 J+L;成員:參與者(survey_discussion_service.py:47) grc.job.read|grc.job-evidence.read(後者限已完成問卷) 無 SurveyPreview org_unit 列表
/survey-discussion POST survey_discussion_route.py:43 J+L;無成員檢查 grc.job.update 無 同上 org_unit 守門缺口:service add_discussion(survey_discussion_service.py:69)不驗身分,見缺口清單
/survey-discussion/<uid> PUT survey_discussion_route.py:65 J+L+C(survey.update);作者本人(jedi_survey/app/service/survey_discussion.py:54) grc.job.update survey.update 同上 org_unit 改自己的留言卻要問卷設計權
/survey-discussion/<uid> DELETE survey_discussion_route.py:85 J+L+C(survey.delete);作者本人(:66) grc.job.update survey.delete 同上 org_unit 同上

討論串放在填答這一組:它掛在任務問卷(ref_id=task_survey_uid)上,是受訪者與審核者的對話,不是問卷設計。今天改/刪留言要 survey.update/delete,等於只會填問卷的人留了言就改不掉。

§5

現有能力點反查(DEV 30 個)

舊名 主表有 route 守它? 新名 其他引用處
device.create 有:建立、匯入三支 asset.device.create/.import 套件 jedi_asset/plugin/contract.py;綁 /device/device-manage;FE DeviceManage.vue:69
device.read 有:列表、明細、選單、引用數、匯出 asset.device.read/.export 角色範本 core/role_templates.py:14;app/module_frame/service/template_lookup_scope.py:16;di_containers/dashboard_apis/device.py:19;FE DeviceManage.vue:70
device.update 有:PUT asset.device.update FE DeviceImportDialog.vue:34(判斷匯入按鈕,與 BE 守 create 不一致)
device.delete 有:DELETE asset.device.delete FE DeviceManage.vue:68
remote-agent-manage.create 有:15 個管理 method 全守它 detection.remote-agent.*(依主表拆) core/plugins/remote_agent.py:167
remote-agent-manage.read/update/delete 無 detection.remote-agent.read/update/delete 只綁 /system/remote-agent-manage 選單;11 個角色持有
plugin.update 有:工具設定建立、修改、重置、測試連線 detection.plugin.create/update 套件 jedi_detection/plugin/contract.py:48
plugin.create/read/delete 無 detection.plugin.create/read/delete 只綁 /plugin/tool-plugin-manage 選單;10 個角色持有
detection-profile.create/read/update/delete 有:基準 18 支 detection.profile.* 套件 contract :49-52;角色範本 :16(read);core/plugins/_file_ownership.py:224-225;FE DetectionProfileManageView.vue:69-71、ProfileVersionTable.vue:227-228
scan-zone.read 有:GET detection.scan-zone.read 套件 contract :53;FE config/scanZone.js
scan-zone.update 有:新增、修改、刪除、建議 detection.scan-zone.create/update/delete 套件 contract :54;FE config/scanZone.js
cloud_integration.create 有:auth-url evidence.cloud-integration.create core/host_capabilities.py:38;FE GoogleDriveIntegrationCard.vue:218
cloud_integration.read 有:sync-jobs、專案資料夾 evidence.cloud-integration.read core/host_capabilities.py:40;FE 同檔 :222
cloud_integration.update 有:retry、rebuild-all、webhook register evidence.cloud-integration.update core/host_capabilities.py:41;FE 同檔 :219、SyncJobHistoryDialog.vue:18;不在選單綁定(見雲端整合段)
cloud_integration.delete 有:中斷連線 evidence.cloud-integration.delete core/host_capabilities.py:39;FE 同檔 :220
storage-config.read 有:restore-default GET、model-options、Drive 憑證驗證(service) 見文末定案結論 jedi_file_upload/plugin/contract.py:35;泛用設定 GROUP 分流 core/plugins/system_core.py:64,78;綁 /system/storage-config、/system/drive-app-config、/system/ai-service-config、/system/support
storage-config.update 有:restore-default POST;AI 分類設定 4 支(service) 見文末定案結論 core/plugins/evidence_classification.py:137;同上四頁選單
storage-config.create/delete 只經泛用設定端點(T-1.4 範圍) 見文末定案結論 只綁 /system/storage-config;11 個角色持有
survey.create 有:建問卷、資料夾、匯入、搬移、複製、題目與頁面的新增/排序/搬移 survey.survey.create/update/import 套件 contract :32;common/authz/license.py:44(範例註解)
survey.read 無 survey.survey.read 綁 /survey/manage;角色範本 :48;di_containers/dashboard_apis/survey.py:30,44;12 個角色持有
survey.update 有:改問卷、資料夾、題目、頁面、發布、改留言 survey.survey.update/publish、grc.job.update 套件 contract :34
survey.delete 有:刪問卷、資料夾、題目、頁面、取消發布、刪留言 survey.survey.delete/update/publish、grc.job.update 套件 contract :35

沒有任何 route 用到的能力點(孤兒)

remote-agent-manage.read/update/delete(3)、plugin.create/read/delete(3)、survey.read(1),共 7 個。全都綁在選單上、角色也持有,孤兒不等於沒用:今天它們決定「看不看得到那一頁」,改名後由主表對應 route 掛上。storage-config.create/delete 只被泛用設定端點(T-1.4)間接守,不算孤兒。

卡片提到的孤兒 cloud_integration.update:不是孤兒,3 支 route 守它,只是沒綁選單。

§6

加值包 → 模組 對照驗證

加值包(license_module) 內含舊 resource_type 新資源 落在模組 整包同一模組?
檢測工具包 remote-agent-manage、detection-profile、plugin detection.remote-agent、detection.profile、detection.plugin detection ✓
雲端整合包 cloud_integration evidence.cloud-integration evidence ✓
問卷包 survey survey.survey survey ✓
基礎設施豁免 scan-zone detection.scan-zone detection 不在照上;歸 detection 不影響授權判定
基礎包 device asset.device asset 與 asset.information-system(T-1.1)同模組 ✓
基礎包 storage-config 見文末定案結論 evidence 或 system 不是加值包,歸哪都不違反硬規則
(無照鍵) 證據分類(今天無 resource_type) evidence.classification evidence 見文末定案結論:新資源的 license_module 填什麼(見文末定案結論)

注意兩處會讓加值包「看起來」跨界:

  1. 檢測執行 9 支若照套件歸屬放 detection,grc.job 的任務操作就有一部分在 detection 模組下,客戶買檢測包後還要另外勾 detection 才能在任務頁跑掃描。建議歸 grc.job,授權照守門 L(plugin) 照留——授權照與能力點是兩層,任務端點受檢測包管制不需要它的能力點也在 detection。
  2. 填答端不另開能力點:歸 grc.job.read/update;授權照 L(survey) 照留,問卷包的能力點仍整包在 survey(survey.survey)。
§7

storage-config 歸屬建議

建議歸 system,名字 system.storage-config.*;同時把借用它的兩頁拆出去。

  • 歸 system 的理由:它設定的是「檔案存到哪台物件儲存、帳密是什麼」,與 SMTP、LDAP 同屬機房設定。客戶授權時會把它跟其他系統設定一起交給 IT,不會跟「證據審閱」交給同一個人。證據模組管的是證據內容,不是儲存媒介;歸 evidence 會讓「能審閱證據分類的人」跟「能改儲存帳密的人」在矩陣上靠在一起,容易誤勾。
  • 今天一顆鑰匙開五扇門:儲存設定、Google 應用程式憑證(平台層)、AI 服務設定、支援頁、AI 證據分類設定。理由是「不新開一顆、省得補 seed 給每個租戶」(core/plugins/evidence_classification.py:126-129、common/constant/ai_service_config.py:11)。FR-132 本來就要整批改名與補 seed,正是拆開的時機:
    • AI 服務設定(金鑰、模型)+ AI 證據分類設定 → 另開資源,歸屬與 T-1.5 的 ai-quota 一起裁(同屬「AI 怎麼用」)。
    • Google 應用程式憑證 → 平台層(T-1.5),今天就已要求平台管理員。
    • 支援頁 /system/support → 交 T-1.4 確認它實際讀什麼,再決定。
  • 反悔條件:若決策者希望「證據」模組涵蓋所有跟證據檔有關的設定(讓客戶一次授權給文管人員),改成 evidence.storage-config,主表只換前綴。
§8

D12 觀察:候選隔離表有沒有子表 API 繞過父表

D12 候選表本卡相關四張:public.devices、compliance.evidence_batches、compliance.detection_executions、survey.surveys。DEV 實查各表都開 RLS;下表看「有 API 直接列子表」時,子表的 RLS 是否也跟著父表收緊。

父表 子表 子表 RLS 形狀(DEV) 有 API 直接讀子表嗎 觀察
public.devices(有 org_unit_id) 無直接子表 — — 單表,加部門隔離只動它
compliance.evidence_batches(有 org_unit_id) evidence_batch_files、evidence_classification_runs 各自獨立用 tenant_id 判,不透過父表(三表 policy 都是 app_tenant_allowed_for_session(tenant_id)) 有:/evidence-batches/<b>/files(寫)、/classification-run/<r>/*(以 run uid 定址,5 支) 會繞過:父表加部門隔離後,拿得到 run uid 的人仍能從 /classification-run/<r>/state、/file/<f>/preview 讀到子表內容。service 層有專案成員檢查擋著,但 RLS 層要補——子表要嘛自己加 org_unit_id 條件(兩張子表都有 org_unit_id 欄),要嘛改成 EXISTS (父表)
compliance.detection_executions(有 org_unit_id) detection_execution_groups(父子關係反過來:群組是上層) 兩表各自 tenant_id 判 有:/detection-tools/execution-groups/<g>/*(5 支)以群組 uid 定址 同上,群組表也要一起加;兩表都有 org_unit_id
survey.surveys(有 org_unit_id) survey_pages、survey_questions、survey_discussions、question_answers 透過父表 EXISTS(pages → surveys、questions → pages、discussions → surveys、answers → questions → pages) 有:題目、頁面、討論、答案端點都直接以子表 uid 定址 不會繞過:子表 policy 鏈到 surveys,父表加條件自動傳下去
同上 task_surveys、question_answer_histories task_surveys 鏈到 compliance.workflow_executions(不是 surveys) 有:/task-survey/*、/project-survey/* 任務問卷跟著專案隔離,不跟著問卷本體。若 D12 要「看不到別部門的問卷」,這條線不受影響是對的(受訪者要能填別部門出的問卷);若要「看不到別部門的填答」,要動的是 workflow_executions 那條
§9

定案結論

決策者 2026-10-06 逐條裁決。

  1. 檢測執行 9 支歸 grc.job;開始、取消、重跑歸 update,不算 assign。授權照 L(plugin) 照留。
  2. 不開 survey.response。 填答端 API 歸 grc.job.read/update,授權照 L(survey) 照留;兩支守錯的(改、刪自己留言要求問卷設計權)改 grc.job.update。survey.survey 只剩問卷設計那組。理由:填問卷的人是任務指派人、在任務頁填,身分由任務決定;授權照與能力點是兩層,硬規則管的是能力點模組,填答不需要自己的能力點,故不違反。
  3. 任務問卷設定歸 grc.job.update。
  4. 遠端代理核准歸 update,不開 approve。
  5. 拆 jedi-remote-agent 套件守門插槽為讀/寫兩個,宿主 core/plugins/remote_agent.py 塞兩顆(讀 detection.remote-agent.read、寫對應動詞);落點 T-2.1 順帶,套件發版等令。決策者裁示:FR-132 屬整體大改,jedi-* 該動就動、不逐條問。
  6. AI 證據分類拆兩個資源:evidence.classification(批次與結果)與 evidence.classification-profile(分類設定);license_module 填 evidence-classification,視為基礎包。
  7. storage-config 歸屬:儲存設定 → system.storage-config.*;AI 服務設定+AI 分類設定 → ai.ai-provider-config.*;Drive 應用程式憑證 → platform 層;支援頁 → platform.support.*(已隨 T-1.4/T-1.5 裁)。
  8. 雲端整合三支專案端點歸 grc.project(folders 歸 read、init/repair 歸 update),L(cloud_integration) 照留。
  9. GET /integrations/google-drive 掛 evidence.cloud-integration.read,出貨範本一般成員組要帶這顆。
  10. /classification-ground-truth 保留,掛 evidence.classification.update(灌人工標註答案給準確率報表用,原廠/顧問導入時用;runbook docs/api/evidence-classification/ground-truth-operations.md)。
  11. 題目與頁面增刪改歸 survey.survey.update。
  12. 取消發布歸 survey.survey.publish。

另:設備「選單」類端點(/devices/menu 等被專案/SSP 頁拿來挑選的)新名是「跟呼叫頁面的權限」,不是 asset.device.read(design 通則 B)。

§10

確認要補的守門缺口

本卡只讀,修正由首腦另開卡。

  1. (併入 T-4.6)GET /task-survey/import-template/<task_uid> 不驗身分:service generate_task_survey_question_import_excel_template_data(jedi_survey/app/service/task_survey_service.py:206)直接以 task uid 查任務問卷產 Excel,知道 task uid 的登入者(同租戶,RLS 只擋跨租戶)就能拿到題目內容。比照同檔其他讀取端點補 assert_task_survey_reader。
  2. (併入 T-4.6)POST /survey-discussion 不驗身分:service add_discussion(jedi_survey/app/service/survey_discussion_service.py:69)沒有呼叫任何守門,同租戶任何登入者可在任一任務問卷留言。比照同檔 get_survey_discussions(:47)補 assert_task_survey_reader。
  3. (併入 T-4.6)GET /classification/available-models 只驗身分:回本租戶有哪些 AI 供應商已設金鑰;定案掛 ai.ai-provider-config.read。
  4. (記錄不併)證據分類套件全系列沒有授權照守門:若 evidence.classification 未來要收進某加值包,宿主要先補 L(x),否則買不買都能用。
§11

改名時要一起動的地方(給下一棒)

  • 套件 contract 預設名:jedi_asset/plugin/contract.py:77-80(device)、jedi_detection/plugin/contract.py:32-54(plugin、detection-profile、scan-zone)、jedi_survey/plugin/contract.py:32-35,89-91、jedi_file_upload/plugin/contract.py:33-37(storage-config)
  • 宿主寫死的能力點:core/plugins/remote_agent.py:167、core/plugins/evidence_classification.py:137、core/host_capabilities.py:38-41、app/cloud_integration/service/drive_app_credential_verify_service.py:46、app/module_frame/service/template_lookup_scope.py:16、core/plugins/_file_ownership.py:224-225、core/plugins/system_core.py:64,78(GROUP 分流表)
  • route 層字面量:api/cloud_integration/routes/google_drive_integration_route.py:69,88、google_drive_sync_route.py:41,88,175,207,237
  • 角色範本:core/role_templates.py:14,16,48
  • 儀表板資料源:di_containers/dashboard_apis/device.py:19、survey.py:30,44
  • DEV route_capabilities:/device/device-manage、/plugin/detection-profile-manage、/plugin/tool-plugin-manage、/settings/cloud-integrations、/survey/manage、/system/remote-agent-manage、/system/scan-zone、/system/storage-config、/system/drive-app-config、/system/ai-service-config、/system/support
  • FE:views/device/DeviceManage.vue:68-70、DeviceImportDialog.vue:34、config/scanZone.js、views/detection-profile/DetectionProfileManageView.vue:69-71、components/ProfileVersionTable.vue:227-228、components/integrations/GoogleDriveIntegrationCard.vue:218-222、SyncJobHistoryDialog.vue:18

沿革見 FR-132-LOG.md 與 git log。