Skip to content

audit: prioritized security, reliability, and release findings #9275

Description

@zouyonghe

This was generated by AI during triage.

审计范围

审计基于 master 当前提交 d98a29600(v4.26.6),覆盖:

  • FastAPI Dashboard API、认证授权、插件/技能、备份恢复、更新、文件接口、沙箱生命周期
  • Vue Dashboard API、iframe/plugin page、上传、SSE、依赖供应链
  • GitHub Actions、发布流程、Python/Node 依赖可复现性、测试入口
  • 当前开放 issues 与 PRs,重点检查是否已有完整修复

本地验证:

  • uv run ruff check .:通过
  • uv run ruff format --check .:480 files already formatted
  • uv run pytest tests/test_fastapi_v1_dashboard.py tests/test_backup.py tests/test_shipyard_neo_booter.py -q:146 passed
  • cd dashboard && pnpm typecheck:通过
  • cd dashboard && pnpm audit --prod --json:4 low / 35 moderate / 18 high

优先级总览

优先级 数量 建议时限
P0 3 个现有公开安全问题 立即阻断或限制暴露面
P1 7 个高风险问题 下一补丁版本前完成
P2 4 个稳定性/DoS 问题 近期迭代完成
P3 2 个长期风险 排期修复

P0:已有公开安全问题

P0-1 多处 SSRF 仍需统一出口策略

  • 现有记录:Server-Side Request Forgery (SSRF) via Multiple Endpoints in AstrBot #7171
  • 当前补充:v1 插件市场自定义 registry 路径仍接受可控 URL,需纳入同一修复范围。
  • 现成修复:未发现覆盖所有出站请求的完整 PR。
  • 修复指导:建立统一 URL/目标地址校验层;仅允许必要协议;拒绝 loopback/private/link-local/reserved 地址;每次重定向后重新验证;设置连接/读取/总超时和响应体上限;高风险部署增加 egress 限制。

P0-2 插件安装链路等价于进程内代码执行

  • 现有记录:Arbitrary Code Execution via Plugin Upload in AstrBot #7168
  • 当前补充:plugin API key scope 同时覆盖查看、配置、安装和加载插件,scope 泄漏后可直接进入代码安装路径。
  • 现成修复:未发现完整权限拆分 PR。
  • 修复指导:拆分只读、配置、启停、代码安装权限;安装要求 system/独立 code_install scope 和短期再认证;验证市场签名;逐步把第三方插件移入低权限隔离进程。

P0-3 MCP 配置可启动任意本地命令

P1:高风险问题

P1-1 插件页面资源令牌与 Dashboard 会话 JWT 类型混淆

  • 位置:astrbot/dashboard/api/auth.py:145-173astrbot/dashboard/services/plugin_page_service.py:894-919
  • 状态:已代码级确认;暂无 issue/PR。
  • 影响:用途受限、短时效的 plugin page asset token 可被通用 API JWT 校验路径接受为全权限 Dashboard 身份。
  • 公开说明:完整复现细节不在公开 issue 展开,建议维护者转入 GitHub Security Advisory 处理。
  • 修复指导:Dashboard session JWT 必须验证固定 token_typeaudiss;资源 token 使用独立签名密钥和独立 decode 函数;require_scope() 明确拒绝非 session token;增加“asset token 请求 system endpoint 必须 401/403”的回归测试。

P1-2 备份知识库元数据可使写入路径逃逸可信根目录

  • 位置:astrbot/core/backup/importer.py:744-789
  • 状态:已代码级确认;暂无完整修复。
  • 影响:不可信备份中的知识库标识被直接拼入目录路径,现有校验只验证目标位于这个已被污染的目录内,不能保证仍在知识库根目录下。
  • 相关 PR:fix: reject invalid backup uploads #9130 仅拒绝缺失/无效 manifest.json 的 ZIP,不能修复此路径问题。
  • 修复指导:严格限制知识库 ID 字符集和长度;拒绝绝对路径、分隔符、...;最终目录必须相对可信 kb_root_dir;所有安全预检必须在清库和写文件前完成;增加临时目录外写入回归测试。

