7 min left
Back to Series

The Stack > Article 9 | Intermediate | 7 min read

Article 9Intermediate7 min read

How Datasette Apps sandboxes untrusted HTML

Simon Willison shipped datasette-apps: custom HTML/JS inside Datasette via CSP-restricted iframes and MessageChannel IPC. The interesting part is how you host user code on sensitive data without trusting it.


Sandboxed iframe architecture for hosting untrusted web applications

Simon Willison released Datasette Apps — a plugin system that runs custom HTML and JavaScript applications inside Datasette, the open-source tool for exploring SQLite databases. The use case is familiar: you have structured data in SQLite, you want bespoke visualizations or workflows, and you do not want to fork Datasette or run a separate frontend server.

The engineering problem is harder: how do you execute untrusted or semi-trusted HTML/JS with access to database queries without handing it the keys to the kingdom? Datasette Apps answers with sandboxed iframes, strict Content Security Policy (CSP), and a narrow MessageChannel API for approved operations.

The threat model

Datasette often sits on sensitive datasets — journalism leaks, internal analytics, personal data exports. A plugin that can run arbitrary JavaScript in the same origin as the parent page could exfiltrate query results, modify other plugins, or attack the admin session.

The design goal is not "trust the plugin author completely." It is "let plugin authors build rich UIs while the host enforces what they can reach."

Think of it like a hotel room with a smart tablet: guests can control lights and temperature through fixed buttons, not rewire the building's electrical panel.

Iframe sandbox plus CSP

Each app runs inside an iframe with sandbox attributes that strip capabilities by default: no top-level navigation, no form submission to parent origins, no plugin execution unless explicitly granted. CSP headers further restrict script sources, inline execution, and network destinations.

The parent Datasette process serves the iframe content from a controlled path. Even if app HTML includes malicious script, the combination of sandbox flags and CSP reduces what that script can touch outside the iframe boundary.

This is defense in depth. Sandbox alone blocks some vectors; CSP blocks others. Neither is sufficient against a determined attacker with full HTML control, but together they raise the bar for accidental or opportunistic abuse — the realistic threat for community-contributed plugins.

MessageChannel as the API surface

Apps cannot call Datasette's Python API directly. Instead, parent and iframe communicate over a MessageChannel — a browser API for structured postMessage-style communication between frames. The parent exposes an allow-listed set of operations: run a read-only SQL query, write to specific tables (when configured), fetch metadata about the database schema.

Every capability crosses an explicit bridge. The app requests; the parent validates and executes. SQL writes go through Datasette's existing permission model rather than arbitrary string execution from JavaScript.

Untrusted UI code should talk to data through a narrow, typed bridge — not shared memory or shared origin privileges.

Willison documented using an LLM-assisted security review during development — which caught a privilege escalation path where an app could request write access outside its intended scope. That feedback loop matters: sandbox designs need adversarial review, not just checklist configuration.

What app authors get

Authors ship a folder of static HTML, CSS, and JavaScript registered through the plugin. Datasette mounts the app at a URL path, injects the bridge script, and handles authentication at the Datasette layer — apps inherit the user's existing login and table permissions.

The model favors small, focused tools: a chart for one table, a form that inserts rows into an approved table, a search UI over a curated view. It is not a general application server — and that limitation is the security feature.

CapabilityTypical patternBlocked by default
Read queryMessageChannel request → Datasette executesArbitrary SQL from JS
Write rowAllow-listed table + validated payloadCross-table writes
DOM in appFull control inside iframeParent DOM access
NetworkCSP allow-listExfil to arbitrary domains

LLM-assisted review as part of the stack

Willison used Claude to review the sandbox implementation — not as a substitute for human audit, but as a first pass that flagged edge cases in permission checks. For teams shipping extensible platforms, that pattern is increasingly practical: generate threat scenarios, trace code paths, fix before external researchers arrive.

It does not replace formal review for high-sensitivity deployments. It compresses the time to first finding.


What this means for builders

Default deny for embedded code. If your product hosts user or third-party HTML, assume it is hostile. Iframe sandbox plus CSP is baseline, not paranoia.

Expose data through typed bridges. MessageChannel-style APIs with explicit method lists beat "here is a fetch token with full API access."

Align write paths with server-side authorization. JavaScript should not construct SQL strings the server executes blindly — even inside a sandbox.

Review sandbox designs adversarially. LLM-assisted review is a useful first pass; pen-test or manual threat modeling still matters for sensitive data.

Scope plugins to small surfaces. The safest extensibility model gives apps one job, one data slice, and one permission profile — not general-purpose hosting.

Conclusion

Datasette Apps solves a common builder problem — custom UI on structured data — without collapsing security to "trust the plugin." Sandboxed iframes, CSP, and a MessageChannel bridge turn extensibility into an explicit contract between host and guest code. The pattern transfers anywhere you need rich client experiences on sensitive backends: analytics dashboards, internal tools, and data portals where contributors are not all fully trusted.


DatasettesandboxsecurityiframeCSPPythonweb apps

Up next in the series

Article 10Live

What reliable agentic AI actually requires

Martin Fowler and Bayer outline reliability engineering for autonomous LLM agents — evals, monitoring, and architecture patterns that treat agents as production systems, not demos.

AI agentsreliabilityLLMmonitoring