authorgravatar for julius0216@outlook.comJulius Marminge <julius0216@outlook.com> 2026-04-10 01:07:05-07:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2026-04-10 01:07:05-07:00
log1cba2f64d39073bb77faac7234e88f52aa395643
treef062965d7179503a13a59e4cdabb03f12e810cbe
parent1a05d8ca1a5ab8d8562eb576bfd473459dcc8ea3
signature Signed by PGP key B5690EEEBB952194

Harden workspace git indexing against repo-configured fsmonitor execution (#1873)


2 files changed, 57 insertions(+), 5 deletions(-)

apps/server/src/git/Layers/GitCore.test.ts+33-1
...@@ -219,7 +219,10 @@ it.layer(TestLayer)("git integration", (it) => {...@@ -219,7 +219,10 @@ it.layer(TestLayer)("git integration", (it) => {
219219
220 const seenChunks: string[][] = [];220 const seenChunks: string[][] = [];
221 const core = yield* makeIsolatedGitCore((input) => {221 const core = yield* makeIsolatedGitCore((input) => {
222 if (input.args.join(" ") !== "check-ignore --no-index -z --stdin") {222 if (
223 input.args.join(" ") !==
224 "-c core.fsmonitor=false -c core.untrackedCache=false check-ignore --no-index -z --stdin"
225 ) {
223 return Effect.fail(226 return Effect.fail(
224 new GitCommandError({227 new GitCommandError({
225 operation: input.operation,228 operation: input.operation,
...@@ -252,6 +255,35 @@ it.layer(TestLayer)("git integration", (it) => {...@@ -252,6 +255,35 @@ it.layer(TestLayer)("git integration", (it) => {
252 expect(result).toEqual(expectedPaths);255 expect(result).toEqual(expectedPaths);
253 }),256 }),
254 );257 );
258
259 it.effect("listWorkspaceFiles disables fsmonitor and untracked cache helpers", () =>
260 Effect.gen(function* () {
261 const core = yield* makeIsolatedGitCore((input) => {
262 expect(input.args).toEqual([
263 "-c",
264 "core.fsmonitor=false",
265 "-c",
266 "core.untrackedCache=false",
267 "ls-files",
268 "--cached",
269 "--others",
270 "--exclude-standard",
271 "-z",
272 ]);
273 return Effect.succeed({
274 code: 0,
275 stdout: "src/index.ts\0README.md\0",
276 stderr: "",
277 stdoutTruncated: false,
278 stderrTruncated: false,
279 });
280 });
281
282 const result = yield* core.listWorkspaceFiles("/virtual/repo");
283 expect(result.paths).toEqual(["src/index.ts", "README.md"]);
284 expect(result.truncated).toBe(false);
285 }),
286 );
255 });287 });
256288
257 // ── listGitBranches ──289 // ── listGitBranches ──
apps/server/src/git/Layers/GitCore.ts+24-4
...@@ -47,6 +47,12 @@ const RANGE_DIFF_SUMMARY_MAX_OUTPUT_BYTES = 19_000;...@@ -47,6 +47,12 @@ const RANGE_DIFF_SUMMARY_MAX_OUTPUT_BYTES = 19_000;
47const RANGE_DIFF_PATCH_MAX_OUTPUT_BYTES = 59_000;47const RANGE_DIFF_PATCH_MAX_OUTPUT_BYTES = 59_000;
48const WORKSPACE_FILES_MAX_OUTPUT_BYTES = 16 * 1024 * 1024;48const WORKSPACE_FILES_MAX_OUTPUT_BYTES = 16 * 1024 * 1024;
49const GIT_CHECK_IGNORE_MAX_STDIN_BYTES = 256 * 1024;49const GIT_CHECK_IGNORE_MAX_STDIN_BYTES = 256 * 1024;
50const WORKSPACE_GIT_HARDENED_CONFIG_ARGS = [
51 "-c",
52 "core.fsmonitor=false",
53 "-c",
54 "core.untrackedCache=false",
55] as const;
50const STATUS_UPSTREAM_REFRESH_INTERVAL = Duration.seconds(15);56const STATUS_UPSTREAM_REFRESH_INTERVAL = Duration.seconds(15);
51const STATUS_UPSTREAM_REFRESH_TIMEOUT = Duration.seconds(5);57const STATUS_UPSTREAM_REFRESH_TIMEOUT = Duration.seconds(5);
52const STATUS_UPSTREAM_REFRESH_FAILURE_COOLDOWN = Duration.seconds(5);58const STATUS_UPSTREAM_REFRESH_FAILURE_COOLDOWN = Duration.seconds(5);
...@@ -1619,7 +1625,14 @@ export const makeGitCore = Effect.fn("makeGitCore")(function* (options?: {...@@ -1619,7 +1625,14 @@ export const makeGitCore = Effect.fn("makeGitCore")(function* (options?: {
1619 executeGit(1625 executeGit(
1620 "GitCore.listWorkspaceFiles",1626 "GitCore.listWorkspaceFiles",
1621 cwd,1627 cwd,
1622 ["ls-files", "--cached", "--others", "--exclude-standard", "-z"],1628 [
1629 ...WORKSPACE_GIT_HARDENED_CONFIG_ARGS,
1630 "ls-files",
1631 "--cached",
1632 "--others",
1633 "--exclude-standard",
1634 "-z",
1635 ],
1623 {1636 {
1624 allowNonZeroExit: true,1637 allowNonZeroExit: true,
1625 timeoutMs: 20_000,1638 timeoutMs: 20_000,
...@@ -1637,7 +1650,14 @@ export const makeGitCore = Effect.fn("makeGitCore")(function* (options?: {...@@ -1637,7 +1650,14 @@ export const makeGitCore = Effect.fn("makeGitCore")(function* (options?: {
1637 createGitCommandError(1650 createGitCommandError(
1638 "GitCore.listWorkspaceFiles",1651 "GitCore.listWorkspaceFiles",
1639 cwd,1652 cwd,
1640 ["ls-files", "--cached", "--others", "--exclude-standard", "-z"],1653 [
1654 ...WORKSPACE_GIT_HARDENED_CONFIG_ARGS,
1655 "ls-files",
1656 "--cached",
1657 "--others",
1658 "--exclude-standard",
1659 "-z",
1660 ],
1641 result.stderr.trim().length > 0 ? result.stderr.trim() : "git ls-files failed",1661 result.stderr.trim().length > 0 ? result.stderr.trim() : "git ls-files failed",
1642 ),1662 ),
1643 ),1663 ),
...@@ -1657,7 +1677,7 @@ export const makeGitCore = Effect.fn("makeGitCore")(function* (options?: {...@@ -1657,7 +1677,7 @@ export const makeGitCore = Effect.fn("makeGitCore")(function* (options?: {
1657 const result = yield* executeGit(1677 const result = yield* executeGit(
1658 "GitCore.filterIgnoredPaths",1678 "GitCore.filterIgnoredPaths",
1659 cwd,1679 cwd,
1660 ["check-ignore", "--no-index", "-z", "--stdin"],1680 [...WORKSPACE_GIT_HARDENED_CONFIG_ARGS, "check-ignore", "--no-index", "-z", "--stdin"],
1661 {1681 {
1662 stdin: `${chunk.join("\0")}\0`,1682 stdin: `${chunk.join("\0")}\0`,
1663 allowNonZeroExit: true,1683 allowNonZeroExit: true,
...@@ -1671,7 +1691,7 @@ export const makeGitCore = Effect.fn("makeGitCore")(function* (options?: {...@@ -1671,7 +1691,7 @@ export const makeGitCore = Effect.fn("makeGitCore")(function* (options?: {
1671 return yield* createGitCommandError(1691 return yield* createGitCommandError(
1672 "GitCore.filterIgnoredPaths",1692 "GitCore.filterIgnoredPaths",
1673 cwd,1693 cwd,
1674 ["check-ignore", "--no-index", "-z", "--stdin"],1694 [...WORKSPACE_GIT_HARDENED_CONFIG_ARGS, "check-ignore", "--no-index", "-z", "--stdin"],
1675 result.stderr.trim().length > 0 ? result.stderr.trim() : "git check-ignore failed",1695 result.stderr.trim().length > 0 ? result.stderr.trim() : "git check-ignore failed",
1676 );1696 );
1677 }1697 }