1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
|
/**
* @license
* Copyright 2025 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import { SandboxConfig } from '@google/gemini-cli-core';
import commandExists from 'command-exists';
import * as os from 'node:os';
import { getPackageJson } from '../utils/package.js';
import { Settings } from './settings.js';
// This is a stripped-down version of the CliArgs interface from config.ts
// to avoid circular dependencies.
interface SandboxCliArgs {
sandbox?: boolean | string;
sandboxImage?: string;
}
const VALID_SANDBOX_COMMANDS: ReadonlyArray<SandboxConfig['command']> = [
'docker',
'podman',
'sandbox-exec',
];
function isSandboxCommand(value: string): value is SandboxConfig['command'] {
return (VALID_SANDBOX_COMMANDS as readonly string[]).includes(value);
}
function getSandboxCommand(
sandbox?: boolean | string,
): SandboxConfig['command'] | '' {
// If the SANDBOX env var is set, we're already inside the sandbox.
if (process.env['SANDBOX']) {
return '';
}
// note environment variable takes precedence over argument (from command line or settings)
const environmentConfiguredSandbox =
process.env['GEMINI_SANDBOX']?.toLowerCase().trim() ?? '';
sandbox =
environmentConfiguredSandbox?.length > 0
? environmentConfiguredSandbox
: sandbox;
if (sandbox === '1' || sandbox === 'true') sandbox = true;
else if (sandbox === '0' || sandbox === 'false' || !sandbox) sandbox = false;
if (sandbox === false) {
return '';
}
if (typeof sandbox === 'string' && sandbox) {
if (!isSandboxCommand(sandbox)) {
console.error(
`ERROR: invalid sandbox command '${sandbox}'. Must be one of ${VALID_SANDBOX_COMMANDS.join(
', ',
)}`,
);
process.exit(1);
}
// confirm that specified command exists
if (commandExists.sync(sandbox)) {
return sandbox;
}
console.error(
`ERROR: missing sandbox command '${sandbox}' (from GEMINI_SANDBOX)`,
);
process.exit(1);
}
// look for seatbelt, docker, or podman, in that order
// for container-based sandboxing, require sandbox to be enabled explicitly
if (os.platform() === 'darwin' && commandExists.sync('sandbox-exec')) {
return 'sandbox-exec';
} else if (commandExists.sync('docker') && sandbox === true) {
return 'docker';
} else if (commandExists.sync('podman') && sandbox === true) {
return 'podman';
}
// throw an error if user requested sandbox but no command was found
if (sandbox === true) {
console.error(
'ERROR: GEMINI_SANDBOX is true but failed to determine command for sandbox; ' +
'install docker or podman or specify command in GEMINI_SANDBOX',
);
process.exit(1);
}
return '';
}
export async function loadSandboxConfig(
settings: Settings,
argv: SandboxCliArgs,
): Promise<SandboxConfig | undefined> {
const sandboxOption = argv.sandbox ?? settings.sandbox;
const command = getSandboxCommand(sandboxOption);
const packageJson = await getPackageJson();
const image =
argv.sandboxImage ??
process.env['GEMINI_SANDBOX_IMAGE'] ??
packageJson?.config?.sandboxImageUri;
return command && image ? { command, image } : undefined;
}
|