Skip to content

[Feat]: Sandbox plugin JavaScript, add a CSP and stop exporting secrets by default聽#973

Description

@debba

馃殌 Feature Proposal

Isolate plugin JavaScript (UI extensions and EXPLAIN parsers) from the main app, so a plugin can only reach the backend through an explicit, limited API.

Motivation

Plugin bundles currently run with new Function(...) directly in the main window:

  • src/contexts/PluginSlotProvider.tsx (UI slots)
  • src/utils/pluginExplainLoader.ts (evaluateBundle)

They share the app's JS context, so they can call any Tauri command the app can, for example export_connections_payload, which returns every saved password, SSH secret and connection URI (include_secrets defaults to true in src-tauri/src/commands.rs). tauri.conf.json sets "csp": null, so nothing stops a fetch to an external host afterwards.

Registry signing (src-tauri/src/plugins/integrity.rs) proves a bundle is the one the registry published. It does not limit what that bundle can do, so a malicious or compromised plugin can read all stored credentials without the user being asked.

Note: native driver plugins already run as local processes with the user's privileges, so this is defence in depth for the JS side, not a full plugin trust model.

Where to start

  • CSP: replace "csp": null with a real policy. List what the app needs today (Monaco workers, ipc: / http://ipc.localhost, inline styles, AI providers / registry hosts if called from the frontend) and keep connect-src as narrow as possible.
  • export_connections_payload: make include_secrets default to false. The only caller (src/pages/Connections.tsx, handleExport) already passes it explicitly. On its own this does not stop a plugin, which can pass true, so also move secret export behind a user confirmation handled in the backend (e.g. a native dialog).
  • Sandbox: run plugin UI in a sandboxed <iframe> (or a separate webview with its own Tauri capability set) and expose __TABULARIS_API__ / __TABULARIS_EXPLAIN__ over postMessage with an allow-list. Tauri capabilities apply per window/webview, so they only help once plugins live in their own webview.
  • Write a short design note in this issue before starting the sandbox part: it changes the plugin API contract and needs a migration path for existing plugins.

Acceptance criteria

  • A CSP is set and the app (editor, ER diagram, visual explain, AI panel, plugin registry) still works.
  • export_connections_payload without arguments returns no secrets; exporting with secrets requires an explicit user confirmation.
  • Plugin JS cannot call Tauri invoke directly; the allowed plugin API is documented.
  • Existing first-party plugins keep working, or the needed migration is documented.

Contributor notes

  • Follow the repo rules in .rules/ (TypeScript, React, Rust, testing).
  • Any new UI string needs a key in every locale under src/i18n/locales/.
  • Run pnpm lint, pnpm typecheck, pnpm test and cargo test before opening the PR.
  • Questions? Ask here or on Discord.

Activity

  1. 1-DARK commented on Oct 11, 2026

    @1-DARK
    Contributor

    Hey, I would like to take this issue. Can you please assign me?

  2. debba commented on Oct 11, 2026

    @debba
    CollaboratorAuthor

    Yeah done @1-DARK

  3. 1-DARK commented on Oct 11, 2026

    @1-DARK
    Contributor

    Thanks @debba. Before I open this for review, how would you like the work structured? The change is large and touches Rust, the CSP, the plugin API and the EXPLAIN call sites, so I think it is easier to review as small, self-contained commits.

    Proposed order, each one buildable and tested on its own:

    Plugin isolation implementation

    I isolated plugin JavaScript from the main app with these changes:

    • Added a Content Security Policy for the app and a stricter guest CSP that blocks network connections.
    • Moved UI extensions and EXPLAIN parser bundles into sandboxed iframes. They communicate with the host through an allow-listed MessagePort API rather than running in the app鈥檚 JavaScript context.
    • Added limited UI plugin APIs for read-only queries, plugin-owned settings, notifications, theme and translation data, iframe-contained modals, and opening HTTP(S) URLs. The guest does not expose Tauri invoke.
    • Changed connection exports to exclude secrets by default and require native confirmation when secrets are requested.
    • Documented the sandbox API and migration behavior, including asynchronous settings writes and iframe-contained modals.
    • Added acceptance tests for the CSP, secret-export guard, RPC allow-list, guest isolation, and migration documentation.

    The plugin guest and frontend builds succeeded, and the full frontend test suite passed. The manual app smoke checks are still pending.

    Could you review this implementation approach and let me know whether it meets the issue鈥檚 acceptance criteria or needs changes?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

feature requesthelp wantedExtra attention is neededroadmapPart of the product roadmapsecuritySecurity hardening or vulnerability fix

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions