mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-05-14 05:42:54 -07:00
fix(sandbox): centralize async git worktree resolution and enforce read-only security (#25040)
This commit is contained in:
@@ -339,4 +339,61 @@ describe.skipIf(os.platform() === 'win32')('buildBwrapArgs', () => {
|
||||
const envIndex = args.indexOf(`${includeDir}/.env`);
|
||||
expect(args[envIndex - 2]).toBe('--bind');
|
||||
});
|
||||
|
||||
it('binds git worktree directories if present', async () => {
|
||||
const worktreeGitDir = '/path/to/worktree/.git';
|
||||
const mainGitDir = '/path/to/main/.git';
|
||||
|
||||
const args = await buildBwrapArgs({
|
||||
...defaultOptions,
|
||||
resolvedPaths: createResolvedPaths({
|
||||
gitWorktree: {
|
||||
worktreeGitDir,
|
||||
mainGitDir,
|
||||
},
|
||||
}),
|
||||
});
|
||||
|
||||
expect(args).toContain(worktreeGitDir);
|
||||
expect(args).toContain(mainGitDir);
|
||||
expect(args[args.indexOf(worktreeGitDir) - 1]).toBe('--ro-bind-try');
|
||||
expect(args[args.indexOf(mainGitDir) - 1]).toBe('--ro-bind-try');
|
||||
});
|
||||
|
||||
it('enforces read-only binding for git worktrees even if workspaceWrite is true', async () => {
|
||||
const worktreeGitDir = '/path/to/worktree/.git';
|
||||
|
||||
const args = await buildBwrapArgs({
|
||||
...defaultOptions,
|
||||
workspaceWrite: true,
|
||||
resolvedPaths: createResolvedPaths({
|
||||
gitWorktree: {
|
||||
worktreeGitDir,
|
||||
},
|
||||
}),
|
||||
});
|
||||
|
||||
expect(args[args.indexOf(worktreeGitDir) - 1]).toBe('--ro-bind-try');
|
||||
});
|
||||
|
||||
it('git worktree read-only bindings should override previous policyWrite bindings', async () => {
|
||||
const worktreeGitDir = '/custom/worktree/.git';
|
||||
|
||||
const args = await buildBwrapArgs({
|
||||
...defaultOptions,
|
||||
resolvedPaths: createResolvedPaths({
|
||||
policyWrite: ['/custom/worktree'],
|
||||
gitWorktree: {
|
||||
worktreeGitDir,
|
||||
},
|
||||
}),
|
||||
});
|
||||
|
||||
const writeBindIndex = args.indexOf('/custom/worktree');
|
||||
const worktreeBindIndex = args.lastIndexOf(worktreeGitDir);
|
||||
|
||||
expect(writeBindIndex).toBeGreaterThan(-1);
|
||||
expect(worktreeBindIndex).toBeGreaterThan(-1);
|
||||
expect(worktreeBindIndex).toBeGreaterThan(writeBindIndex);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -11,7 +11,7 @@ import {
|
||||
getSecretFileFindArgs,
|
||||
type ResolvedSandboxPaths,
|
||||
} from '../../services/sandboxManager.js';
|
||||
import { resolveGitWorktreePaths, isErrnoException } from '../utils/fsUtils.js';
|
||||
import { isErrnoException } from '../utils/fsUtils.js';
|
||||
import { spawnAsync } from '../../utils/shell-utils.js';
|
||||
import { debugLogger } from '../../utils/debugLogger.js';
|
||||
|
||||
@@ -70,16 +70,6 @@ export async function buildBwrapArgs(
|
||||
bwrapArgs.push(bindFlag, workspace.resolved, workspace.resolved);
|
||||
}
|
||||
|
||||
const { worktreeGitDir, mainGitDir } = resolveGitWorktreePaths(
|
||||
workspace.resolved,
|
||||
);
|
||||
if (worktreeGitDir) {
|
||||
bwrapArgs.push(bindFlag, worktreeGitDir, worktreeGitDir);
|
||||
}
|
||||
if (mainGitDir) {
|
||||
bwrapArgs.push(bindFlag, mainGitDir, mainGitDir);
|
||||
}
|
||||
|
||||
for (const includeDir of resolvedPaths.globalIncludes) {
|
||||
bwrapArgs.push('--ro-bind-try', includeDir, includeDir);
|
||||
}
|
||||
@@ -113,6 +103,18 @@ export async function buildBwrapArgs(
|
||||
}
|
||||
}
|
||||
|
||||
// Grant read-only access to git worktrees/submodules. We do this last in order to
|
||||
// ensure that these rules aren't overwritten by broader write policies.
|
||||
if (resolvedPaths.gitWorktree) {
|
||||
const { worktreeGitDir, mainGitDir } = resolvedPaths.gitWorktree;
|
||||
if (worktreeGitDir) {
|
||||
bwrapArgs.push('--ro-bind-try', worktreeGitDir, worktreeGitDir);
|
||||
}
|
||||
if (mainGitDir) {
|
||||
bwrapArgs.push('--ro-bind-try', mainGitDir, mainGitDir);
|
||||
}
|
||||
}
|
||||
|
||||
for (const p of resolvedPaths.forbidden) {
|
||||
if (!fs.existsSync(p)) continue;
|
||||
try {
|
||||
|
||||
Reference in New Issue
Block a user