feat(desktop): 支持 Ghost 设置页与面板受控打开 HTTPS 外链 - #2969
Conversation
There was a problem hiding this comment.
💡 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".
|
| 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
Reviews (2): Last reviewed commit: "feat(desktop): confirm ghost external li..." | Re-trigger Greptile
|
⏸️ 本 PR 触发了维护者确认门(product + security + pluginBase + arch),已创建讨论 issue 等待维护者确认。 触发原因:
维护者确认方式:在本 PR 上直接 Approve(需要修改就 Request Changes,改完再 Approve)。 讨论 issue:#2970 |
Signed-off-by: xutianqi <xutianqi@xd.com>
19f7f75 to
c13e142
Compare
|
@codex review |
There was a problem hiding this comment.
💡 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".
|
@Evol1213 👋 这个 PR 还有 1 条 review conversation 没 resolve(apps/desktop/src/main/cindy-brain/previewGate.ts),auto-review 因此暂时跳过、没法继续审查 / 合并。 如果你已经按评论改完或回应了,请到对应 thread 上点 Resolve conversation;全部 resolve 后,下一轮 auto-review 会自动重新审查这个 PR。 |
MagicLizi
left a comment
There was a problem hiding this comment.
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:
-
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. -
Any installed plugin (malicious, compromised, or low-quality) — even one with zero
networkslot 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 acknowledgesisFocused()is not proof of a real user click. -
workers.xd.teamis 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.
不改。这条自动 review 拿错了尺子,也认错了主机。
- 授信域直开是已确认的 VS Code 外链模型,不是漏掉的 slot。设置页 / 面板普通链接不走 card.externalLinks,也不要求已装插件重新批准。见 #2970。
- workers.xd.team 只信精确主机,也就是控制台 apex(例如 /workspace/published)。用户站点在子域上,如 hello-world-test.workers.xd.team、*.pages.xd.team,对不上 hostname === 'workers.xd.team',不会直开。
- 因此不必改回 Manifest 声明,也不必把 workers.xd.team 降成每次确认。
|
Dismissed #2969 (review) ,作者不用按这条改。 自动 review 把授信域直开写成「绕过插件安全模型」,这和 #2970 已确认的 VS Code 模型冲突:官方域直开、其他 HTTPS 每次确认。这不是新槽位,也不该逼已装插件重批。
|
MagicLizi
left a comment
There was a problem hiding this comment.
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 拒绝用例,确认为假阳性。
|
插件设置页到系统浏览器的外链通道终于有了正式的分级导航策略——比之前的 manifest 精确匹配灵活得多,同时把「用户看到什么 URL」和「实际打开什么 URL」绑在一次 normalize 上,杜绝了 bait-and-switch。 |
这次改了什么
摘要
settingsHtml/panel离开自身cindy-ghost://页面,插件作者也没有可用的宿主能力从设置页打开产品控制台、帮助中心等外部网站;因此 XD Sites 设置页即使渲染普通<a href="https://workers.xd.team/workspace/published">,用户也无法前往控制台。will-navigate链路中的普通 HTTPS 导航,并交给系统默认浏览器:Manifest 现有声明 URL 逐字命中时直开;xd.com/*.xd.com、xd.cn/*.xd.cn、精确workers.xd.team直开;其他合法 HTTPS 先显示系统二次确认。变更类型
feat新功能fix缺陷修复refactor/perf重构或性能优化docs/test/chore文档、测试或工程维护范围
target="_blank"/window.open()仍然拒绝。UI 变化
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 锁步。怎么验证的
自动验证
补充:
index.ts全文件 ESLint 仍命中官方 main 已存在的 12 个未改行错误;本 PR 改动行没有新增错误,其余 10 个 TS 文件已完整通过定向 ESLint。手工验证
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://example.com/。example.com;重新启用后 settings webview 正常恢复。未执行的验证
风险
风险分类
影响与回滚
null明确表示插件当前不可用,与“有效但无声明”的空数组区分;不可用状态在授信直开和确认分支之前拒绝,用户确认后、openExternal前还会重新检查 guest → host → owner 归属与插件可用性。guestContents.isFocused()是 activation 前置条件,并不能证明真实鼠标点击;Electronwill-navigate也会接收聚焦页面的脚本导航。因此声明 URL 与固定授信主机的直开分支,在聚焦状态下也可由插件脚本触发。若要引入真实 user-gesture 证明,需要新的 DOM / preload / IPC 架构,不在本 PR 范围;其他 HTTPS 仍必须经过绑定真实 owner 的系统确认框。shell.openExternal失败只记录 warn,不向插件沙箱返回可探测差异。提交前检查
git commit -s,见 DCO)docs/文件)