Skip to content

feat(desktop): 支持 Ghost 设置页与面板受控打开 HTTPS 外链 - #2969

Merged
MagicLizi merged 1 commit into
makecindy:mainfrom
Evol1213:cindy/zealous-tesla
Aug 19, 2026
Merged

feat(desktop): 支持 Ghost 设置页与面板受控打开 HTTPS 外链#2969
MagicLizi merged 1 commit into
makecindy:mainfrom
Evol1213:cindy/zealous-tesla

Conversation

@Evol1213

@Evol1213 Evol1213 commented Aug 18, 2026

Copy link
Copy Markdown
Contributor

这次改了什么

摘要

  • Ghost 插件沙箱目前会阻止 settingsHtml / panel 离开自身 cindy-ghost:// 页面,插件作者也没有可用的宿主能力从设置页打开产品控制台、帮助中心等外部网站;因此 XD Sites 设置页即使渲染普通 <a href="https://workers.xd.team/workspace/published">,用户也无法前往控制台。
  • 本 PR 让宿主接管 Ghost settings / panel 已有 will-navigate 链路中的普通 HTTPS 导航,并交给系统默认浏览器:Manifest 现有声明 URL 逐字命中时直开;xd.com / *.xd.comxd.cn / *.xd.cn、精确 workers.xd.team 直开;其他合法 HTTPS 先显示系统二次确认。
  • 非 HTTPS、带 username/password、畸形 URL 一律拒绝;插件停用、卸载或会话边界失效后,仍短暂存活的旧 guest 也会立即失去外链能力,确认框在途时撤权会在真正打开前再次 fail closed。
  • 交互思路参考 VS Code 的 Outgoing Link Protection / Trusted DomainsWebview links,但 Cindy 首版边界更窄:不提供复制链接、永久信任或用户可配置名单。

变更类型

  • feat 新功能
  • fix 缺陷修复
  • refactor / perf 重构或性能优化
  • docs / test / chore 文档、测试或工程维护
  • 其他:

范围

  • 关联 Issue / 需求:XD Sites 设置页需要用普通链接打开产品控制台;同时为所有 Ghost 插件 settings / panel 提供同一套受控 HTTPS 外链行为。
  • 本 PR 包含:Desktop Ghost 外链 Gate、Electron 原生确认框协调、撤权生命周期复核、五语主进程文案、settings / panel 统一接线、Forge 内置作者契约和回归测试。
  • 明确不包含:Manifest 新字段、plugin-protocol / Desktop GhostManifest 镜像、Plugin Server、权限投影、新 slot、IPC、preload、preview 链路扩张,以及 XD Sites 插件仓改动。
  • 用户可见变化:Ghost 设置页或面板中的普通 HTTPS 链接可在系统浏览器打开;非授信地址会先询问。target="_blank" / window.open() 仍然拒绝。
  • 是否存在 breaking change:无。既有声明 URL 的逐字直开语义保持不变;没有声明 URL 的有效插件仍可走授信域或二次确认。

UI 变化

  • 使用 Electron 系统原生 MessageBox,绑定发起 Ghost webview 的真实宿主窗口。按钮为“打开网站 / 取消”,主操作是默认按钮;cancelId 仍指向取消,因此 Esc / 关闭弹窗均取消,noLink: true
  • 引用的设计规范:docs/design-rules/DESIGN.md §4「Dialog & Modal」、§11「Voice & Content」与 §14.2;主操作遵循默认按钮规则,操作文案使用“动词 + 对象”,zh-CN / zh-TW / en / ja / ko 锁步。

怎么验证的

自动验证

pnpm --dir apps/desktop exec vitest run \
  src/main/cindy-brain/__tests__/previewGate.test.ts \
  src/main/cindy-brain/__tests__/ghostExternalLinkNavigation.test.ts \
  src/main/cindy-brain/__tests__/ghostExternalLinkNavigation.i18n.test.ts
结果:PASS,3 个文件 / 93 项测试

pnpm test:unit:related
结果:PASS(Node 22.22.3;相对 upstream/main,apps/desktop related 16,48.0s)

pnpm --filter desktop run --if-present typecheck
结果:PASS

pnpm --dir apps/desktop exec eslint <本 PR 改动的 10 个可独立检查 TS 文件>
结果:PASS

pnpm check:i18n
结果:PASS,五语 7378 个 key 一致;仅存量非阻断告警

pnpm check:i18n-glossary
结果:PASS;仅存量 proposed 术语告警

pnpm check:dco
结果:PASS,1 个提交已签署

git diff --check upstream/main...HEAD
结果:PASS

补充:index.ts 全文件 ESLint 仍命中官方 main 已存在的 12 个未改行错误;本 PR 改动行没有新增错误,其余 10 个 TS 文件已完整通过定向 ESLint。

