From 1cba2f64d39073bb77faac7234e88f52aa395643 Mon Sep 17 00:00:00 2001 From: Julius Marminge Date: Fri, 10 Apr 2026 01:07:05 -0700 Subject: [PATCH] Harden workspace git indexing against repo-configured fsmonitor execution (#1873) --- apps/server/src/git/Layers/GitCore.test.ts | 34 +++++++++++++++++++++- apps/server/src/git/Layers/GitCore.ts | 28 +++++++++++++++--- 2 files changed, 57 insertions(+), 5 deletions(-) diff --git a/apps/server/src/git/Layers/GitCore.test.ts b/apps/server/src/git/Layers/GitCore.test.ts index 5ff2714b61378c503ebed089fb58014cb887af61..39576341049236e5bf73121431ccf6fac453462d 100644 --- a/apps/server/src/git/Layers/GitCore.test.ts +++ b/apps/server/src/git/Layers/GitCore.test.ts @@ -219,7 +219,10 @@ it.layer(TestLayer)("git integration", (it) => { const seenChunks: string[][] = []; const core = yield* makeIsolatedGitCore((input) => { - if (input.args.join(" ") !== "check-ignore --no-index -z --stdin") { + if ( + input.args.join(" ") !== + "-c core.fsmonitor=false -c core.untrackedCache=false check-ignore --no-index -z --stdin" + ) { return Effect.fail( new GitCommandError({ operation: input.operation, @@ -252,6 +255,35 @@ it.layer(TestLayer)("git integration", (it) => { expect(result).toEqual(expectedPaths); }), ); + + it.effect("listWorkspaceFiles disables fsmonitor and untracked cache helpers", () => + Effect.gen(function* () { + const core = yield* makeIsolatedGitCore((input) => { + expect(input.args).toEqual([ + "-c", + "core.fsmonitor=false", + "-c", + "core.untrackedCache=false", + "ls-files", + "--cached", + "--others", + "--exclude-standard", + "-z", + ]); + return Effect.succeed({ + code: 0, + stdout: "src/index.ts\0README.md\0", + stderr: "", + stdoutTruncated: false, + stderrTruncated: false, + }); + }); + + const result = yield* core.listWorkspaceFiles("/virtual/repo"); + expect(result.paths).toEqual(["src/index.ts", "README.md"]); + expect(result.truncated).toBe(false); + }), + ); }); // ── listGitBranches ── diff --git a/apps/server/src/git/Layers/GitCore.ts b/apps/server/src/git/Layers/GitCore.ts index 911a60195569cc676bdf1df8b1b74267f51c4d7a..394293593257cffebba4bc6334ddf302edbed6ba 100644 --- a/apps/server/src/git/Layers/GitCore.ts +++ b/apps/server/src/git/Layers/GitCore.ts @@ -47,6 +47,12 @@ const RANGE_DIFF_SUMMARY_MAX_OUTPUT_BYTES = 19_000; const RANGE_DIFF_PATCH_MAX_OUTPUT_BYTES = 59_000; const WORKSPACE_FILES_MAX_OUTPUT_BYTES = 16 * 1024 * 1024; const GIT_CHECK_IGNORE_MAX_STDIN_BYTES = 256 * 1024; +const WORKSPACE_GIT_HARDENED_CONFIG_ARGS = [ + "-c", + "core.fsmonitor=false", + "-c", + "core.untrackedCache=false", +] as const; const STATUS_UPSTREAM_REFRESH_INTERVAL = Duration.seconds(15); const STATUS_UPSTREAM_REFRESH_TIMEOUT = Duration.seconds(5); const STATUS_UPSTREAM_REFRESH_FAILURE_COOLDOWN = Duration.seconds(5); @@ -1619,7 +1625,14 @@ export const makeGitCore = Effect.fn("makeGitCore")(function* (options?: { executeGit( "GitCore.listWorkspaceFiles", cwd, - ["ls-files", "--cached", "--others", "--exclude-standard", "-z"], + [ + ...WORKSPACE_GIT_HARDENED_CONFIG_ARGS, + "ls-files", + "--cached", + "--others", + "--exclude-standard", + "-z", + ], { allowNonZeroExit: true, timeoutMs: 20_000, @@ -1637,7 +1650,14 @@ export const makeGitCore = Effect.fn("makeGitCore")(function* (options?: { createGitCommandError( "GitCore.listWorkspaceFiles", cwd, - ["ls-files", "--cached", "--others", "--exclude-standard", "-z"], + [ + ...WORKSPACE_GIT_HARDENED_CONFIG_ARGS, + "ls-files", + "--cached", + "--others", + "--exclude-standard", + "-z", + ], result.stderr.trim().length > 0 ? result.stderr.trim() : "git ls-files failed", ), ), @@ -1657,7 +1677,7 @@ export const makeGitCore = Effect.fn("makeGitCore")(function* (options?: { const result = yield* executeGit( "GitCore.filterIgnoredPaths", cwd, - ["check-ignore", "--no-index", "-z", "--stdin"], + [...WORKSPACE_GIT_HARDENED_CONFIG_ARGS, "check-ignore", "--no-index", "-z", "--stdin"], { stdin: `${chunk.join("\0")}\0`, allowNonZeroExit: true, @@ -1671,7 +1691,7 @@ export const makeGitCore = Effect.fn("makeGitCore")(function* (options?: { return yield* createGitCommandError( "GitCore.filterIgnoredPaths", cwd, - ["check-ignore", "--no-index", "-z", "--stdin"], + [...WORKSPACE_GIT_HARDENED_CONFIG_ARGS, "check-ignore", "--no-index", "-z", "--stdin"], result.stderr.trim().length > 0 ? result.stderr.trim() : "git check-ignore failed", ); } -- 2.54.0