P1-3 Dashboard/文件下载在证书错误后默认关闭 TLS 验证

  • 位置:astrbot/core/utils/io.py:276-344astrbot/core/utils/io.py:508-600
  • 状态:暂无 issue/PR。
  • 影响:更新资源在证书校验失败时自动以 CERT_NONE 重试,网络路径攻击者可替换下载内容;ZIP 格式检查不提供来源完整性保证。
  • 修复指导:更新、插件、技能等代码/资源下载必须 fail closed;默认删除 insecure fallback;官方发布包增加签名验证;如为兼容私有部署保留开关,必须默认关闭且不能用于官方更新链路。

P1-4 Dashboard 生产依赖存在 18 个 high advisories,CI 不阻断

  • 位置:dashboard/package.jsondashboard/pnpm-lock.yaml.github/workflows/dashboard_ci.yml
  • 状态:审计命令确认 18 high。
  • 涉及:Axios、Lodash/Lodash-es、Vite、DOMPurify、Mermaid、Linkify-it 等。
  • 相关 PR:fix: harden runtime and supply chain security #6116 描述声称升级依赖,但当前 PR diff 不包含 dashboard/package.jsondashboard/pnpm-lock.yaml,不能视为依赖修复。
  • 修复指导:单独提交依赖修复 PR;升级到 audit 提示的 patched versions;加入 pnpm audit --prod --audit-level high 门禁;Dependabot 增加 /dashboard npm ecosystem;无法立即升级的项目建立有到期日的精确 allowlist。

P1-5 手动 release 可把一个 ref 的源码发布到另一个 tag

  • 位置:.github/workflows/release.yml:7-15,30-51,130-151,210-231
  • 状态:暂无 issue/PR。
  • 影响:workflow_dispatch 独立接收 reftag,未验证 checkout HEAD、tag commit、pyproject.toml version 三者一致,可能产生标签、源码、PyPI wheel、Release asset 不一致。
  • 修复指导:手动发布只接受 tag 并 checkout 该 tag;发布前强制验证 HEAD == tag committag == v${project.version};禁止覆盖既有 release asset,除非显式受控重发流程。

P1-6 tag 发布未依赖测试、wheel smoke、依赖审计

  • 位置:.github/workflows/release.yml.github/workflows/docker-image.yml
  • 状态:暂无完整修复。
  • 相关 PR:feat(ci): introduce unified CI Required Gate workflow #6835 改善 PR 主分支门禁,但不覆盖 tag release,且未加入依赖审计。
  • 修复指导:建立可复用 release verification workflow;发布 job 必须依赖完整 pytest、Dashboard frozen build、Python/Node audit、uv build、安装 wheel 后启动 smoke;验证通过后才允许 PyPI、GitHub Release、R2 和 Docker push。

P1-7 Python/Node 发布依赖不可复现

  • 位置:.gitignore:6scripts/run_pytests_ci.sh:18-20.github/workflows/docker-image.yml:47-63,161-171scripts/hatch_build.py:47-62
  • 状态:Python uv.lock 未跟踪;Docker/Hatch 使用 npm install,绕过唯一提交的 pnpm-lock.yaml
  • 相关 PR:fix #5089: remove uv.lock from gitignore #5090 方向正确,但当前 lock 已落后主线,且 PR 未把 CI/Docker 改为 frozen 模式,不能原样合并。
  • 修复指导:在最新 master 重新生成并提交 uv.lock;CI 用 uv sync --frozen --group dev;Docker/export 禁止构建时重新 lock;所有 Dashboard 构建统一固定 pnpm 并使用 pnpm install --frozen-lockfilepackage.json 声明 packageManager

P2:稳定性与资源耗尽

P2-1 备份上传/导入缺少统一资源预算

  • 位置:astrbot/dashboard/services/backup_service.pyastrbot/core/backup/importer.py
  • 风险:上传大小、分片总量、ZIP 成员数、总展开大小、压缩比和单成员大小均缺少完整限制;多处整块 read() 可造成内存或磁盘耗尽。
  • 相关 PR:fix: reject invalid backup uploads #9130 可合并,用于修复“无效 ZIP 上传后被接受和残留”;但必须另补 ZIP bomb、大小预算和路径安全。
  • 修复指导:反向代理与应用双层限制;分片校验声明大小、实际大小和 SHA-256;ZIP 写入前预检成员数、总展开大小、单文件大小、压缩比;全程固定块流式复制。

