What it is

Gemini CLI and Code Assist agent mode run commands with your permissions and read the same~/.gemini/settings.json. Whether a hijacked session can reach your keys or run a repository's code depends on that file, your policy rules and extensions, and on what a trusted project ships in .gemini/ and .env. The audit checks all of it against theSecurity for Gemini Development guide and tells you exactly what to change. It's for Code Assist Standard and Enterprise users; consumer tiers moved to Antigravity CLI in June 2026.

Gemini CLI and Code Assist security audit v1.0.0 (read-only, offline)

FAIL  [4] The project trusts MCP servers, bypassing every confirmation
              tickets
FAIL  [4] The project .env sets Gemini CLI variables
              GEMINI_SANDBOX
WARN  [2] Sandboxing is off (the default)
              Shell commands and file edits run with your full user permissions.
PASS  [2] Folder trust is on
PASS  [3] YOLO mode is disabled
PASS  [3] Policy rules don't allow risky shell commands
...
Summary: 9 pass, 1 warn, 2 fail, 2 info

Save a report with how-to-fix steps? [h]tml, [t]ext, [c]sv, [j]son, [n]o (default n):

Every finding shows, in brackets, the section of the Gemini guide that explains the fix.

Why you can trust it

Small and knownOfficial Alpine Linux 3.24.2 pinned by digest, plus only bash, jq and yq (to read TOML policy files) from Alpine's repositories, pinned to exact versions.
OpenEverything that runs is the Dockerfile, audit.sh, its mappings file and the shared audit library. You build it yourself from source.
OfflineRuns with --network none: it cannot send anything anywhere.
Read-onlyYour files are mounted read-only and the container's own filesystem is read-only. It changes nothing.
What it can read~/.gemini/settings.json, your policy files and each extension's manifest; the machine-wide settings files; your VS Code user settings.json; your shell startup files; and the whole project folder you run it from, including any secrets kept in that project. It never mounts oauth_creds.json, google_accounts.json, chat history or extension code. Its output names settings, files and commands, and masks values that look like secrets.
UnprivilegedRuns as you, with every Linux capability dropped, and is removed when it exits.

Run it

You need Docker Desktop, Docker Engine or Podman. Nothing else is installed on your machine.

macOS, Linux and WSL2

git clone https://github.com/aisecuritylabs-org/coding-agent-security.git ~/coding-agent-security
cd ~/code/my-app            # the project you want to audit
bash ~/coding-agent-security/gemini/audit/run.sh

Windows: Command Prompt

git clone https://github.com/aisecuritylabs-org/coding-agent-security.git "%USERPROFILE%\coding-agent-security"
cd /d C:\Projects\my-app
powershell -NoProfile -ExecutionPolicy Bypass -File "%USERPROFILE%\coding-agent-security\gemini\audit\run.ps1"

Windows: PowerShell

git clone https://github.com/aisecuritylabs-org/coding-agent-security.git "$HOME\coding-agent-security"
Set-Location C:\Projects\my-app
powershell -NoProfile -ExecutionPolicy Bypass -File "$HOME\coding-agent-security\gemini\audit\run.ps1"

Replace the project folder with the one you want to audit. When gemini is on your PATH, the launcher also passes your Gemini CLI version to the audit. On Windows it also checks who can writeC:\ProgramData\gemini-cli, which the container can't see.

What it checks

AreaExamples
Sandbox, trust and approvalsSandboxing on; folder trust on; YOLO mode disabled; no auto_edit default; no risky tools.allowed entries; no sandbox network access; no permanent approvals
Policies and hooksNo allow rules for risky shell commands; no hooks that run remote code; no --yolo or GEMINI_SANDBOX=false in shell startup files
ExtensionsMCP server inventory; unpinned servers; servers that read outsider-written content; "trust": true; secrets in settings; extensions and what they add
Code Assistgeminicodeassist.agentYoloMode off
RepositoriesA .gemini/settings.json that lowers protections, trusts MCP servers or adds hooks; a .env that sets GEMINI_* variables; context files with fetch instructions or hidden Unicode; run-gemini-cli workflows; disguised symlinks; .git/config keys that run programs
Machine and versionProgramData folder permissions on Windows; machine-wide settings; Gemini CLI 0.39.1 or later

The exit code is 0 with no FAIL results and 1 otherwise, so the audit also works in scripts and CI. Admin controls live in the Google Cloud console, which an offline audit can't see; the checklist covers them.

Real attacks it covers

AttackWhat it didCheck
CVE-2026-12537Headless runs in CI trusted the workspace and ignored allowlists under --yoloI01, P11
TracebitAn allowed grep chained with a command that sent environment variables awayG06, P09
Comment and ControlIssue text made the Gemini CLI Action post its API key publiclyP11
ProgramData configuration (Cymulate)One Windows user plants a session-start hook every user runsW01, W02
Plugin4Shell (AIR Security)A commit-pinned extension was swapped for different codeG09, E02

