feat(git): name app-managed git folders after their project instead of a bare hex id - #10349
feat(git): name app-managed git folders after their project instead of a bare hex id#10349pavkout wants to merge 4 commits into
Conversation
There was a problem hiding this comment.
Pull request overview
This PR improves the on-disk readability of app-managed Git repository folders by embedding a filesystem-safe snapshot of the owning project’s name into the managed folder name (while preserving stability by not renaming on subsequent project renames). It also introduces a one-time, best-effort startup backfill that renames existing managed folders before the renderer/window is created to avoid races.
Changes:
- Add
GitRepository.folderSlugand a sharedmodels.gitRepository.getGitRepoFolderName()helper to compute the managed folder name (slugged when available, legacy id otherwise). - Update both main-process and renderer path resolution to use
getGitRepoFolderName()for consistent managed folder naming. - Add
slugify()(with tests) and a startupbackfillAllManagedGitFolderSlugs()pass awaited before window creation.
Reviewed changes
Copilot reviewed 7 out of 7 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| packages/insomnia/src/ui/utils/git-repo-path.ts | Renderer path resolver now uses models.gitRepository.getGitRepoFolderName() and includes folderSlug in the shape. |
| packages/insomnia/src/main/git-service.ts | Main-process path resolution updated; adds startup backfill to rename legacy managed folders safely before window creation. |
| packages/insomnia/src/entry.main.ts | Awaits backfillAllManagedGitFolderSlugs() before launching the app window to avoid rename/path races. |
| packages/insomnia-data/src/models/git-repository.ts | Adds folderSlug field and introduces getGitRepoFolderName() as the folder-name source of truth. |
| packages/insomnia-data/src/models/git-repository.test.ts | Adds unit tests covering getGitRepoFolderName() behavior. |
| packages/insomnia-data/common-src/misc.ts | Adds slugify() helper for creating filesystem-safe project-name slugs. |
| packages/insomnia-data/common-src/misc.test.ts | Adds unit tests for slugify(). |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| export function slugify(input: string, maxLength = 40) { | ||
| const slug = input | ||
| .normalize('NFKD') | ||
| .replace(/[̀-ͯ]/g, '') // strip accents (combining diacritical marks) | ||
| .toLowerCase() | ||
| .replace(/[^a-z0-9]+/g, '-') | ||
| .replace(/^-+|-+$/g, ''); |
| /** | ||
| * A filesystem-safe slug derived from the owning project's name at the time | ||
| * the app-managed folder was created (or, for repos that predate this field, | ||
| * backfilled the first time the repo is loaded). It is baked into the | ||
| * managed folder name for readability (see {@link getGitRepoFolderName}) and |
| // Should be unreachable — getGitRepoFolderName already validates folderSlug — but | ||
| // fall back to the safe bare-id path rather than ever returning one outside gitRoot. | ||
| console.warn('[git] Computed managed repo folder path escaped the git root, falling back to bare id:', folderName); | ||
| return path.join(gitRoot, gitRepositoryId); |
There was a problem hiding this comment.
Potential file inclusion attack via reading file - medium severity
If an attacker can control the input leading into the ReadFile function, they might be able to read sensitive files and launch further attacks with that information.
Show fix
| return path.join(gitRoot, gitRepositoryId); | |
| const base = path.resolve(gitRoot); | |
| const target = path.resolve(base, gitRepositoryId); | |
| const relative = path.relative(base, target); | |
| if (relative.startsWith('..') || path.isAbsolute(relative)) { | |
| throw new Error('Invalid repository path'); | |
| } | |
| return target; |
Reply @AikidoSec ignore: [REASON] to ignore this issue.
More info
What
App-managed git repo folders (
~/…/version-control/git/<id>) are named afternothing but a random 32-char hex id, making it impossible to tell projects
apart on disk (see screenshot in INS-2612). This adds a human-readable slug:
How
GitRepositorygains afolderSlugfield: a filesystem-safe slug of theowning project's name, snapshotted once and not kept in sync with later
project renames (renaming the folder live is unsafe — see below).
models.gitRepository.getGitRepoFolderName()computes the on-disk foldername from
folderSlug(falls back to the bare id when unset), used by boththe main-process
getRepoBaseDir/getGitFSClientand the renderer'sresolveGitRepoBaseDir.New repos, and any existing repo missing a slug, get backfilled by
backfillAllManagedGitFolderSlugs()— a best-effort pass that runs once,awaited before the app window is created (
entry.main.ts).That timing is load-bearing, not incidental: renaming a folder while
anything else (a file watcher, a route loader's git call) can concurrently
resolve the old path is unsafe — a reader holding the stale path can
recreate it via the FS client's auto-mkdir-on-write behavior microseconds
after the rename moves the real data away, silently forking a repo's files
across two directories. An earlier version of this change did the backfill
lazily on each repo's first load instead, and this exact race was reproduced
against a real profile (verified below). Doing it once, pre-window, removes
the race entirely: nothing can be reading these folders yet.
User-chosen
directoryrepos are never touched — Insomnia doesn't own thatfolder.