Files
insomnia/packages
Jack Kavanaghandkwburns-kong e759bbceaf feat(templating): (T1) flip trust model — pluginSandboxEnabled + per-plugin elevated opt-in (#10318)
* feat(templating): (T1) pluginSandboxEnabled + per-plugin elevated opt-in, centralized gate

Introduces the trust-model flip's core logic and wires every untrusted-execution surface to it.

- New setting `pluginSandboxEnabled` (default off) that supersedes/absorbs `templateTagSandboxEnabled`:
  either flag on activates the sandbox, so existing template-tag opt-ins keep working (migration bridge).
- New per-plugin `pluginConfig.elevated` escape hatch: a user plugin marked elevated runs in-process
  with full host access even while the sandbox is on. Widened PluginConfig/PluginConfigMap/Plugin.config.
- New pure resolver `common/plugins/sandbox-mode.ts` (isSandboxEnabled / resolvePluginExecutionMode /
  shouldSandboxPlugin) — the single source of truth replacing the 7 duplicated
  `templateTagSandboxEnabled && directory !== ''` conditions. Dependency-free so main, the plugin
  window, and the inso CLI node runtime all share it. Unit-tested (12 cases).
- Wired all surfaces to shouldSandboxPlugin: load-time discovery (now per-plugin, so an elevated
  plugin is nodeRequire-d for live functions), request/response hooks (plugin-window + node runtime),
  actions, and user template tags. Bundle-tag path reads isSandboxEnabled (bundle stays trusted).

No UI yet (next commit); default-off means no behavior change until a flag is toggled.

* feat(templating): (T1) Preferences UI — plugin-sandbox toggle + per-plugin elevated + mode indicator

- Scripting settings: new "Sandbox all plugin code (experimental)" toggle for pluginSandboxEnabled
  (data-testid toggle-plugin-sandbox), beside the existing template-tag toggle.
- Plugins settings: each user plugin card now shows its resolved execution mode
  (Sandboxed / Elevated / In-process, data-testid plugin-mode-<name>) and a "Full host access"
  checkbox (data-testid plugin-elevated-<name>) that writes pluginConfig.<name>.elevated. Mode +
  toggle read from live settings so they update immediately, before the plugin list reloads.
- Widened SerializablePlugin.config to carry the optional `elevated` flag through the bridge.

* test(templating): (T1) e2e — pluginSandboxEnabled sandboxes a user plugin; elevating runs it in-process

Composes the trust-flip's two user-visible behaviors on one action-probe plugin:
- Enabling the new pluginSandboxEnabled toggle (not the legacy template-tag flag) routes the user
  plugin's action into the sandbox (canary reports ranin-sandboxed).
- Toggling "Full host access" in Preferences → Plugins flips the mode indicator to "Elevated" and the
  same action then runs in-process (marker absent, ranin-mainprocess) — the per-plugin escape hatch.
Reuses the sandbox-action-collection.yaml fixture and the established writePlugin/clearPluginToast
helpers; adds enablePluginSandbox + setPluginElevated helpers.

* fix(templating): (T1) close two plugin-registry trust-cache gaps (#10326)

* fix(templating): close two plugin-registry trust-cache gaps

- pluginConfig.elevated is keyed by declared plugin name, not by folder;
  a same-named folder placed alongside an already-elevated plugin
  inherited its trust grant and ran in-process before any collision was
  even noticed. traversePluginPath now pre-scans for duplicate names
  (order-independent) and refuses to load any colliding folder.

- applyRequestHooks/applyResponseHooks tagged a caught error with
  `error.plugin = plugin`, a plain assignment that a plugin-thrown Error
  could intercept via its own `plugin` property setter, handing the hook
  a live, mutable reference to its own cached registry entry and letting
  it flip `directory`/`config.elevated` to defeat later sandboxing.
  Switched to Object.defineProperty, which bypasses any such setter.

* fix(plugins): use relative-path containment check instead of startsWith

A bare .startsWith(base) on a resolved path accepts a sibling directory
whose name happens to prefix-match the base (e.g. /plugins-evil vs
/plugins). Added a shared isContainedIn helper (path.relative, rejects
.. or an absolute result) and applied it to both the existing
plugin-path containment check and the new duplicate-name pre-pass.

* fix(templating): (T1) address Copilot review — skip CLI settings read, scope bundle-tag sandbox to legacy flag

- network-adapter.node.ts: only read services.settings.get() when canSandbox (Electron). The pure-Node
  inso CLI can never reach the sandbox host, so the read was wasted work; pass undefined otherwise
  (shouldSandboxPlugin treats it as off). Both applyRequestHooks and applyResponseHooks.
- templating-worker-database.ts: bundle (first-party/trusted) template tags now sandbox only under the
  legacy templateTagSandboxEnabled experiment, not the new pluginSandboxEnabled. The T1 flag isolates
  *untrusted* plugins; sandboxing trusted bundle tags under it was an unintended behavior change.
  Dropped the now-unused isSandboxEnabled import.

---------

Co-authored-by: kwburns-kong <kyle.burns@konghq.com>
2026-08-05 14:02:45 +00:00
..