Reports with how-to-fix steps

After the on-screen summary, the audit asks whether to save a report as HTML,text, CSV or JSON. Every finding explains why it matters, gives the exact fix, maps the risk to OWASP Top 10 for LLM Applications 2025, OWASP Top 10 for Agentic Applications 2026, MITRE ATLAS 2026.09 and NIST AI RMF 1.0, and lists its sources. Reports are saved to gemini-audit-reports in your home folder, never inside the project. Reports mask values that look like secrets, such as tokens, passwords and credentials in URLs, but review a report before you share it.

Fix what it finds

Most findings are fixed by a settings file and a policy file, below as examples to adapt and test. Every key is from the Gemini CLI configuration reference and the policy engine docs.

Baseline settings.json

Save as ~/.gemini/settings.json; Code Assist agent mode reads it too. On macOS the default Seatbelt profile allows broad reads and network access, so set SEATBELT_PROFILE to astrict- profile, or set "sandbox": "docker".

{
  "general": {
    "defaultApprovalMode": "default"
  },
  "tools": {
    "sandbox": true,
    "sandboxNetworkAccess": false,
    "allowed": [
      "run_shell_command(git status)",
      "run_shell_command(git diff)",
      "run_shell_command(git log)"
    ]
  },
  "security": {
    "folderTrust": {
      "enabled": true
    },
    "disableYoloMode": true,
    "enablePermanentToolApproval": false
  }
}

Policy rules

Save as ~/.gemini/policies/baseline.toml. The highest-priority matching rule wins. Project-level policies aren't enforced yet, so keep yours in your home folder.

# Save as ~/.gemini/policies/baseline.toml. When several rules match a tool
# call, the one with the highest priority wins. Test these rules before you rely
# on them; see gemini/README.md.

# Never delete recursively or escalate privileges.
[[rule]]
toolName = "run_shell_command"
commandPrefix = ["rm -rf", "rm -r", "sudo"]
decision = "deny"
priority = 200

# Ask before anything that publishes, pushes or reaches the network.
[[rule]]
toolName = "run_shell_command"
commandPrefix = ["git push", "npm publish", "curl", "wget", "ssh", "scp"]
decision = "ask_user"
priority = 100

Gemini Code Assist

Merge into your VS Code user settings.json, so agent mode asks before it acts.

// Merge into your VS Code user settings.json for Gemini Code Assist. Agent mode
// also reads ~/.gemini/settings.json, so apply settings.json from this folder too.
// Test these settings before you rely on them; see gemini/README.md.
{
  // Keep agent mode asking before it acts.
  "geminicodeassist.agentYoloMode": false,

  // Agent mode's YOLO setting works only in trusted workspaces; keep trust on.
  "security.workspace.trust.enabled": true
}

To prove it all works in a live session, run the ten-minute self-test.

Windows: lock the ProgramData folder

Gemini CLI on Windows loads machine-wide settings.json and system-defaults.jsonfrom C:\ProgramData\gemini-cli. The folder isn't created or locked by default, so any standard user could create it and plant a session-start hook that runs for every user. Cymulate reported this, and Google said it would be "addressed as a documentation update" (Cymulate). The audit reports it as W01.

Run this in PowerShell opened with Run as administrator; it's also in the repository as gemini/config/lock-programdata.ps1, and the last line shows the result. If the folder already existed and you didn't create it, check what's inside first.

# Run in PowerShell opened with "Run as administrator".
$p = 'C:\ProgramData\gemini-cli'
New-Item -ItemType Directory -Force -Path $p | Out-Null

# A fresh ACL: owned by Administrators, no inherited or extra entries.
$admins = [Security.Principal.SecurityIdentifier]'S-1-5-32-544'
$acl = New-Object Security.AccessControl.DirectorySecurity
$acl.SetOwner($admins)
$acl.SetAccessRuleProtection($true, $false)
$grants = @(
    @('S-1-5-32-544', 'FullControl'),      # Administrators
    @('S-1-5-18',     'FullControl'),      # SYSTEM
    @('S-1-5-32-545', 'ReadAndExecute')    # Users: read only
)
foreach ($g in $grants) {
    $sid = [Security.Principal.SecurityIdentifier]$g[0]
    $acl.AddAccessRule((New-Object Security.AccessControl.FileSystemAccessRule($sid, $g[1], 'ContainerInherit, ObjectInherit', 'None', 'Allow')))
}
Set-Acl -LiteralPath $p -AclObject $acl

# Anything already inside takes the same owner and permissions.
Get-ChildItem -LiteralPath $p -Recurse -Force | ForEach-Object {
    icacls $_.FullName /setowner '*S-1-5-32-544' /c /q | Out-Null
    icacls $_.FullName /reset /c /q | Out-Null
}
icacls $p

Delete it when you are done

The container is already gone when the audit finishes. To remove the image as well:

docker image rm gemini-audit