google-gemini/gemini-cli
The riskiest surfaces are the ones that execute or fetch code: ShellTool + ShellExecutionService (model-generated commands on the host, mitigated by policy/confirmation/sandbox), the sandbox relaunch path itself (exec/spawn of docker, podman, sandbox-exec), extension installation (git clone / GitHub release downloads, consent-gated), the toolDiscoveryCommand (arbitrary configured command whose stdout is parsed into tools), custom-command shell injection (!{...} processors), and the a2a-server which ships hardcoded demo credentials. Config.ts is a 4200-line god object and the two-process relaunch + ConsolePatcher/stdio interception are fragile coupling hotspots. Credential storage (OAuth via keychain/file fallback, apiKeyCredentialStorage, MCP oauth-token-storage) is sensitive file IO outside the repo.
- a2a-server customUserBuilder authenticates with literal hardcoded values — Bearer 'valid-token' and Basic 'admin:password' — which would accept those credentials on any deployed instance of this Express app.
- ShellTool executes model-generated shell commands; mitigations are layered (shell-utils root-command parsing for policy matching, PolicyDecision gating, user confirmation details of type 'exec', optional sandboxing via sandboxManager, untrusted-context tracking for build/test commands) but the blast radius is the user's shell.
- The sandbox path spawns external binaries (docker/podman/sandbox-exec) and rewrites argv/stdin; sandbox.ts uses exec/execSync/spawnSync heavily and manages temp seatbelt profile files — a failure here can strand processes or silently run unsandboxed if config detection misfires. (inferred)
- ToolRegistry.discoverAndRegisterToolsFromCommand spawns a user-configured toolDiscoveryCommand via spawn and JSON.parses its stdout into FunctionDeclarations; DiscoveredTool invocations then spawn toolCallCommand piping params as JSON on stdin — arbitrary local command execution is a designed-in extension surface.
- Custom TOML commands support !{...} shell injection executed through ShellProcessor, which throws ConfirmationRequiredError to force user approval — a prompt-file → shell-exec path that depends on the confirmation gate not being bypassed.
- Extension installs fetch remote code (cloneFromGit/downloadFromGitHubRelease) and extensions can contribute MCP servers, hooks, policies, and commands — supply-chain risk is gated by maybeRequestConsentOrFail, admin allowlists (applyAdminAllowlist), ExtensionIntegrityManager, and folder trust.
- Config (packages/core/src/config/config.ts, 4205 lines) concentrates ~200 getters plus lifecycle (auth refresh, tool registry, MCP startup, hooks, extensions) — edits here have the largest blast radius and the file already carries patches like minified-class-name normalization in maybeRegister.
- The uncaught-exception handler deliberately swallows specific node-pty errors ('Cannot resize a pty that has already exited', EBADF from node-pty stacks) — pragmatic, but any genuinely new fatal error signature matching those strings will also be suppressed.
- Secrets handling spans multiple stores: OAuth creds via oauth2.ts, GEMINI_API_KEY persisted in apiKeyCredentialStorage (keytar on supported platforms, with a documented Linux Secret-Service hang avoided by early return for OAuth/ADC), MCP tokens via keychain/hybrid/file storage — file-permission and keychain-availability edge cases are the sensitive surface.
- stdio is globally intercepted (patchStdio + ConsolePatcher redirecting console.* into coreEvents) and stdin is toggled into raw mode at multiple layers; ordering bugs here caused real issues — the code comments cite silent keystroke loss when stdin listeners drop to zero.
- MCP servers are spawned/connected from settings and extension manifests with OAuth flows (mcp-oauth-provider) and token storage; allowedMcpServerNames and admin policies constrain them, but a malicious or compromised MCP server can inject tool descriptions, prompts (McpPromptLoader), and resources into the model context — flagged by the prompt_injection_mcp eval. (inferred)
- --raw-output disables ANSI sanitization of model output in TEXT mode; the code itself warns this 'may contain harmful ANSI sequences (e.g. for phishing or command injection)' and requires --accept-raw-output-risk to suppress the warning.
- Workspace-trust is a security boundary: trustedFolders.json gates folder trust, untrusted folders restrict which extensions/commands/hooks load (FileCommandLoader checks isTrustedFolder; LiteRt binaryPath/autoStartServer are deliberately user-scope-only to prevent workspace configs executing arbitrary binaries).