Staging the last entity of a deleted file, or unstaging the last one of a
new file, left a file with only the workspace identity in the index. It
is now removed from the index instead.
Also keep the spliced content when replaying it through import/export
adds entities it doesn't have, eg. a placeholder mock server.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
## Background
Hovering a `{{ _.vault.key }}` chip showed no source: vault secrets are stored under the reserved `__insomnia_vault` key in each environment and only surface under the runtime `vault` namespace after decryption, while the key source map was built by a parallel walk over the raw environment data in `getRenderContext`, duplicating the merge semantics that `buildRenderContext` implements for values.
## Changes
- Move key source registration into `buildRenderContext`, where the values are merged. Registration runs in the same order the runtime resolves values: the `__insomnia_vault` entries of every environment are recorded under the `vault` namespace first, then every environment's plain data walk — so a legacy plaintext `vault` key, which import and sync can carry without reserved-key validation, wins over sealed values just like it does in the final vault spread.
- Unify source labels to `<environment type> : <name>` — Project Environment, Collection Environment and Folder Environment; runner upload data and script variables keep their own type names.
- Prefix the source line in the hover tooltip with `Source`, matching the live preview modal.
- Rename the setting to "Template tags show variable source and value" so its scope is obvious from the name.
* migrate request related p2
- grpc-request
- cookie-jar
- fix ts errors
* add unit test to check field mismatch
* migrate request group model
* migrate the socket io request and import parser
* migrate websocket schema and fix ts error
* fix order issue and add unit tests
* fix: Warn when a Git Sync change cannot be discarded [INS-3689]
When a committed YAML file is not in the exact format Insomnia generates
(hand-edited, or written by an older version), discarding it restores the
file, the repo file watcher imports it and re-exports it, and the change
reappears. Users get stuck with a change they can never discard.
Detect this up front by replaying the import -> export round-trip in memory
on the raw index/HEAD content. When the result equals the working copy, the
whole diff is Insomnia re-generating the file, so the Commit Changes modal
shows a warning above the diff and the discard confirmation explains that
the file needs to be committed once.
- Add sync/git/canonical-file.ts, which mirrors database.update (initModel)
and the getWithDescendants ordering (created, then _id)
- Split getInsomniaV5DataExport into a DB fetch and a pure
serializeInsomniaV5Export
- Return rawHead/rawStage from GitVCS.fileStatus, since head/stage are
migrated and reordered for display
- Export initModel from insomnia-data/node
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* refactor: Replay the Git Sync round-trip on a scratch database [INS-3689]
The undiscardable-change check reimplemented parts of the import/export
pipeline (initModel, getWithDescendants ordering) to avoid touching the app
database. That copy would silently drift whenever the real pipeline changes.
Run the real pipeline instead: write the imported docs to a scratch
in-memory NeDB instance and export them with getInsomniaV5DataExport.
- Keep NeDB collections, change buffer and listeners per instance in
createNedbDatabase. They were module-level, so a second instance's init()
would have replaced the app's collections. flushChangesImpl is now handed
to the wrapper instead of being exported.
- Add an optional db parameter to getInsomniaV5DataExport and drop the
serializeInsomniaV5Export split and the initModel export
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* fix: Ignore import timestamps when detecting undiscardable changes [INS-3689]
Import fills a missing meta.created/modified with the current time, so a
hand-added entry without timestamps got a different value in the replay
than in the working copy the watcher wrote earlier. The exact comparison
then failed and the warning never showed, although the change still came
back after every discard.
Compare the replay with the working copy using hasSignificantChanges, the
same rules the Unstaged changes list uses, so differences the list already
ignores no longer hide the warning.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* chore: Reword the undiscardable change warning [INS-3689]
Explain the cause in user terms, using the wording suggested in the ticket.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* chore: Shorten the undiscardable change warning [INS-3689]
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* refactor: Rename committedContent to baselineContent [INS-3689]
The content a discard restores is the staged content when there is one, so
it is not necessarily committed.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
## Background
Users can define the same variable at many environment levels (project, collection base/sub, folder, script-local). When hovering or clicking a variable in an editor there was no way to tell where its value comes from.
[INS-3943](https://konghq.atlassian.net/browse/INS-3943)
## Changes
- **Render context**: track variable source metadata (`keyContextMeta`) alongside `keyContext` — source label plus the owning workspace / environment / folder ids. Fix `keySource` ancestor ordering to match `buildRenderContext` (closest folder wins), which previously reported the wrong source for shadowed keys — this also affected the existing "Show variable source and value" setting. Keys overridden by runner upload data or `insomnia.variables.set` get a non-navigable source meta (no workspaceId, label `uploadData` / `scriptLocalVariables`) so consumers never point at an environment that no longer supplies the value; the meta label doubles as the `keySource` string the "Show variable source and value" setting renders.
- **Live preview modal**: "Edit Variable" shows a Source row (inside the Live Preview block) with an Open action.
- **Hover tooltip**: hovering a variable shows value + source + Open, rendered by a global `VariableSourceTooltipHost` using the shared tooltip stack (`useOverlayPosition` + `tooltip__bubble` styling). Undefined variables surface the render error instead of vanishing.
- **Open action routing** (via `buildResourceUrl` from the navigation route registry, so the hook holds no hand-built URLs and no tab-specific code):
- folder environment → the folder pane (`?tab=environment`), by plain navigation; the tab-list route sync (`useInsomniaTab`) activates the matching tab or creates a temporary one — the same generic mechanism every deep link uses;
- project environment (environment-type workspace) → its workspace page (`?environmentId=`), likewise plain navigation; both deep-link params are validated against the target's own resources and consumed after applying, so forwarded search params cannot re-select them later;
- collection environment → the workspace's **Manage Environments modal** (the same dialog the environment picker's manage button opens) opened in place, with the source environment preselected. Editors reach it through a new `OPEN_ENVIRONMENTS_MODAL` event on `uiEventBus`, hosted by the workspace pane header.
- **E2E**: data-driven suite `variable-source-live-preview.test.ts` — 5 environment levels × {send value, modal Source/preview/Open, hover tooltip, "Show variable source and value" inline display} + error-chip and tooltip-content-keep-alive regressions. Collection scenarios assert the Manage Environments modal opens with the env row preselected.
## Design notes
- Tooltip timing lives in a small controller (`variable-source-tooltip.ts`) because the trigger is a CodeMirror widget outside the React tree; rendering/positioning/events on the bubble use the existing react-aria stack. No new dependencies.
- The Open hook (`use-open-variable-source.ts`) is reachable from root-mounted modals (CodePromptModal → MarkdownEditor → CodeEditor), so it must not statically import the tab system or the environments modal — either import closes a circular reference back to `root.tsx` (verified against `dependency-cruiser`, which counts type-only and dynamic imports). The tab system is reached through the generic route sync instead, and the modal through the event bus; the modal is hosted by `WorkspacePaneHeader`, which already imports it — zero new edges in the module graph (`check-cycle-references` reports the baseline count).
- Collection environments have no page of their own; the picker's Manage Environments dialog is the canonical editing surface, so Open reuses it instead of navigating. The earlier draft's dedicated env-page tab id (`${workspaceId}-env`) and `buildTabFromNavigation` overrides were removed — `workspace:environment` was already registered in the navigation route registry, so the generic mechanism covers it.
- Old-syntax `{{ a }}` variables have no source: the source map is keyed under the `_` namespace only. Extending it to bare keys is a potential follow-up.
- Security review: no findings (no XSS/prototype-pollution/open-redirect/secrets-exposure regressions; vault masking parity with develop).
The id preservation in addSetCookiesToToughCookieJar() (added in #10475) had no
test for the case this ticket describes: replaying an identical Set-Cookie
response, or a response that sets only one of several cookies, must keep the
existing cookie ids so the Git-synced cookie jar does not get dirtied.
Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
## Background
Project storage conversion reuses the document ids owned by the previous storage. Known problems:
- Id hijack via global upsert: converted projects keep git YAML ids pulled by RepoFileWatcher or cloud backend ids pulled by cloud sync, and external imports upsert documents globally by their original ids — a later import of the same source overwrites or steals this project's documents.
- Id conflicts block the conversion loop: a project-scoped clone rejects with "The repository being cloned contains workspaces that already exist in Insomnia." when the target project still holds the repository's ids.
- The git FS watcher is not stopped during conversion: an active watcher flushes DB changes (including re-keyed docs) back into the repository files mid-conversion.
- Git → cloud conversion creates no backend resources: no backend project initialization and no snapshot push, so the converted cloud project has nothing syncable.
- Stale workspace-level git state is left behind (gitRepositoryId, gitFilePath, gitFileLastSyncTime, hasUncommittedChanges, hasUnpushedChanges), so the project card keeps showing git status after conversion.
- WorkspaceMeta references are not remapped: the model has no rewriteReferences, so active environment/request/test-suite selections dangle after re-keying.
- Conversion gives no warning that regenerating ids invalidates inso commands, CI scripts and template tags referencing the old ids.
### Repro
1. Create a git project and pull its content.
2. Convert the project to cloud storage.
3. Convert it back to git, pointing at the same repository.
Result: the clone is rejected because the converted project still holds the repo's original workspace ids.
## Changes
- Add regenerateProjectDocIds: re-keys every document under the project's workspaces with fresh ids, remapping parent chains and embedded references (including template tags) via models.rewriteReferences; adds the missing WorkspaceMeta remapper
- Call it on every storage conversion branch of the project update route: local ↔ remote, local ↔ git, remote ↔ git; regeneration runs after the previous storage's watcher is stopped and before the new storage's import
- Stop the FS watcher and clean up the managed repo folder before touching DB docs on git conversions, and clear stale workspace-level git state afterwards
- Initialize cloud sync (initialize backend project + push snapshot) on git → cloud conversions so the re-keyed docs reach the backend with their new ids
- Show a confirmation dialog (AskModal) before any storage conversion in project-settings-form
- Tests: re-keying of nested runtime docs (responses, unit tests), multi-workspace independence, empty workspace, and WorkspaceMeta reference remapping
## Background
`isImportModalOpen` in the workspace debug route and the first-request creation screen is initialized to `false` and never set to `true` anywhere, so the `ImportModal` instances they render can never open.
## Changes
- Remove the dead `isImportModalOpen` state, the `ImportModal` render blocks, and the now-unused imports/variables in `debug.tsx` and `first-request-creation.tsx`.
No behavior change: the modals were unreachable.
## Background
Users copy a cURL command from docs and expect pasting it into Insomnia to start an import. Previously this only worked through the URL bar (Paste Curl modal) or the manual import dialogs.
## Changes
- Add `useGlobalCurlPasteImport`: a document-level capture-phase `paste` listener that intercepts cURL-shaped text only when no editable surface (input, textarea, contentEditable — CodeMirror included) has focus, then synchronously prevents the default action and hands the text over.
- Wire it in Root to reuse the deep-link import channel (`setImportObject` → `ImportModal`) with auto-scan enabled.
- Inside a workspace, the current collection is offered as the preselected import target via the existing `defaultWorkspaceId` prop (applied once so revalidation cannot override the user's choice); the modal header reflects the target workspace. All entry points passing the prop now preselect consistently, and deep-link imports keep defaulting to a new collection.
- With no project in context the modal opens on the `-- New Project --` creation flow instead of a toast.
- Fully disabled in the Scratchpad, which holds a single collection.
## Tests
- jsdom unit tests for the hook (`use-global-curl-paste-import.test.tsx`).
- Playwright smoke tests driving the real <kbd>Cmd</kbd>+<kbd>V</kbd> (`paste-curl-import.test.ts`): project page auto-scan, workspace preselect, focused URL bar (existing Paste Curl modal preserved), no-project creation flow, Scratchpad no-op.
* fix: validate paths stay within their intended base directory
* test: add regression coverage for path containment checks
* fix: only contain the symlink's own location, not its target content
* revert: drop fs-client and backup containment, superseded by #10560
* test(path-guard): cover '..test' folder allowed and traversal rejected
* test(path-guard): cover readOrCreateDataDir folder containment
* fix: bump @xmldom/xmldom to ^0.9.12 (CVE-2026-41675)
0.9.10 has a known parser vulnerability; 0.9.12 is the latest patched release.
* fix: bump dompurify to ^3.4.14
3.4.13 has a known sanitizer bypass; 3.4.14 is the patched release.
* fix: bump ajv to ^8.20.0
8.18.0 has a known prototype-pollution issue in schema compilation; 8.20.0 is the patched release.
* fix: bump es-toolkit to ^1.51.0
1.45.1 has a known ReDoS in one of its string utilities; 1.51.0 is the patched release.
* fix: bump @apidevtools/swagger-parser to 12.1.0
Pulls in json-schema-ref-parser 14.0.1, resolving a known vulnerability in the
$ref-resolution path used when validating imported OpenAPI/Swagger documents.
Only SwaggerParser.validate() is used at the two call sites and its signature
is unchanged across this range.
* fix: bump @sentry/electron to ^7.17.0
Pulls in @sentry/node 10.70.0, resolving vulnerabilities in its bundled
@opentelemetry/core and cjs-module-lexer instrumentation dependencies.
Sentry.init options, captureException, browserTracingIntegration, and
getCurrentScope().setUser() are all used unchanged across this range.
* fix: bump zod to ^4.4.0
3.25.76 has a known validation-bypass fixed only in the v4 line. The app's
own schema code already imports from the zod/v4 subpath, so no source
changes are needed for the bump itself.
* fix: bump @rjsf/core, @rjsf/utils, @rjsf/validator-ajv8 to 6.8.0
Moves off the 6.0.0 beta onto the stable release line, which pulls in
markdown-to-jsx 9.x and resolves a known vulnerability in the pinned 7.x
line. The withTheme/FormProps/RJSFSchema/UiSchema/validateForm API surface
used by this app's JSON-schema forms is unchanged across this range.
* fix: refresh jose lockfile entry to 6.2.10
6.2.2 has a known vulnerability; 6.2.10 is already within the existing
allowed range (^6.1.3) declared by @modelcontextprotocol/sdk, so no
manifest change is needed, just an up-to-date resolution.
* fix: adapt rjsf theme templates to @rjsf/utils 6.8.0 prop changes
WrapIfAdditionalTemplate's onKeyChange/onDropPropertyClick became
onKeyRenameBlur/onRemoveProperty, ObjectFieldTemplate's idSchema/onAddClick
became fieldPathId/onAddProperty, and ArrayFieldTemplate no longer receives
raw item pieces - items are now pre-rendered elements, so the per-item
border/delete-button layout moved into a new ArrayFieldItemTemplate.
* Revert "fix: bump zod to ^4.4.0"
This reverts commit 54f1d5aa4c81ff64bf4cb9f9d478440b7311e2cd.
The v3 to v4 bump breaks real-world collection validation: import-v5-parser's
discriminated/recursive union schemas reject valid v5 collection files under
the released zod@4.4.0 (unlike the bundled zod/v4 preview subpath used
during development), failing import, export, and CLI collection-run
end-to-end tests. Deferring this upgrade separately rather than shipping a
functional regression.
* chore: regenerate lockfile after rebase onto develop
* fix: bump undici to ^7.29.1
* fix: bump @xmldom/xmldom 0.8.x to 0.8.15
* fix: bump proxy-addr to 2.0.8
* fix: bump compression to 1.8.2
* fix: bump source-map-js to 1.2.2
* fix: bump http-cache-semantics to 4.3.0
* fix: bump fast-uri to 3.1.8
* fix: bump proxy-addr, compression, source-map-js, http-cache-semantics, fast-uri in component-docs lockfile
* ci(ksai): start a KSAI run only for people who can write to the repository
A comment or review now starts the KSAI run job only when its author
can write to this repository. A new authorize job reads the author's
permission with the workflow token, and the run job needs it. A
dispatched run starts as before.
Signed-off-by: Bart Smykla <bartek@smykla.com>
* ci(ksai): fail the authorize step when the permission read fails
A failed permission read used to answer writes=false with a green
job, so a writer's comment started nothing and the log said nothing.
The step now fails on a read error, the run job still does not start,
and the log names the error. An author who cannot write still reads
as writes=false.
Signed-off-by: Bart Smykla <bartek@smykla.com>
---------
Signed-off-by: Bart Smykla <bartek@smykla.com>
The workspace identity (type/schema_version/name/meta), mock server, API spec,
unit tests and CA certificates weren't entities, so their changes were
invisible in the visual view and a new file's first staged entity left them
behind (eg. a mock server's URL). Each is now one whole entity rendered by
the generic card and staged/discarded as a unit; the workspace identity only
gets actions when the file exists on both sides.
Staged content also keeps source's top-level key order, so adding a section
doesn't leave a key-order-only difference against the working tree.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Discarding a folder that only exists in the working tree deletes everything
nested inside it too, even though those items have their own cards. The
Discard confirm prompt now says so in that case.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Lists cookie changes like the cookie manager, matching cookies by name +
domain + path (their identity; ids aren't stable across imports), plus the
Settings catch-all. This completes dedicated cards for every entity type.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
MCP mirrors its editor (Auth, Headers, Env, Roots) and shows the transport
and server URL/command in the header; env vars are matched by id and roots by
uri. Mock routes show method, path and status in the header with Body and
Headers tabs. Both keep Docs and the Settings catch-all.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Mirrors the folder settings editor: Auth, Headers, Scripts, Environment and
Docs, plus the Settings catch-all. Folder variables are compared by name and
shown as whole values; a pure variable reorder still shows up. Child requests
never appear here since they have their own cards.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Each mirrors its editor's tabs: WebSocket (Params, Auth, Headers, Docs),
Socket.IO (Params, Events, Auth, Headers, Docs) and gRPC (Method, Message,
Headers, Reflection, Docs), with the Settings catch-all for anything else.
Socket.IO listeners are matched by id so a renamed event reads as modified.
Card tests now also render every tab, so render-time crashes are caught.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Move the collapsible header + chips + tabs layout, the header lines and the
shared tab builders (key/value rows, auth, scripts, docs, settings catch-all)
out of RequestDiffCard so the remaining entity types can reuse them. The
request card renders exactly as before.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Moving a request or folder in or out of a folder can be an indentation-only
change in the YAML, which Monaco hid by default, leaving a changed file with
no visible diff.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>