diff --git a/system-dev/docs/3-specs/portal-auth/tasks.md b/system-dev/docs/3-specs/portal-auth/tasks.md index 3b67101..e7b66fe 100644 --- a/system-dev/docs/3-specs/portal-auth/tasks.md +++ b/system-dev/docs/3-specs/portal-auth/tasks.md @@ -30,9 +30,10 @@ 3. 驗收抽查:semantic 帶 `library=` 過濾命中/不帶行為不變 4. 掛號:`cli/src/lib/deploy.ts` `ensureVectorizeMetadataIndexes()` 待補 `library`(cli/ 屬 P1 派工範圍外「只動 kbdb」,隨 P3 或部署 PR 補——kbdb/wrangler.toml 註解已標) -- [ ] cypher `kbdb-proxy` 透傳 `library` 參數(供 owner/admin 面用;portal 面走 P3 的注入,不經這) +- [x] cypher `kbdb-proxy` 透傳 `library` 參數(供 owner/admin 面用;portal 面走 P3 的注入,不經這) - ⚠️ 範圍註(2026-07-14):P1 派工紅線「只動 kbdb、不碰 cypher-executor」與本項矛盾——照窄範圍 執行,本項順延(一行 query 透傳,隨 P3 動 cypher 時一併)。 + - ✅ 已隨 P3 補上(2026-07-14 P3 PR):`/kbdb/search` 與 `/kbdb/entries` 透傳白名單各加 `library`。 - [x] 測試:D1 filter 單元測、NULL fallback、多值、semantic filter(mock VECTORIZE) - `kbdb/tests/library-filter.test.ts` 11 項(SQL 形狀/route 解析/向後相容/降級仍 enforce/$in 構造/ 寫入端正規化)+既有 6 項全綠(17/17);tsc exit 0。 @@ -69,13 +70,34 @@ ## P3 — `/portal` UI:登入殼+搜尋頁+設定頁+scope enforce(design §1/§3.3/§5/§6)|觸碰:`cypher-executor/` -- [🔄] `/portal` HTML 殼(重用 console 樣式/搜尋 view 抽共用 helper;`CONSOLE_BRAND` 品牌;零 Mira 字樣) -- [🔄] 未登入只見登入殼;登入後兩頁:搜尋(keyword/semantic/graph 三模式+source 溯源+卡片詳頁)+設定(改密碼/看自己權限/主題) -- [🔄] `/portal/data/*` server-side enforce:session→record→注入 `owner_id`+`library`;**前端絕不下發租戶字串** -- [🔄] 卡片詳頁逐筆驗 library(越庫 id 直讀 → 404) -- [🔄] graph 粗閘(D-4:無 graph 來源庫權限 → 模式不顯示+API 403) -- [🔄] 測試:curl 帶 user session 直打 data API 驗 filter 繞不過(=#24 驗收 3 的 server-side 證明) +- [x] `/portal` HTML 殼(重用 console 樣式/搜尋 view 抽共用 helper;`CONSOLE_BRAND` 品牌;零 Mira 字樣) + - 實作註(2026-07-14):`routes/portal-ui.ts` 單檔 HTML+原生 JS(console 同形態)。**UI 重用取捨= + 複製 console 樣式子集而非抽共用模組**——抽共用要動 console.ts(違「console 現狀不動」鐵律), + 代價=樣式雙份、改版要兩邊同步(PR 說明誠實記)。零 Mira 字樣由測試機械斷言。 +- [x] 未登入只見登入殼;登入後兩頁:搜尋(keyword/semantic/graph 三模式+source 溯源+卡片詳頁)+設定(改密碼/看自己權限/主題) + - graph 模式按 session `graph_allowed` 顯示(D-4);工作流頁 admin 額外可見(D-8, + `PORTAL_SHOW_WORKFLOWS` 預設 admin,唯讀不開 trigger)——前端藏只是 UX,真閘在路由層。 +- [x] `/portal/data/*` server-side enforce:session→record→注入 `owner_id`+`library`;**前端絕不下發租戶字串** + - `routes/portal-data.ts`(安全核心):caller 自帶 owner_id/library 一律被 server 值靜默覆蓋 + (e2e 攻擊實測);`["*"]`=不注 library;HTML 殼經測試機械斷言零租戶字串/零 + X-Arcrun-API-Key/不打 /kbdb/*。 +- [x] 卡片詳頁逐筆驗 library(越庫 id 直讀 → 404) + - 雙重驗證:owner_id=CONSOLE_TENANT(防拿跨租戶 id 直讀)+library∈用戶集合(NULL→general); + 越庫/跨租戶/不存在**同一句 404**(不洩存在性,e2e 逐字比對)。 +- [x] graph 粗閘(D-4:無 graph 來源庫權限 → 模式不顯示+API 403) + - 來源庫=portal_library `graph_source='true'` 集合(seed 補 slot+ensurePortalTemplates 冪等 + grow 舊 template 缺的 slots;admin libraries PATCH 可標記/取消),**沒有任何標記→預設 + ['general']**(D-4 定案);無權 403、前端模式鈕不顯示;停用庫不算來源。 +- [x] 測試:curl 帶 user session 直打 data API 驗 filter 繞不過(=#24 驗收 3 的 server-side 證明) + - 單元:`cypher-executor/tests/portal-data.test.ts` 21 項(fetchMock 假 host,絕不外連); + cypher 全套 154/155(唯一失敗=executor「不存在的零件」pre-existing,stash 複驗); + kbdb 20/20;兩包 tsc exit 0。端到端:本機隔離雙 worker(9790/9791,demo 8787/8788 不碰) + **47/47 全綠**——含越庫 id 直讀 404、攻擊參數被覆蓋、graph 403→授 general 後放行、 + admin 全庫+跨租戶不可見、權限 PATCH 即時生效、HTML 殼斷言+inline JS node --check、 + 改密碼全鏈。`wrangler deploy --dry-run` 打包過。 - **驗收**(=#24 驗收):leo21c `/portal` 可開;未登入只見登入殼;三模式+溯源可用;A 用戶(僅 general)搜不到 finance 內容——UI 與 curl 雙驗 + - 本機端到端已全數驗過(上項證據);**leo21c 線上面=部署後驗**(gated wrangler 直推,B 類流程; + semantic 線上路徑仍依 P1 部署清單的 Vectorize index+reindex)。 - **工程量**:大(1–1.5 個 CC 工作天,UI 是最大件) ## P4 — admin 頁+工作流顯示(design §6)|觸碰:`cypher-executor/` diff --git a/system-dev/wiki/status.md b/system-dev/wiki/status.md index 19f72bf..002ebd3 100644 --- a/system-dev/wiki/status.md +++ b/system-dev/wiki/status.md @@ -3,7 +3,7 @@ name: status description: 當前進度、進行中 Phase、已知問題、下一步(動態文件,每 session 更新) metadata: type: project - last_updated: 2026-07-02 + last_updated: 2026-07-14 --- # 當前進度(動態) @@ -15,6 +15,31 @@ metadata: ## 📍 當前位置 +> **2026-07-14 本 session(portal-auth P3:Portal UI+查詢 enforce——本 SDD 最大件,PR 待總管審不 merge)**: +> - **實作(分支 `portal-auth-p3-portal-ui`)**:① `routes/portal-data.ts`(安全核心)——`/portal/data/*` +> server-side enforce:session→回讀 user record→server 注入 `owner_id=CONSOLE_TENANT`+`library=<集合>` +> 轉發 KBDB;**前端絕不下發租戶字串**(vs console 把 tenant 交給前端直打 /kbdb/*);caller 自帶 +> owner_id/library 被靜默覆蓋;entries/:id 逐筆驗租戶+庫(越庫/跨租戶/不存在同一句 404 不洩存在性); +> graph D-4 粗閘(來源庫=portal_library `graph_source` 標記、無標記預設 general;無權 403);workflows +> D-8(`PORTAL_SHOW_WORKFLOWS` 預設 admin;唯讀+最近執行,無 webhook_url/trigger 把手)。 +> ② `routes/portal-ui.ts`——`/portal` 單檔 HTML 殼(登入/搜尋三模式+source 溯源+卡片詳頁/設定改 +> 密碼看權限主題;graph 模式與工作流頁按 `/portal/session` 新能力欄位 `graph_allowed`/`workflows_visible` +> 顯示,真閘在路由層)。③ P1 順延項補上:kbdb-proxy `/kbdb/search`+`/kbdb/entries` 透傳 `library`。 +> ④ portal_library seed 加 `graph_source` slot+ensurePortalTemplates 冪等 grow 舊 template 缺 slot+ +> admin libraries PATCH 可標記。 +> - **UI 重用取捨**:複製 console 樣式子集(紙感/明體/琥珀/深淺主題)而非抽共用模組——抽共用要動 +> console.ts(違「console 現狀不動」鐵律);代價=樣式雙份、改版要兩邊同步(誠實記)。 +> - **驗證**:cypher 154/155(唯一失敗=executor「不存在的零件」pre-existing,stash 複驗)+新增 +> `tests/portal-data.test.ts` 21 項;kbdb 20/20;兩包 tsc 0;`wrangler deploy --dry-run` 打包過。 +> **端到端隔離雙 worker(9790/9791,demo 8787/8788 不碰)47/47 全綠**:越庫 id 直讀 404(含跨租戶 +> id)、攻擊參數 `library=hr&owner_id=other-tenant` 被 server 覆蓋、graph 無權 403→授 general 後放行、 +> admin 全庫 3 筆+跨租戶第 4 筆不可見、權限 PATCH 即時生效(同一 session)、HTML 殼零租戶字串/零 +> X-Arcrun-API-Key/零 Mira+inline JS node --check、semantic 誠實降級仍 enforce、改密碼全鏈。 +> - **不 merge 待總管審**(B 類流程);部署後 leo21c 線上驗(semantic 線上路徑仍依 P1 部署清單 +> Vectorize `library` index+reindex backfill)。下一棒 P4(admin 帳號管理頁)。 +> - **附帶**:P2 舊 session 測試補 P3 能力欄位 mock;wrangler.test.toml 補 ANALYTICS_KV+KBDB_GRAPH_URL +> 假 host。本 session 同時補記 #47-#51 五件的 wiki 斷層(見下段)。 +> > **2026-07-13/14 補記(RAG Portal 線五連發——前五個改動當時漏寫 wiki,leo 點破接關斷層,此段補上)**: > - **PR #47**(commit 2e1005e):console rebrand Mira→Arcrun+`CONSOLE_BRAND` var 可覆蓋品牌字樣(Gitea #21)——console 是引擎共用件不寫死產品名。 > - **PR #48**(commit cbebb36):`CONSOLE_PROFILE=rag` 頁面裁剪(企業產品樣:搜尋落地、只留搜尋/工作流/設定;未設=full,Mira 實例零影響)——是 config 裁剪不是 auth。