本卡屬 FR-114 資安修正(母卡 CM-2019),第 5 批子集 C 卡 5C-3,修 SUMMARY #21(M15-1)/#94(M14-1)/#95(M15-4)。三件皆高(資源耗盡可讓全產品對所有客戶停止回應;#95 為敏感欄位外洩)。計畫在 docs/features/FR-114-2609-security-fix-dispatch/batches/plan-b5C.md「卡 5C-3」段。

問題是什麼(白話)

#21:AI 儀表板每次產生畫面要呼叫外部 AI 兩次(一次選要查什麼資料、一次設計版面),目前完全沒有逾時設定、也沒有單一使用者每分鐘呼叫次數上限——比 AI 聊天機器人更嚴重,因為攻擊者付出同樣力氣、這裡打垮系統的成本只要一半。

#94:AI 聊天機器人收到的訊息沒有長度上限、呼叫外部 AI 也沒有逾時、也沒有每分鐘呼叫次數上限。系統本身同時只能處理 4 個並行請求(正式環境預設),外部 AI 天生慢、不回應要等 10 分鐘(套件預設值、從未調整過),系統則在 120 秒把卡住的請求強制中止——4 到 8 個連線各送一則過長訊息就能把系統容量吃滿,其他人連登入畫面都打不開。

#95:AI 儀表板讓 AI 自由決定要查哪些資料、畫面上顯示哪幾欄,但共用的序列化工具目前只排除 3 個 ORM 內部欄位,密碼加鹽值與超級管理員旗標都不在排除名單裡——AI 挑資料是自由選的(26 種不同查詢各自欄位形狀都不同),一但挑到含這些欄位的資料,就會原樣送給外部 AI、也可能顯示在畫面上。最容易誤解的一點:畫面上看到的欄位數量不代表實際送出去的欄位數量——「畫面只顯示 3 欄」不等於「只送了 3 個欄位」。

首腦核對:

工作區

在哪裡

jedi-ai-dashboard/jedi_ai_dashboard/api/routes/ai_dashboard_route.py:101-115    post() 入口,完全沒有速率限制,#21
jedi-ai-dashboard/jedi_ai_dashboard/api/serializers/ai_dashboard.py:41    validate=validate.Length(min=1, max=2000),長度已有上限,不要重加
jedi-ai-dashboard/jedi_ai_dashboard/app/service/ai_dashboard_app_service.py:145    _ask_ai_to_select_api(),第一次呼叫外部 AI
jedi-ai-dashboard/jedi_ai_dashboard/app/service/ai_dashboard_app_service.py:168    _ask_ai_to_design_layout(),第二次呼叫外部 AI
jedi-ai-dashboard/jedi_ai_dashboard/infra/ai_client/claude_client.py:62,99    Anthropic(api_key=self.api_key),無 timeout
jedi-ai-dashboard/jedi_ai_dashboard/infra/ai_client/openai_client.py:39    OpenAI(api_key=self.api_key),無 timeout
jedi-ai-dashboard/jedi_ai_dashboard/infra/ai_client/google_client.py:39    genai.configure(api_key=self.api_key),無 timeout
jedi-ai-bot/jedi_ai_bot/api/routes/ai_bot_route.py:52    message strip 後只檢查非空,#94 無長度上限
jedi-ai-bot/jedi_ai_bot/api/routes/ai_bot_route.py:55-59    空值檢查後直接呼叫 rt.service.chat(...)
jedi-ai-bot/jedi_ai_bot/app/service/ai_bot_service.py:104-108    Anthropic(api_key=...) + client.messages.create(...),無 timeout
jedi-ai-bot/jedi_ai_bot/app/service/ai_bot_service.py:69    既有 max_messages 歷史截斷邏輯,加長度上限不可連帶動到這段
jedi-jedi-common/jedi_common/utils/serialization_util.py:12    _ORM_SKIP_KEYS,#95 核心,只有 3 個 ORM 內部欄位
jedi-jedi-common/jedi_common/utils/serialization_util.py:15    to_serializable() 主體,長期方案(步驟二)要改的地方
jedi-ai-dashboard/jedi_ai_dashboard/app/service/ai_dashboard_app_service.py:190    呼叫點一:送給外部 AI 的樣本(前 3 列)
jedi-ai-dashboard/jedi_ai_dashboard/domain/service/dashboard_generation_domain_service.py:249    呼叫點二:回傳給畫面
jedi-iam/jedi_iam/mfa/app/service/email_service.py:87-92    既有 OTP 重寄節流參考實作(Redis TTL),D-b5C-5 建議照抄這個形狀
core/app_factory.py:459-468    主專案既有把 OtpResendTooFrequentError 轉成 429 + retry_after 的地方,唯讀參考

怎麼修

D-b5C-4/D-b5C-5 決策者已裁照建議寫成定案。