@google/gemini-cli-core 0.21.0-nightly.20251212.54de67536 → 0.21.0-nightly.20251216.bb0c0d8ee

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (146) hide show
  1. package/dist/google-gemini-cli-core-0.21.0-nightly.20251213.977248e09.tgz +0 -0
  2. package/dist/src/agents/executor.js +1 -1
  3. package/dist/src/agents/executor.js.map +1 -1
  4. package/dist/src/agents/executor.test.js.map +1 -1
  5. package/dist/src/availability/policyCatalog.test.js.map +1 -1
  6. package/dist/src/commands/init.d.ts +7 -0
  7. package/dist/src/commands/init.js +53 -0
  8. package/dist/src/commands/init.js.map +1 -0
  9. package/dist/src/commands/init.test.d.ts +6 -0
  10. package/dist/src/commands/init.test.js +25 -0
  11. package/dist/src/commands/init.test.js.map +1 -0
  12. package/dist/src/config/config.test.js.map +1 -1
  13. package/dist/src/config/models.d.ts +9 -1
  14. package/dist/src/config/models.js +11 -1
  15. package/dist/src/config/models.js.map +1 -1
  16. package/dist/src/config/models.test.js +14 -1
  17. package/dist/src/config/models.test.js.map +1 -1
  18. package/dist/src/confirmation-bus/message-bus.js.map +1 -1
  19. package/dist/src/confirmation-bus/types.d.ts +4 -0
  20. package/dist/src/core/baseLlmClient.test.js.map +1 -1
  21. package/dist/src/core/contentGenerator.js +1 -1
  22. package/dist/src/core/contentGenerator.js.map +1 -1
  23. package/dist/src/core/contentGenerator.test.js.map +1 -1
  24. package/dist/src/core/coreToolScheduler.d.ts +1 -1
  25. package/dist/src/core/coreToolScheduler.js +72 -34
  26. package/dist/src/core/coreToolScheduler.js.map +1 -1
  27. package/dist/src/core/coreToolScheduler.test.js +134 -37
  28. package/dist/src/core/coreToolScheduler.test.js.map +1 -1
  29. package/dist/src/core/geminiChat.js +1 -3
  30. package/dist/src/core/geminiChat.js.map +1 -1
  31. package/dist/src/core/geminiChat.test.js.map +1 -1
  32. package/dist/src/core/logger.js.map +1 -1
  33. package/dist/src/core/nonInteractiveToolExecutor.test.js +4 -5
  34. package/dist/src/core/nonInteractiveToolExecutor.test.js.map +1 -1
  35. package/dist/src/core/turn.js +1 -1
  36. package/dist/src/core/turn.js.map +1 -1
  37. package/dist/src/generated/git-commit.d.ts +2 -2
  38. package/dist/src/generated/git-commit.js +2 -2
  39. package/dist/src/hooks/hookRegistry.d.ts +0 -7
  40. package/dist/src/hooks/hookRegistry.js +0 -20
  41. package/dist/src/hooks/hookRegistry.js.map +1 -1
  42. package/dist/src/hooks/hookRegistry.test.js +1 -7
  43. package/dist/src/hooks/hookRegistry.test.js.map +1 -1
  44. package/dist/src/hooks/hookRunner.test.js +1 -1
  45. package/dist/src/hooks/hookRunner.test.js.map +1 -1
  46. package/dist/src/hooks/hookSystem.d.ts +0 -8
  47. package/dist/src/hooks/hookSystem.js +0 -18
  48. package/dist/src/hooks/hookSystem.js.map +1 -1
  49. package/dist/src/hooks/hookSystem.test.js +2 -21
  50. package/dist/src/hooks/hookSystem.test.js.map +1 -1
  51. package/dist/src/ide/detect-ide.test.js +32 -1
  52. package/dist/src/ide/detect-ide.test.js.map +1 -1
  53. package/dist/src/ide/ide-installer.test.js +1 -1
  54. package/dist/src/ide/ide-installer.test.js.map +1 -1
  55. package/dist/src/index.d.ts +2 -0
  56. package/dist/src/index.js +2 -0
  57. package/dist/src/index.js.map +1 -1
  58. package/dist/src/mcp/oauth-provider.js.map +1 -1
  59. package/dist/src/policy/config.js +71 -2
  60. package/dist/src/policy/config.js.map +1 -1
  61. package/dist/src/policy/persistence.test.d.ts +6 -0
  62. package/dist/src/policy/persistence.test.js +149 -0
  63. package/dist/src/policy/persistence.test.js.map +1 -0
  64. package/dist/src/policy/policies/read-only.toml +5 -0
  65. package/dist/src/policy/policy-engine.js +55 -1
  66. package/dist/src/policy/policy-engine.js.map +1 -1
  67. package/dist/src/policy/shell-safety.test.d.ts +6 -0
  68. package/dist/src/policy/shell-safety.test.js +74 -0
  69. package/dist/src/policy/shell-safety.test.js.map +1 -0
  70. package/dist/src/policy/toml-loader.d.ts +8 -0
  71. package/dist/src/policy/toml-loader.js +3 -3
  72. package/dist/src/policy/toml-loader.js.map +1 -1
  73. package/dist/src/safety/checker-runner.js +17 -6
  74. package/dist/src/safety/checker-runner.js.map +1 -1
  75. package/dist/src/services/loopDetectionService.js +2 -2
  76. package/dist/src/services/loopDetectionService.js.map +1 -1
  77. package/dist/src/services/modelConfigService.js.map +1 -1
  78. package/dist/src/services/shellExecutionService.js +1 -2
  79. package/dist/src/services/shellExecutionService.js.map +1 -1
  80. package/dist/src/services/shellExecutionService.test.js +6 -3
  81. package/dist/src/services/shellExecutionService.test.js.map +1 -1
  82. package/dist/src/telemetry/activity-detector.test.js.map +1 -1
  83. package/dist/src/telemetry/clearcut-logger/clearcut-logger.js +5 -5
  84. package/dist/src/telemetry/clearcut-logger/clearcut-logger.js.map +1 -1
  85. package/dist/src/telemetry/clearcut-logger/clearcut-logger.test.js +18 -1
  86. package/dist/src/telemetry/clearcut-logger/clearcut-logger.test.js.map +1 -1
  87. package/dist/src/telemetry/config.js.map +1 -1
  88. package/dist/src/telemetry/loggers.test.js +5 -5
  89. package/dist/src/telemetry/loggers.test.js.map +1 -1
  90. package/dist/src/telemetry/metrics.test.js.map +1 -1
  91. package/dist/src/telemetry/startupProfiler.test.js +1 -2
  92. package/dist/src/telemetry/startupProfiler.test.js.map +1 -1
  93. package/dist/src/telemetry/trace.js.map +1 -1
  94. package/dist/src/telemetry/types.d.ts +10 -5
  95. package/dist/src/telemetry/types.js +16 -8
  96. package/dist/src/telemetry/types.js.map +1 -1
  97. package/dist/src/test-utils/mock-message-bus.js.map +1 -1
  98. package/dist/src/tools/edit.js +1 -0
  99. package/dist/src/tools/edit.js.map +1 -1
  100. package/dist/src/tools/edit.test.js.map +1 -1
  101. package/dist/src/tools/grep.js.map +1 -1
  102. package/dist/src/tools/mcp-tool.js +7 -0
  103. package/dist/src/tools/mcp-tool.js.map +1 -1
  104. package/dist/src/tools/memoryTool.js +1 -0
  105. package/dist/src/tools/memoryTool.js.map +1 -1
  106. package/dist/src/tools/modifiable-tool.js.map +1 -1
  107. package/dist/src/tools/read-file.test.js.map +1 -1
  108. package/dist/src/tools/shell.d.ts +2 -1
  109. package/dist/src/tools/shell.js +9 -1
  110. package/dist/src/tools/shell.js.map +1 -1
  111. package/dist/src/tools/shell.test.js +2 -1
  112. package/dist/src/tools/shell.test.js.map +1 -1
  113. package/dist/src/tools/smart-edit.js +1 -0
  114. package/dist/src/tools/smart-edit.js.map +1 -1
  115. package/dist/src/tools/smart-edit.test.js.map +1 -1
  116. package/dist/src/tools/tools.d.ts +19 -0
  117. package/dist/src/tools/tools.js +28 -9
  118. package/dist/src/tools/tools.js.map +1 -1
  119. package/dist/src/tools/web-fetch.js +1 -0
  120. package/dist/src/tools/web-fetch.js.map +1 -1
  121. package/dist/src/tools/write-file.js +1 -0
  122. package/dist/src/tools/write-file.js.map +1 -1
  123. package/dist/src/tools/write-file.test.js.map +1 -1
  124. package/dist/src/utils/checkpointUtils.js.map +1 -1
  125. package/dist/src/utils/extensionLoader.d.ts +2 -2
  126. package/dist/src/utils/extensionLoader.js +5 -6
  127. package/dist/src/utils/extensionLoader.js.map +1 -1
  128. package/dist/src/utils/extensionLoader.test.js +11 -0
  129. package/dist/src/utils/extensionLoader.test.js.map +1 -1
  130. package/dist/src/utils/filesearch/crawlCache.js.map +1 -1
  131. package/dist/src/utils/filesearch/fileSearch.js.map +1 -1
  132. package/dist/src/utils/shell-permissions.d.ts +52 -0
  133. package/dist/src/utils/shell-permissions.js +188 -0
  134. package/dist/src/utils/shell-permissions.js.map +1 -0
  135. package/dist/src/utils/shell-permissions.test.d.ts +6 -0
  136. package/dist/src/utils/shell-permissions.test.js +342 -0
  137. package/dist/src/utils/shell-permissions.test.js.map +1 -0
  138. package/dist/src/utils/shell-utils.d.ts +10 -47
  139. package/dist/src/utils/shell-utils.js +1 -182
  140. package/dist/src/utils/shell-utils.js.map +1 -1
  141. package/dist/src/utils/shell-utils.test.js +1 -288
  142. package/dist/src/utils/shell-utils.test.js.map +1 -1
  143. package/dist/src/utils/tool-utils.js.map +1 -1
  144. package/dist/tsconfig.tsbuildinfo +1 -1
  145. package/package.json +2 -1
  146. package/dist/google-gemini-cli-core-0.21.0-nightly.20251211.8c83e1ea9.tgz +0 -0
@@ -0,0 +1,342 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2025 Google LLC
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+ import { expect, describe, it, beforeEach, beforeAll, vi, afterEach, } from 'vitest';
7
+ import { initializeShellParsers } from './shell-utils.js';
8
+ import { checkCommandPermissions, isCommandAllowed, isShellInvocationAllowlisted, } from './shell-permissions.js';
9
+ const mockPlatform = vi.hoisted(() => vi.fn());
10
+ const mockHomedir = vi.hoisted(() => vi.fn());
11
+ vi.mock('os', () => ({
12
+ default: {
13
+ platform: mockPlatform,
14
+ homedir: mockHomedir,
15
+ },
16
+ platform: mockPlatform,
17
+ homedir: mockHomedir,
18
+ }));
19
+ const mockQuote = vi.hoisted(() => vi.fn());
20
+ vi.mock('shell-quote', () => ({
21
+ quote: mockQuote,
22
+ }));
23
+ let config;
24
+ const isWindowsRuntime = process.platform === 'win32';
25
+ const describeWindowsOnly = isWindowsRuntime ? describe : describe.skip;
26
+ beforeAll(async () => {
27
+ mockPlatform.mockReturnValue('linux');
28
+ await initializeShellParsers();
29
+ });
30
+ beforeEach(() => {
31
+ mockPlatform.mockReturnValue('linux');
32
+ mockQuote.mockImplementation((args) => args.map((arg) => `'${arg}'`).join(' '));
33
+ config = {
34
+ getCoreTools: () => [],
35
+ getExcludeTools: () => new Set([]),
36
+ getAllowedTools: () => [],
37
+ getApprovalMode: () => 'strict',
38
+ isInteractive: () => false,
39
+ };
40
+ });
41
+ afterEach(() => {
42
+ vi.clearAllMocks();
43
+ });
44
+ describe('isCommandAllowed', () => {
45
+ it('should allow a command if no restrictions are provided', () => {
46
+ const result = isCommandAllowed('goodCommand --safe', config);
47
+ expect(result.allowed).toBe(true);
48
+ });
49
+ it('should allow a command if it is in the global allowlist', () => {
50
+ config.getCoreTools = () => ['ShellTool(goodCommand)'];
51
+ const result = isCommandAllowed('goodCommand --safe', config);
52
+ expect(result.allowed).toBe(true);
53
+ });
54
+ it('should block a command if it is not in a strict global allowlist', () => {
55
+ config.getCoreTools = () => ['ShellTool(goodCommand --safe)'];
56
+ const result = isCommandAllowed('badCommand --danger', config);
57
+ expect(result.allowed).toBe(false);
58
+ expect(result.reason).toBe(`Command(s) not in the allowed commands list. Disallowed commands: "badCommand --danger"`);
59
+ });
60
+ it('should block a command if it is in the blocked list', () => {
61
+ config.getExcludeTools = () => new Set(['ShellTool(badCommand --danger)']);
62
+ const result = isCommandAllowed('badCommand --danger', config);
63
+ expect(result.allowed).toBe(false);
64
+ expect(result.reason).toBe(`Command 'badCommand --danger' is blocked by configuration`);
65
+ });
66
+ it('should prioritize the blocklist over the allowlist', () => {
67
+ config.getCoreTools = () => ['ShellTool(badCommand --danger)'];
68
+ config.getExcludeTools = () => new Set(['ShellTool(badCommand --danger)']);
69
+ const result = isCommandAllowed('badCommand --danger', config);
70
+ expect(result.allowed).toBe(false);
71
+ expect(result.reason).toBe(`Command 'badCommand --danger' is blocked by configuration`);
72
+ });
73
+ it('should allow any command when a wildcard is in coreTools', () => {
74
+ config.getCoreTools = () => ['ShellTool'];
75
+ const result = isCommandAllowed('any random command', config);
76
+ expect(result.allowed).toBe(true);
77
+ });
78
+ it('should block any command when a wildcard is in excludeTools', () => {
79
+ config.getExcludeTools = () => new Set(['run_shell_command']);
80
+ const result = isCommandAllowed('any random command', config);
81
+ expect(result.allowed).toBe(false);
82
+ expect(result.reason).toBe('Shell tool is globally disabled in configuration');
83
+ });
84
+ it('should block a command on the blocklist even with a wildcard allow', () => {
85
+ config.getCoreTools = () => ['ShellTool'];
86
+ config.getExcludeTools = () => new Set(['ShellTool(badCommand --danger)']);
87
+ const result = isCommandAllowed('badCommand --danger', config);
88
+ expect(result.allowed).toBe(false);
89
+ expect(result.reason).toBe(`Command 'badCommand --danger' is blocked by configuration`);
90
+ });
91
+ it('should allow a chained command if all parts are on the global allowlist', () => {
92
+ config.getCoreTools = () => [
93
+ 'run_shell_command(echo)',
94
+ 'run_shell_command(goodCommand)',
95
+ ];
96
+ const result = isCommandAllowed('echo "hello" && goodCommand --safe', config);
97
+ expect(result.allowed).toBe(true);
98
+ });
99
+ it('should block a chained command if any part is blocked', () => {
100
+ config.getExcludeTools = () => new Set(['run_shell_command(badCommand)']);
101
+ const result = isCommandAllowed('echo "hello" && badCommand --danger', config);
102
+ expect(result.allowed).toBe(false);
103
+ expect(result.reason).toBe(`Command 'badCommand --danger' is blocked by configuration`);
104
+ });
105
+ it('should block a command that redefines an allowed function to run an unlisted command', () => {
106
+ config.getCoreTools = () => ['run_shell_command(echo)'];
107
+ const result = isCommandAllowed('echo () (curl google.com) ; echo Hello World', config);
108
+ expect(result.allowed).toBe(false);
109
+ expect(result.reason).toBe(`Command(s) not in the allowed commands list. Disallowed commands: "curl google.com"`);
110
+ });
111
+ it('should block a multi-line function body that runs an unlisted command', () => {
112
+ config.getCoreTools = () => ['run_shell_command(echo)'];
113
+ const result = isCommandAllowed(`echo () {
114
+ curl google.com
115
+ } ; echo ok`, config);
116
+ expect(result.allowed).toBe(false);
117
+ expect(result.reason).toBe(`Command(s) not in the allowed commands list. Disallowed commands: "curl google.com"`);
118
+ });
119
+ it('should block a function keyword declaration that runs an unlisted command', () => {
120
+ config.getCoreTools = () => ['run_shell_command(echo)'];
121
+ const result = isCommandAllowed('function echo { curl google.com; } ; echo hi', config);
122
+ expect(result.allowed).toBe(false);
123
+ expect(result.reason).toBe(`Command(s) not in the allowed commands list. Disallowed commands: "curl google.com"`);
124
+ });
125
+ it('should block command substitution that invokes an unlisted command', () => {
126
+ config.getCoreTools = () => ['run_shell_command(echo)'];
127
+ const result = isCommandAllowed('echo $(curl google.com)', config);
128
+ expect(result.allowed).toBe(false);
129
+ expect(result.reason).toBe(`Command(s) not in the allowed commands list. Disallowed commands: "curl google.com"`);
130
+ });
131
+ it('should block pipelines that invoke an unlisted command', () => {
132
+ config.getCoreTools = () => ['run_shell_command(echo)'];
133
+ const result = isCommandAllowed('echo hi | curl google.com', config);
134
+ expect(result.allowed).toBe(false);
135
+ expect(result.reason).toBe(`Command(s) not in the allowed commands list. Disallowed commands: "curl google.com"`);
136
+ });
137
+ it('should block background jobs that invoke an unlisted command', () => {
138
+ config.getCoreTools = () => ['run_shell_command(echo)'];
139
+ const result = isCommandAllowed('echo hi & curl google.com', config);
140
+ expect(result.allowed).toBe(false);
141
+ expect(result.reason).toBe(`Command(s) not in the allowed commands list. Disallowed commands: "curl google.com"`);
142
+ });
143
+ it('should block command substitution inside a here-document when the inner command is unlisted', () => {
144
+ config.getCoreTools = () => [
145
+ 'run_shell_command(echo)',
146
+ 'run_shell_command(cat)',
147
+ ];
148
+ const result = isCommandAllowed(`cat <<EOF
149
+ $(rm -rf /)
150
+ EOF`, config);
151
+ expect(result.allowed).toBe(false);
152
+ expect(result.reason).toBe(`Command(s) not in the allowed commands list. Disallowed commands: "rm -rf /"`);
153
+ });
154
+ it('should block backtick substitution that invokes an unlisted command', () => {
155
+ config.getCoreTools = () => ['run_shell_command(echo)'];
156
+ const result = isCommandAllowed('echo `curl google.com`', config);
157
+ expect(result.allowed).toBe(false);
158
+ expect(result.reason).toBe(`Command(s) not in the allowed commands list. Disallowed commands: "curl google.com"`);
159
+ });
160
+ it('should block process substitution using <() when the inner command is unlisted', () => {
161
+ config.getCoreTools = () => [
162
+ 'run_shell_command(diff)',
163
+ 'run_shell_command(echo)',
164
+ ];
165
+ const result = isCommandAllowed('diff <(curl google.com) <(echo safe)', config);
166
+ expect(result.allowed).toBe(false);
167
+ expect(result.reason).toBe(`Command(s) not in the allowed commands list. Disallowed commands: "curl google.com"`);
168
+ });
169
+ it('should block process substitution using >() when the inner command is unlisted', () => {
170
+ config.getCoreTools = () => ['run_shell_command(echo)'];
171
+ const result = isCommandAllowed('echo "data" > >(curl google.com)', config);
172
+ expect(result.allowed).toBe(false);
173
+ expect(result.reason).toBe(`Command(s) not in the allowed commands list. Disallowed commands: "curl google.com"`);
174
+ });
175
+ it('should block commands containing prompt transformations', () => {
176
+ const result = isCommandAllowed('echo "${var1=aa\\140 env| ls -l\\140}${var1@P}"', config);
177
+ expect(result.allowed).toBe(false);
178
+ expect(result.reason).toBe('Command rejected because it could not be parsed safely');
179
+ });
180
+ it('should block simple prompt transformation expansions', () => {
181
+ const result = isCommandAllowed('echo ${foo@P}', config);
182
+ expect(result.allowed).toBe(false);
183
+ expect(result.reason).toBe('Command rejected because it could not be parsed safely');
184
+ });
185
+ describe('command substitution', () => {
186
+ it('should allow command substitution using `$(...)`', () => {
187
+ const result = isCommandAllowed('echo $(goodCommand --safe)', config);
188
+ expect(result.allowed).toBe(true);
189
+ expect(result.reason).toBeUndefined();
190
+ });
191
+ it('should allow command substitution using `<(...)`', () => {
192
+ const result = isCommandAllowed('diff <(ls) <(ls -a)', config);
193
+ expect(result.allowed).toBe(true);
194
+ expect(result.reason).toBeUndefined();
195
+ });
196
+ it('should allow command substitution using `>(...)`', () => {
197
+ const result = isCommandAllowed('echo "Log message" > >(tee log.txt)', config);
198
+ expect(result.allowed).toBe(true);
199
+ expect(result.reason).toBeUndefined();
200
+ });
201
+ it('should allow command substitution using backticks', () => {
202
+ const result = isCommandAllowed('echo `goodCommand --safe`', config);
203
+ expect(result.allowed).toBe(true);
204
+ expect(result.reason).toBeUndefined();
205
+ });
206
+ it('should allow substitution-like patterns inside single quotes', () => {
207
+ config.getCoreTools = () => ['ShellTool(echo)'];
208
+ const result = isCommandAllowed("echo '$(pwd)'", config);
209
+ expect(result.allowed).toBe(true);
210
+ });
211
+ it('should block a command when parsing fails', () => {
212
+ const result = isCommandAllowed('ls &&', config);
213
+ expect(result.allowed).toBe(false);
214
+ expect(result.reason).toBe('Command rejected because it could not be parsed safely');
215
+ });
216
+ });
217
+ });
218
+ describe('checkCommandPermissions', () => {
219
+ describe('in "Default Allow" mode (no sessionAllowlist)', () => {
220
+ it('should return a detailed success object for an allowed command', () => {
221
+ const result = checkCommandPermissions('goodCommand --safe', config);
222
+ expect(result).toEqual({
223
+ allAllowed: true,
224
+ disallowedCommands: [],
225
+ });
226
+ });
227
+ it('should block commands that cannot be parsed safely', () => {
228
+ const result = checkCommandPermissions('ls &&', config);
229
+ expect(result).toEqual({
230
+ allAllowed: false,
231
+ disallowedCommands: ['ls &&'],
232
+ blockReason: 'Command rejected because it could not be parsed safely',
233
+ isHardDenial: true,
234
+ });
235
+ });
236
+ it('should return a detailed failure object for a blocked command', () => {
237
+ config.getExcludeTools = () => new Set(['ShellTool(badCommand)']);
238
+ const result = checkCommandPermissions('badCommand --danger', config);
239
+ expect(result).toEqual({
240
+ allAllowed: false,
241
+ disallowedCommands: ['badCommand --danger'],
242
+ blockReason: `Command 'badCommand --danger' is blocked by configuration`,
243
+ isHardDenial: true,
244
+ });
245
+ });
246
+ it('should return a detailed failure object for a command not on a strict allowlist', () => {
247
+ config.getCoreTools = () => ['ShellTool(goodCommand)'];
248
+ const result = checkCommandPermissions('git status && goodCommand', config);
249
+ expect(result).toEqual({
250
+ allAllowed: false,
251
+ disallowedCommands: ['git status'],
252
+ blockReason: `Command(s) not in the allowed commands list. Disallowed commands: "git status"`,
253
+ isHardDenial: false,
254
+ });
255
+ });
256
+ });
257
+ describe('in "Default Deny" mode (with sessionAllowlist)', () => {
258
+ it('should allow a command on the sessionAllowlist', () => {
259
+ const result = checkCommandPermissions('goodCommand --safe', config, new Set(['goodCommand --safe']));
260
+ expect(result.allAllowed).toBe(true);
261
+ });
262
+ it('should block a command not on the sessionAllowlist or global allowlist', () => {
263
+ const result = checkCommandPermissions('badCommand --danger', config, new Set(['goodCommand --safe']));
264
+ expect(result.allAllowed).toBe(false);
265
+ expect(result.blockReason).toContain('not on the global or session allowlist');
266
+ expect(result.disallowedCommands).toEqual(['badCommand --danger']);
267
+ });
268
+ it('should allow a command on the global allowlist even if not on the session allowlist', () => {
269
+ config.getCoreTools = () => ['ShellTool(git status)'];
270
+ const result = checkCommandPermissions('git status', config, new Set(['goodCommand --safe']));
271
+ expect(result.allAllowed).toBe(true);
272
+ });
273
+ it('should allow a chained command if parts are on different allowlists', () => {
274
+ config.getCoreTools = () => ['ShellTool(git status)'];
275
+ const result = checkCommandPermissions('git status && git commit', config, new Set(['git commit']));
276
+ expect(result.allAllowed).toBe(true);
277
+ });
278
+ it('should block a command on the sessionAllowlist if it is also globally blocked', () => {
279
+ config.getExcludeTools = () => new Set(['run_shell_command(badCommand)']);
280
+ const result = checkCommandPermissions('badCommand --danger', config, new Set(['badCommand --danger']));
281
+ expect(result.allAllowed).toBe(false);
282
+ expect(result.blockReason).toContain('is blocked by configuration');
283
+ });
284
+ it('should block a chained command if one part is not on any allowlist', () => {
285
+ config.getCoreTools = () => ['run_shell_command(echo)'];
286
+ const result = checkCommandPermissions('echo "hello" && badCommand --danger', config, new Set(['echo']));
287
+ expect(result.allAllowed).toBe(false);
288
+ expect(result.disallowedCommands).toEqual(['badCommand --danger']);
289
+ });
290
+ });
291
+ });
292
+ describeWindowsOnly('PowerShell integration', () => {
293
+ const originalComSpec = process.env['ComSpec'];
294
+ beforeEach(() => {
295
+ mockPlatform.mockReturnValue('win32');
296
+ const systemRoot = process.env['SystemRoot'] || 'C:\\Windows';
297
+ process.env['ComSpec'] =
298
+ `${systemRoot}\\System32\\WindowsPowerShell\\v1.0\\powershell.exe`;
299
+ });
300
+ afterEach(() => {
301
+ if (originalComSpec === undefined) {
302
+ delete process.env['ComSpec'];
303
+ }
304
+ else {
305
+ process.env['ComSpec'] = originalComSpec;
306
+ }
307
+ });
308
+ it('should block commands when PowerShell parser reports errors', () => {
309
+ const { allowed, reason } = isCommandAllowed('Get-ChildItem |', config);
310
+ expect(allowed).toBe(false);
311
+ expect(reason).toBe('Command rejected because it could not be parsed safely');
312
+ });
313
+ });
314
+ describe('isShellInvocationAllowlisted', () => {
315
+ function createInvocation(command) {
316
+ return { params: { command } };
317
+ }
318
+ it('should return false when any chained command segment is not allowlisted', () => {
319
+ const invocation = createInvocation('git status && rm -rf /tmp/should-not-run');
320
+ expect(isShellInvocationAllowlisted(invocation, ['run_shell_command(git)'])).toBe(false);
321
+ });
322
+ it('should return true when every segment is explicitly allowlisted', () => {
323
+ const invocation = createInvocation('git status && rm -rf /tmp/should-run && git diff');
324
+ expect(isShellInvocationAllowlisted(invocation, [
325
+ 'run_shell_command(git)',
326
+ 'run_shell_command(rm -rf)',
327
+ ])).toBe(true);
328
+ });
329
+ it('should return true when the allowlist contains a wildcard shell entry', () => {
330
+ const invocation = createInvocation('git status && rm -rf /tmp/should-run');
331
+ expect(isShellInvocationAllowlisted(invocation, ['run_shell_command'])).toBe(true);
332
+ });
333
+ it('should treat piped commands as separate segments that must be allowlisted', () => {
334
+ const invocation = createInvocation('git status | tail -n 1');
335
+ expect(isShellInvocationAllowlisted(invocation, ['run_shell_command(git)'])).toBe(false);
336
+ expect(isShellInvocationAllowlisted(invocation, [
337
+ 'run_shell_command(git)',
338
+ 'run_shell_command(tail)',
339
+ ])).toBe(true);
340
+ });
341
+ });
342
+ //# sourceMappingURL=shell-permissions.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shell-permissions.test.js","sourceRoot":"","sources":["../../../src/utils/shell-permissions.test.ts"],"names":[],"mappings":"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"}
@@ -3,8 +3,6 @@
3
3
  * Copyright 2025 Google LLC
4
4
  * SPDX-License-Identifier: Apache-2.0
5
5
  */
6
- import type { AnyToolInvocation } from '../index.js';
7
- import type { Config } from '../config/config.js';
8
6
  import { type SpawnOptionsWithoutStdio } from 'node:child_process';
9
7
  export declare const SHELL_TOOL_NAMES: string[];
10
8
  /**
@@ -25,6 +23,15 @@ export interface ShellConfiguration {
25
23
  shell: ShellType;
26
24
  }
27
25
  export declare function initializeShellParsers(): Promise<void>;
26
+ export interface ParsedCommandDetail {
27
+ name: string;
28
+ text: string;
29
+ }
30
+ interface CommandParseResult {
31
+ details: ParsedCommandDetail[];
32
+ hasError: boolean;
33
+ }
34
+ export declare function parseCommandDetails(command: string): CommandParseResult | null;
28
35
  /**
29
36
  * Determines the appropriate shell configuration for the current platform.
30
37
  *
@@ -73,36 +80,6 @@ export declare function stripShellWrapper(command: string): string;
73
80
  * @param command The shell command string to check
74
81
  * @returns true if command substitution would be executed by bash
75
82
  */
76
- /**
77
- * Checks a shell command against security policies and allowlists.
78
- *
79
- * This function operates in one of two modes depending on the presence of
80
- * the `sessionAllowlist` parameter:
81
- *
82
- * 1. **"Default Deny" Mode (sessionAllowlist is provided):** This is the
83
- * strictest mode, used for user-defined scripts like custom commands.
84
- * A command is only permitted if it is found on the global `coreTools`
85
- * allowlist OR the provided `sessionAllowlist`. It must not be on the
86
- * global `excludeTools` blocklist.
87
- *
88
- * 2. **"Default Allow" Mode (sessionAllowlist is NOT provided):** This mode
89
- * is used for direct tool invocations (e.g., by the model). If a strict
90
- * global `coreTools` allowlist exists, commands must be on it. Otherwise,
91
- * any command is permitted as long as it is not on the `excludeTools`
92
- * blocklist.
93
- *
94
- * @param command The shell command string to validate.
95
- * @param config The application configuration.
96
- * @param sessionAllowlist A session-level list of approved commands. Its
97
- * presence activates "Default Deny" mode.
98
- * @returns An object detailing which commands are not allowed.
99
- */
100
- export declare function checkCommandPermissions(command: string, config: Config, sessionAllowlist?: Set<string>): {
101
- allAllowed: boolean;
102
- disallowedCommands: string[];
103
- blockReason?: string;
104
- isHardDenial?: boolean;
105
- };
106
83
  /**
107
84
  * Determines whether a given shell command is allowed to execute based on
108
85
  * the tool's configuration including allowlists and blocklists.
@@ -118,18 +95,4 @@ export declare const spawnAsync: (command: string, args: string[], options?: Spa
118
95
  stdout: string;
119
96
  stderr: string;
120
97
  }>;
121
- export declare function isCommandAllowed(command: string, config: Config): {
122
- allowed: boolean;
123
- reason?: string;
124
- };
125
- /**
126
- * Determines whether a shell invocation should be auto-approved based on an allowlist.
127
- *
128
- * This reuses the same parsing logic as command-permission enforcement so that
129
- * chained commands must be individually covered by the allowlist.
130
- *
131
- * @param invocation The shell tool invocation being evaluated.
132
- * @param allowedPatterns The configured allowlist patterns (e.g. `run_shell_command(git)`).
133
- * @returns True if every parsed command segment is allowed by the patterns; false otherwise.
134
- */
135
- export declare function isShellInvocationAllowlisted(invocation: AnyToolInvocation, allowedPatterns: string[]): boolean;
98
+ export {};
@@ -5,7 +5,6 @@
5
5
  */
6
6
  import os from 'node:os';
7
7
  import { quote } from 'shell-quote';
8
- import { doesToolInvocationMatch } from './tool-utils.js';
9
8
  import { spawn, spawnSync, } from 'node:child_process';
10
9
  import { Language, Parser } from 'web-tree-sitter';
11
10
  import { loadWasmBinary } from './fileUtils.js';
@@ -298,7 +297,7 @@ function parsePowerShellCommandDetails(command, executable) {
298
297
  return null;
299
298
  }
300
299
  }
301
- function parseCommandDetails(command) {
300
+ export function parseCommandDetails(command) {
302
301
  const configuration = getShellConfiguration();
303
302
  if (configuration.shell === 'powershell') {
304
303
  return parsePowerShellCommandDetails(command, configuration.executable);
@@ -428,132 +427,6 @@ export function stripShellWrapper(command) {
428
427
  * @param command The shell command string to check
429
428
  * @returns true if command substitution would be executed by bash
430
429
  */
431
- /**
432
- * Checks a shell command against security policies and allowlists.
433
- *
434
- * This function operates in one of two modes depending on the presence of
435
- * the `sessionAllowlist` parameter:
436
- *
437
- * 1. **"Default Deny" Mode (sessionAllowlist is provided):** This is the
438
- * strictest mode, used for user-defined scripts like custom commands.
439
- * A command is only permitted if it is found on the global `coreTools`
440
- * allowlist OR the provided `sessionAllowlist`. It must not be on the
441
- * global `excludeTools` blocklist.
442
- *
443
- * 2. **"Default Allow" Mode (sessionAllowlist is NOT provided):** This mode
444
- * is used for direct tool invocations (e.g., by the model). If a strict
445
- * global `coreTools` allowlist exists, commands must be on it. Otherwise,
446
- * any command is permitted as long as it is not on the `excludeTools`
447
- * blocklist.
448
- *
449
- * @param command The shell command string to validate.
450
- * @param config The application configuration.
451
- * @param sessionAllowlist A session-level list of approved commands. Its
452
- * presence activates "Default Deny" mode.
453
- * @returns An object detailing which commands are not allowed.
454
- */
455
- export function checkCommandPermissions(command, config, sessionAllowlist) {
456
- const parseResult = parseCommandDetails(command);
457
- if (!parseResult || parseResult.hasError) {
458
- return {
459
- allAllowed: false,
460
- disallowedCommands: [command],
461
- blockReason: 'Command rejected because it could not be parsed safely',
462
- isHardDenial: true,
463
- };
464
- }
465
- const normalize = (cmd) => cmd.trim().replace(/\s+/g, ' ');
466
- const commandsToValidate = parseResult.details
467
- .map((detail) => normalize(detail.text))
468
- .filter(Boolean);
469
- const invocation = {
470
- params: { command: '' },
471
- };
472
- // 1. Blocklist Check (Highest Priority)
473
- const excludeTools = config.getExcludeTools() || new Set([]);
474
- const isWildcardBlocked = SHELL_TOOL_NAMES.some((name) => excludeTools.has(name));
475
- if (isWildcardBlocked) {
476
- return {
477
- allAllowed: false,
478
- disallowedCommands: commandsToValidate,
479
- blockReason: 'Shell tool is globally disabled in configuration',
480
- isHardDenial: true,
481
- };
482
- }
483
- for (const cmd of commandsToValidate) {
484
- invocation.params['command'] = cmd;
485
- if (doesToolInvocationMatch('run_shell_command', invocation, [
486
- ...excludeTools,
487
- ])) {
488
- return {
489
- allAllowed: false,
490
- disallowedCommands: [cmd],
491
- blockReason: `Command '${cmd}' is blocked by configuration`,
492
- isHardDenial: true,
493
- };
494
- }
495
- }
496
- const coreTools = config.getCoreTools() || [];
497
- const isWildcardAllowed = SHELL_TOOL_NAMES.some((name) => coreTools.includes(name));
498
- // If there's a global wildcard, all commands are allowed at this point
499
- // because they have already passed the blocklist check.
500
- if (isWildcardAllowed) {
501
- return { allAllowed: true, disallowedCommands: [] };
502
- }
503
- const disallowedCommands = [];
504
- if (sessionAllowlist) {
505
- // "DEFAULT DENY" MODE: A session allowlist is provided.
506
- // All commands must be in either the session or global allowlist.
507
- const normalizedSessionAllowlist = new Set([...sessionAllowlist].flatMap((cmd) => SHELL_TOOL_NAMES.map((name) => `${name}(${cmd})`)));
508
- for (const cmd of commandsToValidate) {
509
- invocation.params['command'] = cmd;
510
- const isSessionAllowed = doesToolInvocationMatch('run_shell_command', invocation, [...normalizedSessionAllowlist]);
511
- if (isSessionAllowed)
512
- continue;
513
- const isGloballyAllowed = doesToolInvocationMatch('run_shell_command', invocation, coreTools);
514
- if (isGloballyAllowed)
515
- continue;
516
- disallowedCommands.push(cmd);
517
- }
518
- if (disallowedCommands.length > 0) {
519
- return {
520
- allAllowed: false,
521
- disallowedCommands,
522
- blockReason: `Command(s) not on the global or session allowlist. Disallowed commands: ${disallowedCommands
523
- .map((c) => JSON.stringify(c))
524
- .join(', ')}`,
525
- isHardDenial: false, // This is a soft denial; confirmation is possible.
526
- };
527
- }
528
- }
529
- else {
530
- // "DEFAULT ALLOW" MODE: No session allowlist.
531
- const hasSpecificAllowedCommands = coreTools.filter((tool) => SHELL_TOOL_NAMES.some((name) => tool.startsWith(`${name}(`))).length > 0;
532
- if (hasSpecificAllowedCommands) {
533
- for (const cmd of commandsToValidate) {
534
- invocation.params['command'] = cmd;
535
- const isGloballyAllowed = doesToolInvocationMatch('run_shell_command', invocation, coreTools);
536
- if (!isGloballyAllowed) {
537
- disallowedCommands.push(cmd);
538
- }
539
- }
540
- if (disallowedCommands.length > 0) {
541
- return {
542
- allAllowed: false,
543
- disallowedCommands,
544
- blockReason: `Command(s) not in the allowed commands list. Disallowed commands: ${disallowedCommands
545
- .map((c) => JSON.stringify(c))
546
- .join(', ')}`,
547
- isHardDenial: false, // This is a soft denial.
548
- };
549
- }
550
- }
551
- // If no specific global allowlist exists, and it passed the blocklist,
552
- // the command is allowed by default.
553
- }
554
- // If all checks for the current mode pass, the command is allowed.
555
- return { allAllowed: true, disallowedCommands: [] };
556
- }
557
430
  /**
558
431
  * Determines whether a given shell command is allowed to execute based on
559
432
  * the tool's configuration including allowlists and blocklists.
@@ -587,58 +460,4 @@ export const spawnAsync = (command, args, options) => new Promise((resolve, reje
587
460
  reject(err);
588
461
  });
589
462
  });
590
- export function isCommandAllowed(command, config) {
591
- // By not providing a sessionAllowlist, we invoke "default allow" behavior.
592
- const { allAllowed, blockReason } = checkCommandPermissions(command, config);
593
- if (allAllowed) {
594
- return { allowed: true };
595
- }
596
- return { allowed: false, reason: blockReason };
597
- }
598
- /**
599
- * Determines whether a shell invocation should be auto-approved based on an allowlist.
600
- *
601
- * This reuses the same parsing logic as command-permission enforcement so that
602
- * chained commands must be individually covered by the allowlist.
603
- *
604
- * @param invocation The shell tool invocation being evaluated.
605
- * @param allowedPatterns The configured allowlist patterns (e.g. `run_shell_command(git)`).
606
- * @returns True if every parsed command segment is allowed by the patterns; false otherwise.
607
- */
608
- export function isShellInvocationAllowlisted(invocation, allowedPatterns) {
609
- if (!allowedPatterns.length) {
610
- return false;
611
- }
612
- const hasShellWildcard = allowedPatterns.some((pattern) => SHELL_TOOL_NAMES.includes(pattern));
613
- const hasShellSpecificPattern = allowedPatterns.some((pattern) => SHELL_TOOL_NAMES.some((name) => pattern.startsWith(`${name}(`)));
614
- if (!hasShellWildcard && !hasShellSpecificPattern) {
615
- return false;
616
- }
617
- if (hasShellWildcard) {
618
- return true;
619
- }
620
- if (!('params' in invocation) ||
621
- typeof invocation.params !== 'object' ||
622
- invocation.params === null ||
623
- !('command' in invocation.params)) {
624
- return false;
625
- }
626
- const commandValue = invocation.params.command;
627
- if (typeof commandValue !== 'string' || !commandValue.trim()) {
628
- return false;
629
- }
630
- const command = commandValue.trim();
631
- const parseResult = parseCommandDetails(command);
632
- if (!parseResult || parseResult.hasError) {
633
- return false;
634
- }
635
- const normalize = (cmd) => cmd.trim().replace(/\s+/g, ' ');
636
- const commandsToValidate = parseResult.details
637
- .map((detail) => normalize(detail.text))
638
- .filter(Boolean);
639
- if (commandsToValidate.length === 0) {
640
- return false;
641
- }
642
- return commandsToValidate.every((commandSegment) => doesToolInvocationMatch(SHELL_TOOL_NAMES[0], { params: { command: commandSegment } }, allowedPatterns));
643
- }
644
463
  //# sourceMappingURL=shell-utils.js.map