Back to skill

Security audit

Gemini CLI

Security checks for vulnerabilities and agentic risk

Overview

This is a legitimate Gemini CLI helper, but it should be reviewed because it can send local code, images, and PDFs to an external AI service and one batch script can submit unintended files in edge cases.

Install only if you are comfortable using Google's Gemini service on the selected files. Avoid using it on proprietary, secret-bearing, regulated, or client data unless your organization allows that transfer. Prefer a session-scoped GEMINI_API_KEY, avoid passing keys directly on the command line, review output paths before using --save or --output, and be careful with batch-analyze.sh on untrusted directories or filenames.

Vulnerability Patterns
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Agent Memory PoisoningWrites attacker-controlled rules into memory that affect later sessions
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (1)

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/batch-analyze.sh:25
Finding

Unsafe pathname splitting can submit unintended local files to Gemini

Content
View full analysis

Vulnerability Details

File Location: scripts/batch-analyze.sh, lines 25–59
Vulnerability Type: Unsafe shell word splitting and pathname expansion
Risk Level: Medium

bash
# Find all code files
FILES=$(find "$INPUT_DIR" -type f \( -name "*.js" -o -name "*.ts" -o -name "*.tsx" -o -name "*.jsx" -o -name "*.py" -o -name "*.go" \) | grep -v node_modules | sort)

TOTAL=$(echo "$FILES" | wc -l)
COUNT=0

if [ "$TOTAL" -eq 0 ]; then
  echo "⚠️  No code files found in $INPUT_DIR"
  exit 1
fi

echo "📊 Found $TOTAL files to analyze"
echo ""

# Process each file
for file in $FILES; do
  COUNT=$((COUNT + 1))
  
  # Create output filename
  SAFE_NAME=$(echo "$file" | sed 's/[^a-zA-Z0-9_.-]/-/g')
  OUTPUT_FILE="$OUTPUT_DIR/${SAFE_NAME}.md"
  
  echo "[$COUNT/$TOTAL] Analyzing: $file"
  
  case "$OPERATION" in
    explain)
      gemini code --explain "$file" --format markdown > "$OUTPUT_FILE" 2>/dev/null
      ;;
    review)
      gemini code --review "$file" --format markdown > "$OUTPUT_FILE" 2>/dev/null
      ;;
    fix)
      gemini code --fix "$file" --format markdown > "$OUTPUT_FILE" 2>/dev/null
      ;;
    test)
      gemini code --test "$file" --format markdown > "$OUTPUT_FILE" 2>/dev/null
      ;;

Technical Analysis

The script stores newline-delimited output from find in the scalar variable FILES and subsequently expands it without quotes in for file in $FILES. Bash applies word splitting and pathname expansion to this expansion. Consequently, filenames containing spaces, tabs, newlines, or glob metacharacters are not preserved as individual pathnames.

This behavior can transform one path returned by the extension-restricted find command into multiple arguments. Some resulting tokens can identify files that did not match the original code-file filter. Each token is then passed to the network-connected gemini process, potentially causing an uninten ...[truncated 1971 chars]

Remediation
View remediation

Remediation Suggestions

Process filenames as NUL-delimited records and avoid storing the result of find in a scalar variable:

bash
COUNT=0

while IFS= read -r -d '' file; do
  COUNT=$((COUNT + 1))

  SAFE_NAME=$(printf '%s' "$file" | sed 's/[^a-zA-Z0-9_.-]/-/g')
  OUTPUT_FILE="$OUTPUT_DIR/${SAFE_NAME}.md"

  case "$OPERATION" in
    explain)
      gemini code --explain "$file" --format markdown > "$OUTPUT_FILE" 2>/dev/null
      ;;
    review)
      gemini code --review "$file" --format markdown > "$OUTPUT_FILE" 2>/dev/null
      ;;
    fix)
      gemini code --fix "$file" --format markdown > "$OUTPUT_FILE" 2>/dev/null
      ;;
    test)
      gemini code --test "$file" --format markdown > "$OUTPUT_FILE" 2>/dev/null
      ;;
  esac
done < <(
  find "$INPUT_DIR" -type f \
    \( -name '*.js' -o -name '*.ts' -o -name '*.tsx' -o -name '*.jsx' \
       -o -name '*.py' -o -name '*.go' \) \
    -not -path '*/node_modules/*' -print0
)

