Skip to content
Back to skills

Embed Csp And Iframe Sandboxing

ASecurity

Configure CSP `frame-ancestors`, iframe `sandbox` attributes, postMessage origin checks, and web-component shadow-DOM boundaries for embedded dashboards. Invoked by `ravenclaude-core/security-reviewer` when a diff touches embed-auth flow; generated alongside dashboard code by `dashboard-builder`.

  • 7 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added September 23, 2026
ai-agentsjavascriptrustjavasqlreactapisecurity

Works with

  • cli
  • api

Security analysis

A100/100

Scanned September 23, 2026

npx -y skills add mcorbett51090/RavenClaude --skill embed-csp-and-iframe-sandboxing --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Embed Csp And Iframe Sandboxing?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Embed Csp And Iframe Sandboxing
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/mcorbett51090-embed-csp-and-iframe-sandboxing/badge)](https://www.skillsdirectory.com/skills/mcorbett51090-embed-csp-and-iframe-sandboxing)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: embed-csp-and-iframe-sandboxing
description: Configure CSP `frame-ancestors`, iframe `sandbox` attributes, postMessage origin checks, and web-component shadow-DOM boundaries for embedded dashboards. Invoked by `ravenclaude-core/security-reviewer` when a diff touches embed-auth flow; generated alongside dashboard code by `dashboard-builder`.
invoked_by: ravenclaude-core/security-reviewer
---

# Skill: embed-csp-and-iframe-sandboxing

> **Invoked by:** `ravenclaude-core/security-reviewer` (mandatory for any embed-iframe / web-component change). Generated by `dashboard-builder`.
>
> **When to invoke:** designing the iframe / web-component boundary that contains an embedded dashboard. Reviewing CSP headers. Verifying postMessage origin checks. Web-component shadow-DOM boundary concerns.
>
> **Output:** correct CSP `frame-ancestors` directive; iframe `sandbox` attributes; postMessage origin allowlist; web-component boundary documented.

## The three concerns

### 1. CSP `frame-ancestors` (the host side)

The host application (e.g., `ravenpower.net` or the client's web app) needs a CSP header that **either allows** the dashboard to be embedded into specific origins, or **forbids** embedding entirely if the dashboard is not third-party-facing.

```http
Content-Security-Policy: frame-ancestors 'self' https://client-app.example.com https://*.client-tenant.example.com;
```

- `'self'` — the host's own origin
- Explicit origins — the third-party apps allowed to embed
- `'none'` — forbid embedding entirely (use for admin-only internal dashboards)
- Wildcards — allow sub-domains; use sparingly

`X-Frame-Options: SAMEORIGIN` is the legacy fallback; modern browsers honor `frame-ancestors` when both are present.

### 2. iframe `sandbox` attribute (the embed side)

When the host page renders the dashboard via iframe, the `sandbox` attribute scopes what the iframe can do:

```html
<iframe
  src="https://dashboards.example.com/embed/abc123"
  sandbox="allow-scripts allow-same-origin allow-forms"
  ...
></iframe>
```

| Sandbox flag | Effect |
|---|---|
| `allow-scripts` | iframe can execute JS (usually required for interactive dashboards) |
| `allow-same-origin` | iframe content can be treated as same-origin if the URL matches (required for cookies / localStorage scoped to the embed origin) |
| `allow-forms` | iframe can submit forms (filtering, export) |
| `allow-popups` | iframe can open new windows (PDF export, share link) |
| `allow-popups-to-escape-sandbox` | popups don't inherit the sandbox |

**Default:** start with `sandbox="allow-scripts allow-same-origin"` and add flags only when needed. **No `sandbox` attribute at all = full permissions, which is wrong.**

### 3. postMessage origin checks (the message-passing boundary)

When the iframe and host page need to communicate (resize signals, filter changes, theme updates), they use `window.postMessage()`. Both sides must verify the origin:

```javascript
// Host page receiving messages from the embed
window.addEventListener('message', (event) => {
  // ALWAYS check origin — never trust event.data without this
  if (event.origin !== 'https://dashboards.example.com') return;
  // ALWAYS check structure of event.data before using it
  if (typeof event.data !== 'object' || !event.data.type) return;
  // Now safe to act
});

// Embed iframe sending messages to host
window.parent.postMessage({ type: 'resize', height: 600 }, 'https://host-app.example.com');
//                                                          ^ explicit target origin, not '*'
```

**Anti-patterns:**
- `event.origin` not checked
- `postMessage(..., '*')` — broadcasts to any listener; leaks data
- Trusting `event.data` shape without validation

## Web components (alternative to iframes)

For tools like Embeddable.com that use web components rather than iframes, the boundary becomes the **shadow-DOM boundary**:

```html
<dashboard-embed
  api-key="<short-lived-token>"
  tenant-id="from-server"
></dashboard-embed>
```

- Shadow DOM scopes CSS and DOM access — the host page can't reach into the dashboard's shadow root, the dashboard can't reach out
- Custom-element attributes (like `api-key`) are visible in the host's DOM — pass short-lived tokens, never long-lived secrets
- Event communication via `dispatchEvent` + `addEventListener` on the custom element

## Tool-specific patterns

### Apache Superset
- Embed SDK manages an iframe internally; the host page only needs `frame-ancestors` allowing Superset's origin and the `<superset-embedded-dashboard>` custom element
- Guest token is short-lived (configurable; default ~1 hour — recommend 5-15 min for production)

### Metabase
- Static embed: signed iframe URL; `frame-ancestors` covers it
- Interactive Embedding (Pro+): SDK + JWT flow with parameterized iframes

### Power BI Embedded
- Embed via `powerbi-client` JS library; the library handles the iframe internally
- CSP must allow `frame-ancestors` for `*.powerbi.com` (or the F-SKU embed URL); Microsoft also requires CSP nonce values for the embed page's inline scripts

### Cube (with custom React UI)
- No iframe — direct REST/SQL API calls from the React app to Cube
- CSP `connect-src` directive must allow the Cube API origin
- No `frame-ancestors` concern unless Cube itself is being embedded somewhere

### Embeddable.com
- Web-component-based; shadow-DOM boundary is the primary container
- API key passed as element attribute (must be short-lived)

## Required CSP companions

Beyond `frame-ancestors`, the host page should set:

```http
Content-Security-Policy:
  default-src 'self';
  script-src 'self' 'nonce-{server-generated}';
  style-src 'self' 'unsafe-inline';
  img-src 'self' data:;
  connect-src 'self' https://api.cube.example.com;
  frame-src 'self' https://dashboards.example.com;
  frame-ancestors 'self' https://client-app.example.com;
```

- `frame-src` (or `child-src`) — what the host is allowed to load inside iframes
- `connect-src` — what the host's JS is allowed to fetch from (Cube API, JWT-issuer endpoint)
- `script-src 'nonce-...'` — nonce-based script allowlist (more secure than `unsafe-inline`)

## Anti-patterns this skill flags

- `frame-ancestors` set to `*` (allows embedding anywhere — embed-jacking risk)
- iframe with no `sandbox` attribute (full permissions)
- `sandbox="allow-scripts allow-same-origin allow-top-navigation"` (allow-top-navigation lets the iframe redirect the parent page — phishing risk)
- `postMessage(..., '*')` instead of explicit target origin
- `event.origin` not checked in message handlers
- Host page that doesn't set CSP headers at all (the security review skips entirely)
- Long-lived API keys passed as web-component attributes (visible in host DOM; short-lived tokens only)
- CSP with `unsafe-eval` enabled because "Cube needs it" — Cube doesn't; verify the requirement
- Power BI Embedded engagement that forgets the CSP nonce values Microsoft requires
- Web-component embed with no shadow-DOM (defeats the boundary)

## References

- Knowledge: [`../../knowledge/embedded-analytics-landscape-2026.md`](../../knowledge/embedded-analytics-landscape-2026.md) — tool-specific embed patterns
- Skill: [`../jwt-embed-issuance/SKILL.md`](../jwt-embed-issuance/SKILL.md) — the JWT that travels through the boundary
- Skill: [`../rls-policy-authoring/SKILL.md`](../rls-policy-authoring/SKILL.md) — what the JWT claim ultimately enforces
- MDN: [CSP `frame-ancestors`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/frame-ancestors)
- MDN: [iframe `sandbox`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/iframe#sandbox)
- MDN: [`window.postMessage`](https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage)
- Upstream review rubric: [`../../../ravenclaude-core/agents/security-reviewer.md`](../../../ravenclaude-core/agents/security-reviewer.md)

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…