* perf: rewrite command palette search to eliminate UI freezes and stale results
Problems fixed:
- clientLoader blocked navigation on every palette open, freezing the UI during
full sequential DB scan
- no AbortController: stale searches could overwrite newer results mid-type
- no debounce: every keystroke triggered a full DB traversal immediately
- React Aria's default contains filter re-filtered server-side fuzzy results,
silently dropping valid matches and making fuzzy work redundant
- no warm baseline: palette started blank until the entire load completed
Changes:
- move fuzzyMatch/fuzzyMatchAll → insomnia-data/common-src/search.ts (shared,
testable in node)
- move search logic → insomnia-data/node-src/services/helpers/command-search.ts
- per-request AbortController cancellation via abort(requestId)
- recursion depth guard (max 20) on request group traversal
- safeParent() replaces non-null assertions, safe against orphaned documents
- add CommandSearchResult type in insomnia-data/src/command-search-types.ts
- add useCommandSearch hook: 250ms debounce + abort on filter change + warm
baseline on mount
- add defaultFilter={() => true} to ComboBox, delegating all filtering to the
service
- delete routes/commands.tsx
* fix
* fix
* feat(templating): PoC run plugin template tags in a QuickJS-WASM sandbox
Behind a new `templateTagSandboxEnabled` setting (default off), route plugin
template-tag execution through a QuickJS-WASM sandbox instead of invoking the
plugin's `run()` directly in the main process.
Approach (per PR #10072): bulk-copy render state into the sandbox as JSON,
rebuild the plugin `context` API in pure JS inside the sandbox, and bridge only
async work back to the host via the existing `pluginToMainAPI` handlers.
`node:crypto` is exposed as synchronous host functions so `require('crypto')`
works without a sync/async mismatch.
- templating/sandbox/: quickjs-runtime, marshal, host-bridge, in-sandbox-bootstrap,
plugin-tag-sandbox (+ parity tests vs in-process tags and node:crypto)
- main/templating-worker-database.ts: route execute handlers through the sandbox
when the flag is on; legacy path unchanged otherwise
- esbuild: keep quickjs-emscripten external so its .wasm resolves at runtime
- settings + scripting-settings UI toggle
- examples/insomnia-plugin-sandbox-demo: manual E2E fixture
Scope: template tags only; sandbox runs in main. require shim covers path + crypto
(other modules throw a clear error — follow-up work).
* test(smoke): e2e canary for the template-tag sandbox flag
Installs an inline probe plugin, renders its tags via the tag editor Live
Preview, and asserts the execution path flips main-process -> sandbox when
templateTagSandboxEnabled is toggled in Preferences > Scripting, with a
require('crypto') sha256 workload staying byte-identical across both paths.
* test(sandbox): suppress hardcoded-hmac-key semgrep finding on parity fixture
The HMAC key is a test vector for sandbox-vs-node:crypto parity, not a
credential; rename it to make that self-evident and add the repo-standard
nosemgrep suppression.
* fix(templating): contain sandbox plugin entry resolution to the plugin directory
Reject a package.json "main" that resolves outside the plugin's own folder
and bundled-plugin names that look like paths, so the sandbox source loader
cannot be steered into reading arbitrary files.
* fix(review): inline nosemgrep placement, plugin-load error context, cross-arch-safe canary
- Move the hardcoded-hmac-key suppression onto the flagged line (line-above
placement was not honored by the scanner).
- Wrap getPluginEntrySource failures with the plugin name for diagnosability.
- Derive the canary's expected arch from the Electron main process instead of
the Playwright runner so cross-arch setups can't flake the assertion.
* sec(templating): QuickJS template-tag sandbox additions (#10209)
* fix(sandbox): enforce timeout on synchronous plugin loops
QuickJS's executePendingJobs() blocks the host thread until a synchronous
call returns, so the wall-clock deadline in drivePromiseToString was never
checked during a tight sync loop in plugin code, hanging the Electron main
process indefinitely. Add a QuickJS interrupt handler, which is polled
during synchronous execution, to enforce the deadline.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(sandbox): clamp crypto.randomBytes size to prevent OOM
hostCrypto.randomBytes(size) passed the sandboxed number straight to
Node's crypto.randomBytes with no upper bound, letting a plugin request
a multi-GB allocation (e.g. crypto.randomBytes(2 ** 31)) and crash the
host process. Clamp to 64KB before the call.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(sandbox): cap QuickJS heap to prevent unbounded allocation
QuickJS.newContext() had no memory limit, so a plugin allocating without
bound could exhaust the WASM heap and crash the host process. Set a 32MB
ceiling via ctx.runtime.setMemoryLimit().
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(sandbox): resolve symlinks before validating plugin entry path
getPluginEntrySource's containment check compared raw path strings, so a
plugin directory with a symlinked entry (e.g. index.js -> ../../../etc/secret)
passed the check while fs.readFileSync followed the symlink and read the
out-of-directory target. Re-run the check against fs.realpathSync'd paths.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(sandbox): close util.render sandbox escape
context.util.render() bridged to the shared render() pipeline, whose Liquid
engine dispatches any registered tag's real run() directly, in-process,
regardless of templateTagSandboxEnabled. A sandboxed plugin could hand it a
string containing "{% anyTag %}" (including its own tag) and have that tag
execute completely unsandboxed. Verified with a PoC that reached
child_process execution from inside a plugin tag with no require() or Node
access.
util.render is now restricted to plain {{ variable }} interpolation (the
only real existing use, confirmed against all built-in tag call sites) via
a second Liquid engine with no tags registered; {% tag %} syntax now fails
to parse instead of dispatching. Default render() behavior is unchanged for
every other caller.
Also drops the dead renderDepth field's misleading doc comment: within one
sandboxed execution the envelope's renderDepth is always 0, so depth could
never exceed 1 regardless of enforcement — it couldn't have caught this
recursion anyway.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(sandbox): fixed linting issue
* fix(sandbox): fixed linting issue
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: kwburns-kong <kyle.burns@konghq.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Move insomnia-data models, services, database code, and common utilities into a dedicated workspace package. Update consumers to import from the new package entrypoints and declare workspace dependencies for the extracted package.