Skip to content

fix(dashboard): relax frame security headers when running under launcher#8554

Merged
Soulter merged 3 commits into
AstrBotDevs:masterfrom
lxfight:fix/plugin-page-frame-ancestors-under-launcher
Jun 3, 2026
Merged

fix(dashboard): relax frame security headers when running under launcher#8554
Soulter merged 3 commits into
AstrBotDevs:masterfrom
lxfight:fix/plugin-page-frame-ancestors-under-launcher

Conversation

@lxfight
Copy link
Copy Markdown
Member

@lxfight lxfight commented Jun 3, 2026

When AstrBot is launched by the AstrBot Launcher, the dashboard is embedded in a cross-origin iframe (the Tauri webview). The plugin page responses set X-Frame-Options: SAMEORIGIN and CSP frame-ancestors 'self', both of which inspect the entire ancestor chain — not just the immediate parent. Because the top-level Tauri webview has a different origin, these headers block the plugin page from loading inside the nested iframe, resulting in 'localhost refused to connect'.

Fix: skip the restrictive frame headers when ASTRBOT_LAUNCHER=1 is set, which the launcher already injects as an environment variable. Other security measures (iframe sandbox, JWT asset_token, postMessage bridge) remain in place.

Modifications / 改动点

  • astrbot/dashboard/routes/plugin.py — _apply_plugin_page_security_headers(): conditionally set X-Frame-Options and frame-ancestors CSP only when NOT running under the AstrBot Launcher.
  • This is NOT a breaking change. / 这不是一个破坏性变更。

Screenshots or Test Results / 运行截图或测试结果

Before the repair:
6d425942776186f37d4a5863102b2725
After repair:
image


Checklist / 检查清单

  • 😊 If there are new features added in the PR, I have discussed it with the authors through issues/emails, etc.
    / 如果 PR 中有新加入的功能,已经通过 Issue / 邮件等方式和作者讨论过。

  • 👀 My changes have been well-tested, and "Verification Steps" and "Screenshots" have been provided above.
    / 我的更改经过了良好的测试,并已在上方提供了“验证步骤”和“运行截图”

  • 🤓 I have ensured that no new dependencies are introduced, OR if new dependencies are introduced, they have been added to the appropriate locations in requirements.txt and pyproject.toml.
    / 我确保没有引入新依赖库,或者引入了新依赖库的同时将其添加到 requirements.txtpyproject.toml 文件相应位置。

  • 😮 My changes do not introduce malicious code.
    / 我的更改没有引入恶意代码。

Summary by Sourcery

Bug Fixes:

  • Fix plugin pages failing to load under the AstrBot Launcher due to restrictive X-Frame-Options and frame-ancestors CSP on the dashboard responses.

When AstrBot is launched by the AstrBot Launcher, the dashboard is
embedded in a cross-origin iframe (the Tauri webview).  The plugin page
responses set X-Frame-Options: SAMEORIGIN and CSP frame-ancestors
'self', both of which inspect the *entire* ancestor chain — not just the
immediate parent.  Because the top-level Tauri webview has a different
origin, these headers block the plugin page from loading inside the
nested iframe, resulting in 'localhost refused to connect'.

Fix: skip the restrictive frame headers when ASTRBOT_LAUNCHER=1 is set,
which the launcher already injects as an environment variable.  Other
security measures (iframe sandbox, JWT asset_token, postMessage bridge)
remain in place.
@dosubot dosubot Bot added size:S This PR changes 10-29 lines, ignoring generated files. area:webui The bug / feature is about webui(dashboard) of astrbot. labels Jun 3, 2026
Copy link
Copy Markdown
Contributor

@sourcery-ai sourcery-ai Bot left a comment

Choose a reason for hiding this comment

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

Hey - I've found 1 issue, and left some high level feedback:

  • Right now the CSP header is completely omitted when ASTRBOT_LAUNCHER is set, which also drops the object-src 'none' and base-uri 'self' protections; consider still sending CSP under the launcher and only relaxing or omitting the frame-ancestors directive.
  • Instead of reading os.environ directly in the route helper, it may be cleaner to centralize the ASTRBOT_LAUNCHER check in a configuration/utility function so that launcher-specific behavior stays consistent and easier to adjust later.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- Right now the CSP header is completely omitted when `ASTRBOT_LAUNCHER` is set, which also drops the `object-src 'none'` and `base-uri 'self'` protections; consider still sending CSP under the launcher and only relaxing or omitting the `frame-ancestors` directive.
- Instead of reading `os.environ` directly in the route helper, it may be cleaner to centralize the `ASTRBOT_LAUNCHER` check in a configuration/utility function so that launcher-specific behavior stays consistent and easier to adjust later.

## Individual Comments

### Comment 1
<location path="astrbot/dashboard/routes/plugin.py" line_range="801-797" />
<code_context>
+        # cross-origin iframe (the Tauri webview).  Since frame-ancestors and
+        # X-Frame-Options inspect the *entire* ancestor chain, enforcing them here
+        # would block plugin pages from loading inside the nested iframe.
+        if not os.environ.get("ASTRBOT_LAUNCHER"):
+            response.headers["X-Frame-Options"] = "SAMEORIGIN"
+            response.headers["Content-Security-Policy"] = (
+                "frame-ancestors 'self'; object-src 'none'; base-uri 'self'"
+            )
</code_context>
<issue_to_address>
**🚨 suggestion (security):** In launcher mode the CSP is dropped entirely; consider preserving non-framing directives for defense in depth.

