setKeyValuePairs resolved the new row by its data-key, which changes from
the provisional pair-blank_* key to the persisted id once the name is
saved. The value editor locator then never resolved, timing out in
setCodeMirrorValue (template-tag header-name, http happy-path, query-params).
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Drop the pass-through re-exports of expect from misc/fixtures.ts and
misc/git-fixtures.ts, import it from @playwright/test in specs, and
update the write-insomnia-test skill accordingly.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
WorkspaceFlow.create/duplicate/rename, ExportFlow.export and
EnvironmentFlow.create dispatched on argument types via typeof/instanceof
and casts, so a call site did not show which flow it triggered. Replace
them with explicitly named methods (createProject, openLocalGitFolder,
cloneGitProject, createCollection, duplicateRequest, renameCollection,
exportProject, createBaseEnvironment, ...) and update the
write-insomnia-test skill docs.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The type represents the response pane (response data plus console, stream
events and script test results), not the network response, and clashed
with the insomnia-data Response name. Update flows, pages, specs, JSDoc
and the write-insomnia-test skill accordingly.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
WorkspaceFlow now takes a structural RequestFlows dependency instead of
importing FlowManager, which removes the 3 new circular references.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Adds the event-stream, socket.io, ws-echo, mcp, grpc, git, oauth2 and
mock-bin servers (bound to 127.0.0.1), their proto/upload fixtures, npm
scripts and Playwright webServer entries, so flows that reference them
no longer fail with connection refused.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
- bind mock-api and echo-server to 127.0.0.1
- remove per-test dataPath after use
- throw when setCodeMirrorValue does not hold after retries
- drop continue-on-error from the build job
- shorten cookie waitForPersisted docstring
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
misc/echo-server.js reads misc/fixtures/petstore-swagger.json when it
starts, so the fixture must land together with the server; otherwise every
shard's webServer exits with ENOENT.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
- folder-pane on the request group Tabs
- stage-this-file / unstage-this-file on the git staging preview button
- drop-indicator-folder-name and data-valid on the sidebar drop indicator
- test-result-case / test-result-error inside the test result name
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
- Added data-testid attributes to various components for improved testing.
- Updated mock-url-bar to include test IDs for method and path.
- Enhanced generate-code-modal with test IDs for dropdowns.
- Included test IDs in git-branches-modal and git-project-branches-modal for error messages.
- Added test IDs to import-modal header and input-vault-key-modal for error display.
- Enhanced new-workspace-modal with test IDs for mock server type options.
- Included test IDs in select-modal for the select element.
- Added test IDs in sync-merge-modal for merge conflict items.
- Enhanced visual-diff components with test IDs for better test coverage.
- Included test IDs in workspace-certificates-modal for CA certificate section.
- Enhanced workspace-environments-edit-modal with test IDs for environment icons.
- Added test IDs in pane-header for breadcrumbs.
- Included test IDs in request-result-card for runner results.
- Enhanced request-test-result-pane with test IDs for test result rows.
- Added test IDs in project-create-form for unknown fields warning.
- Enhanced AI settings with test IDs for toggle switches.
- Included test IDs in boolean-setting for settings labels.
- Enhanced scripting-settings with test IDs for rule toggles.
- Added test IDs in sidebar components for search fields and nodes.
- Enhanced spec-view with test IDs for method buttons.
- Included test IDs in tab components for tab titles.
- Enhanced tags with test IDs for size, time, and URL tags.
- Added test IDs in tag-editor for tag arguments.
- Enhanced toast-notification with test IDs for toast body and content.
- Included test IDs in response-headers-viewer for header rows and values.
- Enhanced event-log-view with test IDs for event rows and cells.
## Background
CI occasionally fails while uploading smoke test traces with `The path for one of the files in artifact is not valid: ... Colon :`.
The Electron-context trace in the `app` fixture names the zip file from the raw `testInfo.title`. Titles such as `project environment: show variable source and value renders the source inline` contain a colon, which `actions/upload-artifact` rejects. On Windows the file cannot be written at all. Because traces are retained only for failed tests, the upload error also hides the real test failure.
## Changes
- Name the trace `trace-${testInfo.status}.zip` instead of embedding the test title.
- `testInfo.outputDir` already contains the sanitized test title, project, retry, and repeat index, so the title in the file name adds only redundancy and invalid characters.
## Testing
- `npm run test:dev -w insomnia-smoke-test -- --project=Smoke --trace on -g "project environment: show variable source and value renders the source inline"` → 1 passed; trace written as `traces/smoke-variable-source-live-...-Smoke/trace-passed.zip`.
- `eslint packages/insomnia-smoke-test/playwright/test.ts`
[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.
[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.
## 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
* 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.
* 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>
## 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
## 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
* 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>
* 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>
* 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>
* 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>
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>
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>
* 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