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

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

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.

* fix(dashboard): preserve object-src and base-uri CSP directives under 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.

* fix(dashboard): tighten ASTRBOT_LAUNCHER check and always emit CSP

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.
This commit is contained in:
lxfight
2026-06-03 18:42:15 +08:00
committed by GitHub
parent 1daa0e3367
commit e5d7b43090

View File

@@ -789,14 +789,21 @@ class PluginRoute(Route):
response.headers["Cache-Control"] = "no-store"
response.headers["Referrer-Policy"] = "no-referrer"
response.headers["X-Content-Type-Options"] = "nosniff"
response.headers["X-Frame-Options"] = "SAMEORIGIN"
response.headers["Cross-Origin-Resource-Policy"] = "cross-origin"
# Sandboxed iframes without allow-same-origin load ES modules with Origin: null.
# CORS read access is allowed here; JWT/asset_token still protects the assets.
response.headers["Access-Control-Allow-Origin"] = "*"
response.headers["Content-Security-Policy"] = (
"frame-ancestors 'self'; object-src 'none'; base-uri 'self'"
)
# When running under the AstrBot Launcher the dashboard is embedded in a
# 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.
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
async def _serve_plugin_page_html_asset(