When `ASTRBOT_LAUNCHER` is set, we currently remove both `X-Frame-Options` and the entire CSP. While that’s necessary for `frame-ancestors`, it also drops `object-src 'none'` and `base-uri 'self'`, which are still valuable protections. Consider adding an `else` branch that sets a CSP omitting only `frame-ancestors` (e.g. `object-src 'none'; base-uri 'self'`) so launcher mode keeps these defenses while allowing framing.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread astrbot/dashboard/routes/plugin.py
Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request conditionally disables X-Frame-Options and frame-ancestors CSP directives when running under the AstrBot Launcher to allow embedding in a cross-origin iframe. The review feedback identifies a security regression where other critical CSP directives (object-src and base-uri) are inadvertently disabled under the launcher, and points out that the environment variable check is too loose. A code suggestion is provided to resolve both issues.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread astrbot/dashboard/routes/plugin.py Outdated
lxfight added 2 commits June 3, 2026 16:54
… launcher

Keep object-src 'none' and base-uri 'self' in the CSP header even when
ASTRBOT_LAUNCHER is set.  Only frame-ancestors and X-Frame-Options need
to be relaxed because the Tauri webview is a cross-origin ancestor.
Use explicit value check ('1' / 'true') instead of truthiness for the
ASTRBOT_LAUNCHER env var.  Always emit a Content-Security-Policy header
and only conditionally prepend frame-ancestors 'self' — this keeps
object-src 'none' and base-uri 'self' active under the launcher.
@lxfight
Copy link
Copy Markdown
Member Author

lxfight commented Jun 3, 2026

@sourcery-ai review

Copy link
Copy Markdown
Contributor

@sourcery-ai sourcery-ai Bot left a comment

Choose a reason for hiding this comment

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

Hey - I've found 1 issue, and left some high level feedback:

  • Consider caching the ASTRBOT_LAUNCHER flag at module import time (e.g., LAUNCHER_MODE = os.getenv('ASTRBOT_LAUNCHER', '').lower() in ('1', 'true')) instead of reading os.environ on every response to avoid repeated env lookups and to centralize the parsing logic.
  • The CSP construction could be made clearer and less error-prone by building it from a list of directives (conditionally appending frame-ancestors 'self') and joining them, rather than mutating a string in place.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- Consider caching the `ASTRBOT_LAUNCHER` flag at module import time (e.g., `LAUNCHER_MODE = os.getenv('ASTRBOT_LAUNCHER', '').lower() in ('1', 'true')`) instead of reading `os.environ` on every response to avoid repeated env lookups and to centralize the parsing logic.
- The CSP construction could be made clearer and less error-prone by building it from a list of directives (conditionally appending `frame-ancestors 'self'`) and joining them, rather than mutating a string in place.

## Individual Comments

### Comment 1
<location path="astrbot/dashboard/routes/plugin.py" line_range="802-805" />
<code_context>
+        # X-Frame-Options inspect the *entire* ancestor chain, enforcing them here
+        # would block plugin pages from loading inside the nested iframe.
+        csp = "object-src 'none'; base-uri 'self'"
+        if os.environ.get("ASTRBOT_LAUNCHER") not in ("1", "true"):
+            response.headers["X-Frame-Options"] = "SAMEORIGIN"
+            csp = f"frame-ancestors 'self'; {csp}"
+        response.headers["Content-Security-Policy"] = csp
+
         return response
</code_context>
<issue_to_address>
**🚨 question (security):** Revisiting CSP policy when ASTRBOT_LAUNCHER is enabled to ensure framing is only as permissive as needed.

With `ASTRBOT_LAUNCHER` enabled, we drop both `X-Frame-Options` and `frame-ancestors`, so the dashboard can be framed by any origin, exposing it to clickjacking in that mode. If the launcher’s origin (or a stable scheme/host) is known, it’d be safer to restrict `frame-ancestors` to `'self'` plus that origin instead of fully disabling it. Otherwise, consider adding a brief note to security docs or config explaining why this trade-off is acceptable.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread astrbot/dashboard/routes/plugin.py
@lxfight
Copy link
Copy Markdown
Member Author

lxfight commented Jun 3, 2026

  • format-check:要格式化的 3 个文件是 tool_loop_agent_runner.pyinternal.pygemini_embedding_source.py,与本次PR修改的 plugin.py无关。
  • pytest:测试失败也与本 PR 改动无关

@dosubot dosubot Bot added the lgtm This PR has been approved by a maintainer label Jun 3, 2026
@Soulter Soulter merged commit e5d7b43 into AstrBotDevs:master Jun 3, 2026
19 of 21 checks passed
@Soulter Soulter deleted the fix/plugin-page-frame-ancestors-under-launcher branch June 3, 2026 10:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:webui The bug / feature is about webui(dashboard) of astrbot. lgtm This PR has been approved by a maintainer size:S This PR changes 10-29 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants