mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-07-14 12:00:18 -07:00
refactor(core): delegate sandbox denial parsing to SandboxManager (#23928)
This commit is contained in:
committed by
GitHub
parent
73dd7328df
commit
8868b34c75
@@ -0,0 +1,43 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2026 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { parsePosixSandboxDenials } from './sandboxDenialUtils.js';
|
||||
import type { ShellExecutionResult } from '../../services/shellExecutionService.js';
|
||||
|
||||
describe('parsePosixSandboxDenials', () => {
|
||||
it('should detect file system denial and extract paths', () => {
|
||||
const parsed = parsePosixSandboxDenials({
|
||||
output: 'ls: /root: Operation not permitted',
|
||||
} as unknown as ShellExecutionResult);
|
||||
expect(parsed).toBeDefined();
|
||||
expect(parsed?.filePaths).toContain('/root');
|
||||
});
|
||||
|
||||
it('should detect network denial', () => {
|
||||
const parsed = parsePosixSandboxDenials({
|
||||
output: 'curl: (6) Could not resolve host: google.com',
|
||||
} as unknown as ShellExecutionResult);
|
||||
expect(parsed).toBeDefined();
|
||||
expect(parsed?.network).toBe(true);
|
||||
});
|
||||
|
||||
it('should use fallback heuristic for absolute paths', () => {
|
||||
const parsed = parsePosixSandboxDenials({
|
||||
output:
|
||||
'operation not permitted\nsome error happened with /some/path/to/file',
|
||||
} as unknown as ShellExecutionResult);
|
||||
expect(parsed).toBeDefined();
|
||||
expect(parsed?.filePaths).toContain('/some/path/to/file');
|
||||
});
|
||||
|
||||
it('should return undefined if no denial detected', () => {
|
||||
const parsed = parsePosixSandboxDenials({
|
||||
output: 'hello world',
|
||||
} as unknown as ShellExecutionResult);
|
||||
expect(parsed).toBeUndefined();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,81 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2026 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import { type ParsedSandboxDenial } from '../../services/sandboxManager.js';
|
||||
import type { ShellExecutionResult } from '../../services/shellExecutionService.js';
|
||||
|
||||
/**
|
||||
* Common POSIX-style sandbox denial detection.
|
||||
* Used by macOS and Linux sandbox managers.
|
||||
*/
|
||||
export function parsePosixSandboxDenials(
|
||||
result: ShellExecutionResult,
|
||||
): ParsedSandboxDenial | undefined {
|
||||
const output = result.output || '';
|
||||
const errorOutput = result.error?.message;
|
||||
const combined = (output + ' ' + (errorOutput || '')).toLowerCase();
|
||||
|
||||
const isFileDenial = [
|
||||
'operation not permitted',
|
||||
'vim:e303',
|
||||
'should be read/write',
|
||||
'sandbox_apply',
|
||||
'sandbox: ',
|
||||
].some((keyword) => combined.includes(keyword));
|
||||
|
||||
const isNetworkDenial = [
|
||||
'error connecting to',
|
||||
'network is unreachable',
|
||||
'could not resolve host',
|
||||
'connection refused',
|
||||
'no address associated with hostname',
|
||||
].some((keyword) => combined.includes(keyword));
|
||||
|
||||
if (!isFileDenial && !isNetworkDenial) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const filePaths = new Set<string>();
|
||||
|
||||
// Extract denied paths (POSIX absolute paths)
|
||||
const regex =
|
||||
/(?:^|\s)['"]?(\/[\w.-/]+)['"]?:\s*[Oo]peration not permitted/gi;
|
||||
let match;
|
||||
while ((match = regex.exec(output)) !== null) {
|
||||
filePaths.add(match[1]);
|
||||
}
|
||||
if (errorOutput) {
|
||||
while ((match = regex.exec(errorOutput)) !== null) {
|
||||
filePaths.add(match[1]);
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback heuristic: look for any absolute path in the output if it was a file denial
|
||||
if (isFileDenial && filePaths.size === 0) {
|
||||
const fallbackRegex =
|
||||
/(?:^|[\s"'[\]])(\/[a-zA-Z0-9_.-]+(?:\/[a-zA-Z0-9_.-]+)+)(?:$|[\s"'[\]:])/gi;
|
||||
let m;
|
||||
while ((m = fallbackRegex.exec(output)) !== null) {
|
||||
const p = m[1];
|
||||
if (p && !p.startsWith('/bin/') && !p.startsWith('/usr/bin/')) {
|
||||
filePaths.add(p);
|
||||
}
|
||||
}
|
||||
if (errorOutput) {
|
||||
while ((m = fallbackRegex.exec(errorOutput)) !== null) {
|
||||
const p = m[1];
|
||||
if (p && !p.startsWith('/bin/') && !p.startsWith('/usr/bin/')) {
|
||||
filePaths.add(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
network: isNetworkDenial || undefined,
|
||||
filePaths: filePaths.size > 0 ? Array.from(filePaths) : undefined,
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user