6607 Commits
Author SHA1 Message Date
Bingbing f6f3849594 refactor: extract useRootLoaderData out of root.tsx (break 55 circular imports) (#10494)
[INS-3831](https://konghq.atlassian.net/browse/INS-3831)

## Summary
- Extract `useRootLoaderData` + `RootLoaderData` from `src/root.tsx` into `src/ui/hooks/use-root-loader-data.ts`.
- Repoint all 65 importers from `~/root` to the new module, plus the two `vi.mock('~/root')` test stubs; remove the now-dead exports (and divergent stale shape) from root.tsx; annotate `clientLoader` return as `Promise<RootLoaderData>`.
- Hook return type keeps the documented `| undefined` contract of `useRouteLoaderData`.

## Why
root.tsx imports modals/settings/codemirror modules which import `useRootLoaderData` back — 55 of the 83 baselined cycles close through this single edge.
2026-10-29 10:55:26 +00:00
George d6e9ea3ade refactor: improve scratchpad tutorial panel snooze time (#10672)
* chore: improve scratchpad tutorial panel snooze time

* fix: comment
2026-10-29 08:32:05 +00:00
Bingbing f4d768160b refactor: inject curl executor into scripting environment (#10483)
[INS-3767](https://konghq.atlassian.net/browse/INS-3767)

## Summary
- Remove the scripting environment dependency on Insomnia's libcurl module.
- Inject a curl request executor into InsomniaObject instead.
- Keep node execution on the node curl implementation and renderer execution on the hidden-window bridge.
- Enable TypeScript checking for the scripting-environment source after the application runtime dependency is removed.

## Risk isolation
This is intentionally stacked on #10482 so the curlRequest plumbing can be reviewed separately.
2026-10-29 07:59:13 +00:00
yaoweiprcandClaude Sonnet 5 547a7f291a fix(Git Diff): clarify invalid file message in visual diff view (#10677)
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
2026-10-29 07:26:59 +00:00
Curry Yang 1d42a66ac8 chore: update dompurify (#10667) 2026-10-28 10:21:22 +00:00
Bingbing 7863413757 fix: clear stale sync flags after a project leaves cloud sync [INS-3899] (#10668)
## Background

Workspace `hasUncommittedChanges` / `hasUnpushedChanges` flags are written only by cloud sync. When a project stopped being a cloud project, those flags were never cleared, so the project overview cards kept showing "Uncommitted changes".

Repro:

1. Open a cloud project whose workspaces have uncommitted changes.
2. Remove the project from the remote.
3. Open the project overview. The cards still show "Uncommitted changes" although the project is now local.

A project converted from cloud to git carries the same residual flags, because the values come from its former cloud state.

## Changes

- Clear both workspace sync flags when a project's `remoteId` becomes `null` in the project service. Remote deletion, conversion to local, cross-organization move, and conversion to git all update the project through this service.
- Reuse the cleanup in the startup database repair to fix existing stale records. Only dirty workspace metas are written, and `modified` is preserved.
- Add service and repair tests.

## Testing

- `npm test -w insomnia-data` (538 tests)
- `npm run type-check -w insomnia-data`
- `npm run lint -w insomnia-data`

## References

- INS-3899
2026-10-28 09:58:34 +00:00
kwburns-kong 1251e39523 fix(sync): INS-3700 (#10560)
* fix(sync): enforce write containment against symlinks in git sync

isomorphic-git checkout could create a symlink whose relative target
resolves outside the repo's working directory (fs-client.ts had no
containment check on symlink targets). Later, automatic disk flushes
(project lint ruleset, workspace YAML exports) wrote through that path
with a plain fs.promises.writeFile, which follows symlinks, letting
committed repo content land on a file outside the repo.

- fs-client.ts: reject symlink targets that resolve outside the repo
  during checkout.
- Add main/safe-fs-write.ts: a shared write helper that opens the final
  path component with O_NOFOLLOW and verifies (via realpath) that no
  symlinked intermediate directory can redirect the write outside the
  intended base directory.
- Apply it to every write site that joins a stored/imported relative
  path onto a project directory: repo-file-watcher.ts's four flush
  writers, the clone-flow ruleset write in git-service.ts, and
  git-repo-migration.ts's workspace export writer.
- Sanitize gitFilePath at its source (common/import.ts) so an imported
  workspace/collection name can no longer inject a path separator or
  traversal sequence.
- Add the same containment check to backup.ts's restoreBackup, which
  joins a renderer-supplied version string onto the backups directory
  with no prior validation.

Each fix has a regression test verified to fail against the prior
code and pass against the fix.

* fix(sync): surface writes blocked outside the git working directory

isomorphic-git's checkout write phase catches each file/symlink write
error itself and never rethrows, so a containment refusal during
checkout previously had no way to reach the user. Report it instead:

- fs-client.ts: call an onBlockedWrite hook before throwing.
- git-service.ts: route it into a pull's own result while a pull is in
  flight, otherwise record it as a file problem.
- git-vcs.ts: pullWithConflictSupport collects and dedupes blocked
  writes and folds them into that pull's own success/failure result.
- repo-file-watcher.ts: the DB→FS flush now reports and clears
  write-blocked problems per path instead of only logging them.
- git-project-sync-dropdown.tsx: toast once per newly-blocked path for
  writes blocked outside of an active pull.

* fix(sync): don't reject in-repo files whose name legally starts with ..

path.relative() returns a string starting with .. for a legitimate
filename like ..notes.yaml inside the base directory, not just for
a real traversal. The lexical containment check in fs-client.ts and
safe-fs-write.ts was misreading that as an escape attempt, so status,
pull, and push failed (and checkout silently dropped the file) for
such filenames. Only treat the relative path as an escape when it is
exactly .. or starts with a .. path segment.

* fix(sync): refuse a symlinked leaf on Windows before opening it

O_NOFOLLOW is unavailable on Windows, so the open() call there
followed a symlink at the final path component instead of refusing
it. Windows has allowed unprivileged symlink creation via Developer
Mode since 10 1703, so a crafted checkout's commit can still plant
such a symlink. Add an lstat pre-check gated on O_NOFOLLOW being
unavailable, refusing the write if the leaf is a symlink.

* fix(sync): show the real error in the write-blocked toast

Every write failure from the DB→FS flush (permission errors, disk
full, etc.) was labeled as a write blocked outside the git working
directory, hiding the actual cause from the user. Show the
underlying problem message alongside the path instead.

* fix(sync): report blocked writes with the correct client base path

The workspace-scoped FS client's onBlockedWrite handler always
resolved relPath against baseDir, even when reporting for the
"other" data client, which is actually rooted at baseDir/other. A
write blocked by that client recorded a filePath/relPath pair that
didn't match the real target, misleading the write-blocked problem
list. Bind the handler to each client's own base.

* fix(sync): skip orphan removal on pull when writes were blocked

importAllFiles ran before pullFromGitRemote checked pullResult for
blocked writes, so removeOrphanedWorkspaces could delete a
previously-synced workspace whose YAML checkout left disk in an
inconsistent state (file removed, replacement write refused) —
before the pull ever reported failure. Add a skipOrphanRemoval
option and set it whenever the pull reports blocked writes.

* fix(sync): surface blocked writes during the initial clone

The RepoFileWatcher isn't started until after initFromClone runs, so
its reportWriteBlocked was a no-op for the whole clone: a repository
whose checked-out commit held an escaping symlink or path silently
dropped that file and the clone still reported success. Collect
blocked writes directly around the clone call and return them as a
failure, matching how pull already reports them.

* fix(sync): harden fs-client symlink containment against pre-existing symlinks

resolveWithinBase only did a lexical ..-check, unlike safe-fs-write.ts's
assertPathWithinDir which also resolves ancestor-directory symlinks. A
directory or leaf file inside basePath that's already a symlink pointing
outside it (planted by some other mechanism) could let reads/writes
transparently escape basePath. Reuse assertPathWithinDir for the ancestor
check, and add a leaf-symlink guard for readFile/writeFile/stat.

* fix(git-sync): allow the repo working dir itself in safe-fs-write containment check

assertPathWithinDir checked the parent of the target, so when the target was the base dir itself (isomorphic-git stats/readdirs '.') the parent resolved outside the base and was rejected as a symlink escape, breaking git sync. The base is now treated as the trusted root. Also tighten the realpath '..' prefix check so names like '..foo' are not misclassified. Adds regression tests for '.', symlinked base dir, and in-repo symlink escape still being blocked.
2026-10-28 09:35:22 +00:00
yaoweiprcandClaude Sonnet 5.5 9cfd499185 fix: upgrade node-forge to 1.4.2 for CVE-2026-85393 (#10669)
* fix: upgrade node-forge to 1.4.2 for CVE-2026-85393 (INS-3936)

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* fix: upgrade node-forge to 1.4.2 in component-docs lockfile (INS-3936)

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-28 08:32:49 +00:00
Kent Wang eecc5b004e fix: handle invalid vault tag when there is no args set (#10663)
* handle invalid vault tag when no args is set

* add a errorboundary
2026-10-27 14:43:27 +08:00
Jay WuandClaude Sonnet 5.5 d94f8dbf72 ci: apply macOS stapling workaround before packaging app (#10661)
The pfctl rule that blocks QUIC (UDP 443) was only applied before the
Inso CLI stapling step. The app packaging step also staples through
https://api.apple-cloudkit.com and hits the same issue, so run the
workaround before "Package app (MacOS only)" instead.

Ref: actions/runner-images#14924

Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-27 06:03:42 +00:00
Kent Wang 038eb606ed fix: Request name is applied to request list css (#10659)
* fix request name is applied to request list css issue

* using test id instead
2026-10-27 03:07:47 +00:00
Kent Wang 96edcab595 fix: Hashcorp vault form to support the new credential pattern (#10658)
* fix credential id issue

* add unit test

* fix issues from comment
2026-10-26 10:42:49 +00:00
GeorgeandClaude Sonnet 5.5 7dc422dfac fix: closing modal when deleting local proj workspace (#10657)
Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-26 09:34:51 +00:00
Kent Wang 0fe6a0c576 fix: MCP primitive UI issue when returning item with same name (#10655)
* fix mcp item with same name issue

* fix selected issue
2026-10-26 16:11:29 +08:00
Bingbing 9a468f990f fix: keep variable source tooltip interactive in the environments modal (#10650)
## Background

Inside the Manage Environments modal, hovering a template tag shows the variable-source tooltip but the bubble cannot be hovered, focused, or clicked, so its **Open** action does nothing. The bubble is portalled to `<body>`, outside the modal subtree, and react-aria marks everything outside an open modal inert.

Repro:

1. Open a collection's Manage Environments modal.
2. Hover a template tag in an environment value.
3. Hover the tooltip or click Open — no response.

Related issues found while fixing it:

- Clicking Open on a folder-environment source navigated the app but left the modal mounted over the destination.
- Once another environment was selected, Open for the same source did nothing, because the modal only reacted to a changed environment id.
- On the project environment page, the `?environmentId=` deep link could stop reapplying after an interrupted cleanup, so a repeated Open left the page on the previous environment.

## Changes

- Mark the tooltip bubble as a react-aria top layer (`data-react-aria-top-layer`), so an open modal does not make it inert or treat clicks inside it as outside interactions.
- Track the location key the environments modal was opened on and close it on any navigation, including search-only changes such as `?tab=environment` or `?environmentId=`.
- Keep the environments modal mounted and carry a request id with each open request, so every Open reselects the requested environment.
- Reapply the project environment page's `?environmentId=` selection on every navigation, not only when the search string changes.
- Add smoke coverage: Open works inside the modal, repeated Open reselects the source, no second dialog for a collection source, the modal closes on path and search navigation, and project-environment sources land on the environment page with the source selected.

## Testing

- `npm run test:smoke:dev -- tests/smoke/variable-source-live-preview.test.ts tests/smoke/environment-cascade.test.ts tests/smoke/global-environments.test.ts`
- `npm run type-check -w insomnia`
- eslint on changed files

New smoke cases were verified red before their fix and green after. The existing hover tooltip cases now reuse the hover retry helper to remove a flake.

## References

- INS-4113
2026-10-26 07:08:23 +00:00
Kent Wang 992605adf9 fix: Loose the id meta check for certain request models (#10656)
* loose the id meta id check

* update schema
2026-10-26 14:43:34 +08:00
Bingbing 121ab68e17 fix: attribute project environment page sources to the project [INS-4083] (#10649)
## Background

On a project environment page, a variable that resolves from the environment file's own base or sub environment showed `Collection Environment : <name>` as its source. The rendered value and the `Open` navigation were already correct, so only the source type was wrong.

Repro: open a project environment page, select a sub environment whose value references a base environment variable, then hover or click the variable tag. The source reads `Collection Environment` instead of `Project Environment`.

## Changes

- `getRenderContext` now tells `buildRenderContext` whether the rendered workspace is a project environment file.
- `buildEnvironmentRenderLayers` labels that workspace's own base and sub environment layers as `Project Environment` in that case.
- Merge order, source metadata ids, vault and confidentiality handling are unchanged.

## Testing

- New unit test in `render.test.ts` and a smoke test with a dedicated `project-environment-source.yaml` fixture; both fail without the fix.
- `vitest run src/common`: 831 passed.
- `tsc` and `eslint` pass on the changed packages.
- `variable-source-live-preview` smoke suite: 18 passed (one unrelated fixture-import flake passed on retry).

Refs: INS-4083, INS-3943
2026-10-26 03:13:02 +00:00
Kent Wang 88ae88d6e5 add unite test for mcp, focus on undici upgrade (#10648) 2026-10-23 15:17:42 +08:00
Jay Wu 61922aea77 fix(ci): work around macOS stapling issue in release build (#10647)
Block outgoing UDP on port 443 before stapling the Inso CLI installer,
as a workaround for actions/runner-images[#14924](https://github.com/actions/runner-images/issues/14924).
2026-10-22 21:56:08 +08:00
Kent Wang fee93b675a fix issues caused by undici bump (#10645) 2026-10-22 09:24:47 +00:00
George 25858ff7c8 fix(one-line-editor): after clicking add button, focus should be on the new one line editor (#10631)
* fix: after clicking add button, focus should be on the new one line editor

* fix: comment
2026-10-22 08:25:03 +00:00
yaoweiprcandClaude Opus 5.5 95fcf1b4fc feat(Git Diff): Warn when a file isn't in the format Insomnia writes [8/8] (#10641)
* feat(Git Diff): warn when a file isn't in the format Insomnia writes

The Visual view matches entities by id and splices them into a file written
the way Insomnia exports it, so a file in any other shape, eg. edited by hand,
can show incomplete cards, and acting on a card rewrites the whole file.

Each previewed diff now checks both sides by replaying them through Git Sync's
import -> export: a side that doesn't match the Insomnia file format shows a
note pointing to the Text view instead of cards, and a valid side Insomnia would
write differently (comments, formatting, order, missing or duplicated parts,
unrecognized fields, another version) shows a warning above the cards.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* fix(Git Diff): show one format warning at most above the diff

A file with unrecognized fields, or one whose change is only Insomnia
reformatting it, already gets its own warning above the diff, which says more.
The Visual view's format warning no longer stacks on top of those, and its text
is shorter.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* perf(Git Diff): check a file's format in the background, not in diffFileLoader

The format check replays both sides of the diff through import -> export, which
held up every diff, including in the Text view. It's now a separate loader the
Visual view calls after rendering its cards, so the diff shows right away and
the warning follows once the check is done.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-22 08:04:44 +00:00
George 7498c30b57 fix(sidebar): scope the del shortcut without backspace (#10644)
* fix: scope the del shortcut without backspace

* test: add e2e
2026-10-22 07:49:51 +00:00
Curry YangandClaude Opus 5 59d832ba10 Fix/socket io&websocket payload in export (#10634)
* fix: carry Socket.IO and WebSocket payloads through the v5 format [INS-3123]

A Socket.IO request's body (event name, ack, args) lives in its own
SocketIOPayload document rather than on the request, and the Insomnia v5
YAML format did not represent it at all. WebSocketPayload has the same
shape and the same gap.

Duplicating a collection goes through a v5 export/import round-trip, so
the payload was silently dropped and the copy came back with an empty
Body tab while url and headers survived.

The same gap is worse under Git Sync projects: `deleteOrphans` removes
every workspace descendant that is `canSync` and absent from the imported
file, and the importer could never produce a payload. A `git pull`, a
checkout, an edit to any part of the YAML, or merely restarting the app
therefore deleted them. Editing a payload in the app never wrote to disk
either, since the exported YAML did not change, so those edits only ever
existed in the database until the next sync removed them.

Both request nodes now carry an optional `body` holding the payload
document's own id, so a re-import updates the same document instead of
orphaning it. An untouched payload is omitted entirely: opening a
Socket.IO request creates an empty payload document, and writing that out
would add a spurious block and a git diff to every repository.

The branches of `RequestCollectionSchema` are told apart only by the
`meta.id` prefix, and `z.object` strips unknown keys rather than
rejecting them, so a node without `meta` matched the WebSocket branch and
lost its Socket.IO-only keys — `eventListeners` already went this way.
Extend the existing `z.undefined()` guard idiom to cover them.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* fix: export Socket.IO requests from design documents

The design-scope export filter listed request groups, HTTP, WebSocket and
gRPC requests but not Socket.IO ones, so a Socket.IO request created in a
design document vanished from its YAML.

Under a Git Sync project that is data loss rather than a missing field:
the request is absent from the imported file, so `deleteOrphans` removes
the request document itself on the next sync.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* test(smoke): assert the Socket.IO body survives import

The fixture had a Socket.IO request whose body the v5 format could not
express, so the smoke test connected and sent nothing. Give it an event
name and a payload, and assert both are populated after importing.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* fix comment

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-10-22 07:30:58 +00:00
Curry YangandClaude Opus 5.5 1a5a33aaed fix: keep confidential flags through v5 export/import and Git Sync (#10632)
* fix: keep confidential flags through v5 export/import and Git Sync

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* update schema

* fix comment

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 06:31:11 +00:00
5c14ddfa81 fix(vul): update grpc reflection deps (#10642)
* chore: update google-protobuf to 4.0.3 (INS-3888)

google-protobuf <4.0.3 has no depth limit when skipping unknown group
fields during binary deserialization, so a crafted payload of nested
START_GROUP tags overflows the call stack (AIKIDO-2026-834673).

It is only a transitive dependency (grpc-reflection-js at runtime,
@ravanallc/grpc-server-reflection in the smoke tests) and both declare a
^3.x range, so force 4.0.3 through root overrides. npm does not apply the
global override to the @ravanallc edge, hence the scoped override.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* chore: replace @ravanallc/grpc-server-reflection with @grpc/reflection (INS-3888)

@ravanallc/grpc-server-reflection is unmaintained (last release 2022) and
depends on google-protobuf ^3, which forced a scoped override to 4.0.3 for
the unknown-group recursion DoS fix.

The official @grpc/reflection uses protobufjs and does not depend on
google-protobuf. The smoke test server already loads the proto through
@grpc/proto-loader, so it builds the reflection service from that package
definition and the pre-built route_guide.bin fixture is no longer needed.

The scoped override is removed. Only the runtime grpc-reflection-js fork
still needs the global google-protobuf override.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* chore: update grpc-reflection-js to 0.4.0 and drop google-protobuf override (INS-3888)

grpc-reflection-js 0.4.0 depends on google-protobuf ^4.0.3, which contains
the fix for the unknown-group recursion DoS. The global google-protobuf
override is no longer needed, so remove it and refresh the lockfile to the
new fork commit.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

---------

Co-authored-by: yaoweiprc <6896642+yaoweiprc@users.noreply.github.com>
Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-22 06:00:01 +00:00
yaoweiprcandClaude Opus 5.5 1a0c2ad2db fix(Git Diff): don't let stage / unstage / discard actions overlap (#10638)
A card's Stage clicked right before "Stage this file" ran both at once: they
rewrite the same index entry, and whichever finished last won, so the file
could end up with only that one entity staged. The cards' busy guard didn't
cover the file-level buttons.

Every stage / unstage / discard in the commit modal now goes through one
gate. A card's action is skipped while another is in flight (it's computed
from the diff that one is about to change); file-level actions don't depend
on the diff on screen, so they wait for it instead of being dropped.

Fixes INS-4079.

Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-22 13:05:46 +08:00
yaoweiprcandClaude Opus 5.5 b647e8d515 fix(Git Diff): no Discard for an added base environment or cookie jar (#10637)
Insomnia recreates a workspace's base environment and cookie jar as soon as
they're missing, so discarding one that was just added (eg. in a new file)
was undone the next time the collection was opened. Their cards no longer
offer Discard; the file-level Discard removes a new file as a whole.

Fixes INS-4076.

Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-22 12:39:40 +08:00
Kent Wang 53b9cf5ac0 feat: Show validation errors for invalid MCP tool/resource/prompt list responses (#10554)
* show primitive error detail

* fix type failures

* update the error content

* make update and refresh primitive could handle mcp server error properly

* fix issues from comment

* fix errors

* fix empty error issue

* fix issue from bugs

* add related unit test
2026-10-22 04:04:21 +00:00
Curry Yang c9b20dd809 fix/confidential variable (#10629)
* fix: url preview

* fix: mask confidential value in auth header

* hide eye icon when there are no confidential values

* fix comment

* fix

* fix: keep timeline lines parseable when redacting short confidential values

* fix: url preview prefix

* fix: url preview in codegen & copy as curl scenario

* fix: smoke test
2026-10-22 11:39:22 +08:00
0eb0410769 feat(Git Diff): discard individual entities, and write results the way Git Sync exports them (#10619)
Unstaged cards get a click-to-confirm Discard that rewrites only that entity in
the working-tree file and syncs it back into the database; discarding a folder
that only exists in the working tree warns that its contents go with it.

Before a per-entity stage, unstage or discard is written, the grafted file is
replayed through Git Sync's import -> export in a scratch in-memory database,
so it comes out exactly as Insomnia would export it. Files that can't be
replayed, or whose replay would add placeholder entities, are written as is.

Co-authored-by: James Gatz <jamesgatzos@gmail.com>
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-22 03:23:03 +00:00
Bingbing 319c18e11f fix: ignore inherited keys when resolving variable source (#10640)
keyContextMeta is a plain object, so keys such as constructor or __proto__ resolved through Object.prototype and produced an empty Source row. Require an own property before indexing it, and cover the case in the smoke test.
2026-10-22 02:22:56 +00:00
yaoweiprcandClaude Sonnet 5.5 db419cdb87 feat: Debug Mode setting that enables the Git commit diagnostic panel [INS-3829] (#10470)
* diagnostic panel

* ci: temporarily enable code signing for PR #10470 recurring builds

* Revert "ci: temporarily enable code signing for PR #10470 recurring builds"

This reverts commit d10322c979.

* feat: gate the Git change analysis behind a Debug Mode setting [INS-3829]

Add a per-user `debugMode` setting, toggled from Help > Enable/Disable Debug
Mode (right under Show App Logs Folder). The Git commit dialog's change
analysis is now only computed and rendered while it is on.

- diffFileLoader skips buildInsomniaChangeReport and returns no diagnostics
  when debugMode is off, so the dialog pays nothing for it.
- The menu label follows the setting across every open window via db.onChange.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* fix: key change-report containers by parent id instead of display path

The container identity embedded the parent's display path, so renaming a
folder or collection made every descendant look moved and skipped reorder
detection inside the renamed parent, and same-named parents were merged into
one reorder group. Key containers by the parent id and keep the path only as
a display label.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-21 13:53:50 +00:00
9d2222cc7c feat(Git Diff): Stage and unstage individual entities [4/5] (#10618)
* feat(Git Diff): stage and unstage individual entities from the Visual diff view

Every card gets a Stage (or Unstage) button. The file's index entry is set to
the base side with only that entity grafted in from the other side, written
straight into git's index so the rest of the file's changes stay where they
are. Staging the last entity of a deleted file stages the deletion, and
unstaging the last entity of a new file takes it out of the index.

Co-authored-by: James Gatz <jamesgatzos@gmail.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* chore: retrigger CI

A queued e2e re-run on the previous commit got stuck and kept the required
checks from being reported. An empty commit starts a fresh set of runs.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: James Gatz <jamesgatzos@gmail.com>
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-21 20:14:33 +08:00
Curry Yang 4c35c25626 feat/option toggle prompt (#10607)
* change option text

* add space between icon

* add option toggle prompt

* fix smoke test

* fix comment

* update response clear UI
2026-10-21 18:32:18 +08:00
Bingbing b6c7a0c53c fix: sync variable source with live preview edits (#10639)
## Background:
The Edit Variable modal captured a variable's source when its chip was clicked and kept it for the modal's lifetime. Switching the selected variable or editing a custom template refreshed the Live Preview but left the Source row and Open action pointing at the previous environment.

## Changes:
- Derive the source from the current template via the render context inside VariableEditor; hide the row unless the template is exactly one known variable reference.
- Ignore superseded async runs so preview, error, options and source always describe the same template.
- Drop the one-shot variableSource option from NunjucksModal; onOpenSource now receives the freshly derived metadata.
- Cover variable switching, custom-template editing, hiding and Open in the variable-source smoke test.

Follow-up to #10583.
2026-10-21 10:14:48 +00:00
George 59931459f5 feat: support del shortcut in the sidebar (#10633) 2026-10-21 17:11:08 +08:00
Curry YangandClaude Opus 5 f86e37397d feat: handle websocket socket.io mcp response (#10589)
* feat: handle websocket socket.io mcp response

* update

* feat: redact Event Stream timeline, event log and response document

T10 wired WebSocket, Socket.IO and MCP, but Event Stream was missed: the
connect route's SSE branch sits in the same function as the other three and
never passed sensitiveValues on to curl.open, and curl.ts had no redaction at
all. Its writes happen in the main process, so they bypass the renderer's
SendActionRuntime wrapper the same way the other realtime protocols do.

The renderer side already produced the values - request-url-bar's sendOrConnect
renders an Event Stream request through renderRealtimeConnectPayload like any
other realtime request - so only the consumer was missing.

curl.ts now mirrors websocket.ts: a per-requestId Set that only grows, with
every timeline and event-log write going through writeTimelineEntry /
writeCurlEventLog, and the set dropped in deleteRequestMaps. The two Response
documents are covered too: the effective URL on the success path and the error
message on the failure path.

Error messages need care about ordering. Both error paths call deleteRequestMaps
- which clears the request's registered values - before building the error
Response, so redacting inside createErrorResponse would be a no-op. The message
is redacted before deleteRequestMaps instead and passed down already clean; a
curl error readily echoes the rendered URL.

Verified with the existing event-stream smoke specs. The confidential-redaction
smoke spec lives on the upstream branch, so SSE has no targeted on-disk
assertion until this branch is rebased onto it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* fix: only redact realtime Basic Auth when the credentials are confidential

base64(user:pass) is a derived form - it does not contain the plaintext
password, so it never matches the values the renderer registered and has to be
registered separately. T10 did that for WebSocket, but gated it on the setting
alone, so with the setting on (the default) every WebSocket Basic Auth header
was masked in the console, including credentials nobody had marked. The
websocket smoke spec, which asserts a literal credential stays readable, has
been failing on that.

Registration is now conditional on an input actually being confidential, which
the main process can tell by running its own registered values over the
username and password. The setting check goes away with it: when the setting is
off the renderer sends no values, so the set is empty and nothing ever looks
confidential - one less switch that can drift out of sync.

Event Stream had the mirror-image problem and gets the same treatment. It builds
its auth header in main through the shared getAuthHeader, after the render that
registered the credentials, so until now the base64 of a confidential password
went to disk in the clear. Socket.IO and MCP are unaffected: neither builds an
auth header in main, and Socket.IO's singleToken is rendered in the renderer, so
its plaintext is registered normally. The same derived-form gap still applies to
the plain HTTP send path, which is the known limitation already recorded in the
plan.

Both directions are pinned, because only one of them fails loudly if the
predicate is wrong: the existing websocket spec covers "a literal credential
stays readable", and a new case in the confidential-redaction spec covers "a
credential derived from a confidential value is masked" - without the latter, a
predicate that always returned false would pass silently.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* fix comment

* extract common function

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-10-21 16:14:33 +08:00
634af33e58 feat(Git Diff): add Visual diff cards for the remaining entity types (#10617)
Purpose-built cards for WebSocket, Socket.IO and gRPC requests, the MCP
request, mock routes, the cookie jar and environments, each with tabs that
mirror its editor. Vault-backed secrets are always masked.

Co-authored-by: James Gatz <jamesgatzos@gmail.com>
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-21 14:45:31 +08:00
Bingbing 0ebcd43654 refactor: decouple scripting environment imports (#10482)
[INS-3767](https://konghq.atlassian.net/browse/INS-3767)

## Summary
- Remove scripting-environment imports from Insomnia application modules.
- Move shared faker, URL helpers, and response reason data into appropriate insomnia-data modules.
- Move certificate matching behavior and its test into the ClientCertificate model.
- Update all callers and remove obsolete module paths.

## Verification
- insomnia-data type-check and lint
- insomnia-scripting-environment lint and tests
- insomnia type-check
- URL/certificate tests: 50 passed
- circular-reference check: no new circular dependencies; insomnia-scripting-environment has 0 cycles

The existing scripting-environment tsconfig is intentionally unchanged here; its source-wide type-check scope is enabled in the stacked sendRequest PR after the remaining Insomnia runtime dependency is removed.
2026-10-21 14:29:55 +08:00
Jay Wu 165f7ce06e chore: update undici version (#10635) 2026-10-21 13:51:14 +08:00
7faa34c0b9 feat(Git Diff): Add a read-only Visual diff view [2/5] (#10616)
* feat(Git Diff): add a read-only Visual diff view to the commit modal

Adds a "Visual (Beta)" toggle next to the Text diff in the staging modal. The
Visual view lists the changed entities of the previewed file as cards: requests
and folders get tabbed cards that mirror their editors, every other entity uses
a generic card for now. Each card has a Settings catch-all for changes no tab
shows, and long values are shown as a lazily created Monaco diff.

Also stops the Text view from hiding indentation-only changes, which are
structural in YAML.

Co-authored-by: James Gatz <jamesgatzos@gmail.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* test(Git Diff): show same-named params as rows, not via the Settings catch-all

With same-named items told apart in the diff, a change to one of them now
shows as its own row in the tab rather than as a raw value in the Settings
catch-all. The catch-all stays for changes a tab can't show as rows, such as
reordering.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* fix(Git Diff): scroll long values inside a card instead of stretching it

A long value shown on one side only (eg. the body of a newly added request)
and a highlighted diff of a large change both grew to their full height, so one
card could be thousands of pixels tall. Both are now capped at `max-h-96` and
scroll inside; past their top or bottom the wheel scrolls the card list.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* fix(Git Diff): start a card collapsed when it has nothing below its header

A card with no tabs (eg. only the name or URL changed) only had "No further
details to show." below its header, taking up a row for nothing. It now starts
collapsed; expanding it still shows that line.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: James Gatz <jamesgatzos@gmail.com>
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-20 18:33:09 +08:00
Kent Wang 07a0b24adf feat: Add F2 shortcut to rename entity name on sidebar (#10614)
* support rename by f2 shortcut on sidebar

* fix issues from comment

* refine the modal
2026-10-20 09:53:21 +00:00
Curry YangandClaude Opus 5 23d1b1a2e7 feat: mask confidential value in timeline (http request & script) (#10519)
* feat: mask confidential valut in timeline

* mask longest first

* handle script log

* prevent circular dependency

* fix comment

* fix test

* feat: close the remaining confidential-value leaks to disk

Covers items #2-#5 and #7a of the post-T10 follow-up list.

Register values the script itself wrote (#2)
  A pre-request script's console output is written before any render, and an
  after-response script is followed by no render at all, so a value the script
  put into a confidential key (insomnia.environment.set('token', ...)) was never
  registered and reached the timeline in cleartext. tryToExecuteScript now
  registers the script's output data before writing its logs.

  The registration logic moves into registerConfidentialValues(layers, collector),
  which takes confidential flags from kvPairData and values from the layer's data
  - the same split buildRenderContext already uses at render time. The pre-fill
  now goes through it too, so there is one implementation rather than two, and
  disabled rows, SECRET rows and duplicate names behave identically in both.

Register numeric leaves (#5)
  collectLeafStrings only collected strings, so a confidential JSON value like
  { "pin": 1234 } rendered into the request as plaintext. Renamed to
  collectLeafValues; numbers register as String(value). Booleans, null and
  non-finite numbers stay skipped - they carry no secrecy and would mangle
  unrelated JSON. No minimum-length threshold, matching the trade-off already
  accepted for short strings: over-redaction beats an unprotected short secret.

Redact response documents and test results (#3, #4)
  Response documents are persisted without passing through the timeline's
  withRedaction wrapper. Their url (the effective URL), error (most often a
  script throw, which readily echoes a value - chai prints the actual value, and
  V8 echoes its input for things like JSON.parse(token)) and requestTestResults
  (user-supplied test names and chai's ACTUAL/EXPECTED) are now redacted at every
  write point, including the RunnerTestResult the collection runner persists.

  {% response 'url' %} now throws when it finds a redacted URL instead of
  silently chaining the mask to the server. Storing the real URL and masking only
  on display was the alternative, but that keeps plaintext secrets on disk.

Redact the four send-bypass paths (#7a)
  The OAuth2 token request, plugin sendRequest, the templating worker's
  dependent-request path and mock send all call sendCurlAndWriteTimeline without
  a runtime. That does not skip the timeline - it falls back to the non-redacting
  default - so all four wrote rendered confidential values to disk, in the
  timeline as well as the response document.

  The OAuth2 call also passed no render purpose, and the policy is fail-closed,
  so it was interpolating the mask into the token request instead of the
  credential. develop has the same omission (it affected SECRET vault values
  there); marking a variable confidential widened it. Fixed with purpose: 'send',
  which has to land together with the collector: on its own it would turn a
  broken-but-safe path into a leaking one.

Verification
  Unit tests for the helpers, plus a smoke spec that drives the real app and
  reads what landed on disk, since none of this wiring is reachable from the
  node test runtime. Three cases: setting on (nothing in the timeline or the
  response document, but the real value still goes over the wire), a request
  re-sent by a template tag, and - as a negative control, so the first case
  cannot pass vacuously - setting off, where the values must be written in
  cleartext.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* fix test

* fix comment

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-10-20 17:15:54 +08:00
Bingbing bf61f6ba45 fix: stop restamping ApiSpec.modified on startup and git round-trips (#10625)
## Background

In a Git-synced project, restarting Insomnia without any change marked every design document as modified in the Git staging list. The diff of each affected YAML contained exactly one line: the embedded ApiSpec `meta.modified` timestamp.

The v5 design-doc YAML does not serialize `ApiSpec.fileName` (the v5 schema has no such field), while v5 import parses a missing `fileName` as an empty string. Any git round-trip therefore reset `fileName` to empty in the database, and the startup repair (`_applyApiSpecName`) rewrote it via `docUpdate`, which unconditionally restamped `modified: Date.now()` for every design document in one burst. RepoFileWatcher then re-exported the database to disk and git status reported all files as modified, repeating on every startup until committed.

## Changes

- Import: fall back `ApiSpec.fileName` to the file top-level name when the spec uses the `{ contents }` shape, so a round-trip never stores an empty fileName.
- Repair: skip the fileName backfill when it would write the existing value; a no-op write only restamps `modified`.
- Regression tests for both behaviors.

Fixes INS-4058
2026-10-20 02:58:17 +00:00
Kent Wang 25c20ce0c3 feat: Add incompatible fields check for cloud sync document (#10605)
* add basic utility for cloud sync check
* do not show error when entire deleted
* add confirmation window when submit contains unknown fields in cloud sync
2026-10-19 18:21:13 +08:00
Curry YangandClaude Sonnet 4.6 0307676574 feat: apply confidential policy to preview surfaces (#10510)
* feat(phase3-ui): apply confidential policy to all preview surfaces

Wire purpose:'preview' explicitly into RenderedText, RenderedQueryString,
CookiesModal, CodeEditor, and OneLineEditor so they respond to the
hideSecretValuesInPreviewAndConsole setting (Phase 3.2).

Refactor VariableEditor to use isRevealed state with forceReveal instead
of toggling purpose, making the eye button available for all variable
types (normal confidential, SECRET, external vault).

Short-circuit external vault tag run() in both liquid-extension.ts and
liquid-extension-worker.ts via shouldMaskExternalVaultTag() so no
provider fetch occurs when the policy says mask (Phase 3.3).

Add isRevealed/forceReveal to TagEditor for consistent reveal UX.

Wire purpose:'codegen' in request-actions-dropdown and
generate-code-modal callers to complete the codegen/export split
(Phase 3.4 UI callers).

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>

* refactor(phase3-ui): generate-code callers use purpose:preview instead of codegen

Now that codegen purpose has been removed from RenderPurpose, switch both
Generate Code modal and Copy as cURL (request-actions-dropdown) to pass
purpose:'preview'. Behaviour is identical: setting ON masks confidential values,
setting OFF reveals them.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>

* update

* fix: process in worker

* fix: smoke test

* fix lint

---------

Co-authored-by: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
2026-10-19 18:05:29 +08:00
Curry YangandClaude Sonnet 5 e192271245 feat: confidential value policy and render engine (#10509)
* feat(phase3-engine): implement confidential value policy and render engine

Introduce getConfidentialValuePolicy() as the single decision point for
reveal/mask across all render surfaces (Phase 3.0). Extend RenderPurpose
with 'codegen' and add forceReveal/getSettings() to BaseRenderContext.

Update buildRenderContext() to track per-key ConfidentialValueKind via
EnvironmentRenderLayer so normal-confidential KV pairs are masked before
entering renderSubContext() — preview cannot pollute subsequent send
(Phase 3.1).

Wire maskOrDecryptVaultDataIfNecessary() through the same policy so
SECRET reveal/mask follows hideSecretValuesInPreviewAndConsole.

Add explicit purpose:'codegen' to exportHarWithRequest/exportHarRequest
IPC path so Generate Code always masks confidential values independent of
the user setting (Phase 3.4 IPC layer).

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>

* refactor(phase3-engine): remove codegen purpose — generate-code callers use preview

codegen and preview now have identical policy semantics (both controlled by
hideSecretValuesInPreviewAndConsole; forceReveal only applies at call-site for
preview). Keeping a separate purpose added complexity with no behavioural benefit.

Remove 'codegen' from RenderPurpose, drop the codegen branch from
getConfidentialValuePolicy, and restore shouldMaskExternalVaultTag to intercept
only 'preview'. Generate Code / Copy as cURL callers will pass purpose:'preview'
instead (updated in the UI branch).

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>

* refactor(phase3-engine): extract buildEnvironmentRenderLayers from buildRenderContext

Pure extract-function: the environment layer construction is unchanged,
just moved into its own exported function so downstream stacked PRs
(T9/T10 confidential-value prefill) can reuse the same layer
enumeration instead of re-implementing it. buildRenderContext's
behavior is unchanged and remains covered by render.test.ts.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* fix(phase3-engine): stop buildEnvironmentRenderLayers mutating the ancestors array

for (const doc of (ancestors || []).reverse()) reversed the caller's
array in place. Now that the layer construction is extracted into a
standalone function, downstream callers (e.g. a T9/T10 prefill pass
that enumerates the same layers before the real render pass) can
receive the same ancestors array and call it twice, flipping the
folder precedence order on the second call. Copy before reversing so
the caller's array is left untouched.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* update

* fix: comment

---------

Co-authored-by: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
2026-10-19 17:51:16 +08:00
463f04abd1 feat(Git Diff): Add an entity-level diff engine [1/5] (#10615)
* feat(Git Diff): add an entity-level diff engine for Insomnia YAML files

Parses both sides of a git-synced Insomnia file, matches requests, folders,
environments, mock routes, the MCP request, the cookie jar and the file-level
sections by id, and returns the added / removed / modified entities with their
field-level changes. Not wired into any UI yet.

Co-authored-by: James Gatz <jamesgatzos@gmail.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* fix(Git Diff): tell same-named params and headers apart in the diff

diffByKey matched items by name only, so of two same-named items (eg. a
disabled default User-Agent header next to a custom one) the later hid the
earlier: a change to the first was invisible, and adding or removing one read
as a modification. Items sharing a name are now numbered by their order among
that name, as "User-Agent (1)" and "User-Agent (2)", on both sides.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: James Gatz <jamesgatzos@gmail.com>
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-19 17:38:20 +08:00
Kent Wang a7eb49414c bracket-quote Postman variable names that aren't valid identifiers (#10604) 2026-10-19 07:31:58 +00:00