Implements a comprehensive security system for Superset extensions: Backend: - Add EXTENSIONS_TRUST_CONFIG to superset_config.py for admin control - Create ExtensionSecurityManager for trust validation and signature verification - Support Ed25519 signatures for extension manifests - Integrate trust validation into extension loading pipeline CLI: - Add `generate-keys` command for creating Ed25519 signing keypairs - Add `sign` command and `--sign` option to `bundle` for manifest signing Frontend: - Add WASM support to webpack config for QuickJS sandbox - Update Extension interface with trust-related fields - ExtensionsManager now uses backend-validated trust levels Documentation: - Add Administrator Configuration guide for trust settings - Add Extension Signing guide for developers - Update security.md and sandbox.md with cross-references - Add Security subcategory to sidebar Tests: - Add 21 unit tests for trust validation and signature verification Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
11 KiB
title, sidebar_position
| title | sidebar_position |
|---|---|
| Extension Sandboxing | 10 |
Extension Sandboxing
Superset provides a tiered sandbox architecture for running extensions with varying levels of trust and isolation. This system balances security with functionality, allowing extensions to be safely executed based on their trust level and requirements.
Overview
The sandbox system supports three tiers of trust:
| Tier | Trust Level | Isolation | Use Case |
|---|---|---|---|
| Tier 1 | core |
None (main context) | Official/signed extensions |
| Tier 2 | iframe |
Browser sandbox | Community UI extensions |
| Tier 3 | wasm |
WASM sandbox | Logic-only extensions |
Trust Levels
Tier 1: Core (Trusted)
Core extensions run in the main JavaScript context with full access to Superset APIs, DOM, and browser capabilities. This is the same behavior as legacy extensions.
Requirements:
- Must be in the trusted extensions list, OR
allowUnsignedCoreconfiguration must be enabled
Use cases:
- Official Apache Superset extensions
- Enterprise-verified plugins
- Extensions from trusted sources
{
"id": "official-extension",
"sandbox": {
"trustLevel": "core",
"requiresSignature": true
}
}
Tier 2: Iframe (Semi-Trusted)
Iframe-sandboxed extensions run in isolated browser sandboxes with controlled API access via postMessage. This provides strong browser-enforced isolation while still allowing full UI rendering.
Security features:
- Browser-enforced same-origin isolation
- Content Security Policy (CSP) restrictions
- Permission-based API access
- No access to parent window's cookies, localStorage, or DOM
Use cases:
- Community-contributed extensions
- Third-party plugins
- Extensions that render custom UI
{
"id": "community-extension",
"sandbox": {
"trustLevel": "iframe",
"permissions": ["sqllab:read", "notification:show"],
"csp": {
"connectSrc": ["https://api.example.com"]
}
}
}
Tier 3: WASM (Untrusted)
WASM-sandboxed extensions run in a QuickJS WebAssembly sandbox with no DOM access. Only explicitly injected APIs are available. This provides the highest level of isolation.
Security features:
- Complete isolation from browser APIs
- Memory limits to prevent DoS
- Execution time limits
- No network or DOM access
Use cases:
- Custom data transformations
- Calculated fields and formatters
- Data validation rules
- Custom aggregation functions
{
"id": "formatter-extension",
"sandbox": {
"trustLevel": "wasm",
"resourceLimits": {
"maxMemory": 10485760,
"maxExecutionTime": 5000
}
}
}
Permissions
Sandboxed extensions (Tier 2 and 3) must declare the permissions they need. Permissions follow a least-privilege model.
Available Permissions
| Permission | Description |
|---|---|
api:read |
Read-only access to Superset APIs |
api:write |
Write access to Superset APIs |
sqllab:read |
Read SQL Lab state (queries, results) |
sqllab:execute |
Execute SQL queries |
dashboard:read |
Read dashboard data |
dashboard:write |
Modify dashboards |
chart:read |
Read chart data |
chart:write |
Modify charts |
user:read |
Read current user info |
notification:show |
Show notifications to user |
modal:open |
Open modal dialogs |
navigation:redirect |
Navigate to other pages |
clipboard:write |
Write to clipboard |
download:file |
Trigger file downloads |
Example Permission Declaration
{
"sandbox": {
"trustLevel": "iframe",
"permissions": [
"sqllab:read",
"notification:show",
"download:file"
]
}
}
Sandboxed Extension API
Extensions running in iframe sandboxes have access to a controlled API through the window.superset object.
SQL Lab API
// Get the current SQL Lab tab (requires sqllab:read)
const tab = await window.superset.sqlLab.getCurrentTab();
// Get query results (requires sqllab:read)
const results = await window.superset.sqlLab.getQueryResults(queryId);
Dashboard API
// Get dashboard context (requires dashboard:read)
const context = await window.superset.dashboard.getContext();
// Get dashboard filters (requires dashboard:read)
const filters = await window.superset.dashboard.getFilters();
Chart API
// Get chart data (requires chart:read)
const chartData = await window.superset.chart.getData(chartId);
User API
// Get current user (requires user:read)
const user = await window.superset.user.getCurrentUser();
UI API
// Show notification (requires notification:show)
window.superset.ui.showNotification('Success!', 'success');
// Open modal (requires modal:open)
const result = await window.superset.ui.openModal({
title: 'Confirm',
content: 'Are you sure?',
type: 'confirm'
});
// Navigate (requires navigation:redirect)
window.superset.ui.navigateTo('/dashboard/1');
Utility API
// Copy to clipboard (requires clipboard:write)
await window.superset.utils.copyToClipboard('text');
// Download file (requires download:file)
window.superset.utils.downloadFile(blob, 'filename.csv');
// Get CSRF token (no permission required)
const token = await window.superset.utils.getCSRFToken();
Event Subscriptions
// Subscribe to events
const unsubscribe = window.superset.on('dashboard:filterChange', (filters) => {
console.log('Filters changed:', filters);
});
// Later, unsubscribe
unsubscribe();
Content Security Policy
Iframe-sandboxed extensions can customize their Content Security Policy through the csp configuration:
{
"sandbox": {
"trustLevel": "iframe",
"csp": {
"defaultSrc": ["'none'"],
"scriptSrc": ["'unsafe-inline'"],
"styleSrc": ["'unsafe-inline'"],
"imgSrc": ["data:", "blob:", "https://cdn.example.com"],
"connectSrc": ["https://api.example.com"],
"fontSrc": ["data:"]
}
}
}
Default CSP
By default, iframe sandboxes use a restrictive CSP:
default-src 'none';
script-src 'unsafe-inline';
style-src 'unsafe-inline';
img-src data: blob:;
font-src data:;
connect-src 'none';
frame-src 'none';
WASM Resource Limits
WASM-sandboxed extensions can configure resource limits:
{
"sandbox": {
"trustLevel": "wasm",
"resourceLimits": {
"maxMemory": 10485760, // 10MB max memory
"maxExecutionTime": 5000, // 5 second timeout
"maxStackSize": 1000 // Max call stack depth
}
}
}
Defaults
- maxMemory: 10MB
- maxExecutionTime: 5000ms (5 seconds)
- maxStackSize: 1000 calls
Migration Guide
Migrating from Legacy Extensions
Existing extensions that don't specify a sandbox configuration will continue to run as core extensions for backward compatibility. To migrate to a sandboxed model:
-
Assess your extension's requirements:
- Does it need to render UI? Use
iframe - Is it logic-only (formatters, validators)? Use
wasm - Does it need full access? Keep as
core(requires trust)
- Does it need to render UI? Use
-
Add sandbox configuration to extension.json:
{
"sandbox": {
"trustLevel": "iframe",
"permissions": ["sqllab:read"]
}
}
- Update your code to use the sandboxed API:
Before (core extension):
import { sqlLab } from '@apache-superset/core';
const tab = sqlLab.getCurrentTab();
After (sandboxed extension):
const tab = await window.superset.sqlLab.getCurrentTab();
- Test thoroughly to ensure all functionality works within the sandbox
Security Comparison
| Aspect | Core | Iframe | WASM |
|---|---|---|---|
| DOM Access | Full | Own iframe only | None |
| Network | Full | Restricted (CSP) | None |
| Cookies | Full | None | None |
| localStorage | Full | None | None |
| Superset APIs | Full | Controlled bridge | Injected only |
| Performance | Native | Near-native | ~40% slower |
| React rendering | Full | Own instance | Via descriptors |
Administrator Configuration
Administrators can configure trust settings for their Superset deployment:
# In superset_config.py
EXTENSIONS_TRUST_CONFIG = {
# Extensions allowed to run as 'core'
"trusted_extensions": [
"official-extension-1",
"enterprise-plugin",
],
# Allow unsigned extensions to run as core (not recommended for production)
"allow_unsigned_core": False,
# Default trust level for extensions without sandbox config
"default_trust_level": "iframe",
}
Best Practices
-
Request minimal permissions - Only request the permissions your extension actually needs
-
Prefer iframe over core - Unless your extension requires deep integration, use iframe sandboxing
-
Use WASM for pure logic - If your extension doesn't need UI, WASM provides the best isolation
-
Handle permission denials gracefully - Your extension should degrade gracefully if a permission is not granted
-
Don't store sensitive data - Sandboxed extensions should not store sensitive user data
-
Test in sandboxed mode - Always test your extension in its intended sandbox environment
Troubleshooting
Permission Denied Errors
If you see "Permission denied" errors, verify that:
- The permission is declared in your extension.json
- The permission was granted by the administrator
- You're calling the correct API method for that permission
Timeout Errors (WASM)
If your WASM extension times out:
- Optimize your code for faster execution
- Request a higher
maxExecutionTimelimit - Break large operations into smaller chunks
CSP Violations (Iframe)
If resources fail to load due to CSP:
- Add the required domains to your CSP configuration
- Ensure you're using HTTPS for external resources
- Avoid inline scripts and styles where possible
Core Trust Denied
If your extension is downgraded from core to another trust level:
- Check if the extension ID is in the administrator's
trusted_extensionslist - If signature verification is required, ensure the extension is signed
- Verify the signing key is in the administrator's
trusted_signers
See Extension Signing for how to sign your extension.
Related Documentation
- Security Overview - Extension security fundamentals
- Extension Signing - How to sign extensions for core trust
- Administrator Configuration - Trust configuration for admins