P2-2 文件 DELETE 接口返回成功但不删除,上传无配额

  • 位置:astrbot/dashboard/api/files.py:70-111astrbot/dashboard/services/chat_service.py:589-637
  • 风险:DELETE /api/v1/files/{attachment_id} 仅回显 ID;文件和 DB 记录保留。file scope 可持续上传且无文件/主体/全局配额。
  • 修复指导:DELETE 调用实际 attachment 删除逻辑并校验所有权/引用;流式上传累计字节并在超限时清理临时文件;增加每 key 和全局配额;测试上传、删除、DB 记录、磁盘文件一致性。

P2-3 Shipyard Neo 启动异常会泄漏 client/远端 sandbox

  • 位置:astrbot/core/computer/booters/shipyard_neo.py:396-463
  • 风险:client 已进入、sandbox 已创建后,profile/readiness 网络异常或任务取消没有统一清理,可能留下连接和计费资源。
  • 修复指导:初始化用 try/except BaseException 包围;异常时先删除 sandbox,再关闭 client,清空组件字段,最后原样抛出;取消清理考虑 asyncio.shield();增加 refresh 异常和 cancellation 测试。

P2-4 Docker 手动 release tag 获取参数拼写错误

  • 位置:.github/workflows/docker-image.yml:22-27,127-142
  • 风险:fetch-tag: true 不是 actions/checkout 支持参数;配合 fetch-depth: 1,手动流程中的 git describe --tags 不可靠。
  • 修复指导:改为 fetch-tags: truefetch-depth: 0;随后验证 checkout HEAD 与解析 tag commit 一致。

P3:长期风险

P3-1 API key/JWT 支持 URL query 认证

  • 位置:astrbot/dashboard/api/auth.py:44-55、Open API/Live Chat WebSocket 认证路径
  • 风险:长期凭据进入代理日志、APM、浏览器历史和复制 URL。
  • 修复指导:HTTP 仅接受 Authorization/X-API-Key;WebSocket 通过认证 HTTP endpoint 换取一次性短期 ticket;统一日志脱敏。

P3-2 QQ Official 会话缓存无 TTL/容量限制

  • 位置:astrbot/core/platform/sources/qqofficial/qqofficial_platform_adapter.py
  • 风险:历史唯一会话持续常驻;过期消息 ID 可能继续用于回复。
  • 修复指导:缓存增加 TTL 和容量上限;消息 ID TTL 对齐平台回复窗口;adapter shutdown 时清空;增加大量唯一会话和过期测试。

现有 PR Review 结论

PR 结论 意见
#9130 建议合并后继续补强 无效 ZIP 校验和失败清理正确,21 checks passed;不解决路径穿越、ZIP bomb、上传预算。
#5090 需要更新后再合并 提交 lock 方向正确,6 checks passed;应 rebase 后重生成 lock,并同步改 frozen CI/Docker。
#6116 Request changes Draft 且 Sourcery check failed;PR 过大、混合多个安全主题;正文与当前 diff 的依赖修复声明不一致。建议拆分,依赖升级独立 PR。
#6835 可作为 PR 门禁基础,不是 release 修复 目前仅 Sourcery check;应补 frozen dependency、依赖审计,并让 release workflow 显式复用验证结果。

建议执行顺序

  1. 立即私下修复 P1-1、P1-2,并轮换可能泄漏的 token/key。
  2. 关闭官方更新链路 TLS 降级;升级 Dashboard 高危依赖。
  3. 合并并扩展 fix: reject invalid backup uploads #9130;更新 fix #5089: remove uv.lock from gitignore #5090;拆分 fix: harden runtime and supply chain security #6116
  4. 修复 release/tag 一致性并加入发布门禁。
  5. 完成上传/ZIP 资源预算、文件删除语义、Shipyard 清理。
  6. 处理 query 凭据和长期缓存风险。

完成标准

  • 每项修复包含可失败的回归测试,不只做输入检查。
  • P0/P1 安全项使用 Security Advisory 或私有分支协调,修复发布前不公开完整 PoC。
  • 发布门禁覆盖源码、Dashboard、wheel、Docker 使用的同一依赖图。
  • 关闭问题前附验证命令、测试输出和实际发布版本。

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:coreThe bug / feature is about astrbot's core, backendarea:webuiThe bug / feature is about webui(dashboard) of astrbot.bugSomething isn't workingdependenciesPull requests that update a dependency filepriority: p0

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions