Guidant AI v1.18.0 Release Note¶
| 項目 | 內容 |
|---|---|
| 版本 | 1.18.0(功能版號;主軸=系統設定頁全面可用——四頁存檔連環修 + 全新安全政策設定頁把登入政策從「請原廠下 SQL」變成畫面上的開關) |
| 發布日期 | 2026-08-29 |
| 上一版本 | 1.17.0(2026-08-27) |
| 涵蓋區間 | 2026-08-27 ~ 2026-08-29(9e3b763b..4d873065) |
| 涵蓋需求 | CM-1423 安全政策設定頁(MFA 開關進 UI,含開啟前防鎖死守門)+ 系統設定存檔五連環修(CM-1331 追修/CM-1418 ×3)+ CM-1425 檢測執行紀錄可刪除失敗項+逾時收斂 + CM-1420 缺失證據分組 + CM-1421 覆核輪審閱標記 reset + CM-1419 落地版寄信吐翻譯 key + CM-1417 installer 補產 REDIS_SECRET + FR-063/FR-065 落地版工具鏈續修 |
| 規模 | BE 21 commits(feat 7/fix 13/chore 1);FE 4 commits(feat 3/fix 1);DB migration 2 支 |
| 適用部署 | 188 STG + 189 POC(皆為安裝版 docker stack,走 install.sh --upgrade) |
| 部署性質 | 含 2 支 DB migration(envs=*,由 init image 的 migrate 模式套用)。兩台皆為既有資料升級——只走升級模式、不重建庫。FE image 本版有異動必須重 build,不可 retag。不走正式簽章(維持 --skip-prod-key-check,決策者裁示)。 |
0. 發版前回歸 gate¶
本版未執行 site-regression 自動 Core gate,理由與 v1.16.0/v1.17.0 同:落地版 stack 形態的 E2E 套件尚未補齊。放行依據為 DEV 上各卡的 BE 實跑驗收(各 commit message 內逐項列出)+ 188 STG 升級後功能抽驗 + 189 POC 升級後的舊資料筆數對照(見 §6)。自動化 gate 待落地版 E2E 套件補齊後於後續版本恢復。
1. 概述¶
v1.17.0 把「系統自己的紀錄」送進了客戶的 log 平台。v1.18.0 處理的是更前面一步的問題:那些系統設定頁,客戶到底改不改得動。
答案在本版開工時是「幾乎都不行」。SMTP、LDAP、Discord、Telegram 四頁的存檔按鈕,在落地版上按下去只會拿到 403、400 或 404——五顆彼此獨立的缺陷疊在同一條路徑上,一顆修掉才露出下一顆(詳見 §3)。而登入政策(要不要強制兩階段驗證、密碼多久過期、連續失敗幾次鎖帳號)連頁面都沒有,客戶要改只能請原廠連進資料庫下 SQL。
本版把這條線補完:
- 四頁存檔全通——五連環修完,共用設定(SMTP/LDAP)的讀寫落點統一到 ROOT 租戶,四支 RLS policy 對稱化。
- 新增安全政策設定頁——登入政策五項進 UI,其中「強制 MFA」帶一道防鎖死守門:開錯就沒有人進得來,故開啟前會檢查驗證碼寄得出去的前置條件。
- 落地版的兩顆靜默雷——寄出去的信是翻譯 key 不是中文(CM-1419)、installer 沒產 jedi-mfa 要的 Redis 設定導致「開了 MFA 全員登不進來」(CM-1417)。兩顆都是「服務全綠、log 乾淨、就是行為錯」的類型。
另外三項與稽核業務直接相關:檢測執行紀錄終於刪得掉失敗項並且不再卡在 running(CM-1425)、缺失改善的證據 tab 按控制項分組不再看起來像系統重複(CM-1420)、發起覆核後不再帶著母輪的已審閱勾勾(CM-1421)。
2. 重點新功能¶
2.1 安全政策設定頁(CM-1423,BE d8c8061d + FE 48cca59)¶
登入政策自 FR-063.1g 起就存在資料庫、登入流程每次即時讀取,但沒有任何管理介面。本版補上讀寫端點、授權與選單登記——多半是接線不是新做:設定值、讀取器、ROOT 讀寫器都沿用既有的,沒有新表。
| 項目 | 說明 |
|---|---|
| 端點 | GET/PUT /api/1.0/system/security-policy(group 粒度,一次讀寫整組) |
| 位置 | 系統設定 → 安全政策(介於「郵件伺服器」與「LDAP」之間) |
| 能力點 | security-policy.{read,update}(租戶層,授予對象=既有持有 smtp-config.update 的角色) |
| 可調項目 | 強制兩階段驗證、登入失敗鎖定次數與時間、密碼有效期、閒置帳號停用天數、JWT 效期 |
🔴 防鎖死守門是這頁的主要風險控制。強制 MFA 是唯一一個開錯就沒有人進得來的設定:驗證碼靠寄信送達,SMTP 沒設好等於含操作者自己在內全員卡在登入第二關,且無法從畫面救回。因此:
- 讀取時一併回
otp_readiness(檢查 ROOT 的 SMTP 設定存在且 host/port/寄件人齊全)。 - 「從關到開」且通道未就緒 → 回 412(
SECURITY_POLICY_412001),要帶confirm_risk=true明示確認才放行。不硬性禁止——客戶可能用 TOTP,或明知風險而為之。 - 只擋「開啟」方向:關閉 MFA 是解除鎖死的動作,任何情況都不能被擋住。
- 檢查的是「設定齊全」不是「寄得出去」(真試寄要收件人且有延遲,不適合擋在存檔路徑),故畫面文案一律措辭為「可能寄不出」,不誇大這道檢查的效力。
其他行為細節:數值超出合理範圍拋 400 而非靜默夾成邊界值;改動 JWT 效期會回 restart_required(該值由 flask_jwt_extended 啟動時讀取,改資料庫要重啟才生效,如實回報不讓使用者誤以為即時生效);每次修改寫稽核事件 SECURITY_POLICY_UPDATED(6110)——改登入政策等於改全站登入強度,要留痕。
2.2 檢測執行紀錄可刪除失敗項 + 逾時收斂(CM-1425,BE 9a0623db + FE fefe226)¶
檢測工具的執行紀錄過去無法從畫面刪除,失敗紀錄(agent 缺工具、連線失敗、設定錯)只會一直累積。本版補上單筆與整組刪除,並依決策者擴充範圍一併做執行逾時收斂。
只准刪 failed,這是刻意的:succeeded 掛著報告檔與證據,刪了證據鏈就斷;running/scheduled 還在跑,要刪得先取消(既有端點已有);cancelled 是使用者自己按的、屬有意義的軌跡。整組刪看的是該組每一筆(含重跑歷史)——夾著一筆成功就代表這組留有報告,不該整組消失。單筆刪不重算整組彙總:刪掉一筆失敗紀錄不代表這次執行變成功了。
逾時收斂(running/scheduled 超過閾值且 agent 始終沒回報 → 判 failed 並寫明原因)採排程而非「有人開抽屜才算」:卡住的掃描恰恰是最沒人盯的那些,且卡在 running 的組會擋掉同任務的下一次執行。閾值 DETECTION_EXECUTION_TIMEOUT_HOURS 預設 4 小時可調——訂太短會誤殺正常的長掃描(逐台登入型工具一次派 256 台是十幾小時的量級)。
2.3 缺失改善證據按控制項分組(CM-1420,BE 2521b013 + FE 5a73754)¶
缺失改善側欄的「證據」tab 原本把一筆缺失底下所有觀察的證據攤平成單一清單,同一份檔案被多個查核項各自引用時會連著排好幾列,看起來像系統重複。BE 端在觀察記錄回傳中加上來源 target_id(如 AC.L1-b.1.iv),FE 據此按控制項/查核項分組收合,組內同名檔去重。純加欄位、無 migration、無新端點。
3. 重要修補¶
3.1 系統設定四頁存檔的五連環(CM-1331 追修 + CM-1418 ×3)¶
這五顆彼此獨立、依序遮蔽,是本版最耗時的一段。按露出順序:
| # | commit | 症狀 | 根因 |
|---|---|---|---|
| 1 | 7b9b363a |
SMTP/LDAP 存檔永遠 403 | by-uid 端點的守門前置查詢只走一般租戶查詢,而共用設定的實體在 ROOT 租戶、業務租戶因 RLS 看不到 → 查無 → 能力點對照 fallback 成誰都沒有的 system_config.* |
| 2 | b4340bd1 ① |
存檔 400「缺少 value」 | 共用設定寫入路徑期待 {"value": {...}},但這條 by-group-key 端點從來就是扁平契約(前端送的、舊路徑收的都是扁平) |
| 3 | b4340bd1 ② |
存檔會弄丟既有密碼 | changePwd=false 時沿用既有 secret 的邏輯在舊路徑的 domain 層,新路徑繞開 domain 直接寫 ROOT,這段整個漏了 |
| 4 | b4340bd1 ③ |
SMTP 頁點不開也存不了(一律 404) | SMTP 走 by-uid 端點,而清單端點回給前端的正是 ROOT 那列的 uid,業務租戶拿它去讀寫受 RLS 看不到 |
| 5 | c96d3d5f |
新裝機器第一次存 LDAP 必炸 400 | system_configs 四支 RLS policy 只有 INSERT 不認 super_admin(SELECT/UPDATE/DELETE 走 USING 吃得到,INSERT 走 WITH CHECK 不看) |
第 5 顆特別值得記:DEV 上 15 項驗收全綠卻測不出來,因為寫入是「先 UPDATE、影響 0 列才 INSERT」,而 DEV 的 ROOT LDAP 列一直存在(歷史資料)永遠走 UPDATE 分支。出貨基線刻意不帶 LDAP 列(由客戶自行設定),所以每一台新裝的客戶機器第一次設 LDAP 都必踩。這是「開發環境有歷史資料掩蓋出貨情境」的典型案例。
修法一律往共用處收:繞 RLS 的 ROOT 讀取只留一份實作、by-uid 的 PUT 拆成扁平後與 by-group-key 共用同一條寫入路徑(changePwd 邏輯不寫第二份)。RLS 只修 system_configs 一張——實查發現多張表的 INSERT policy 也有同樣不對稱,但那些表沒有「跨租戶寫 ROOT」的使用情境,一起放寬等於平白擴大攻擊面。
3.2 落地版寄出的信是翻譯 key 不是中文(CM-1419,2446fc2e)¶
189 落地版寄出的帳號建立通知信,主旨與內文都是原始翻譯 key(account_created_subject),開發環境正常。根因是 jedi_auth 被 Nuitka 整包編成機器碼,而 Nuitka 只收 .py——套件自帶的 translations/**/*.mo 不會進產物,gettext 的 fallback 靜默回 msgid 本身:信照發、HTTP 200、log 乾淨。
與 CM-1416(googleapiclient 的 discovery JSON)同型但更陰——那顆至少會拋例外,這顆全程無聲。修法是把套件資料目錄複製進產物(先在 188 用最小 Nuitka 專案實測確認編譯後的路徑推導仍會命中該落點),並新增 smoke 探針⑦:實際從產物落點載入 .mo 並 gettext 兩個真實 key,斷言回傳不等於 key 且含中文。這型缺陷全是靜默的,只靠 build 期的檔案存在斷言不夠。
3.3 installer 沒產 REDIS_SECRET,一開 MFA 全員登不進來(CM-1417,02ce7fd8)¶
jedi-mfa(驗證碼暫存)自帶的 Redis client 只讀舊 JSON 格式的 REDIS_SECRET,不認 installer 產的平鋪 REDIS_HOST/REDIS_USER/REDIS_PASSWORD。缺了這個鍵,它拿到的帳密是 None——服務六顆全綠、平常登入正常,一旦開啟 MFA 就全員登不進來(發驗證碼時才第一次連 Redis)。DEV 測不出來:DEV 的 Redis 允許免帳密,落地版的有密碼必炸。
本版由 installer 補產(決策者裁示的方案 A;治本=改 jedi-mfa 認新格式,另案)。升級路徑一併顧到:既有安裝的設定檔沒這行,--upgrade 時偵測缺就補寫,既有內容一律不動(比照物件儲存憑證的補齊模式)。
3.4 發起覆核後帶著母輪的已審閱勾勾(CM-1421,274e0813)¶
覆核輪的控制項與查核項仍顯示母輪已審閱的勾勾,覆核者會誤以為本輪已審過。根因是審閱標記掛專案不掛輪次(FR-014 的刻意設計,非漏設計),加上快照時 catalog 不重複複製、控制 id 跨輪穩定,勾勾就「穿透」到覆核輪。
採最小改:發起覆核時清掉收斂範圍內控制(及其底下查核項)的審閱標記,不動 schema。不加輪次維度的理由是那等於改變既有設計語意,要一併改寫入/讀取/計數四處且需 backfill 既有標記的輪次歸屬(無從推導);而「保留母輪歷史」的價值有限——母輪已結案,該控制在覆核輪重查過,舊勾勾反而是誤導,真正的歷史留痕在 AR findings 與 POA&M。
3.5 順帶修掉一個全專案的地雷:背景排程呼叫翻譯函式必炸(9a0623db 內)¶
select_locale 直接讀 request,但背景排程在自己的執行緒跑、只有 app context 沒有 request context → 任何在背景 job 裡產生文案的程式碼都會整輪掛掉。因為 tick 都包在 try/except 裡,症狀是「排程靜悄悄什麼都沒做」而非明顯報錯。改成沒有 request context 時退回預設語系。不是單一功能獨有——任何未來的背景 job 只要碰文案都會踩到。
3.6 落地版工具鏈續修(FR-063/FR-065)¶
| commit | 內容 |
|---|---|
b7ba5223 |
Google Drive 授權炸 UnknownApiNameOrVersion——googleapiclient 改不編譯原樣附帶 |
dae47169 |
image 憑證斷言改認「標頭+金鑰本體」,文件裡的空範例不再誤判成私鑰 |
6884da4d |
補 config 漏定義的 DRIVE_WEBHOOK_PUBLIC_BASE_URL(授權註冊 webhook 頻道時炸 NoneType) |
e872d5d9 |
維運指令抽成系統層 guidant CLI + restart 設定變更守門 |
6a1e8449 |
新增測試用 dev image(直接跑源碼、容器內 git pull 換版) |
e0a8fd62 |
build/smoke 加 DB 前置斷言——連錯庫從三個 Traceback 變一句人話 |
1617dd01 |
雲端空間整合下放 Tenant Admin——root 旗標守門改能力點(FE 8680bfb 對應) |
4. Breaking Changes¶
無。本版無 API 契約破壞性變更。
一項行為澄清:系統設定 by-group-key 端點的 payload 一律是扁平形狀(設定本體本身,不包 value)。這是回歸既有契約而非變更——前端與舊路徑本來就是扁平,v1.17.0 期間新增的共用設定路徑形狀寫錯,本版修正。
5. DB Migration¶
2 支,皆 envs=*(三環境都適用),由 init image 的 migrate 模式套用:
| 檔案 | 內容 |
|---|---|
2026-08-28-cm1418-system-configs-insert-rls-super-admin.sql |
system_configs 的 INSERT policy 補 super_admin 逃生口(四支 policy 對稱) |
2026-08-29-cm1423-security-policy-page.sql |
security-policy.{read,update} 能力點 + 授予 + ui_routes 選單登記(sort=15) |
🔴 出貨基線產物同步更新:第二支是 seed 型 migration(能力點與選單列),全新安裝必須帶到,否則客戶裝出來沒有安全政策設定頁。故基線庫已補套本波兩支並重跑三支產生器(gen_schema_sql.sh/gen_seed_sql.sh/gen_stamp_sql.sh),產物隨本版一併提交。
一則過程紀錄(4d873065):CM-1423 的 migration 檔曾漏登記 manifest.tsv。manifest 是套用清單的唯一來源,不在裡面的 SQL 檔對 migrate 而言不存在——後果是全新安裝少那支、升級的乾跑清單也不列它而一路回報成功。由 scripts/check_migration_manifest.sh 在打包前抓到並補登。這正是 v1.17.0(CM-1407)那類「沒有錯誤訊息、退出碼 0」失敗型態的同族,值得作為往後每次出包前必跑該檢查的依據。
6. 部署順序¶
- 基線庫補套 + 重跑三支產生器 + 提交產物(已於本版完成,見 §5)
- BE build(
build_all.sh --all,含 Nuitka 編譯與 smoke 全綠守門,新增探針⑦) - FE build——本版必須重 build,不可 retag(CM-1420/CM-1423/CM-1425 皆有 FE 異動)
- init image build(帶新的 manifest 與基線產物)
- 封包(
build_bundle.sh) - 188 STG
install.sh --upgrade→ 乾跑清單應列出本波 2 支 → 「資料庫已與本版對齊(待套 0 支)」斷言出現 → 六容器 healthy - 189 POC
install.sh --upgrade(同上;既有資料升級,不重建庫)
升級後必驗:guidant.env 內存在 REDIS_SECRET 且 JSON 可解析、舊資料筆數對照無損、SMTP 設定頁存檔不再 403、寄出的信是中文不是翻譯 key、安全政策頁打得開、檢測執行紀錄的失敗項有垃圾桶。
7. 相依套件版本¶
| 套件 | 版本 | 變更 |
|---|---|---|
jedi-system-config |
0.0.14 → 0.0.15 | CM-1418 changePwd 防呆(既有列沒有 secret 時不硬抄) |
其餘 jedi-* 套件版本不變。
8. 已知 follow-up¶
REDIS_SECRET治本:改jedi-mfa認平鋪格式的 Redis 設定,讓落地版不必維護兩份同值的憑證。本版走 installer 補產(方案 A),治本另案。- 其餘表的 INSERT policy 不對稱:
bulletins/devices/projects/surveys/workflow_*等多張表有與system_configs相同的 policy 不對稱,本版刻意不一起改(那些表沒有跨租戶寫 ROOT 的使用情境)。日後若有表需要該情境,比照本版逐張評估。 - 落地版 E2E 套件:site-regression 尚未覆蓋 stack 形態,自動 gate 仍暫停,待補齊後恢復。
- 輪次層審閱標記:CM-1421 採最小改保留專案層語意。日後若真要輪次層審閱,需另案評估(本版的修法不擋那條路)。
9. 完整 commit 索引¶
BE(9e3b763b..4d873065,21 支)
| commit | 內容 |
|---|---|
9e3b763b |
v1.17.0 進版 |
c84d4591 |
出貨基線產物補齊 FR-068 |
1617dd01 |
雲端空間整合下放 Tenant Admin |
b7ba5223 |
落地版 Google Drive 授權炸 UnknownApiNameOrVersion |
dae47169 |
image 憑證斷言改認「標頭+金鑰本體」 |
e872d5d9 |
維運指令抽成系統層 guidant CLI + restart 守門 |
6884da4d |
補 config 漏定義的 DRIVE_WEBHOOK_PUBLIC_BASE_URL |
6a1e8449 |
新增測試用 dev image |
e0a8fd62 |
build/smoke 加 DB 前置斷言 |
7b9b363a |
SMTP/LDAP 存檔永遠 403(by-uid 守門補 ROOT fallback) |
b4340bd1 |
設定存檔契約形狀錯/密碼保留漏失/by-uid 404 |
41bdb99f |
jedi-system-config 0.0.14 → 0.0.15 |
c96d3d5f |
system_configs INSERT policy 漏 super_admin |
2446fc2e |
落地版寄信吐翻譯 key + smoke 探針⑦ |
2521b013 |
POA&M 觀察記錄回傳 target_id |
274e0813 |
發起覆核後清審閱標記 |
d8c8061d |
安全政策設定頁 BE(MFA 開關 + 防鎖死守門) |
9a0623db |
檢測執行紀錄可刪除失敗項 + 逾時收斂 + 背景排程 _() 修復 |
02ce7fd8 |
installer 補產 REDIS_SECRET + 備份檔權限收緊 |
4d873065 |
cm1423 migration 補登記 manifest |
FE(0a47f5b..,4 支)
| commit | 內容 |
|---|---|
8680bfb |
雲端整合頁按鈕顯示條件改綁能力點 |
5a73754 |
缺失改善證據 tab 按控制項/查核項分組收合 |
48cca59 |
安全政策設定頁 UI(含防鎖死警告) |
fefe226 |
執行紀錄失敗項刪除鈕(單筆 + 整組) |
10. 相關文件索引¶
- 系統設定頁 spec:
docs/specs/current/(系統設定群) - 落地版 build/出包:
scripts/build/README.md(canonical) - 出貨基線產物:
scripts/init/README.md(含 regenerate 鐵則) - Installer:
scripts/installer/install.sh - 前版 release note:
docs/release_notes/v1.17.0.md