Back to skill

Security audit

BizyAir 图生图(Image-to-Image)助手

Security checks for vulnerabilities and agentic risk

Overview

The skill is a real BizyAir image-to-image workflow, but its upload script can send any readable local file to external storage if given that path.

Review before installing. Use only non-sensitive images, verify the exact resolved path before upload, and do not let the agent infer file paths from untrusted prompts. Prefer BIZYAIR_API_KEY or a secret store over --api-key, run the skill in an isolated environment, and install reviewed pinned dependencies. Install only if you are comfortable sending selected images and prompts to BizyAir/Alibaba OSS for processing.

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

T09 · Insecure Skill Coding Practices

Error
Location
scripts/i2i_workflow.py:125
Finding
Unrestricted Local File Upload to External Storage<![CDATA[ ## Vulnerability Details **File Location**: `scripts/i2i_workflow.py`, lines 125–153 **Vulnerability Type**: T09: Insecure Skill Coding Practices **Risk Level**: High ### Vulnerable Code ```python def upload_image(file_path: str, api_key: str = None) -> str: """上传图片并返回 URL""" if not api_key: api_key = get_api_key() file_path = Path(file_path).resolve() if not file_path.exists(): print(f"❌ 文件不存在: {file_path}") sys.exit(1) file_name = file_path.name print(f"📁 上传图片: {file_name}") # 获取上传凭证 upload_params = get_upload_token(file_name, api_key) # 上传到 OSS if not upload_to_oss( region=upload_params["region"], endpoint=upload_params["endpoint"], bucket=upload_params["bucket"], object_key=upload_params["object_key"], file_path=str(file_path), access_key_id=upload_params["access_key_id"], access_key_secret=upload_params["access_key_secret"], security_token=upload_params["security_token"], ): sys.exit(1) ``` ### Technical Analysis The image-to-image workflow legitimately requires uploading a user-selected image. However, the implementation only verifies that the resolved path exists. It does not verify that the path: - Refers to a regular file. - Contains a valid image. - Has an approved image format. - Is within an expected user-controlled directory. - Is below a safe size limit. - Is not a symbolic link to a sensitive file. As a result, the `--image` argument can identify any locally readable filesystem object accepted by the OSS client. Sensitive files such as environment files, SSH private keys, cloud credentials, application configuration, or agent state could therefore be sent to external BizyAir-provided Alibaba OSS storage. This exceeds the minimum filesystem privileges necessary for the declared image-to-image functionality. ### Attack Path 1. An attacker supplies a request that presents a sensitive local ...[truncated 1260 chars]
Remediation
<![CDATA[ ## Remediation Suggestions Apply defense-in-depth validation before requesting upload credentials or transmitting data: 1. Require `file_path.is_file()` rather than checking only `exists()`. 2. Allowlist supported extensions such as `.jpg`, `.jpeg`, `.png`, and `.webp`. 3. Decode the file with a trusted image library and reject content that is not a valid supported image. Do not rely only on extensions or MIME names. 4. Set maximum decoded dimensions and file-size limits to prevent unintended large uploads and image decompression attacks. 5. Decide on an explicit symlink policy. Prefer rejecting symlinks and verifying the final resolved path. 6. Restrict uploads to user-approved directories or files explicitly attached to the current request. 7. Display the resolved path, detected image type, and destination service, then obtain explicit user confirmation before upload. 8. Avoid presenting the returned resource URL as public unless its access characteristics and retention policy are known. 9. Document BizyAir/OSS retention, deletion, and privacy implications. 10. Add automated tests proving that credential files, directories, symlinks, malformed images, and oversized files are rejected. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/i2i_workflow.py:333
Finding
API Key Exposure Through Command-Line Arguments<![CDATA[ ## Vulnerability Details **File Location**: `scripts/i2i_workflow.py`, lines 333–337 **Vulnerability Type**: T09: Insecure Skill Coding Practices **Risk Level**: Medium ### Vulnerable Code ```python parser.add_argument("--api-key", help="BizyAir API Key") args = parser.parse_args() api_key = args.api_key or get_api_key() ``` ### Technical Analysis The script permits a BizyAir bearer token to be supplied through the `--api-key` command-line option. Command-line arguments are not an appropriate secret transport mechanism because they may be exposed through: - Shell history files. - Process inspection utilities. - Process accounting or telemetry. - Agent tool-call records and execution logs. - CI/CD logs. - Error reports that capture full command lines. Although the documented environment-variable mechanism is preferable, retaining the command-line alternative creates an avoidable credential-disclosure path. ### Attack Path 1. A user or agent executes the script with `--api-key` followed by a valid BizyAir token. 2. The shell may save the complete command in its history. 3. While the process runs, another authorized local process or user may inspect its command line. 4. Execution infrastructure may record the command and arguments. 5. An attacker with access to any resulting history, telemetry, or logs extracts the token. 6. The attacker reuses the bearer token against BizyAir APIs until it expires or is revoked. This path requires access to local process metadata, shell history, or execution logs; it does not independently bypass operating-system access controls. ### Impact Assessment A disclosed API key may permit unauthorized BizyAir API operations within the permissions assigned to that key. Potential consequences include: - Consumption of the victim’s paid quota. - Submission or querying of tasks under the victim’s account. - Access to account-associated task metadata or outputs, depending on API authorization rules. - Service disruption ...[truncated 176 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove the `--api-key` command-line argument. 2. Continue supporting `BIZYAIR_API_KEY`, while ensuring execution systems do not dump the environment into logs. 3. Prefer an operating-system keyring, secrets manager, or protected configuration file where available. 4. If interactive entry is necessary, use a hidden prompt such as Python’s `getpass`. 5. Issue narrowly scoped, short-lived API tokens where BizyAir supports them. 6. Ensure exceptions and diagnostic output never include authorization headers or tokens. 7. Document token revocation and rotation procedures. 8. Review CI/CD, agent, and shell logs for previously exposed tokens and rotate any affected credentials. ]]>

T08 · Insecure Dependencies

Warning
Location
scripts/i2i_workflow.py:14
Finding
Unpinned Runtime Dependency Installation Guidance<![CDATA[ ## Vulnerability Details **File Location**: `scripts/i2i_workflow.py`, lines 14–19 **Vulnerability Type**: T08: Insecure Dependencies **Risk Level**: Medium ### Vulnerable Code ```python # 尝试导入阿里云 OSS SDK try: import alibabacloud_oss_v2 as oss except ImportError: print("❌ 请先安装 alibabacloud_oss_v2 库: pip install alibabacloud_oss_v2") sys.exit(1) ``` ### Technical Analysis When the Alibaba Cloud OSS SDK is unavailable, the script instructs the user to install it without specifying an exact version, integrity hash, trusted package index, or reviewed lock file. The project also imports `requests` without declaring or pinning it in a dependency manifest. An unpinned command resolves to whichever package version the configured Python package index serves at installation time. This creates supply-chain and reproducibility risks, including: - A future compromised package release. - A compromised or attacker-controlled package mirror. - Dependency substitution caused by pip index configuration. - Unexpected breaking or insecure transitive dependency updates. There is no evidence that the named package is currently malicious or typosquatted. The confirmed issue is the unsafe and non-reproducible dependency acquisition process. ### Attack Path 1. The required OSS SDK is absent from the environment. 2. The script displays the unpinned `pip install alibabacloud_oss_v2` instruction. 3. The user executes the command against the currently configured package index. 4. pip selects the latest matching release and its transitive dependencies without project-provided integrity constraints. 5. If the index, package release, or dependency chain has been compromised, attacker-controlled code is installed. 6. Malicious package code can execute during installation or when the workflow imports and uses the package. Exploitation depends on a compromised distribution channel, package release, mirror, or dependency. The project does not itself retrieve and exe ...[truncated 644 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Add a reviewed dependency manifest covering both `alibabacloud_oss_v2` and `requests`. 2. Pin exact versions known to be compatible with the project. 3. Generate a lock file that includes all transitive dependencies. 4. Use package hashes and enforce hash verification during installation. 5. Document and enforce a trusted Python package index. 6. Install dependencies inside an isolated virtual environment rather than globally. 7. Run dependency vulnerability and provenance scanning in CI. 8. Use an update process that reviews changelogs and security advisories before changing pinned versions. 9. Replace the runtime installation prompt with a reference to the project’s reproducible installation command. 10. Avoid running pip with administrator or root privileges. ]]>
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 (18)

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

Critical
Category
Data Flow
Content
headers = {"Authorization": f"Bearer {api_key}"}
    params = {"file_name": file_name, "file_type": "inputs"}

    response = requests.get(UPLOAD_TOKEN_URL, headers=headers, params=params)

    if response.status_code != 200:
        print(f"❌ 获取上传凭证失败: HTTP {response.status_code}")
Confidence
90% confidence
Finding
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

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

Critical
Category
Data Flow
Content
}
    data = {"name": name, "object_key": object_key}

    response = requests.post(COMMIT_RESOURCE_URL, headers=headers, json=data)

    if response.status_code != 200:
        print(f"❌ 提交资源失败: HTTP {response.status_code}")
Confidence
90% confidence
Finding
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

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

Critical
Category
Data Flow
Content
print(f"   提示词: {prompt[:50]}...")
    print(f"   比例: {aspect_ratio}")

    response = requests.post(TASK_CREATE_URL, headers=headers, json=payload)

    if response.status_code != 200:
        print(f"❌ 创建任务失败: HTTP {response.status_code}")
Confidence
90% confidence
Finding
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

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

Critical
Category
Data Flow
Content
headers = {"Authorization": f"Bearer {api_key}"}

    print(f"🔍 查询任务结果...")
    response = requests.get(TASK_OUTPUTS_URL, params={"requestId": request_id}, headers=headers)

    if response.status_code != 200:
        print(f"❌ 查询失败: HTTP {response.status_code}")
Confidence
90% confidence
Finding
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Missing User Warnings

High
Confidence
97% confidence
Finding
The description says the assistant will use a local image as reference, but it does not clearly warn at activation time that the image will be uploaded to BizyAir's remote servers. This is a significant privacy and data-handling issue because users may provide sensitive local images without informed consent, and the skill's core workflow depends on external transfer.

Lp3

Medium
Category
MCP Least Privilege
Confidence
93% confidence
Finding
The skill invokes Python scripts that require environment variables and make outbound network requests, but it does not declare any explicit tool scope or permissions. This creates a transparency and containment gap: a host may allow the skill to access local files, secrets, and external services without clear user or platform review of those capabilities.

Vague Triggers

Medium
Confidence
93% confidence
Finding
The trigger phrases include broad natural-language expressions such as '根据这张图片生成' and '参考图片生成', which can match ordinary conversation and cause the skill to activate unexpectedly. In this skill's context, accidental activation is more dangerous because activation can lead to local image handling and upload to a remote server.

Natural-Language Policy Violations

Medium
Confidence
88% confidence
Finding
L003 和 L010 以中文直接规定技能身份与交互方式,但没有说明仅面向中文用户,也没有给出语言可选项。若技能默认强制单一语言而未提供用户选择,属于自然语言层面的语言/区域策略风险。

External Transmission

Medium
Category
Data Exfiltration
Content
import requests
import json

url = "https://api.bizyair.cn/w/v1/webapp/task/openapi/create"
headers = {
    "Content-Type": "application/json",
    "Authorization": "Bearer YOUR_API_KEY"
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Description-Behavior Mismatch

Medium
Confidence
96% confidence
Finding
清单描述该技能会“将本地图片上传后作为参考”进行图生图,但代码里 `2:LoadImage.image` 使用的是一个固定的远程 OSS 图片 URL,且后续直接调用生成接口,没有任何本地文件读取或上传逻辑。这说明当前代码行为更像是‘基于预置网络图片发起图生图请求’,与描述的本地图片上传流程不一致。

Natural-Language Policy Violations

Medium
Confidence
95% confidence
Finding
The prompt text explicitly requires that the generated content be in Simplified Chinese and clearly visible, imposing a specific language/locale choice in natural-language instructions. The file does not offer any user choice or opt-in for this locale requirement.

External Transmission

Medium
Category
Data Exfiltration
Content
}
    }

response = requests.post(url, headers=headers, json=data)
result = response.json()
print("生成结果:", result)
Confidence
94% confidence
Finding
The code transmits user-supplied prompt and image-reference data to an external service over the network. In this skill's context, that is expected functionality, but it is still security-relevant because users may provide sensitive images or text, and the sample includes no guardrails around consent, data classification, or handling of third-party processing.

Missing User Warnings

Medium
Confidence
90% confidence
Finding
The code sends image and prompt content to an external third-party API without any visible consent, disclosure, or warning mechanism. In a skill context that suggests users provide their own images, this can expose personal, copyrighted, or sensitive content to an external processor without the user understanding where their data is going.

Natural-Language Policy Violations

Medium
Confidence
95% confidence
Finding
This code presents its title, help, prompts, and error messages in Chinese only, including the module docstring and user-facing CLI output. That forces a specific language experience without user opt-in, which matches the language/locale policy violation criteria.

External Transmission

Medium
Category
Data Exfiltration
Content
}
    data = {"name": name, "object_key": object_key}

    response = requests.post(COMMIT_RESOURCE_URL, headers=headers, json=data)

    if response.status_code != 200:
        print(f"❌ 提交资源失败: HTTP {response.status_code}")
Confidence
84% confidence
Finding
The script uploads user-provided image metadata and commits the resource to an external third-party service, which constitutes real external data transmission. In this skill context, that is expected functionality, but it is still security-relevant because local image content and associated identifiers leave the host and may expose sensitive user data if users are not clearly informed.

Tainted flow: 'data' from requests.get (line 225, network input) → requests.post (network output)

Medium
Category
Data Flow
Content
}
    data = {"name": name, "object_key": object_key}

    response = requests.post(COMMIT_RESOURCE_URL, headers=headers, json=data)

    if response.status_code != 200:
        print(f"❌ 提交资源失败: HTTP {response.status_code}")
Confidence
65% confidence
Finding
Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.

External Transmission

Medium
Category
Data Exfiltration
Content
print(f"   提示词: {prompt[:50]}...")
    print(f"   比例: {aspect_ratio}")

    response = requests.post(TASK_CREATE_URL, headers=headers, json=payload)

    if response.status_code != 200:
        print(f"❌ 创建任务失败: HTTP {response.status_code}")
Confidence
86% confidence
Finding
The script transmits the user's prompt and previously uploaded image URL to an external image-generation API. This is expected for the feature, but it is still a genuine data-exposure risk because prompts may contain confidential information and reference images may be sensitive; the skill context increases relevance because the entire purpose is sending local content off-device for processing.

Tainted flow: 'request_id' from requests.get (line 203, network input) → requests.get (network output)

Medium
Category
Data Flow
Content
headers = {"Authorization": f"Bearer {api_key}"}

    print(f"🔍 查询任务结果...")
    response = requests.get(TASK_OUTPUTS_URL, params={"requestId": request_id}, headers=headers)

    if response.status_code != 200:
        print(f"❌ 查询失败: HTTP {response.status_code}")
Confidence
65% confidence
Finding
Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.

Static analysis

No suspicious patterns detected.