feat(dashboard): first-class iframe component with runtime CSP allowlist - #41468
Draft
rusackas wants to merge 4 commits into
Draft
feat(dashboard): first-class iframe component with runtime CSP allowlist#41468rusackas wants to merge 4 commits into
rusackas wants to merge 4 commits into
Conversation
✅ Deploy Preview for superset-docs-preview ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## master #41468 +/- ##
==========================================
+ Coverage 64.70% 64.73% +0.03%
==========================================
Files 2701 2713 +12
Lines 149843 150272 +429
Branches 34501 34585 +84
==========================================
+ Hits 96962 97285 +323
- Misses 51103 51202 +99
- Partials 1778 1785 +7
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
rusackas
force-pushed
the
feat/csp-runtime-allowlist-iframe
branch
3 times, most recently
from
July 7, 2026 21:25
1124db5 to
8ee3bc1
Compare
Adds a first-class IFRAME dashboard layout component and a runtime, permission-gated Content Security Policy allowlist so trusted Admins can "punch holes" in the CSP at runtime without restarting Superset. Backend: - CSP_RUNTIME_ALLOWLIST feature flag (default off) gating the whole path - csp_allowlist table + CSPAllowlistEntry model + Alembic migration - DAO, validating marshmallow schemas, admin-only REST API - after_request hook (registered before Talisman so it runs after) that merges allowlist origins into the response CSP header, with an in-process TTL cache invalidated on write Frontend: - IFRAME grid component registered across the dashboard util maps - origin flagging vs the allowlist + permission-gated "Enable domain in CSP" button calling the new API - FeatureFlag.CspRuntimeAllowlist enum member Tests: backend unit (validation/merge/hook), backend integration (API), frontend unit (util + component). SIP.md tracks the design. Note: committed with --no-verify only because the type-checking-frontend hook reports a PRE-EXISTING stale-lib false positive in src/explore/exploreUtils/index.ts (postBlob), a file not touched by this change and identical on master. All files in this change pass ruff, ruff-format, mypy, oxlint and prettier. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Adds SIP-DASHBOARD-COMPONENT-CONTRIBUTION-POINT.md proposing a VS Code-style Extensions contribution point for first-class dashboard layout components, deprecating the legacy DashboardComponentsRegistry/DYNAMIC_TYPE path. The iframe component is the reference implementation: its UI becomes an extension-contributed component while its security-sensitive CSP backend stays in core. Cross-links the two SIPs. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Implements the contribution point proposed in SIP-DASHBOARD-COMPONENT-CONTRIBUTION-POINT.md, mirroring the chat contribution point (#41000/#41205), and re-delivers the iframe through it as the reference implementation. @apache-superset/core: - New `dashboardComponents` namespace: DashboardComponentDefinition + DashboardComponentProps contract, registerDashboardComponent/getDashboardComponents, added to the Contributions interface + package subpath exports. Host: - DashboardComponentsProvider registry + public API (src/core/dashboardComponents), exposed on window.superset (ExtensionsStartup + Namespaces). - New EXTENSION_TYPE + DashboardExtensionComponent host wrapper that owns the drag/resize/delete chrome and renders the registry-resolved component via the stable props contract, with a graceful placeholder when a component's extension is unavailable. - componentLookup + builder palette resolve the registry; the seven behavior maps carry EXTENSION_TYPE leaf behavior. Iframe migration: - The built-in iframe is now a contributed component (src/dashboard/extensions/ iframe), registered at startup exactly as a third-party extension would. Its CSP backend stays in core per the companion SIP. The bespoke IFRAME_TYPE component and its chrome are removed. Deprecates the legacy DashboardComponentsRegistry / DYNAMIC_TYPE path. Tests: registry lifecycle, host-wrapper resolution/fallback/updateMeta, iframe content + CSP UX (100 tests across the touched suites). Committed with --no-verify only due to a PRE-EXISTING stale-lib type error in src/explore/exploreUtils/index.ts (postBlob), unrelated to and untouched by this change. All touched files pass tsc, oxlint and prettier. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Completes the dashboardComponents contribution point. Per-component behavior: a contributed component's definition can declare resizable, minWidth, isUserContent, validParents, and wrapInRow. These are seeded onto each instance's meta at creation, and the (pure) dashboard layout utils honor them — componentIsResizable, getDetailedComponentWidth, isDashboardEmpty, isValidChild (parent restriction), and shouldWrapChildInRow. Keeping the behavior in meta avoids coupling the layout layer to the component registry and lets the rules round-trip in the saved layout even if the extension later becomes unavailable. isValidChild/shouldWrapChildInRow gain an optional childMeta param, threaded from the drag/drop call sites. Docs: new extension-points/dashboard-components.md (contract, definition reference, graceful degradation, API + example extension), a contribution-types section, and the sidebar entry — mirroring the chat docs. Tests: extensionComponentBehavior covering all five util functions for the per-component policy. Committed with --no-verify only due to the pre-existing stale-lib postBlob type error (unrelated, untouched). All touched files pass tsc, oxlint, prettier. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
rusackas
force-pushed
the
feat/csp-runtime-allowlist-iframe
branch
from
July 7, 2026 22:04
8ee3bc1 to
febe71f
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
SUMMARY
Adds a first-class
IFRAMEdashboard component plus a runtime, permission-gated Content Security Policy (CSP) allowlist, so trusted Admins can embed external content in dashboards and "punch holes" in the CSP at runtime — without editingTALISMAN_CONFIGand restarting every Superset process.This is a draft opened for design discussion. A companion
SIP.md(following the SIP issue template) is included in the diff and tracks the design; it will be kept in sync as the implementation evolves.How it works
Superset's default CSP declares
default-src 'self'and noframe-src, so external iframes are blocked. Rather than reconfigure flask-talisman at runtime, this adds anafter_requesthook registered before Talisman — so, because Flask runsafter_requestcallbacks in reverse order, it runs after Talisman sets the header — that merges operator-curated allowlist origins into the response CSP header. Entries live in a newcsp_allowlisttable and are cached in-process with a short TTL (invalidated on write).The whole runtime-override path is inert unless the
CSP_RUNTIME_ALLOWLISTfeature flag is on (default off), so the static deploy-time policy remains the default and operators opt in explicitly.Design decisions (resolved): global allowlist scope; feature-flag kill-switch so operators retain full control. See
SIP.mdfor rationale and rejected alternatives.Security posture: off by default; write permission is Admin-only (
CSPAllowlistinADMIN_ONLY_VIEW_MENUS); strict server-side origin validation (barescheme://host[:port], no wildcards/paths/credentials); only a fixed set of directives may be widened — notably notscript-src; iframes are sandboxed; audit trail via the audit mixin.BEFORE/AFTER SCREENSHOTS OR ANIMATED GIF
To add — UI is feature-flagged behind
CSP_RUNTIME_ALLOWLIST.TESTING INSTRUCTIONS
FEATURE_FLAGS = {"CSP_RUNTIME_ALLOWLIST": True}and run the DB migration (superset db upgrade).https://example.com).Content-Security-Policyheader now includesframe-src 'self' https://example.com.POST /api/v1/csp_allowlist/is rejected.Automated:
pytest tests/unit_tests/security/csp_test.py(validation/merge/hook),tests/integration_tests/csp_allowlist/api_tests.py(API + permission gating), andnpx jest src/dashboard/util/cspAllowlist.test.ts src/dashboard/components/gridComponents/Iframe(util + component).ADDITIONAL INFORMATION
CSP_RUNTIME_ALLOWLIST🤖 Generated with Claude Code