feat: WebUI 迁移 FastAPI 并对齐 AstrBot v4.26–v4.27.5(3.7.0) - #247
Conversation
…Bot v4.26-v4.27.5 (3.7.0) - Add webui/compat.py: run existing Quart-style blueprint handlers on a real FastAPI app served by uvicorn (context proxies for request/session, jsonify, redirect/url_for/render_template/send_file, tuple-return normalization, Quart-semantic test client); blueprint files only swap their imports. - Switch server thread from Hypercorn to uvicorn; session handling moves to starlette SessionMiddleware (itsdangerous cookie, HttpOnly + Lax + 7d). - requirements.txt: quart/quart-cors -> fastapi/uvicorn/itsdangerous; update WebUI basic dependency tier accordingly. - AstrBot v4.26.0-v4.27.5 compatibility sweep: migrate 3 deprecated Context.get_using_provider() calls via utils/framework_compat.py (async-first with sync fallback); verify register_web_api bridge, PersonaManager/StarTools APIs, plugin page routes all compatible. - Security hardening: PBKDF2-HMAC-SHA256 password hashing with transparent MD5/plaintext upgrade on login (constant-time compare); drop Access-Control-Allow-Credentials from CORS; add nosniff / SAMEORIGIN / Referrer-Policy / Permissions-Policy response headers; add /api/password_status endpoint + one-time passwordless-mode reminder banner. - page_api: read request body/query via astrbot.api.web request proxy (official FastAPI bridge contract) instead of lazy quart import. - Tests: 759 passed; dashboard bundle rebuilt for 3.7.0.
Reviewer's Guide本 PR 将独立 WebUI 的运行时从 Quart/Hypercorn 重构为 FastAPI/uvicorn,以 762 行兼容层承载既有 Quart 风格处理器并保持路由和测试语义,同时升级密码存储、补充安全响应头与免密提醒,修复 AstrBot v4.27 provider 弃用兼容性并统一发布版本至 3.7.0;验证覆盖 759 个 Python 测试、前端类型检查及 39 个前端测试。 Sequence diagram for transparent WebUI password migrationsequenceDiagram
actor User
User->>WebUI: Login request
WebUI->>AuthService: verify_password_with_migration
AuthService->>PasswordHasher: verify_password
PasswordHasher-->>AuthService: Valid MD5 or PBKDF2 result
alt Legacy plaintext or MD5 password
AuthService->>PasswordHasher: hash_password
PasswordHasher-->>AuthService: PBKDF2 hash and salt
AuthService->>AuthService: Persist upgraded password config
end
AuthService-->>WebUI: Authentication result
WebUI-->>User: Session cookie or login failure
Sequence diagram for the password status remindersequenceDiagram
actor User
User->>Dashboard: Open WebUI
Dashboard->>Dashboard: Check localStorage dismissal
alt Reminder not dismissed
Dashboard->>WebUI: GET /api/password_status
WebUI->>AuthService: is_password_enabled
AuthService-->>WebUI: password_enabled status
WebUI-->>Dashboard: Password status
alt Password disabled
Dashboard-->>User: Show password reminder banner
User->>Dashboard: Click dismissal
Dashboard->>Dashboard: Save dismissal in localStorage
end
end
File-Level Changes
Possibly linked issues
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
|
|
||
|
|
||
| def jsonify(data: Any, **kwargs) -> WebUIJSONResponse: | ||
| return WebUIJSONResponse(data, **kwargs) |
There was a problem hiding this comment.
Hey - I've found 2 issues
Prompt for AI Agents
Please address the comments from this code review:
## Individual Comments
### Comment 1
<location path="utils/framework_compat.py" line_range="25" />
<code_context>
+ async_getter = getattr(context, "get_using_provider_async", None)
+ if inspect.iscoroutinefunction(async_getter):
+ return await async_getter()
+ return context.get_using_provider()
</code_context>
<issue_to_address>
**issue (bug_risk):** The compatibility helper calls `context.get_using_provider()` unconditionally when the async accessor is absent, despite documenting that unavailable providers return `None`. A compatible context lacking both accessors therefore raises `AttributeError` instead of returning `None` or handling the missing API.
**Triggers:** When a context object exposes neither `get_using_provider_async` nor `get_using_provider`.
**Suggested fix:** Check for the synchronous getter before calling it and return `None` when neither accessor exists.
```suggestion
sync_getter = getattr(context, "get_using_provider", None)
if sync_getter is None:
return None
return sync_getter()
```
</issue_to_address>
### Comment 2
<location path="utils/security_utils.py" line_range="366" />
<code_context>
# 直接比较明文
if password == stored_password:
- # 验证成功后迁移到新格式
+ # 验证成功后迁移到哈希格式
new_config = migrate_password_to_hashed(password_config)
</code_context>
<issue_to_address>
**🚨 issue (security):** Legacy plaintext passwords are still compared with ordinary `==`, while the new implementation only uses `secrets.compare_digest` for hashed passwords. Login timing therefore remains dependent on the matching prefix for plaintext configurations, contradicting the claimed constant-time password comparison during migration.
**Triggers:** When a user logs in against a legacy plaintext password configuration that has not yet been migrated.
**Suggested fix:** Compare encoded plaintext values with `secrets.compare_digest` before migrating the configuration.
</issue_to_address>Sourcery assessment
Needs a human reviewer. 2 findings to address first, and this changes password hashing and legacy-login migration, session-cookie handling, CORS, and the production ASGI server, while routing all existing handlers through a large compatibility layer. A defect could expose or reject WebUI access, leak authenticated data through incorrect cookie/CORS behavior, or cause an outage; reverting would not undo sessions or data already exposed, although the affected scope is bounded and can be repaired.
Blocking findings: utils/framework_compat.py:25, utils/security_utils.py:366
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
- Constant-time compare for legacy plaintext password verification - Guard both provider accessors in framework_compat (return None if absent) - Suppress CodeQL weak-hashing alert on legacy MD5 verification path with justification; break exception chain in compat.get_json
… connect - Remove hash_password_md5 and the MD5 upgrade branch: legacy MD5 password configs are now rejected with a reset hint instead of being accepted (no real-world MD5 config instances exist; PBKDF2 is the only format). - Probe port availability with connect instead of binding the wildcard address (CodeQL py/bind-socket-all-network-interfaces). - Raise the JSON parse error outside the except block so no exception chain can leak to clients (CodeQL py/stack-trace-exposure).
…esponses Replace str(e)/f-string exception details in jsonify/error_response payloads with fixed user-facing messages across blueprints; internal details stay in server logs via logger.error(..., exc_info=True). ValueError messages from the persona backup service are kept (they are user-facing validation copy, not stack traces) and annotated accordingly.
|
@sourcery-ai review |
|
Sorry @EterUltimate, you've used your own review budget of 250,000 diff characters for the last 7 days. You can request another review in 1 day and 11 hours by commenting |
概述
基于 AstrBot v4.26.0 → v4.27.5(2026-06-24 ~ 2026-09-01 共 14 个稳定版、269 个 commit)的逐项排查结果,完成三部分工作:兼容性核查与修复、独立 WebUI 从 Quart 重构为 FastAPI(与 AstrBot core 技术栈对齐)、安全审查加强。版本号提升至 3.7.0。
一、兼容性排查结论(v4.26.0 → v4.27.5)
逐项核对插件可见的变更面,确认以下全部兼容、无需改动:
register_web_api签名不变,dashboard 侧_match_registered_web_api桥接 + Quart 兼容适配层完整保留;插件页/plugins/{author}/{name}/pages/{page}路由扫描契约不变。PersonaManager.update_persona/get_default_persona_v3/StarTools.get_data_dir/astrbot.api.web等签名核对无破坏;to_openai_to_calls_model旧别名保留。astrbot.api.logger插件级路由:零改动受益(热运行已验证生效)。已修复的 1 处不兼容:3 处已废弃的
Context.get_using_provider()同步调用(v4.27.0 起运行时 DeprecationWarning)。新增utils/framework_compat.py兼容层,v4.27+ 优先get_using_provider_async(),旧版自动回退同步接口。另:
core/page_api.py的_body()/_query()由惰性from quart import request改为官方契约astrbot.api.web.request代理(.json(default)/.query)。二、WebUI 重构:Quart + Hypercorn → FastAPI + uvicorn
webui/compat.py兼容层:真实 FastAPI 应用(uvicorn 承载)之上运行既有 Quart 风格处理器——模块级request/sessionContextVar 代理、jsonify/redirect/url_for/render_template/send_file、(body, status)元组返回归一化、<int:x>路由转换;19 个蓝图文件仅更换导入行,约 135 条路由行为不变。SessionMiddleware(itsdangerous 签名 cookie,HttpOnly + SameSite=Lax + 7 天有效期不变);旧 Quart 会话 cookie 自然失效,需重新登录一次。uvicorn.Server(非主线程安全);端口清理与启动校验逻辑保留。requirements.txt:quart/quart-cors→fastapi>=0.124.0/uvicorn>=0.30.0/itsdangerous>=2.2.0(core 自带,声明用于最小化安装);WebUI「依赖安装」基础清单同步。session_transaction语义);test_webui_manager_imports_without_manual_web_dependencies更新为新契约:禁用 quart/hypercorn 导入时服务器创建必须成功(防回退守卫保留)。三、安全审查加强(VulnClaw MCP + 代码审查)
secrets.compare_digest。/api/password_status状态端点 + 前端一次性「建议启用 WebUI 密码」提醒横幅(可关闭,localStorage 记忆)。Access-Control-Allow-Credentials: true(原 Quart 回退路径会对任意 Origin 反射凭据授权)。X-Content-Type-Options: nosniff、X-Frame-Options: SAMEORIGIN、Referrer-Policy: no-referrer、Permissions-Policy。secrets.token_urlsafe(32)+ 过期 + 撤销;无 KV、无 LLM Tool、无 shell 注入参数来源。四、验证
pytest tests/全量 759 passed(基线 756 + 新增密码迁移/免密提醒相关用例);web_srctypecheck + 39 前端测试全绿;dashboard 产物已按 3.7.0 本地重建并提交。版本同步
metadata.yaml、
__init__.py、web_src/package.json、README.md、README_EN.md、docs/README.md 六处 + CHANGELOG 条目。Summary by Sourcery
将独立 WebUI 迁移至 FastAPI/uvicorn,完成 AstrBot v4.26–v4.27.5 兼容性对齐,并强化密码认证与 WebUI 安全防护。
New Features:
Bug Fixes:
Enhancements:
Build:
Documentation:
Tests: