Skip to content

Design: is the extension permission model a security boundary or trusted-code metadata? #36

@pablopunk

Description

@pablopunk

Decision needed: is the extension permission model a security boundary, or trusted-code metadata?

This is a design discussion, not a task — it's a strategic fork that shapes the
extension platform, so it doesn't fit the atomic-issue model the other improve
issues (#25#35) use.

The situation

Generated extensions are loaded with await import(url.href) in the Electron
main process (src/electron/main.ts, loadExtensionModule/loadExtensions).
That means extension code runs as fully-privileged Node/Electron code at module
import time — before any permission check runs
. Permission gates only affect the
host-provided ctx capabilities, not what top-level extension code can already do.

Consequence: an extension's permissions: [...] manifest is declarative UX, not
a security boundary
. A malicious or AI-misgenerated extension can read/modify the
filesystem, process env, auth state, app memory, spawn child processes, and reach
host internals regardless of what it declares — by running code at import, before
gates apply.

This was flagged as the top item (Critical #1) in the 2026-06-03 security audit
(.plans/security-audit-report.md) and re-confirmed still open at commit
e581e95.

Why it's a fork, not a bug fix

The product thesis is "the AI writes your extensions." So the right answer depends
on a product/trust decision, not just engineering:

Option A — accept it (treat extensions as trusted code):

  • Document explicitly that extension permissions are UX/declarative metadata, not
    a sandbox.
  • Move the security boundary to who/what can author extension files (e.g. only
    the app's own AI flow writes them; guard the extensions directory; consider
    signing/integrity checks).
  • Lowest effort; honest about current reality. Risk: as soon as extensions can
    come from anywhere (sharing, marketplace, copy-paste), the trust assumption
    breaks.

Option B — sandbox generated extensions:

  • Run extension code in an isolated runtime with no Node/Electron primitives
    (e.g. a locked-down worker/vm/separate process with no require/import of
    host modules).
  • Expose only typed host RPCs from main, with permission checks enforced at every
    capability boundary.
  • Highest effort, but makes permissions: [...] a real boundary and lets
    untrusted/shared extensions be safe.

Questions to resolve

  • Will extensions ever come from outside the app's own AI generation (sharing,
    import, a marketplace)? If yes, Option A's trust assumption is temporary.
  • Is permissions: [...] meant to protect the user from the extension, or just
    to describe capabilities? Today it implies protection it doesn't provide.
  • If Option B: what's the minimal viable sandbox (worker vs separate process vs
    vm), and what's the host-RPC surface?

Suggested next step

A short design spike: pick Option A or B, and if B, prototype the isolated runtime

  • host-RPC surface and list open questions. The outcome becomes concrete
    implementation issues.

References

  • src/electron/main.tsloadExtensionModule / loadExtensions
  • src/electron/extension-permissions.ts — current permission gating (ctx-level)
  • .plans/security-audit-report.md — finding [ImgBot] Optimize images #1 (Critical)
  • Related hardening already shipped: extension window sandboxing, webview
    permission allowlist, navigation policy (these reduce blast radius but do not
    close the import-time execution path).

Metadata

Metadata

Assignees

No one assigned

    Labels

    improveSurfaced by the improve skillsecuritySecurity finding

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions