Files
Nexus/docs/reports/2026-06-12-remote-browser-bug-patrol.md
T
Nexus Agent e833b923c8 feat(browser): 远程浏览器 Worker 桥接与顶栏固定悬浮窗
接入 Playwright Worker、会话 WebSocket 与全局浏览器面板(固定于 App Bar 下);
含验证码 stealth、设置项默认音与 URL 安全校验;附带 worker 部署与设计文档。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-12 06:49:20 +08:00

5.0 KiB
Raw Blame History

Bug 巡查 — 远程浏览器

日期2026-06-12
范围browser-worker/、桥接 API、前端 canvas 客户端、生产 66.154.115.131:8443
方法:代码走读 + 单测 + 生产探针复验
关联docs/audit/2026-06-12-remote-browser-worker.mddocs/reports/2026-06-12-remote-browser-patrol.md

发现与处理

# 严重度 问题 根因 状态
1 P0 生产无浏览器 UI,用户「连不上」 后端 BROWSER_ENABLED=1,但 web/app ws/browser bundle 已修index-BimRgA6s.js 已同步,容器 grep ws/browser ≥1
2 P1 Worker sessions:1 长期占槽 ready=false 不进 idle 清理 已修WORKER_RESERVE_TIMEOUT_SEC=120 + 重建 Workersessions:0
3 P1 attach 失败留下僵尸 reservation RuntimeErrordestroy_session 已修main.py 失败路径销毁
4 P1 页内点击/重定向 SSRF NAVIGATE 校验;RELOAD/GO_BACK/页内导航 无复验 未修(审计 F1
5 P1 Worker 8443 公网暴露 /health ufw 过宽 未修(审计 F3
6 P2 前进/后退与远程历史不同步 UI stack 仅本地维护;updateActiveFromRemote 更新 stackcanvas 内点击只改 url 未修
7 P2 connect() 失败后无显式 DELETE 同上 已修 — WS/未登录失败时 DELETE /browser/sessions/{id}
8 P2 attach_stream 并发竞态 ready=false 时锁在 L140 释放,双 WS 可能各建 BrowserContext 低概率(单桥接)
9 P2 delete_worker_session 失败静默 日志 warningWorker 会话残留 未修(审计 F4
10 P3 切标签每次新建 Worker 会话 watch(activeTabId)disconnect() → 新 POST 设计可接受,略耗资源
11 P3 persistState catch 空实现 离线时本地状态与 MySQL 可能短暂不一致 可接受

P0 — 前端未部署(用户可见)

生产容器: BROWSER_ENABLED=1
web/app/assets: grep ws/browser → 0 文件
Worker /health: sessions=1, max_sessions=2

管理员打开面板没有任何地球按钮 / canvas 客户端代码,与「连不上」完全一致。后端与 Worker 已就绪,阻塞在 SPA 同步

P1 — Worker 僵尸会话(占槽根因)

空闲清理漏掉 ready=false

            stale = [
                sid for sid, sess in self._sessions.items()
                if sess.ready and now - sess.last_activity > settings.WORKER_IDLE_TIMEOUT_SEC
            ]

POST /v1/sessionsreserve_sessionChromium 在 WS attach_stream 后才 ready=True
任一路径「POST 成功但 stream 未建立」都会永久占 session_count,直至 max_sessions 打满。

典型路径:

  • 前端未部署时 API 测试 / 桥接探测只 POST 不挂 WS
  • Nexus 桥接连 Worker WS 失败(WORKER_UNREACHABLE
  • Worker attach_streamRuntimeError 后:
    except RuntimeError:
        await websocket.close(code=4503, reason="Browser not ready")
        return

此处 destroy_sessionreservation 泄漏。

建议idle 对 not readycreated_at 超时(如 120s);RuntimeError/attach 失败路径 destroy_session;运维可 docker restart nexus-browser-worker 清槽。

P1 — SSRF / 暴露面(审计复查)

docs/audit/2026-06-12-remote-browser-worker.md 一致,代码层无新增修复

P2 — 前进/后退逻辑 Bug

GlobalBrowserPanel.onGoBack

function onGoBack() {
  goBack()                    // 改本地 stack + addressDraft
  if (connected.value) remoteGoBack()  // Playwright page.go_back()
}
  • 用户在 canvas 内点击链接 → PAGE_INFOupdateActiveFromRemote 只更新 tab.url不 push stack
  • canGoBack 仍看本地 stack → 按钮状态错误
  • 点后退时本地 stack 与 Chromium 历史可能指向不同 URL

建议:以远程为准时去掉本地 stack,或 PAGE_INFO 时同步 push stack;后退只发 GO_BACK 不回写本地 stack 直到 PAGE_INFO

本地验证

pytest tests/test_browser_url_safe.py tests/test_browser_ui_state.py → 7 passed
npm run type-check → PASS
curl https://api.synaglobal.vip/health → ok

建议修复顺序

  1. P0vite build + sync_webapp_to_container.sh(立刻恢复可用性)
  2. P1 — Worker 重启清槽 + 修 reservation 泄漏(idle 未 ready + RuntimeError 清理)
  3. P1 — ufw 收口 8443
  4. P1framenavigated SSRF
  5. P2 — 历史栈与 GO_BACK 统一

复巡命令

ssh nexus 'sudo docker exec nexus-prod-nexus-1 grep -rl ws/browser /app/web/app/assets/ | wc -l'
curl -s http://66.154.115.131:8443/health
pytest tests/test_browser_url_safe.py tests/test_browser_ui_state.py -q