Back to skill

Security audit

Nano Banana Image T8

Security checks for vulnerabilities and agentic risk

Overview

This image-generation skill is mostly purpose-aligned, but it handles API keys and network destinations in ways that could expose a user's credential or private images.

Install only if you are comfortable with this skill sending prompts and selected images to the configured Nano Banana-compatible service and storing an API key in a plaintext file under your home directory. Avoid using --base-url with untrusted endpoints, prefer temporary environment-based keys when possible, and delete or rotate the saved key if you no longer need the skill.

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 (3)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/test_nano_banana_2.py:62
Finding
API Key Disclosure and SSRF Through Untrusted Image Result URLs<![CDATA[ ## Vulnerability Details **File Location**: `scripts/test_nano_banana_2.py:62-66`, with the credential-bearing client created at `scripts/test_nano_banana_2.py:344` **Vulnerability Type**: Credential disclosure through an untrusted URL and server-side request forgery **Risk Level**: High ### Vulnerable Code ```python def _extract_image_bytes(item: dict[str, Any], client: httpx.Client) -> bytes: if "b64_json" in item and isinstance(item["b64_json"], str): return base64.b64decode(item["b64_json"]) url_value = item.get("url") if isinstance(url_value, str) and url_value: resp = client.get(url_value, timeout=60) resp.raise_for_status() return resp.content raise RuntimeError("响应中未包含 b64_json 或 url") ``` The client passed to this function is initialized with the API key as a default header: ```python with httpx.Client(headers=_build_headers(api_key), follow_redirects=True) as client: ``` The header construction is: ```python def _build_headers(api_key: str) -> dict[str, str]: return {"Authorization": f"Bearer {api_key}"} ``` ### Technical Analysis The image-generation service controls the `url` field returned in its JSON response. The script performs a GET request to that URL using the same `httpx.Client` that has the API key configured as a default `Authorization` header. No validation is performed on the URL scheme, hostname, resolved IP address, port, or redirect destination. Consequently, a compromised or malicious API response can direct the client to: - An attacker-controlled HTTPS endpoint, potentially receiving the bearer credential. - Loopback or private-network services. - Link-local cloud metadata services. - Unexpected non-image resources. - Redirect chains whose destinations have not been independently validated. The request also downloads the complete response into memory without enforcing an explicit size limit. This creates an additional resource-exhaustion risk if the destinatio ...[truncated 1584 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Use a separate HTTP client without authentication headers for image downloads: ```python with httpx.Client( follow_redirects=False, timeout=httpx.Timeout(60), ) as download_client: image_bytes = _extract_image_bytes(item, download_client) ``` 2. Require the `https` scheme and allowlist the exact image-delivery domains expected from the service. 3. Resolve the hostname and reject loopback, private, link-local, multicast, reserved, and unspecified IP addresses. 4. Disable redirects or manually validate every redirect target before following it. 5. Never copy the API `Authorization` header to image-download requests. 6. Stream response bodies and enforce a conservative maximum download size. 7. Validate `Content-Type` and verify the downloaded bytes are a supported image format before writing them. 8. Prefer Base64 image data in the authenticated API response where the service supports it, avoiding secondary URL retrieval entirely. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/test_nano_banana_2.py:278
Finding
User-Controlled API Base URL Can Receive Credentials and Private User Content<![CDATA[ ## Vulnerability Details **File Location**: `scripts/test_nano_banana_2.py:278`, with credential-bearing requests at `scripts/test_nano_banana_2.py:107` and `scripts/test_nano_banana_2.py:149-154` **Vulnerability Type**: Unrestricted credential-bearing network destination **Risk Level**: High ### Vulnerable Code The command-line interface accepts an unrestricted API origin: ```python parser.add_argument("--base-url", default="https://ai.t8star.cn") ``` Text-to-image requests are sent to the supplied origin: ```python resp = client.post(f"{base_url}/v1/images/generations", json=payload, timeout=300) resp.raise_for_status() data = resp.json() ``` Image-edit requests, including uploaded image files, are also sent to the supplied origin: ```python resp = client.post( f"{base_url}/v1/images/edits", data=form_data, files=files, timeout=300, ) ``` The client used for both requests carries the bearer credential: ```python with httpx.Client(headers=_build_headers(api_key), follow_redirects=True) as client: ``` The Skill documentation describes the base URL as fixed, but also advertises `--base-url` as an available parameter in `SKILL.md:137`: ```text - `--base-url`:默认 `https://ai.t8star.cn` ``` ### Technical Analysis The declared Skill behavior requires communication with `https://ai.t8star.cn`. Allowing arbitrary replacement of that origin is unnecessary for the stated functionality and exceeds least-privilege requirements. The script does not validate that the supplied URL: - Uses HTTPS. - Has the expected hostname. - Uses the expected port. - Excludes embedded user information. - Resolves to an approved public address. - Remains on an approved origin after redirects. Because the bearer key is installed as a default client header, the selected server receives the API credential. Text prompts are included in generation requests, while image-edit requests additionally upload user-selected image files. ### Attack Path 1. An atta ...[truncated 1340 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove the `--base-url` option if alternate services are not a required feature. 2. Hardcode and validate the expected origin: ```python _ALLOWED_ORIGIN = "https://ai.t8star.cn" ``` 3. If configurability is operationally necessary, use an explicit allowlist of exact HTTPS origins rather than accepting arbitrary URLs. 4. Parse the URL and reject unexpected schemes, hosts, ports, credentials, query strings, fragments, and path prefixes. 5. Disable automatic redirects for authenticated API calls, or only follow redirects after validating that the target remains on the approved origin. 6. Scope the `Authorization` header per request after destination validation rather than configuring it as a global client default. 7. Ensure Skill instructions do not advertise an option that contradicts the documented fixed-origin policy. 8. Add automated tests proving that malicious, private, loopback, link-local, and non-HTTPS destinations are rejected before any credential-bearing request occurs. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/test_nano_banana_2.py:178
Finding
API Key Persisted as Plaintext in the User Profile<![CDATA[ ## Vulnerability Details **File Location**: `scripts/test_nano_banana_2.py:178-183`, with interactive persistence triggered at `scripts/test_nano_banana_2.py:208-210` **Vulnerability Type**: Plaintext sensitive credential storage **Risk Level**: Medium ### Vulnerable Code ```python def _save_api_key(api_key: str) -> None: _KEY_FILE.parent.mkdir(parents=True, exist_ok=True) _KEY_FILE.write_text(api_key.strip(), encoding="utf-8") if sys.platform != "win32": os.chmod(_KEY_FILE, 0o600) ``` The interactive credential path saves the entered key: ```python entered = getpass.getpass("请输入 API Key(输入过程不可见): ").strip() if not entered: raise SystemExit( "缺少 API key,请通过 --api-key / 环境变量 NANO_BANANA_API_KEY / 交互输入提供" ) _save_api_key(entered) print(f"API Key 已保存到: {_KEY_FILE}") return entered ``` The configured destination is: ```python _KEY_FILE = Path.home() / ".whaleclaw" / "credentials" / "nano_banana_api_key.txt" ``` `SKILL.md:36` also configures persistent storage: ```yaml saved_file: "~/.whaleclaw/credentials/nano_banana_api_key.txt" ``` The instructions at `SKILL.md:73-74` explicitly state that the key is stored and reused in later sessions. ### Technical Analysis Persistent plaintext storage is not required for a single image-generation operation. Although Unix-like systems apply mode `0600` after writing the file, this protection has limitations: - The key remains recoverable by any process running as the same user. - Host compromise, user-profile backups, support bundles, or accidental file copying can expose it. - On Windows, the code applies no explicit access-control hardening. - The file is written before `chmod()` executes, which can create a short exposure window depending on the process umask and filesystem behavior. - The implementation does not use atomic exclusive creation or protect against a pre-existing symbolic link. - There is no documented key deletion, expiration, rotation, or opt-out workflo ...[truncated 1850 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Do not persist API keys by default. Require explicit, informed user consent for any “remember key” feature. 2. Store persistent credentials in the operating system's protected credential facility, such as Windows Credential Manager, macOS Keychain, or a Linux Secret Service implementation. 3. Remove the `saved_file` parameter configuration unless the framework encrypts and access-controls the value appropriately. 4. Prefer ephemeral environment or protected standard-input transfer over command-line arguments. 5. If file storage must be retained: - Create the file atomically with exclusive creation and restrictive permissions from the outset. - Reject symbolic links and verify file ownership. - Apply secure Windows ACLs. - Restrict permissions on the containing credentials directory. - Avoid logging the credential or including it in errors. 6. Provide a command to delete the saved key and document key revocation and rotation procedures. 7. Clearly distinguish between one-time use and persistent storage in the user interface. 8. Add tests confirming that noninteractive keys are not persisted without explicit consent. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (10)

Tp2

High
Category
MCP Tool Poisoning
Confidence
85% confidence
Finding
Mixing characters from multiple Unicode scripts in a single identifier is a common technique to create visually ambiguous tool names.

Tp2

High
Category
MCP Tool Poisoning
Confidence
85% confidence
Finding
Mixing characters from multiple Unicode scripts in a single identifier is a common technique to create visually ambiguous tool names.

Tp2

High
Category
MCP Tool Poisoning
Confidence
85% confidence
Finding
Mixing characters from multiple Unicode scripts in a single identifier is a common technique to create visually ambiguous tool names.

Tp2

High
Category
MCP Tool Poisoning
Confidence
85% confidence
Finding
Mixing characters from multiple Unicode scripts in a single identifier is a common technique to create visually ambiguous tool names.

Missing User Warnings

High
Confidence
98% confidence
Finding
The skill explicitly allows persisting the user's API key to disk and silently reusing it later, but the user-facing flow does not require clear informed consent at the moment of storage or reuse. This creates credential-retention risk, especially because the skill is also broadly triggerable and performs shell/network actions, so a user may unintentionally seed long-lived secrets into the environment.

Lp3

Medium
Category
MCP Least Privilege
Confidence
91% confidence
Finding
The skill invokes shell execution and networked scripts but does not declare a restrictive tool scope such as explicit allowed tools or permissions. That increases the blast radius if the skill is triggered unexpectedly or later modified, because the runtime may permit broader file, environment, or shell access than users expect.

Vague Triggers

Medium
Confidence
95% confidence
Finding
The trigger list includes broad phrases like '文生图', '图生图', and generic testing phrases that can overlap with ordinary user requests. Because this skill can run shell commands, use credentials, save API keys, and contact an external service, overbroad activation materially increases the chance of unintended execution in unrelated conversations.

Missing User Warnings

Medium
Confidence
81% confidence
Finding
In image-to-image mode, the code opens user-supplied image files and sends them to the remote endpoint via an HTTP POST request. While the operation is part of the feature, the code does not explicitly disclose at the point of execution that local image contents will be transmitted off the system.

Context-Inappropriate Capability

Medium
Confidence
93% confidence
Finding
The script is a test utility, but it persists an API key to a fixed file under the user's home directory and reuses it automatically in later runs. Storing long-lived credentials beyond the immediate test increases exposure if the workstation, home directory, backups, or adjacent processes are compromised, and this is not strictly necessary for one-off image generation testing.

Missing User Warnings

Medium
Confidence
97% confidence
Finding
When a user enters an API key interactively, the script immediately saves it locally without obtaining explicit consent or warning that the credential will be written to disk. Even with restrictive file permissions on non-Windows systems, silent plaintext persistence can surprise users and unnecessarily broaden the secret's lifetime and exposure surface.

Static analysis

No suspicious patterns detected.