Embedded app missing frame-ancestors CSP directive
Detects embedded apps that set document response headers but omit frame-ancestors.
| Detail | Value |
|---|---|
| Check ID | MISSING_EMBEDDED_CSP |
| Severity | Medium |
| How it runs | Agent review |
Anchor to Vulnerable exampleVulnerable example
app/entry.server.tsx
export default async function handleRequest(
request: Request,
responseStatusCode: number,
responseHeaders: Headers,
reactRouterContext: EntryContext,
) {
// Document headers are set here, but Shopify's headers are
// never added, so no frame-ancestors directive reaches the
// response and any site can iframe the embedded app.
responseHeaders.set("Cache-Control", "no-store");
// ... the rest of the template's handleRequest is unchanged.
}
Anchor to How to fixHow to fix
Add frame-ancestors to the CSP, restricted to the authenticated shop domain and admin.shopify.com.
For more detail, see Set up iframe protection.
app/entry.server.tsx
import { addDocumentResponseHeaders } from "./shopify.server";
export default async function handleRequest(
request: Request,
responseStatusCode: number,
responseHeaders: Headers,
reactRouterContext: EntryContext,
) {
// This is what emits the Content-Security-Policy, with
// frame-ancestors scoped to admin.shopify.com and the
// authenticated shop. The template calls it here already.
addDocumentResponseHeaders(request, responseHeaders);
// ... the rest of the template's handleRequest is unchanged.
}
// boundary.headers in app/routes/app.tsx is a different job: it
// propagates route headers onto thrown responses. It does not
// create a CSP.
shopify app security check, the command that runs this check- All app security checks
Was this page helpful?