Files
Nexus/docs/changelog/2026-06-02-medium-bug-fix-6.md
2026-07-08 22:31:31 +08:00

3.9 KiB
Raw Permalink Blame History

2026-06-02 MEDIUM 级 Bug 修复(6 项)

日期

2026-06-02

变更摘要

修复系统扫描中发现的 6 个 MEDIUM 级 Bug,提升安全性、数据准确性和系统健壮性。


M1: 登录锁定按 username+ip 计数,可换 IP 绕过

文件: server/infrastructure/database/admin_repo.py

问题: count_recent_failures 的 WHERE 条件同时过滤 username AND ip_address,攻击者只需更换 IP 即可无限尝试同一账号密码。

修复: 移除 ip_address 维度,改为仅按 username 计数失败次数。换 IP 不再能绕过锁定。


M2: 重复 PushRetryJob 创建(同一服务器多条 pending)

文件: server/application/services/sync_engine_v2.py

问题: 每次 push 失败都无条件创建新的 PushRetryJob,若多次触发同一路径的推送且反复失败,会堆积大量重复 pending 任务。

修复: 创建前先查询是否已存在相同 (server_id, source_path, target_path, status=pending) 的记录,存在则跳过创建。


M3: schedule_runner 先标记已执行再同步,失败时调度记录丢失

文件: server/background/schedule_runner.py

问题: 执行前先将 last_run_at = nowcommit(),若同步任务抛出异常,last_run_at 已更新,该次调度不会重新触发,相当于"静默跳过"了一次调度。

修复: 保存 prev_last_run_at,执行失败时将 last_run_at 回滚,允许下一个周期重新触发。once 模式不回滚(已标记 enabled=False,防止重复执行)。


M4: servers.py is_online 过滤后 total 仍用 DB 总数,分页不准

文件: server/api/servers.py

问题: 当传入 is_online 过滤参数时,Redis 覆盖后的内存过滤结果与 DB 总数不一致,totalpages 字段仍反映 DB 全量数量,前端分页逻辑错乱。

修复: 内存过滤后重新计算 total = len(result)pages 同步更新。


M5: asyncssh_pool exec_ssh_command 超时后未踢出坏连接

文件: server/infrastructure/ssh/asyncssh_pool.py

问题: asyncio.TimeoutErrorfinally 中仍会 release() 超时的连接,将其放回连接池,下一次 acquire() 可能复用这个状态未知的坏连接,导致命令执行结果不可靠。

修复: 超时和连接错误时改为 close_connection() 而非 release(),将坏连接从池中彻底移除;一般异常仍 release()(可能仍可用)。


M6: token_version += 1 在 NULL 时 TypeError

文件: server/api/auth.py, server/application/services/auth_service.py3 处)

问题: DB 中 token_versionNULL(直接 INSERT 未设默认值时)时,admin.token_version += 1TypeError: unsupported operand type(s) for +=: 'NoneType' and 'int',导致修改密码 / 禁用 TOTP / 强制登出接口 500。

修复: 所有 token_version += 1 统一改为 (token_version or 0) + 1,共修复 3 处。


涉及文件

文件 修复项
server/infrastructure/database/admin_repo.py M1(锁定维度)
server/application/services/sync_engine_v2.py M2(重复 RetryJob
server/background/schedule_runner.py M3(调度回滚)
server/api/servers.py M4(分页 total
server/infrastructure/ssh/asyncssh_pool.py M5(超时踢连接)
server/api/auth.py M6token_version None
server/application/services/auth_service.py M6token_version None2 处)

需要迁移/重启

  • 后端重启生效(supervisorctl restart nexus
  • 无数据库 schema 变更

验证方式

  1. python -c "import server.main" — 导入无报错 ✓
  2. 同一 IP 登录失败 5 次后,换 IP 仍被锁定
  3. 查看 push_retry_jobs 表,相同路径不再出现多条 pending
  4. 调度执行失败后 last_run_at 应保持原值
  5. 服务器列表按在线状态过滤,分页 total 与实际条目数一致