手工验证

  • 平台:macOS;当前 worktree;Node 22.22.3;中国大陆 remote 配置;Cindy-dev2-xd-plugin-dev 隔离沙箱、被动调度;XD Sites 1.0.3 测试安装(额外带一条 https://example.com/ 测试链接,未写入 XD Sites 仓)。
  • 点击 https://workers.xd.team/workspace/published:Ghost settings webview 保持原页,系统默认浏览器 Google Chrome 前台打开精确 URL。
  • 点击普通 HTTPS:按 Esc / 取消不新增目标标签;再次点击并按默认 Enter 后,Chrome 打开 https://example.com/
  • 确认框在途时通过现有宿主管理能力停用 XD Sites,再按默认 Enter:没有打开 example.com;重新启用后 settings webview 正常恢复。
  • 手工验证结束后已停止隔离测试实例;未触碰正式 Cindy profile。

未执行的验证

  • Windows / Linux 原生确认框与默认浏览器实机验证未执行。
  • 未附截图:测试机当前不允许命令行进程截取屏幕;Dialog 的精确 title / message / detail / buttons / defaultId / cancelId / noLink 配置由集成测试锁定。

风险

风险分类

  • 无已知风险
  • SQLite / migration
  • system prompt
  • 协议兼容
  • 权限 / 安全 / 用户数据
  • 存量插件兼容(批准状态 / 指纹 / manifest 校验 / 安装布局 / 包格式)
  • 原生层 / fingerprint / OTA
  • 跨平台差异
  • 其他:

影响与回滚

  • 影响范围:仅 Ghost 插件 guest 的主 frame 外链导航;不扩张 Cindy 其他页面的外链策略。
  • 授权生命周期:Gate 用 null 明确表示插件当前不可用,与“有效但无声明”的空数组区分;不可用状态在授信直开和确认分支之前拒绝,用户确认后、openExternal 前还会重新检查 guest → host → owner 归属与插件可用性。
  • 已知设计取舍:guestContents.isFocused() 是 activation 前置条件,并不能证明真实鼠标点击;Electron will-navigate 也会接收聚焦页面的脚本导航。因此声明 URL 与固定授信主机的直开分支,在聚焦状态下也可由插件脚本触发。若要引入真实 user-gesture 证明,需要新的 DOM / preload / IPC 架构,不在本 PR 范围;其他 HTTPS 仍必须经过绑定真实 owner 的系统确认框。
  • 确认后只使用弹窗展示的不可变规范 URL;打开、取消、关闭、owner / guest 销毁或异常都会释放单 Ghost 防重入状态。shell.openExternal 失败只记录 warn,不向插件沙箱返回可探测差异。
  • 存量插件影响:无。没有修改 Manifest、批准 receipt、指纹、安装布局或包格式;用户无需重装、重新批准或重新配置。旧客户端行为也不受协议变化影响,因为本 PR 没有协议变化。
  • 回滚 / 降级方式:回滚本提交即可恢复为“仅 Manifest 现有声明 URL 可直开,其他导航静默拒绝”;无数据迁移与持久状态需要清理。

提交前检查

  • 已 review 完整 diff,并由独立 reviewer 对最终 Review 修复做 P0 / P1 复核
  • 每个 commit 都带 DCO 签名(git commit -s,见 DCO
  • UI 改动已在「UI 变化」注明引用的设计规范章节
  • 未提交凭证、令牌或授权文件
  • 已补充必要作者指南(Forge 内置作者契约;未新增或提交 docs/ 文件)
  • 已确认测试结果或说明未执行原因

@Evol1213
Evol1213 requested a review from a team as a code owner August 18, 2026 12:55

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 19f7f759f6

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread apps/desktop/src/main/cindy-brain/previewGate.ts
Comment thread apps/desktop/src/main/cindy-brain/ghostExternalLinkNavigation.ts Outdated
Comment thread apps/desktop/src/renderer/i18n/locales/en/common.json Outdated
@greptile-apps

greptile-apps Bot commented Aug 18, 2026

Copy link
Copy Markdown

Greptile Summary

本 PR 扩展 Desktop Ghost 的主框架外链处理,在保留 Manifest 声明 URL 兼容行为的同时,为普通 HTTPS 地址加入原生确认流程。

  • settingsHtml 与 panel 统一接入 will-navigate 外链链路,并继续拒绝 popup
  • 新增 HTTPS 校验、固定授信主机、焦点与限速控制,以及每 Ghost 单确认防重入
  • 将确认框绑定到实际宿主窗口,并在确认后复核 Guest、Host、Owner 与 Ghost 可用状态
  • 补齐五种语言文案、Forge 作者契约及外链安全回归测试

Confidence Score: 5/5

当前可用证据中没有仍需阻止合并的故障,因此该 PR 看起来可以安全合并。

未发现仍然存在的阻断性故障。

Important Files Changed

Filename Overview
apps/desktop/src/main/cindy-brain/ghostExternalLinkNavigation.ts 新增原生确认框协调、宿主窗口归属复核和系统浏览器 best-effort 打开流程,未发现可发布的新问题。
apps/desktop/src/main/cindy-brain/previewGate.ts 将外链闸扩展为拒绝、直开和确认三类结果,并加入 URL 校验、授信主机及防重入状态。
apps/desktop/src/main/webview-security.ts 抽取 settingsHtml 与 panel 共用的 Ghost 导航接线,同时维持 popup 全拒策略。
apps/desktop/src/main/cindy-brain/index.ts 将外链闸、Electron Dialog、宿主窗口解析与 shell.openExternal 组装到主进程处理链。
apps/desktop/src/main/cindy-brain/tests/ghostExternalLinkNavigation.test.ts 覆盖直开、确认、取消、生命周期失效、防重入及 Electron API 异常等关键分支。

Sequence Diagram

sequenceDiagram
  participant Guest as Ghost Webview
  participant Handler as will-navigate Handler
  participant Gate as External Link Gate
  participant Dialog as Electron MessageBox
  participant Browser as System Browser

  Guest->>Handler: navigate(url)
  Handler-->>Guest: preventDefault()
  Handler->>Gate: request(ghostId, url, focus)
  alt 非法、失焦、限速或 Ghost 不可用
    Gate-->>Handler: reject
  else Manifest 声明或固定授信主机
    Gate-->>Handler: direct-open
    Handler->>Browser: openExternal(url)
  else 其他合法 HTTPS
    Gate-->>Handler: confirm(normalizedUrl)
    Handler->>Dialog: showMessageBox(owner, normalizedUrl)
    alt 用户确认且归属链仍有效
      Dialog-->>Handler: open
      Handler->>Browser: openExternal(normalizedUrl)
    else 取消、异常或归属失效
      Dialog-->>Handler: cancel
    end
    Handler->>Gate: releaseConfirmation(ghostId)
  end
Loading

Reviews (2): Last reviewed commit: "feat(desktop): confirm ghost external li..." | Re-trigger Greptile

@MagicLizi

Copy link
Copy Markdown
Contributor

⏸️ 本 PR 触发了维护者确认门(product + security + pluginBase + arch),已创建讨论 issue 等待维护者确认。

触发原因:

  • feat 类型(新功能)
  • 危险写法 8 处(navigation-escape / security-guard-removed)
  • 改到插件基座(影响全部已装插件)
  • 核心路径 1063 行改动

维护者确认方式:在本 PR 上直接 Approve(需要修改就 Request Changes,改完再 Approve)。

讨论 issue:#2970

@MagicLizi MagicLizi added awaiting-discussion 等待维护者讨论(review-pr) touches:core 改动碰到架构核心路径(review-pr 自动维护,仅展示) touches:plugin-base 改动碰到插件基座(review-pr 自动维护,仅展示) touches:product-ui 改动碰到产品 / UI 面(review-pr 自动维护,仅展示) touches:security 改动碰到安全边界(review-pr 自动维护,仅展示) labels Aug 18, 2026
Signed-off-by: xutianqi <xutianqi@xd.com>
@Evol1213
Evol1213 force-pushed the cindy/zealous-tesla branch from 19f7f75 to c13e142 Compare August 18, 2026 14:46
@Evol1213

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c13e142961

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread apps/desktop/src/main/cindy-brain/previewGate.ts
@Evol1213 Evol1213 changed the title feat(desktop): confirm ghost external links feat(desktop): 支持 Ghost 设置页与面板受控打开 HTTPS 外链 Aug 18, 2026
@MagicLizi

Copy link
Copy Markdown
Contributor

@Evol1213 👋 这个 PR 还有 1 条 review conversation 没 resolve(apps/desktop/src/main/cindy-brain/previewGate.ts),auto-review 因此暂时跳过、没法继续审查 / 合并。

如果你已经按评论改完或回应了,请到对应 thread 上点 Resolve conversation;全部 resolve 后,下一轮 auto-review 会自动重新审查这个 PR。

@fmfsaisai

Copy link
Copy Markdown
Collaborator

维护者确认

#2970 上的两个产品问题我确认如下,按 VS Code 外链模型推进:

  1. Ghost 设置页 / 面板的普通 HTTPS 外链,采用宿主运行时确认:授信域直开,其他合法 HTTPS 每次确认后再打开。不要求新增 Manifest externalLinks,也不要求已装插件重新批准。
  2. 接受当前固定授信域:xd.com / *.xd.comxd.cn / *.xd.cn、精确 workers.xd.team。首版不加「加入可信名单」。

详细说明写在 #2970 。合入前与 #2711 rebase 即可。

@MagicLizi MagicLizi added status:threads-open 还有未 resolve 的评审讨论(review-pr 自动维护,仅展示) and removed awaiting-discussion 等待维护者讨论(review-pr) status:threads-open 还有未 resolve 的评审讨论(review-pr 自动维护,仅展示) labels Aug 19, 2026

@MagicLizi MagicLizi left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Auto Review — P1×1

[P1] Global trusted-host allowlist bypasses plugin security model

Location: apps/desktop/src/main/cindy-brain/previewGate.ts:239-247,308-310

The new hardcoded trusted-host allowlist (xd.com/*.xd.com, xd.cn/*.xd.cn, workers.xd.team) grants every installed Ghost — not just the one whose manifest declares the URL — the ability to silently direct-open any HTTPS URL on those hosts, gated only by guestContents.isFocused().

Why this is a problem:

  1. Bypasses the manifest-declaration invariant (docs/dev-rules/plugin-security-and-authoring.md §3: "所有能力必须先在 manifest 声明 slot...再由 host 代码强制授权"). This new capability requires no manifest declaration, no slot, and no confirmation UI — unlike every other host capability.

  2. Any installed plugin (malicious, compromised, or low-quality) — even one with zero network slot permission — can force-navigate the user's system browser to these vendor domains purely via script while its page has focus. The PR's own risk section acknowledges isFocused() is not proof of a real user click.

  3. workers.xd.team is multi-tenant — the PR's own example URL (workers.xd.team/workspace/published) shows it hosts user-published content. Trusting the entire hostname (not specific reviewed paths) means trusting arbitrary third-party content for silent, unconfirmed opening. If any state-changing GET endpoint or open-redirect exists anywhere under these domains, this becomes a confused-deputy/forced-navigation primitive reachable by any plugin with zero user awareness.

Recommendation: Either scope the auto-open trust per-declaring-ghost (require manifest declaration even for vendor domains) or require the confirm step for workers.xd.team given it hosts user-generated content.

@fmfsaisai
fmfsaisai dismissed MagicLizi’s stale review August 19, 2026 05:28

不改。这条自动 review 拿错了尺子,也认错了主机。

  1. 授信域直开是已确认的 VS Code 外链模型,不是漏掉的 slot。设置页 / 面板普通链接不走 card.externalLinks,也不要求已装插件重新批准。见 #2970
  2. workers.xd.team 只信精确主机,也就是控制台 apex(例如 /workspace/published)。用户站点在子域上,如 hello-world-test.workers.xd.team、*.pages.xd.team,对不上 hostname === 'workers.xd.team',不会直开。
  3. 因此不必改回 Manifest 声明,也不必把 workers.xd.team 降成每次确认。
@fmfsaisai

Copy link
Copy Markdown
Collaborator

Dismissed #2969 (review) ,作者不用按这条改。

自动 review 把授信域直开写成「绕过插件安全模型」,这和 #2970 已确认的 VS Code 模型冲突:官方域直开、其他 HTTPS 每次确认。这不是新槽位,也不该逼已装插件重批。

workers.xd.team 也认错了。名单只信精确主机,对应控制台 apex;用户发布内容在子域(*.workers.xd.team / *.pages.xd.team),本来就不会直开。把它改成每次确认,XD Sites 点控制台还是会弹窗,这个 PR 的直接需求就没了。

@MagicLizi MagicLizi left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Heavy-tier 审查通过 — 0 P0/P1。安全面验证:HTTPS-only 强制执行(classify 阶段 block 所有非 https:// 前缀)、无导航逃逸路径(javascript:/data:/file: 均在 classify 层拦截)、可信域名列表用 hostname===xxx 或 .endsWith('.xxx') 防止子串匹配、确认对话框前后二次验证(TOCTOU 保护)、ghost 不可用时 fail-closed。security.softHits 7 条均为测试固件中的凭证 URL 拒绝用例,确认为假阳性。

@MagicLizi
MagicLizi merged commit 689cbbf into makecindy:main Aug 19, 2026
13 of 15 checks passed
@MagicLizi

Copy link
Copy Markdown
Contributor

插件设置页到系统浏览器的外链通道终于有了正式的分级导航策略——比之前的 manifest 精确匹配灵活得多,同时把「用户看到什么 URL」和「实际打开什么 URL」绑在一次 normalize 上,杜绝了 bait-and-switch。

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

Labels

touches:core 改动碰到架构核心路径(review-pr 自动维护,仅展示) touches:plugin-base 改动碰到插件基座(review-pr 自动维护,仅展示) touches:product-ui 改动碰到产品 / UI 面(review-pr 自动维护,仅展示) touches:security 改动碰到安全边界(review-pr 自动维护,仅展示)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants