Skip to content

fix(terminal): 恢复平台只读访客在 Web 终端只读页的 SSO 登录引导 - #985

Merged
deepcoldy merged 1 commit into
masterfrom
fix/terminal-platform-guest-login-banner
Aug 24, 2026
Merged

fix(terminal): 恢复平台只读访客在 Web 终端只读页的 SSO 登录引导#985
deepcoldy merged 1 commit into
masterfrom
fix/terminal-platform-guest-login-banner

Conversation

@deepcoldy

Copy link
Copy Markdown
Owner

问题

冷打开飞书卡片「打开 Web 终端」链接(浏览器无 t- 子域 SSO 会话)时,页面是只读的,且没有任何登录入口——「owner 登录后可操作 →」引导横幅不显示,用户无法从页面走 SSO 变可写,观感即“打开的界面没法直接操作”。

根因

#933 的 P1-6 让 dashboard 前端代理剥掉平台注入的 Cookie / X-Botmux-Role(显式 query capability 走 stripBrowserCredentials;无 capability 路径换成内部签名 grant 时同样剥离)。worker 侧 platformReadonly 的判定依赖看到这对凭证,从此恒为 false → 只读页渲染的是无引导的「只读模式」横幅而非 SSO 登录引导。#960 修复的三处 #933 回归(t- Origin、owner+viewToken 可写、banner 死 div)不含这条显示层;owner 路径正常可写把它盖住,直到访客以冷态(guest)落到该页才暴露。

修法

  • dashboard/terminal-front-proxy.tsprepare() 在剥凭证之后,对平台只读身份(terminalCapability === 'readonly',只可能由平台注入的 teammate/guest 产生;legacy/H5 为 controlled、平台 owner 为 owner)补发仅展示用提示头 x-botmux-platform-readonly: 1。设置于 terminalForwardHeaders 丢弃全部客户端 x-botmux-* 之后,浏览器无法伪造夹带
  • core/terminal-write-auth.ts:新增该头常量与语义注释
  • worker.ts:终端页 GET 渲染时消费该提示头(platformReadonly || platformReadonlyHint),恢复登录引导横幅;hasRead/hasWrite 判定在其之前完成,该头不参与任何授权判定——直连伪造它最多让一个本就只读的页面多显示一条登录链接

P1-6 边界原样保留:凭证剥离不变、写授权仍只走签名 grant / 显式 token。

影响面

测试验证

  • pnpm build 通过
  • terminal-front-proxy.test.ts(20 用例)新增身份矩阵:仅 platform-teammate/guest 在「viewToken 链接」与「无 token」两条路径收到提示头,owner/legacy/H5 不收;客户端伪造的同名头被丢弃;cookie/role 照旧剥离
  • worker-terminal-read-auth.integration.test.ts(真实 worker 进程):带提示头的只读页渲染 platformReadonly=true、授权不变仍 hasToken=false;「伪造提示头 + 错误 token」仍整体 403
  • 周边回归:terminal-control / terminal-control-route / terminal-write-frame / terminal-view-capability / debug-terminal / web-terminal-touch-scroll(接缝字面量同步更新)/ terminal-proxy / agent-workbench-terminal-control / agent-workbench-api 全绿
  • live 实测(pnpm switch:here && pnpm daemon:restart 后经真实平台 t- 域):修复前只读页 platformReadonly=false(无登录入口),修复后 =true 且登录链接完整指向 /open/<machineId>?next=/s/<sessionId>;owner 回归 HTTP hasToken=true、WS 首帧 write:true 无影响;冷态无痕窗口点卡片链接可见登录引导、SSO 回跳后可直接输入

Made with Cursor

@deepcoldy

Copy link
Copy Markdown
Owner Author

Review 结论:✅ Approve(无阻塞 findings,可合码)

注:本 checkout 唯一可用的 GitHub 凭证是 PR 作者本人账号,GitHub 禁止自 approve,故以评论形式给出同等结论。

按交接要求重点核查了两条安全边界,均成立;另补跑了相邻共用路径的回归测试。

① 提示头只影响展示,无法参与任何读/写授权判定 —— 成立

  • resolveTerminalAccessForReq(worker 侧唯一授权入口,HTTP 页面与 WS upgrade 共用)完全不读该头hasRead/hasWrite/platformReadonly 在读取提示头之前已定死,!hasRead 的 403 也先于它返回。
  • 提示头的唯一消费点是 getTerminalHtml(hasWrite, platformReadonly || platformReadonlyHint, …);模板内 platformReadonly 仅决定 !hasToken 时渲染 login-banner(SSO 引导)还是 readonly-banner(纯只读提示)。WS 写权限由首帧 write-token 解码独立判定,与该头无关。
  • 直连 worker loopback 端口伪造该头:无任何凭据 → 403(页面都读不到);自带有效平台凭据 → worker 已自行推出 platformReadonly,伪造无增益。最坏结果只是一个本来就能读的只读页换了条横幅文案。

② P1-6 剥凭证语义原样保留 —— 成立

  • 提示头设在 terminalForwardHeaders 之后。两条剥凭证路径(显式 ?token=/?viewToken=stripBrowserCredentials;actor 换内部签名 grant)都会经 sensitiveBrowserHeader 丢弃全部客户端 x-botmux-*,浏览器无法夹带同名头。
  • 测试矩阵覆盖完整:平台 teammate/guest 得 '1',平台 owner / legacy / H5 均不得;客户端伪造的同名头在两条路径上均被整片丢弃(断言值恒为前门自设的 '1'undefined);cookie/role 照旧被剥。
  • 集成测试验证:伪造提示头 + 错误 ?token= 仍 403;提示头不会把 hasToken 翻成 true。

其他核查

  • terminalCapability === 'readonly' 确实只可能来自平台注入身份(request-identity.ts:平台非 owner → 'readonly';legacy/H5 → 'controlled'),前门补发条件精确,不会误伤 owner 可写路径。
  • 唯一的 legacy passthrough 路径(无 actor 且无 query capability)会原样转发客户端头,但该路径下能拿到 hasRead 的请求必然自带有效平台凭据、worker 自行推出 platformReadonly,伪造提示头无效果;无凭据则 403。无安全影响,仅作记录。

验证

  • pnpm build 通过
  • terminal-front-proxy / worker-terminal-read-auth.integration / web-terminal-touch-scroll:37 tests 全绿
  • 相邻共用路径 terminal-control / terminal-write-auth / dashboard-auth:168 tests 全绿

#933 的 P1-6 把平台注入的 Cookie/X-Botmux-Role 在 dashboard 前端代理剥掉
(显式 query capability 走 stripBrowserCredentials;无 capability 时换内部
签名 grant),worker 因此判不出「平台认证过的只读访客」,platformReadonly
恒为 false——只读终端页上的「owner 登录后可操作 →」SSO 引导从此不显示,
冷打开飞书卡片「打开 Web 终端」链接(无 t- 子域 SSO 会话)的人被困在一个
没有任何登录入口的只读页里。#960 修复的三处回归不含这条显示层。

修法:前端代理在剥凭证后,对平台只读身份(terminalCapability='readonly',
只可能由平台注入的 teammate/guest 产生)补发仅展示用的
x-botmux-platform-readonly 提示头;worker 渲染只读页时据此恢复登录引导
横幅。该头不参与任何读/写授权判定(hasRead/hasWrite 在其之前定死),且设
置于 terminalForwardHeaders 丢弃全部客户端 x-botmux-* 之后,浏览器无法伪
造夹带;P1-6 的凭证剥离与签名 grant 写授权路径原样保留。

影响面:dashboard 前端代理(平台浏览器终端全部流量经过)+ worker 终端页
渲染,均为展示层增量;owner 可写路径、H5/legacy 身份、本地直连路径不受
影响。

验证:pnpm build 通过;terminal-front-proxy(含新增身份矩阵用例:仅
teammate/guest 收到提示头、客户端伪造被丢弃)、worker-terminal-read-auth
集成(含「伪造提示头 + 错误 token 仍 403」)、terminal-control /
workbench-terminal / touch-scroll 等周边测试全绿;经真实平台 t- 域实测:
修复前只读页 platformReadonly=false(无登录入口),修复后=true 且登录链
接完整指向 /open/<machineId>?next=/s/<sessionId>;owner 回归 HTTP
hasToken=true、WS 首帧 write:true 无影响。

Co-authored-by: Cursor <cursoragent@cursor.com>
@deepcoldy
deepcoldy force-pushed the fix/terminal-platform-guest-login-banner branch from 54ab438 to fb2b4b4 Compare August 24, 2026 07:43
@deepcoldy
deepcoldy merged commit d69fc31 into master Aug 24, 2026
6 checks passed
@deepcoldy
deepcoldy deleted the fix/terminal-platform-guest-login-banner branch August 24, 2026 07:53
@github-actions

Copy link
Copy Markdown

🚀 Released in v3.17.0

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant