Back to skill

Security audit

Alibaba Cloud AI Image Qwen Image

Security checks for vulnerabilities and agentic risk

Overview

This image-generation skill is mostly coherent, but its helper script can send arbitrary local files as reference images and fetch unvalidated returned URLs, which deserves review before installation.

Review before installing. Use it only with trusted request JSON, do not pass untrusted reference_image paths, prefer explicit image files from a dedicated input folder, protect the DashScope API key, and consider pinning the dashscope dependency and constraining outbound downloads before production use.

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/generate_image.py:87
Finding

Arbitrary Local File Disclosure Through reference_image

Content
View full analysis

Vulnerability Details

File Location: scripts/generate_image.py:87-92, 115-126
Vulnerability Type: Unrestricted local file read followed by external transmission
Risk Level: High

Vulnerable Code

python
def resolve_reference_image(value: str) -> Any:
    if value.startswith("http://") or value.startswith("https://"):
        return value
    path = Path(value)
    if path.exists():
        return path.read_bytes()
    return value
python
def call_generate(req: dict[str, Any]) -> dict[str, Any]:
    prompt = req.get("prompt")
    if not prompt:
        raise ValueError("prompt is required")

    messages = [{"role": "user", "content": [{"text": prompt}]}]
    reference_image = req.get("reference_image")
    if reference_image:
        messages[0]["content"].insert(
            0,
            {"image": resolve_reference_image(reference_image)},
        )

    response = ImageGeneration.call(
        model=MODEL_NAME,
        messages=messages,
        size=req.get("size", DEFAULT_SIZE),
        api_key=os.getenv("DASHSCOPE_API_KEY"),
        negative_prompt=req.get("negative_prompt"),
        style=req.get("style"),
        seed=req.get("seed"),
    )

Technical Analysis

The reference_image field is accepted from an inline JSON request or request file. If its value resolves to any existing filesystem path, the script reads the entire file using Path.read_bytes() and places those bytes into the request sent to the external DashScope service.

There is no restriction to an approved image directory, no canonical-path containment check, no symlink protection, no regular-file check, and no validation of the file format or maximum size. Consequently, any local file readable by the process can be treated as a reference image. Reading a local reference image is relevant to the declared functionality, but unrestricted access to the entire readable filesy ...[truncated 1147 chars]

Remediation
View remediation

Remediation Suggestions

  • Disable local-file reference images by default and require an explicit option to enable them.
  • Resolve the supplied path with Path.resolve() and enforce containment within a dedicated, approved input directory.
  • Reject absolute paths, traversal outside the approved directory, symlinks, device files, sockets, and other non-regular files.
  • Permit only reviewed image formats and verify content using an image decoder rather than trusting the filename extension.
  • Enforce a strict maximum file size before reading the file.
  • Run the script under a dedicated, least-privileged account that cannot read unrelated credentials or configuration.
  • Inform users before local content is transmitted to an external service and require confirmation where appropriate.
  • Consider accepting explicit uploaded bytes from a trusted caller instead of allowing arbitrary filesystem paths.

T09 · Insecure Skill Coding Practices

Error
Location
scripts/generate_image.py:137
Finding

Unvalidated Retrieval of a Server-Provided Image URL

Content
View full analysis

Vulnerability Details

File Location: scripts/generate_image.py:137-158
Vulnerability Type: Server-side request forgery and unbounded remote content download
Risk Level: High

Vulnerable Code

python
    image_url = None
    for item in content:
        if isinstance(item, dict) and item.get("image"):
            image_url = item["image"]
            break

    if not image_url:
        raise RuntimeError("No image URL returned by DashScope")

    usage = _get_field(
        response,
        "usage",
        response.usage if hasattr(response, "usage") else {},
    )
    return {
        "image_url": image_url,
        "width": _get_field(usage, "width"),
        "height": _get_field(usage, "height"),
        "seed": req.get("seed"),
    }


def download_image(image_url: str, output_path: Path) -> None:
    output_path.parent.mkdir(parents=True, exist_ok=True)
    with urllib.request.urlopen(image_url) as response:
        output_path.write_bytes(response.read())

The returned value is subsequently used without validation:

python
result = call_generate(req)
download_image(result["image_url"], Path(args.output))

Technical Analysis

The script treats the image URL returned by the SDK as trusted and passes it directly to urllib.request.urlopen(). It does not enforce HTTPS, allowlist expected storage hosts, validate redirect destinations, resolve and reject private network addresses, set a timeout, limit the number of downloaded bytes, or verify that the response is an image.

The vulnerability requires control or compromise of the upstream response path, such as a compromised API endpoint, SDK, network trust chain, or malicious response fixture. Under that condition, the script becomes a request primitive originating from the host where the Skill runs.

Reading the generated image is necessary for the declared function, but unrestricted retrieval fro ...[truncated 1340 chars]

Remediation
View remediation

Remediation Suggestions

  • Accept only https URLs.
  • Maintain an allowlist of documented DashScope image-storage hostnames.
  • Resolve destination hostnames and reject loopback, private, link-local, multicast, unspecified, and cloud-metadata address ranges.
  • Disable automatic redirects or validate the scheme, hostname, and resolved address after every redirect.
  • Set explicit connection and read timeouts.
  • Stream the response in bounded chunks instead of calling unbounded response.read().
  • Enforce a conservative maximum download size using both Content-Length and a running byte counter.
  • Validate the response status, media type, and image signature before saving it.
  • Delete partial files after failed validation.
  • Apply outbound network controls so the runtime can contact only required external service endpoints.

