Back to skill

Security audit

nanobanana-openrouter

Security checks for vulnerabilities and agentic risk

Overview

This image-generation skill fits its stated purpose, but users should review it because it handles an OpenRouter API key insecurely and sends prompts or images to a third-party service.

Install only if you are comfortable sending prompts and any input images to OpenRouter. Prefer setting OPENROUTER_KEY through a protected environment or secret manager, avoid pasting API keys into chat, and avoid using confidential images or prompts unless your OpenRouter account and data-handling expectations allow it.

Vulnerability Patterns
  • Insecure DependenciesIntroduces malicious components through unsafe dependency sources
  • 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
Findings (2)

T08 · Insecure Dependencies

Warning
Location
scripts/generate_image.py:4
Finding

Unpinned Runtime Dependencies Create a Supply-Chain Risk

Content
View full analysis

Vulnerability Details

File Location: scripts/generate_image.py:4-7; scripts/generate_and_return.py:4-7
Vulnerability Type: Unpinned third-party dependencies
Risk Level: Medium

Affected code in scripts/generate_image.py:

python
# requires-python = ">=3.10"
# dependencies = [
#     "requests>=2.31.0",
#     "pillow>=10.0.0",
# ]

Affected code in scripts/generate_and_return.py:

python
# requires-python = ">=3.10"
# dependencies = [
#     "requests>=2.31.0",
#     "pillow>=10.0.0",
# ]

Technical Analysis

Both scripts use minimum-version constraints rather than exact, reviewed package versions. When the documented uv run workflow resolves these dependencies, it may install any later compatible release available through the configured package index.

The project does not include a reviewed lock file, package hashes, or an explicit trusted-index restriction. Consequently, execution is not reproducible and the effective third-party code may change after the Skill itself has been audited. This creates exposure to compromised future releases, unsafe upstream updates, or malicious packages supplied through a manipulated package-index configuration.

Because imported Python packages execute in the same process and security context as the scripts, a compromised dependency could access the OpenRouter API key, prompts, input images, generated images, environment variables, and any local resources available to the invoking user.

Attack Path

  1. An attacker compromises a permitted future release of requests or pillow, or causes dependency resolution to use a malicious package source.
  2. A user invokes one of the scripts through the documented uv run workflow.
  3. uv resolves a package version satisfying the broad lower-bound constraint and installs or loads it.
  4. Malicious package initialization or runtime code executes with the privileges of the Skill proc ...[truncated 884 chars]
Remediation
View remediation

Remediation Suggestions

  1. Pin every dependency to an exact, reviewed version instead of using lower-bound constraints.
  2. Generate and commit a lock file that records the complete transitive dependency graph.
  3. Require package hashes and reject artifacts whose hashes do not match reviewed values.
  4. Configure uv to resolve packages only from an approved HTTPS package index.
  5. Review dependency updates before changing the lock file, preferably using automated vulnerability and provenance checks.
  6. Run image processing in a restricted environment with minimal filesystem access, a limited environment, and constrained network access.

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/generate_and_return.py:93
Finding

API Key Exposure Through Chat and Process Arguments

Content
View full analysis

Vulnerability Details

File Location: SKILL.md:58-61; scripts/generate_image.py:102-105; scripts/generate_and_return.py:93-94
Vulnerability Type: Insecure secret handling
Risk Level: Medium

Affected documentation in SKILL.md:

markdown
The script checks for API key in this order:
1. `--api-key` argument (use if user provided key in chat)
2. `OPENROUTER_KEY` environment variable

Affected argument definition in scripts/generate_image.py:

python
parser.add_argument(
    "--api-key", "-k",
    help="OpenRouter API key (overrides OPENROUTER_KEY env var)"
)

Affected key forwarding in scripts/generate_and_return.py:

python
if args.api_key:
    cmd.extend(["--api-key", args.api_key])

Technical Analysis

The documentation explicitly permits users to provide an API key in chat. Secrets entered into a conversation may be retained in conversation history, telemetry, audit logs, transcripts, or other systems with a broader audience and retention period than a dedicated secret store.

The scripts also accept the key as a command-line argument. Command-line arguments may be visible to local process-inspection tools, monitoring agents, crash diagnostics, or execution logs. The wrapper further copies the key into the child process argument vector when invoking generate_image.py, increasing the number of processes and diagnostic surfaces through which the credential may be disclosed.

