Back to skill

Security audit

Nano Banana API

Security checks for vulnerabilities and agentic risk

Overview

The skill largely matches its image-generation purpose, but an undocumented endpoint override can redirect API-key-authenticated requests to another server if the environment is tampered with.

Install only if you are comfortable sending prompts, reference image URLs, and generated-image requests to Nano Banana. Before use, ensure NANO_BANANA_BASE_URL is unset or trusted, keep the API key in environment variables rather than command history, and use --download-dir only for directories where generated files are expected.

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/nano_banana_api.py:81
Finding
Arbitrary API Base URL Override Can Disclose Credentials and User Data## Vulnerability Details **File Location**: `scripts/nano_banana_api.py`, lines 81–85 and 216–223 **Vulnerability Type**: Unvalidated network destination for authenticated requests **Risk Level**: Medium The CLI supports the undocumented `NANO_BANANA_BASE_URL` environment variable. This variable controls the destination of API requests without validating the URL scheme or hostname. The application subsequently attaches the Nano Banana Bearer token to requests sent to that destination. ### Vulnerable Code General JSON requests at lines 81–85: ```python base_url = os.getenv("NANO_BANANA_BASE_URL", DEFAULT_BASE_URL).rstrip("/") url = f"{base_url}{path}" headers = build_headers(api_key, require_auth=require_auth, include_json=body is not None) payload = None if body is None else json.dumps(body).encode("utf-8") request = urllib.request.Request(url, data=payload, headers=headers, method=method) ``` Streaming generation requests at lines 216–223: ```python base_url = os.getenv("NANO_BANANA_BASE_URL", DEFAULT_BASE_URL).rstrip("/") headers = build_headers(args.api_key, require_auth=True, include_json=True) request = urllib.request.Request( f"{base_url}/generate", data=json.dumps(body).encode("utf-8"), headers=headers, method="POST", ) ``` The associated header construction adds the credential to any configured destination: ```python resolved_key = get_api_key(api_key) if resolved_key: headers["Authorization"] = f"Bearer {resolved_key}" ``` ### Technical Analysis Sending the API key and generation data to the documented endpoint, `https://www.nananobanana.com/api/v1`, is necessary for the Skill's declared image-generation functionality. However, allowing an environment variable to silently replace that endpoint exceeds the minimum privilege needed for normal operation. No validation ensures that the override: - Uses HTTPS. - Resolves to the official Nano Banana hostname ...[truncated 2155 chars]
Remediation
## Remediation Suggestions 1. Remove `NANO_BANANA_BASE_URL` if custom endpoints are not required. Always use the documented constant: ```python base_url = DEFAULT_BASE_URL ``` 2. If an override is required for development, enforce HTTPS and validate the hostname before adding credentials: ```python parsed = urllib.parse.urlparse(base_url) if parsed.scheme != "https": raise SystemExit("The API base URL must use HTTPS.") if parsed.hostname != "www.nananobanana.com": raise SystemExit("Refusing to send credentials to an untrusted API host.") ``` 3. Separate endpoint customization from credential forwarding. Never attach the production API key to a non-official hostname by default. 4. Require an explicit command-line option and informed confirmation for custom endpoints rather than silently trusting an inherited environment variable. 5. Apply the same centralized URL-validation function to both `request_json()` and `handle_stream()` so streaming requests cannot bypass the control. 6. Reject URLs containing embedded credentials, unexpected ports, fragments, or non-HTTP(S) schemes. Normalize and compare parsed hostnames rather than using substring or suffix checks. 7. Document all supported endpoint overrides and warn users that prompts, reference-image URLs, generation IDs, and credentials are transmitted to the selected service. 8. Prefer environment variables over the `--api-key` argument for credentials because command-line arguments may be visible in process listings or shell history.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
Findings (6)

Tainted flow: 'request' from os.getenv (line 215, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Content
request = urllib.request.Request(url, data=payload, headers=headers, method=method)

    try:
        with urllib.request.urlopen(request, timeout=timeout) as response:
            return response.status, parse_body(response.read())
    except urllib.error.HTTPError as exc:
        return exc.code, parse_body(exc.read())
Confidence
92% confidence
Finding
The request destination is built from the NANO_BANANA_BASE_URL environment variable and then fetched with urlopen, so any attacker who can influence the environment can redirect authenticated requests to an arbitrary host. Because Authorization headers containing the API key are attached for protected endpoints, this becomes an SSRF-style outbound request issue with credential exfiltration risk.

Tainted flow: 'request' from os.getenv (line 215, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Content
filename = f"{prefix}-{index}{infer_extension(image_url)}"
        destination = target_dir / filename
        request = urllib.request.Request(image_url, headers={"User-Agent": DEFAULT_USER_AGENT})
        with urllib.request.urlopen(request, timeout=120) as response, destination.open("wb") as fh:
            shutil.copyfileobj(response, fh)
        downloaded.append(str(destination))
Confidence
90% confidence
Finding
The script downloads image URLs returned by the remote API without validating the URL scheme, host, or resulting content, and writes the response directly to disk. If the upstream service or its response is malicious or compromised, this can trigger arbitrary outbound fetches and untrusted file writes, including downloads from internal or unexpected network locations.

Tainted flow: 'request' from os.getenv (line 215, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Content
)

    try:
        with urllib.request.urlopen(request, timeout=args.timeout) as response:
            final_event: dict[str, Any] | None = None
            for raw_line in response:
                line = raw_line.decode("utf-8", errors="replace").strip()
Confidence
91% confidence
Finding
The streaming generate request also uses the environment-controlled base URL and includes authentication headers, so a hostile or misconfigured environment can redirect the stream connection to an attacker-controlled server. In this mode the exposure is especially relevant because the code will establish a long-lived authenticated connection and process arbitrary streamed data from that endpoint.

Lp3

Medium
Category
MCP Least Privilege
Confidence
87% confidence
Finding
The skill instructs agents to use environment variables, invoke a local Python script, perform network calls to an external API, and download files, but it does not declare an explicit tool/permission scope. That omission weakens least-privilege controls and can let an agent use broader capabilities than reviewers or runtimes expect, increasing the risk of unintended secret access, network exfiltration, or filesystem writes.

Missing User Warnings

Medium
Confidence
93% confidence
Finding
The API reference describes sending prompts and referenceImageUrls to a third-party image generation service but does not explicitly warn users that potentially sensitive text and image inputs leave the local environment and are transmitted to an external provider. In an agent context, this omission can cause accidental disclosure of confidential user data, internal URLs, or regulated content when developers treat the operation as a normal local tool call rather than an external data-sharing action.

Missing User Warnings

Low
Confidence
86% confidence
Finding
The code creates directories and writes downloaded image content to local files when image URLs are returned. Although the option name `--download-dir` implies downloads, there is no confirmation prompt or explicit user-facing disclosure in code comments/docstrings at the point of the write about creating files on disk.

Static analysis

No suspicious patterns detected.