Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion dashboard/src/views/PluginPagePage.vue
Original file line number Diff line number Diff line change
Expand Up @@ -496,7 +496,7 @@ watch(locale, () => {
:src="iframeSrc"
class="plugin-page-frame"
referrerpolicy="no-referrer"
sandbox="allow-scripts allow-forms allow-downloads"
sandbox="allow-scripts allow-forms allow-downloads allow-popups allow-popups-to-escape-sandbox"
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.

🚨 issue (security): Reconsider allow-popups-to-escape-sandbox because it significantly relaxes isolation and can lead to navigation of the top window.

This flag lets popup windows break out of the iframe sandbox and potentially navigate the opener/top window, which increases the impact of any compromised or malicious plugin. If plugins only need to open external links, consider using just allow-popups and routing external navigation through a controlled in-app handler instead of enabling allow-popups-to-escape-sandbox.

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.

security-high high

⚠️ 安全风险提示:allow-popups-to-escape-sandbox 导致沙箱逃逸\n\n由于插件页面的 iframe 与 Dashboard 处于同源(均在 /api/plugin/page/content/... 下),如果启用了 allow-popups-to-escape-sandbox,会引入严重的沙箱逃逸风险:\n\n1. 逃逸原理:iframe 内的插件页面可以通过 window.open 打开一个同源的页面(例如 Dashboard 的其他路由,或其自身的页面)。\n2. 权限提升:由于新打开的窗口/标签页脱离了沙箱限制,它将以完整的同源权限运行,从而可以直接访问 Dashboard 的 localStorage(窃取 JWT Token)、cookie 并调用后台敏感 API。\n3. 危害:这使得原本通过 sandbox(未设置 allow-same-origin)建立的隔离屏障完全失效。\n\n---\n\n### 💡 推荐的黄金方案:通过 Bridge 代理打开外链\n\n为了在保持严格沙箱(不启用 allow-popups-to-escape-sandbox)的同时,允许插件页面正常打开外部链接,推荐使用 Bridge 代理机制:\n\n#### 1. 在 PluginPagePage.vue 中支持 window:open 动作\n在 handleBridgeRequest(约第 210 行起)中增加对 window:open 的处理,由父窗口(Dashboard)来安全地执行 window.open:\n```javascript\nif (action === "window:open") {\n const { url } = message;\n if (typeof url === "string" && /^https?:\/\//i.test(url)) {

window.open(url, \"_blank\", \"noopener,noreferrer\");\n    sendBridgeResponse(requestId, true, {});\n  } else {
throw new Error(\"Invalid or unsafe URL\");\n  }\n  return;\n}\n```\n\n#### 2. 在 `astrbot/dashboard/plugin_page_bridge.js` 中自动拦截与代理\n在 Bridge SDK 中,可以重写 `window.open` 并全局拦截 `target=\"_blank\"` 的链接点击,将其无缝转发给父窗口:\n```javascript\n// 1. 导出代理方法\nwindow.AstrBotPluginPage = {\n  // ... 其他方法\n  openExternal(url) {\n    return makeRequest(\"window:open\", { url });\n  }\n};\n\n// 2. 自动代理 window.open\nconst originalOpen = window.open;\nwindow.open = function(url, target, features) {\n  if (target === \"_blank\" || !target) {\n    window.AstrBotPluginPage.openExternal(url);\n    return null;\n  }\n  return originalOpen.apply(this, arguments);\n};\n\n// 3. 自动拦截 a[target=\"_blank\"] 点击\ndocument.addEventListener(\"click\", (event) => {\n  const anchor = event.target.closest(\"a\");\n  if (anchor && anchor.target === \"_blank\") {\n    const href = anchor.href;\n    if (href && /^https?:\\/\\//i.test(href)) {\n      event.preventDefault();\n      window.AstrBotPluginPage.openExternal(href);\n    }\n  }\n});\n```\n\n通过这种方式,插件开发者无需修改任何代码(标准的 `window.open` 和 `<a>` 标签会被自动代理),同时 Dashboard 能够保持极高的安全性。
          sandbox="allow-scripts allow-forms allow-downloads"

@load="handleIframeLoad"
></iframe>
</v-card-text>
Expand Down
Loading