The list-based subprocess.run invocation prevents shell-command injection; the issue is credential exposure rather than command injection. The legitimate HTTPS authorization-header transmission to OpenRouter is necessary for the declared functionality and is not itself an unauthorized exfiltration channel.

Attack Path

Chat-history exposure path:

  1. A user follows the documentation and supplies an OpenRouter API key in chat.
  2. The key becomes part of the conversa ...[truncated 1191 chars]
Remediation
View remediation

Remediation Suggestions

  1. Remove the instruction encouraging users to provide API keys in chat.
  2. Remove or deprecate the --api-key command-line option.
  3. Obtain the credential from a protected environment variable, operating-system credential store, restricted-permission secret file, or non-echoing standard-input prompt.
  4. Avoid forwarding secrets in child-process arguments. If the wrapper must launch a child process, pass the credential through a narrowly constructed environment or a protected inter-process channel.
  5. Ensure application, telemetry, and error logs redact authorization credentials.
  6. Document key rotation and revocation procedures for users who previously supplied a key through chat or command-line arguments.
  7. Where supported, use a narrowly scoped key with spending limits and only the API permissions required for image generation.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Memory PoisoningPersistent Context Injection, Context Window Stuffing, Memory Manipulation
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (8)

Memory Manipulation

High
Category
Memory Poisoning
Confidence
80% confidence
Finding

Skill manipulates agent memory, state, or stored context. Memory corruption can alter personality, override safety rules, or cause unpredictable behavior.

Content

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

md
- Prompt "A serene Japanese garden" → `2025-11-23-14-23-05-japanese-garden.png`
- Prompt "sunset over mountains" → `2025-11-23-15-30-12-sunset-mountains.png`
- Prompt "create an image of a robot" → `2025-11-23-16-45-33-robot.png`
- Unclear context → `2025-11-23-17-12-48-x9k2.png`

## Image Editing

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/generate_image.py (reported line 24)May include surrounding context.

python
from typing import Any

def get_api_key(provided_key: str | None) -> str | None:
    """Get API key from argument first, then environment."""
    if provided_key:
        return provided_key
    return os.environ.get("OPENROUTER_KEY")

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
94% confidence
Finding

The skill invokes a Python script through uv run, relies on environment variables for secrets, and calls an external API, but the manifest declares no permissions or allowed-tools scope. That omission weakens least-privilege controls and can cause an agent/runtime to permit shell, network, or env access more broadly than reviewers expect.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

The skill recommends passing the API key via --api-key, which can expose the secret in shell history, process listings, audit logs, and agent execution traces. Because this key grants access to a paid external service, disclosure can lead to unauthorized usage, billing abuse, and possible access to associated account data.

Content

No source excerpt is available for this finding.

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · scripts/generate_and_return.py (reported line 96)May include surrounding context.

python
# Run generation
    try:
        result = subprocess.run(
            cmd,
            cwd=str(skill_dir),
            capture_output=True,

External Transmission

Medium
Category
Data Exfiltration
Confidence
94% confidence
Finding

This code performs an outbound HTTPS request to OpenRouter containing the full payload, which may include the user's prompt and base64-encoded input image. While external transmission is core to the tool's purpose, it is still a genuine security/privacy-relevant behavior because sensitive local data can be exfiltrated to a remote service if users are not fully aware of the destination and implications.

Content

Scanner excerpt · scripts/generate_image.py (reported line 62)May include surrounding context.

python
"Authorization": f"Bearer {api_key}",
        "Content-Type": "application/json",
    }
    resp = requests.post(url, headers=headers, json=payload, timeout=180)
    if not resp.ok:
        raise RuntimeError(f"OpenRouter API error: HTTP {resp.status_code}: {resp.text}")
    return resp.json()

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The script sends user-supplied prompts and optional input images to OpenRouter, which is an external third-party service, but it does not clearly warn users at the point of use that their content will leave the local environment. This creates a real privacy and data-handling risk, especially if users provide sensitive images or confidential prompts under the assumption that processing is local.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Low
Category
Not specified by scanner
Confidence
89% confidence
Finding

Line L124 states that the script saves PNGs to the current directory or a path specified in the filename. This contradicts L029-L032, which document a default ./output_images directory and a dedicated --output-dir option, creating inconsistent operator guidance about where files are written.

Content

No source excerpt is available for this finding.