掃描日期:2026-09-05 掃描版本:jedi-python-package
feature/FR-075@67cb075(乾淨,無未 commit 變更) 工具:Claude Codeclaude-securityplugin v0.10.2.3,effort: low範圍:jedi-iam/jedi_iam/mfa、jedi-iam/jedi_iam/turnstile,共 34 個受版控檔案 狀態:✅ 已經過工具驗證面板完整審查(單一研究員掃完整個範圍 → 3 人面板逐條投票) 裁決狀態:✅ 已全數處理——F1 CM-1557、F2 CM-1558 均已修(1.21.0 出貨)
verified(render_report.py 蓋出的官方戳記,非自行宣稱)CLAUDE-SECURITY-20260905-064153-s3/(CLAUDE-SECURITY-RESULTS.md / .jsonl / .sarif / revision stamp),該目錄有自己的 .gitignore 排除版控,本檔為手動摘要進 repo 供裁決追蹤第一輪試掃在同一塊範圍(OTP + Turnstile)找到過 H-6(OTP 端點無認證無次數限制)與 M-4(Turnstile fallback 到永遠通過的測試金鑰)。這一輪跑完整條含驗證面板後:
LoginDomainService.is_user_locked_within_limit)有 3 次/15 分鐘的鎖定機制,但 MFA 驗證路徑完全沒有對應控制——兩條路徑走的是不同程式碼,不是同一套邏輯漏鎖一處。FE 有前端計數器(OtpVerification.vue errorCount≥5)但直接打 API 可繞過。TURNSTILE_ENABLED 設為 false,且所有已 commit 的 env 檔在 Turnstile 開啟時都有明確設定金鑰(即使是測試金鑰),所以這條路徑在目前實際部署設定下不會被觸發。程式碼本身仍是不設防的,但反映的是「未來或非標準部署方式的風險」,不是「現在正在裸奔」。random 模組的其他輸出才能重建亂數狀態,而整個 codebase grep 後找不到任何這樣的洩漏管道(唯一另一處用到 random 的是從未被實例化的死碼)。這是本輪跑完整驗證面板才篩掉的假陽性,印證了母卡強調的「跑完整條含驗證面板」的價值。jedi-iam/jedi_iam/mfa/infra/email/adapter/email_adapter.py:14 EmailAdapter.verify(TOTP 路徑 jedi-iam/jedi_iam/mfa/infra/totp/adapter/totp_adapter.py:11-18 有相同缺口)if stored_code and stored_code == code:,沒有任何次數限制、退避、鎖定機制。呼叫它的 MfaVerifyRoute.post(jedi-iam/jedi_iam/api/routes/otp_route.py:86-114)刻意不掛認證(因為這是登入第二關,此時還沒 access token),驗證通過就直接發出完整的 access/refresh JWT。login_domain_service.py:61-76),但這套機制完全沒有被 MFA 驗證路徑呼叫——是兩條分開的程式碼路徑,不是共用邏輯漏設定。唯一存在的節流是 /otp-resend 的 60 秒冷卻,管的是「重發驗證碼」不是「驗證嘗試次數」,對暴力猜碼沒有任何阻擋作用。BE 與 FE 兩邊都查過沒有 nginx/WAF/Flask-Limiter 之類的外部速率限制。EmailAdapter.verify / ToptAdapter.verify(或上一層的 EmailMfaService/TotpMfaService)加上 per-uid 失敗次數計數+鎖定;每組驗證碼設定總嘗試上限(例如 5 次),超過即讓該碼失效。jedi-iam/jedi_iam/turnstile/verifier.py:38 _resolve_secret_keyTURNSTILE_SECRET_KEY 時,_resolve_secret_key() 會 fallback 到 Cloudflare 官方文件公開的「永遠通過」測試金鑰。這個值送去真正的 Cloudflare siteverify 端點,不管前端送了什麼 token(甚至空值)都會回傳成功。呼叫端 TurnstileCaptchaVerifier.verify(compliance-manager-be/app/auth/service/login_adapters.py:35-41)只有在明確設 TURNSTILE_ENABLED=false 時才跳過,該旗標預設是 true(config/config.py:286)。fallback 發生時完全沒有 log 警告。scripts/installer/install.sh:1408)預設把 TURNSTILE_ENABLED 寫死為 false,而且目前所有已 commit 的 env 檔(.env / .env.sample / .env.test)在 Turnstile 開啟的情況下都有明確設定金鑰(即使是文件記載的測試金鑰)。也就是說目前實際的部署設定不會踩到這條路徑,一位驗證者因此投了否決票——但另外兩位仍認為這是一段真實、沒有任何防護的程式碼行為,未來任何偏離目前 installer / env 檔慣例的部署方式(例如雲端 SaaS 安裝流程)都可能踩到,且踩到時完全沒有告警訊號,所以維持 MEDIUM 而非直接下架。TURNSTILE_ENABLED=true(預設值)且 TURNSTILE_SECRET_KEY 完全未設定——依目前 installer 與 env 檔慣例,這不是預設會發生的狀態。本輪掃描完整跑完含驗證面板,render_report.py 蓋出的 verification.status 為 verified。範圍限定在 mfa/ 與 turnstile/ 兩個目錄共 34 個受版控檔案,範圍外(jedi-iam 其他模組、其他 jedi-* 套件)本輪未檢查,不在此份清單的宣稱範圍內。