Additional hardening should include:

  1. Use mapfile -d '' if a precomputed file count is required.
  2. Replace grep -v node_modules with a structural find exclusion so paths merely containing that text are not incorrectly discarded.
  3. Add a hash of the complete source path to each output filename to prevent normalization collisions.
  4. Reject symbolic links or resolve and verify canonical paths if analysis must remain within INPUT_DIR.
  5. Where supported by the Gemini CLI, place -- before positional path arguments to prevent filenames beginning with a hyphen from being interpreted as options.
  6. Clearly warn users that selected files are transmitted to an external service and recommend scanning only trusted directory trees.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Rogue AgentSelf-Modification, Session Persistence
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (12)

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The skill is scoped extremely broadly for common coding, debugging, automation, and code-query tasks, which increases the chance it is invoked unnecessarily and sends local code or documents to an external AI service. In a skill-routing system, overbroad activation criteria can cause unintended data exposure and inappropriate tool use beyond the user's actual need.

Content

No source excerpt is available for this finding.

Session Persistence

Medium
Category
Rogue Agent
Confidence
78% confidence
Finding

The skill promotes interactive and workflow-oriented use of an external AI CLI against codebases, images, and PDFs, which can create session/context persistence and repeated transmission of sensitive project data to a remote service. Even if credentials are only in environment variables, the operational context may retain or reuse sensitive prompts, files, or outputs across a session in ways the documentation does not bound clearly.

Content

Scanner excerpt · SKILL.md (reported line 59)May include surrounding context.

md
Gemini CLI enables command-line access to Google's Gemini models for:
- **Query Codebases** — Analyze and understand large codebases
- **Generate from Images** — Create code, docs, or apps from screenshot/images
- **Generate from PDFs** — Extract and build from PDF documents
- **Automate Workflows** — Chain AI tasks for complex automation
- **Interactive Shell** — Chat with Gemini about your project

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The document instructs users to set an API key and send local code, images, PDFs, and project context to an external AI service, but it does not warn about confidentiality, sensitive data exposure, or organizational approval requirements. In a CLI skill centered on codebase querying and file submission, that omission materially increases the chance that users will upload proprietary source, secrets, or regulated data unintentionally.

Content

No source excerpt is available for this finding.

Session Persistence

Medium
Category
Rogue Agent
Confidence
60% confidence
Finding

Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Content

Scanner excerpt · references/commands.md (reported line 49)May include surrounding context.

Code Generation

bash
gemini create --from-image ./design.png  # Code from image
gemini create --from-pdf ./spec.pdf      # Code from PDF
gemini create --template react           # Generate boilerplate
gemini create --lang javascript --prompt "todo app"  # Language-specific

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

The examples show GEMINI_API_KEY="your-key-here" gemini chat and gemini chat --api-key "different-key-here" without warning that command-line secrets may be captured in shell history, process listings, logs, or CI output. This creates a realistic credential exposure path, especially in shared systems, terminals with history sync, or automated environments.

Content

No source excerpt is available for this finding.

Rp1

Medium
Category
MCP Rug Pull
Confidence
70% confidence
Finding

npx commands without a version suffix (e.g. @1.0.0) create a rug-pull risk if the upstream server is compromised and publishes a malicious update.

Content

No source excerpt is available for this finding.

Session Persistence

Medium
Category
Rogue Agent
Confidence
60% confidence
Finding

Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Content

Scanner excerpt · references/examples.md (reported line 74)May include surrounding context.

bash
# Take a screenshot of your Figma design
gemini create --from-image ./design.png --template react

Output: React component matching the design

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The script sends a user-supplied image or PDF to the external Gemini service via gemini create, but it does not clearly warn the user that local files will be transmitted off-host. This can lead to unintended disclosure of sensitive designs, documents, or embedded secrets, especially because the tool is positioned as a local CLI helper and the file preview/output flow may make the network transfer easy to overlook.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Low
Category
Not specified by scanner
Confidence
87% confidence
Finding

The security section states there is 'No persistent credential storage' and specifically says credentials are not stored in ~/.gemini/, implying a non-persistent model. Later, the same document instructs users to add the API key to ~/.bashrc or ~/.zshrc to persist it across sessions, which is persistent local credential storage even if not performed by the binary itself.

Content

No source excerpt is available for this finding.

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
92% confidence
Finding

This markdown file includes commands that write generated output to local source files using --output, but the surrounding description does not warn that these commands will create or overwrite files. Because SQP-2 for markdown covers omitted warnings about behaviors affecting user data or system integrity, the lack of disclosure is notable here.

Content

No source excerpt is available for this finding.

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
91% confidence
Finding

The examples write to ./api/client.ts and ./schema.sql, which can alter a user's working tree, but the markdown does not disclose that these commands modify local files. The behavior is user-impacting and should be explicitly called out in usage guidance.

Content

No source excerpt is available for this finding.

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
86% confidence
Finding

The PR review workflow appends results to pr-review.md, modifying a local file, but the markdown does not mention that it will create or grow that file. Similar save/redirection patterns appear elsewhere, so a user-facing warning about local file changes is missing.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.