Skip to content

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

Merged
jackkav merged 2 commits into
claude/sandbox-pr12-trust-flipfrom
sec/sandbox-t1-trust-cache
Aug 5, 2026
Merged

fix(templating): (T1) close two plugin-registry trust-cache gaps#10326
jackkav merged 2 commits into
claude/sandbox-pr12-trust-flipfrom
sec/sandbox-t1-trust-cache

Conversation

@kwburns-kong

Copy link
Copy Markdown
Contributor

What this PR does

  • pluginConfig.elevated is keyed by plugin name, not folder. A same-named folder placed alongside an already-elevated plugin inherited its trust grant and ran in-process before any collision was noticed. Now detected via an order-independent pre-pass; colliding folders are refused.

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

Comment thread packages/insomnia/src/plugins/index.ts Outdated
@github-actions

github-actions Bot commented Aug 4, 2026

Copy link
Copy Markdown

✅ Circular References Report

Generated at: 2026-08-04T17:23:15.633Z
Status: ✅ NO CHANGE

Summary

Metric Base (claude/sandbox-pr12-trust-flip) PR Change
Total Circular References 9 9 0 (0.00%)
Click to view all circular references in PR (9)
insomnia-inso/src/db/models/types.ts -> insomnia-inso/src/db/types.ts
insomnia/src/main/prompt-bridge.ts -> insomnia/src/main/window-utils.ts -> insomnia/src/main/plugin-window.ts
insomnia/src/main/window-utils.ts -> insomnia/src/main/plugin-window.ts
insomnia/src/network/network.ts -> insomnia-scripting-environment/src/objects/index.ts -> insomnia-scripting-environment/src/objects/collection.ts -> insomnia-scripting-environment/src/objects/response.ts
insomnia/src/network/network.ts -> insomnia/src/common/render.ts
insomnia/src/ui/components/settings/import-export.tsx -> insomnia/src/ui/components/modals/export-requests-modal.tsx
insomnia/src/ui/components/tabs/tab-list.tsx -> insomnia/src/ui/components/tabs/tab.tsx
insomnia/src/ui/components/templating/tag-editor-arg-sub-form.tsx -> insomnia/src/ui/components/templating/external-vault/external-vault-form.tsx
insomnia/src/ui/components/viewers/response-viewer.tsx -> insomnia/src/ui/components/viewers/response-multipart-viewer.tsx
Click to view all circular references in base branch (9)
insomnia-inso/src/db/models/types.ts -> insomnia-inso/src/db/types.ts
insomnia/src/main/prompt-bridge.ts -> insomnia/src/main/window-utils.ts -> insomnia/src/main/plugin-window.ts
insomnia/src/main/window-utils.ts -> insomnia/src/main/plugin-window.ts
insomnia/src/network/network.ts -> insomnia-scripting-environment/src/objects/index.ts -> insomnia-scripting-environment/src/objects/collection.ts -> insomnia-scripting-environment/src/objects/response.ts
insomnia/src/network/network.ts -> insomnia/src/common/render.ts
insomnia/src/ui/components/settings/import-export.tsx -> insomnia/src/ui/components/modals/export-requests-modal.tsx
insomnia/src/ui/components/tabs/tab-list.tsx -> insomnia/src/ui/components/tabs/tab.tsx
insomnia/src/ui/components/templating/tag-editor-arg-sub-form.tsx -> insomnia/src/ui/components/templating/external-vault/external-vault-form.tsx
insomnia/src/ui/components/viewers/response-viewer.tsx -> insomnia/src/ui/components/viewers/response-multipart-viewer.tsx

Analysis

No Change: This PR does not introduce or remove any circular references.


This report was generated automatically by comparing against the claude/sandbox-pr12-trust-flip branch.

- 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.
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.
@kwburns-kong
kwburns-kong force-pushed the sec/sandbox-t1-trust-cache branch from 5c5b467 to 7d0dd73 Compare August 4, 2026 17:07
Comment thread packages/insomnia/src/plugins/index.ts
@kwburns-kong

kwburns-kong commented Aug 4, 2026

Copy link
Copy Markdown
Contributor Author

7d0dd73 — replaces a .startsWith(base) path-containment check with a path.relative-based one. .startsWith accepts a sibling directory whose name happens to prefix-match the base (e.g. /plugins-example vs /plugins); the new check resolves the relative path and rejects .. or an absolute result. Applied to both the new duplicate-name pre-pass and the pre-existing plugin-path check in the same file, with a direct unit test of the comparison logic.

@jackkav jackkav left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed — approving. Both fixes close real gaps in the T1 trust logic:

  1. Name-collision trust inheritance. pluginConfig.elevated is keyed by plugin name, and load was order-dependent (last-writer-wins in pluginMap), so a colliding folder could shadow an elevated name and run in-process. The order-independent findDuplicatePluginNames pre-pass + fail-closed refusal is the right fix; the pre-pass walk mirrors traversePluginPath's (same isContainedIn, same 'insomnia' in pluginJson gate) so it can't disagree with the real pass. Reading package.json in the pre-pass runs no plugin code. Also nice catch upgrading the startsWith(base) containment check to a path.relative one (/plugins-evil no longer prefix-matches /plugins).

  2. Cached-object leak via a plugin setter trap. (error as any).plugin = plugin would invoke a plugin-defined setter, handing plugin code a live reference to its own cached registry entry to flip directory/elevated. Object.defineProperty (data descriptor) bypasses the setter; the try/catch keeps it best-effort. Correct.

Tests are properly adversarial (prefix-sibling + .. escape for containment, an unrelated folder failing to inherit an elevated name, and the setter-trap not firing with the cached object left unmutated). CI green across all shards.

One deliberate behavior change worth noting for the changelog: a name claimed by two discoverable folders is now refused entirely (with a warning) rather than resolved last-wins. That's the correct fail-closed posture for an elevated-trust key, just a change from the previous silent dedup.

🤖 Reviewed by Claude Code

@jackkav
jackkav merged commit d0d7d3d into claude/sandbox-pr12-trust-flip Aug 5, 2026
29 of 32 checks passed
@jackkav
jackkav deleted the sec/sandbox-t1-trust-cache branch August 5, 2026 05:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants