Back to skill

Security audit

Nano Banana Pro CN

Security checks for vulnerabilities and agentic risk

Overview

This image tool appears to do its advertised job, but it can upload any local file path it is given to a third-party API without validating that the file is really an image.

Review before installing. Use this only with images and prompts you are comfortable sending to APIYi, avoid passing sensitive file paths, and run it in a restricted workspace where the process cannot read credentials, private keys, or unrelated documents. Prefer an environment variable for the API key rather than the command-line flag, and confirm which backend/provider you expect to process the images.

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

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/generate_image.py:85
Finding
Arbitrary Local File Disclosure Through Unvalidated Python Image Input<![CDATA[ ## Vulnerability Details **File Location**: `scripts/generate_image.py:85-90`, `scripts/generate_image.py:153-163`, and `scripts/generate_image.py:206` **Vulnerability Type**: Arbitrary local file read and third-party disclosure **Risk Level**: Medium ### Vulnerable Code ```python def encode_image_to_base64(image_path): """将图片文件转换为base64编码""" try: with open(image_path, "rb") as f: return base64.b64encode(f.read()).decode("utf-8") except Exception as e: print(f"错误: 无法读取图片文件 {image_path} - {e}") sys.exit(1) ``` ```python for image_path in input_images: if not os.path.exists(image_path): print(f"错误: 输入图片不存在: {image_path}") sys.exit(1) image_base64 = encode_image_to_base64(image_path) parts.append({"inlineData": {"mimeType": "image/png", "data": image_base64}}) ``` ```python response = requests.post(url, headers=headers, json=payload, timeout=400) ``` ### Technical Analysis The `--input-image` argument accepts an arbitrary filesystem path. The implementation checks only whether that path exists before opening it in binary mode and reading its complete contents. It does not: - Verify that the path is inside an approved user workspace. - Reject symbolic links, device files, or other special files. - Verify image signatures or decode the file as an image. - Enforce a maximum input size. - Derive the MIME type from validated content. All input is labeled as `image/png`, regardless of its actual format. Consequently, any readable non-image file can be Base64-encoded and inserted into the outbound JSON request. Base64 is expected as a transport encoding for legitimate image editing and is not itself evidence of malicious obfuscation. Likewise, sending valid user-selected images to the declared image-generation service is necessary for image editing. The vulnerability arises because the script does not constrain this capability to actual, authorized image files. ### Attack Pat ...[truncated 1362 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. **Restrict accessible paths** - Resolve every path with `Path.resolve(strict=True)`. - Require the resolved path to be inside an explicitly configured workspace or upload directory. - Reject paths that escape the approved directory. 2. **Reject unsafe filesystem objects** - Refuse symbolic links. - Require `Path.is_file()` rather than checking only existence. - Reject device files, sockets, named pipes, and directories. - Consider opening files with platform-supported no-follow semantics to reduce time-of-check/time-of-use risks. 3. **Validate actual image content** - Decode the input with a trusted image library. - Permit only explicitly supported formats. - Verify the magic bytes and decoded image structure instead of relying on an extension. - Set the outbound MIME type from the validated format rather than always using `image/png`. 4. **Enforce resource limits** - Apply conservative maximum file-size and image-dimension limits before encoding. - Read only regular files and fail closed on malformed input. 5. **Require informed authorization** - Display the canonical path and remote destination before upload. - Require explicit confirmation for each local file, particularly when invocation is Agent-generated. - Clearly document that prompts and images are transmitted to a third-party proxy. 6. **Apply runtime least privilege** - Run the Skill under an account or sandbox that can read only the approved workspace. - Do not expose home directories, credential stores, SSH directories, or unrelated project files to the process. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/generate_image.js:153
Finding
Arbitrary Local File Disclosure Through Unvalidated Node.js Image Input<![CDATA[ ## Vulnerability Details **File Location**: `scripts/generate_image.js:153-160`, `scripts/generate_image.js:346-358`, and `scripts/generate_image.js:397` **Vulnerability Type**: Arbitrary local file read and third-party disclosure **Risk Level**: Medium ### Vulnerable Code ```javascript function encodeImageToBase64(imagePath) { try { const bytes = fs.readFileSync(imagePath); return bytes.toString('base64'); } catch (e) { exitWithError(`错误: 无法读取图片文件 ${imagePath} - ${e.message || String(e)}`); } } ``` ```javascript if (args.inputImages && args.inputImages.length > 0) { if (args.inputImages.length > 14) { exitWithError(`错误: 输入图片最多支持14张,当前为 ${args.inputImages.length} 张`); } for (const imgPath of args.inputImages) { if (!fs.existsSync(imgPath)) { exitWithError(`错误: 输入图片不存在: ${imgPath}`); } const imageBase64 = encodeImageToBase64(imgPath); parts.push({ inlineData: { mimeType: 'image/png', data: imageBase64, }, }); } modeStr = '编辑图片'; } ``` ```javascript data = await postJson(url, headers, payload, 120_000); ``` ### Technical Analysis The preferred Node.js implementation has the same trust-boundary weakness as the Python fallback. It accepts one or more arbitrary paths from `--input-image`, checks only whether each path exists, and then reads the complete object with `fs.readFileSync()`. No content decoding or image-signature validation occurs. A regular text file, credential file, symbolic-link target, or other readable filesystem object can therefore be Base64-encoded and placed in the request as `image/png`. The resulting JSON payload is sent to the declared APIYi endpoint. Uploading genuine user-selected images is necessary for image-editing functionality and is documented by the Skill. Nevertheless, unrestricted local file access exceeds the minimum privileges needed because only actual, explicitly approved image files should be eligible for upload. ### Attac ...[truncated 1242 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. **Constrain file selection** - Canonicalize paths with `fs.realpathSync()`. - Permit files only under an approved workspace or upload directory. - Compare canonical paths carefully to prevent traversal and prefix-confusion errors. 2. **Validate filesystem object types** - Use `fs.lstatSync()` and reject symbolic links. - Use `fs.statSync()` after canonicalization and require a regular file. - Reject directories, devices, sockets, and named pipes. - Where supported, use safe open flags or descriptor-based checks to reduce race conditions. 3. **Validate decoded image data** - Parse the file with a maintained image-decoding library. - Allow only explicitly supported formats. - Verify magic bytes and image structure before reading the file into the request. - Use the validated format’s correct MIME type. 4. **Limit resource consumption** - Enforce maximum file-size, pixel-dimension, and file-count limits before Base64 conversion. - Avoid reading unbounded files synchronously into memory. 5. **Add user-visible consent** - Show each canonical path and identify `api.apiyi.com` as the upload destination. - Require confirmation before uploading files selected through Agent-generated commands. - Correct documentation that says no external network access is needed so that it clearly states that prompts and input images are sent to a third-party API proxy. 6. **Sandbox the process** - Limit filesystem access to a dedicated image-input directory. - Exclude credential stores, home-directory secrets, and unrelated projects from the Skill’s runtime view. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (23)

Tainted flow: 'headers' from os.environ.get (line 126, credential/environment) → requests.post (network output)

Critical
Category
Data Flow
Content
print(f"输出请求参数: {json.dumps(payload_log, indent=2, ensure_ascii=False)}")
    print(f"image generation in progress...")
    try:
        response = requests.post(url, headers=headers, json=payload, timeout=400)
        response.raise_for_status()

        data = response.json()
Confidence
90% confidence
Finding
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Ae1

High
Category
analysis-evasion
Content
- **优先尝试 Node.js 版本**:如果环境有 Node(`node` 命令可用),优先使用 `scripts/generate_image.js`(零依赖,参数与 Python 保持一致)。
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
- **优先尝试 Node.js 版本**:如果环境有 Node(`node` 命令可用),优先使用 `scripts/generate_image.js`(零依赖,参数与 Python 保持一致)。
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
- **优先尝试 Node.js 版本**:如果环境有 Node(`node` 命令可用),优先使用 `scripts/generate_image.js`(零依赖,参数与 Python 保持一致)。
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
- **优先尝试 Node.js 版本**:如果环境有 Node(`node` 命令可用),优先使用 `scripts/generate_image.js`(零依赖,参数与 Python 保持一致)。
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
- **优先尝试 Node.js 版本**:如果环境有 Node(`node` 命令可用),优先使用 `scripts/generate_image.js`(零依赖,参数与 Python 保持一致)。
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
- **优先尝试 Node.js 版本**:如果环境有 Node(`node` 命令可用),优先使用 `scripts/generate_image.js`(零依赖,参数与 Python 保持一致)。
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
- **优先尝试 Node.js 版本**:如果环境有 Node(`node` 命令可用),优先使用 `scripts/generate_image.js`(零依赖,参数与 Python 保持一致)。
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Lp3

Medium
Category
MCP Least Privilege
Confidence
92% confidence
Finding
The skill instructs the agent to use environment variables and make outbound network requests to a third-party image generation proxy, but it does not declare any explicit tool scope or allowed-tools boundary. In an agent ecosystem, missing permission declarations weakens least-privilege controls and can allow broader-than-intended access to secrets or network capabilities during execution.

Natural-Language Policy Violations

Medium
Confidence
92% confidence
Finding
The natural-language content of the skill, including its description, usage instructions, and examples, is presented only in Chinese. Under the policy, forcing a specific language without giving the user an explicit choice or documenting a justified locale restriction is a natural-language policy violation.

Intent-Code Divergence

Medium
Confidence
95% confidence
Finding
L034-L035 instructs that the default resolution is 2K, suggesting the skill should actively use 2K unless otherwise specified. But L152 says that if resolution is omitted, it is not included in the request and the API side decides; L178 also labels the default as 2K. These statements conflict on actual behavior and can mislead operators about what the skill does.

Intent-Code Divergence

Medium
Confidence
93% confidence
Finding
The API key guidance is contradictory and includes passing the secret on the command line, which is commonly exposed via shell history, process listings, logs, and audit trails. In a skill that invokes local commands, normalizing CLI secret input increases the risk of credential leakage to other users or monitoring systems on the host.

Description-Behavior Mismatch

Medium
Confidence
98% confidence
Finding
The skill metadata claims to use NanoBananaPro, but the code actually sends prompts and images to a Gemini 3 Pro image endpoint through a third-party proxy. This mismatch can mislead users and operators about where sensitive image data and prompts are processed, undermining informed consent, data handling expectations, and security review assumptions.

External Transmission

Medium
Category
Data Exfiltration
Content
const apiKey = getApiKey(args.apiKey);
  const url =
    'https://api.apiyi.com/v1beta/models/gemini-3-pro-image-preview:generateContent';

  const headers = {
    Authorization: `Bearer ${apiKey}`,
Confidence
91% confidence
Finding
The script transmits user prompts, API credentials, and optionally base64-encoded local images to an external service at apiyi.com. In an image-editing skill, this is expected functionality, but it is still security-relevant because local files may contain sensitive content and the skill description emphasizes domestic proxy access rather than clearly foregrounding third-party data exfiltration risk.

Missing User Warnings

Medium
Confidence
92% confidence
Finding
The script transmits user prompts and, in edit mode, locally supplied images to a third-party remote service, but it provides no explicit privacy warning or consent checkpoint at the point of use. In a skill context, this can cause users to unknowingly send sensitive images or confidential text off-host to an external provider/proxy.

External Transmission

Medium
Category
Data Exfiltration
Content
api_key = get_api_key(api_key)
    url = (
        "https://api.apiyi.com/v1beta/models/gemini-3-pro-image-preview:generateContent"
    )

    headers = {"Authorization": f"Bearer {api_key}", "Content-Type": "application/json"}
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
print(f"输出请求参数: {json.dumps(payload_log, indent=2, ensure_ascii=False)}")
    print(f"image generation in progress...")
    try:
        response = requests.post(url, headers=headers, json=payload, timeout=400)
        response.raise_for_status()

        data = response.json()
Confidence
93% confidence
Finding
The script sends user-supplied prompt text and optional input images to an external HTTPS endpoint for processing. This is core functionality, but in security terms it is still an external data transmission path that may expose sensitive user content to a third-party service, especially because the service is described as a domestic proxy for another model provider.

Vague Triggers

Medium
Confidence
92% confidence
Finding
The trigger list includes broad phrases such as '生成图片', '图片生成', and 'image generation', which can match many ordinary requests and cause the skill to activate when the user did not specifically intend to use this provider-backed image tool. Because the skill requires an API-backed external service, unintended invocation can lead to unnecessary external data transfer, accidental use of configured credentials, and confusion about which tool is operating.

Intent-Code Divergence

Low
Confidence
84% confidence
Finding
At L027 the guidance says output filenames '需包含文件随机标识,避免重复' and implies uniqueness via randomness. However, L150 describes automatic naming as only a timestamp-based PNG filename. This is a direct documentation inconsistency about how filename uniqueness is ensured.

Natural-Language Policy Violations

Low
Confidence
89% confidence
Finding
This markdown file contains only Chinese-language instructions and examples, with no note that the skill supports other languages or that Chinese is an intentional locale constraint. Under the policy, forcing a specific language without user opt-in can be a natural-language policy violation.

Intent-Code Divergence

Low
Confidence
95% confidence
Finding
文件顶部说明将该脚本描述为“基于NanoBananaPro/Gemini 3 Pro”,而帮助文本在 L060 单独宣称是“基于Gemini 3 Pro的图片生成与编辑工具”。同时代码实际调用的也是 gemini-3-pro-image-preview 接口,这与技能清单强调的 NanoBananaPro 模型表述形成明显不一致,属于文档层面的意图漂移。

Natural-Language Policy Violations

Low
Confidence
84% confidence
Finding
The natural-language instructions and argument descriptions in the module docstring are entirely Chinese, and no alternative language or opt-in is offered. Under the stated policy, forcing a specific language without user choice can be a locale-policy violation unless clearly justified as region-specific.

Natural-Language Policy Violations

Low
Confidence
87% confidence
Finding
The manifest description is written entirely in Chinese and does not indicate any language choice or opt-in for users. This can create a language/locale policy concern if the skill is presented in multilingual contexts without making the language expectation explicit.

Static analysis

No suspicious patterns detected.