T08 · Insecure Dependencies

Warning
Location
SKILL.md:29
Finding

Unpinned DashScope Dependency Installation

Content
View full analysis

Vulnerability Details

File Location: SKILL.md:29-34
Additional Location: references/api_reference.md:5-11
Vulnerability Type: Unpinned third-party dependency
Risk Level: Medium

Vulnerable Code

From SKILL.md:

bash
python3 -m venv .venv
. .venv/bin/activate
python -m pip install dashscope

The same unpinned installation is recommended in references/api_reference.md:

bash
python3 -m venv .venv
. .venv/bin/activate
python -m pip install dashscope

Technical Analysis

The installation instructions request the latest available dashscope package from the user's configured Python package index. No exact version, lock file, package hash, or trusted-index requirement is specified.

As a result, installation behavior is not reproducible and can change without a corresponding review of this Skill. If a configured package index is compromised, shadowed by an untrusted mirror, or serves a malicious or incompatible release, arbitrary package code may execute during installation or when the package is imported.

This finding concerns unsafe dependency acquisition. The audit did not identify evidence that the named dashscope package is itself malicious.

Attack Path

  1. A user follows the documented installation command.
  2. pip resolves dashscope from the currently configured package index without an exact version or expected hash.
  3. An unsafe future release, compromised repository artifact, or malicious artifact from an untrusted configured mirror is selected.
  4. Package build or installation hooks execute under the user's account, or malicious package code runs when generate_image.py imports the SDK.
  5. The dependency obtains the same local and network privileges as the invoking process.

Impact Assessment

A compromised dependency can execute arbitrary Python code with the privileges of the user running installation or invoking the script. It cou ...[truncated 287 chars]

Remediation
View remediation

Remediation Suggestions

  • Pin dashscope to a specifically reviewed version using an exact requirement such as dashscope==X.Y.Z.
  • Maintain a lock file containing all transitive dependency versions.
  • Use hash-verified installation, such as pip install --require-hashes -r requirements.txt.
  • Specify and document an approved package index, and prevent fallback to untrusted mirrors.
  • Review package provenance, release signatures where available, and known vulnerabilities before updating.
  • Perform dependency updates through an explicit review and testing process.
  • Install dependencies in an isolated virtual environment under a non-privileged account.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Rogue AgentSelf-Modification, Session Persistence
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (8)

Credential Access

High
Category
Privilege Escalation
Confidence
60% 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 44)May include surrounding context.

python
def _load_env() -> None:
    _load_dotenv(Path.cwd() / ".env")
    repo_root = _find_repo_root(Path(__file__).resolve())
    if repo_root:
        _load_dotenv(repo_root / ".env")

Credential Access

High
Category
Privilege Escalation
Confidence
60% 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 47)May include surrounding context.

python
def _load_env() -> None:
    _load_dotenv(Path.cwd() / ".env")
    repo_root = _find_repo_root(Path(__file__).resolve())
    if repo_root:
        _load_dotenv(repo_root / ".env")

Credential Access

High
Category
Privilege Escalation
Confidence
60% 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 184)May include surrounding context.

python
_load_dashscope_api_key_from_credentials()
    if not os.environ.get("DASHSCOPE_API_KEY"):
        print(
            "Error: DASHSCOPE_API_KEY is not set. Configure it via env/.env or ~/.alibabacloud/credentials.",
            file=sys.stderr,
        )
        print("Example .env:\n  DASHSCOPE_API_KEY=your_key_here", file=sys.stderr)

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
87% confidence
Finding

The skill performs network access, reads environment secrets, and reads/writes files, but it declares no explicit tool scope or permissions boundary. In an agent runtime, that omission can cause the skill to execute with broader ambient privileges than intended, increasing the chance of credential misuse, unexpected filesystem access, or uncontrolled outbound requests.

Content

No source excerpt is available for this finding.

Session Persistence

Medium
Category
Rogue Agent
Confidence
60% confidence
Finding

Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Content

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

Validation

bash
mkdir -p output/alicloud-ai-image-qwen-image
python -m py_compile skills/ai/image/alicloud-ai-image-qwen-image/scripts/generate_image.py && echo "py_compile_ok" > output/alicloud-ai-image-qwen-image/validate.txt

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The workflow includes generic guidance about confirming region/identifiers, running connectivity checks, and distinguishing read-only versus mutating operations, which does not fit a simple image-generation skill. While not an exploit by itself, mismatched operational instructions can prompt an agent to perform unnecessary external actions or broaden execution beyond the intended image API call.

Content

No source excerpt is available for this finding.

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
78% confidence
Finding

This markdown file documents placing dashscope_api_key in a credentials file, which involves handling a sensitive secret. The reference does not include any warning about protecting that file, avoiding commits, or using secure storage, so users are not explicitly alerted to the sensitivity of the operation.

Content

No source excerpt is available for this finding.

Dynamic attribute access via getattr()

Low
Category
Dangerous Code Execution
Confidence
50% confidence
Finding

Dynamic getattr() with a non-literal attribute name can access arbitrary object attributes, potentially bypassing access controls.

Content

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

python
try:
        return obj[key]
    except Exception:
        return getattr(obj, key, default)


def call_generate(req: dict[str, Any]) -> dict[str, Any]:

Static analysis

No suspicious patterns detected.