Back to skill

Security audit

agnes-image-video

Security checks for vulnerabilities and agentic risk

Overview

This skill is a disclosed Agnes AI image and video generation helper, with expected third-party API use and local media downloads but some hardening gaps users should understand.

Install only if you intend to send prompts, image URLs, and an Agnes API key to Agnes AI. Avoid sensitive prompts or private media unless you trust that service, and run the helper script in a private output directory without elevated privileges because it saves downloaded media locally and lacks download URL, size, timeout, and symlink protections.

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.py:48
Finding

Unvalidated API-Supplied URLs Enable Arbitrary Network Requests and Unbounded Downloads

Content
View full analysis

Vulnerability Details

File Location: scripts/generate.py, lines 48-51, 98-101, 139, and 156
Vulnerability Type: Unrestricted server-supplied URL retrieval
Risk Level: Medium

Vulnerable Code

python
if "data" in data:
    url = data["data"][0]["url"]
    print(f"图片生成成功: {url}")
    return url
python
if status == "completed":
    video_url = data.get("remixed_from_video_id")
    print(f"视频生成成功: {video_url}")
    return video_url
python
# Download the image
resp = httpx.get(url)
ext = "jpg" if b"JPEG" in resp.content else "png"
file_path = output_dir / f"image_{int(time.time())}.{ext}"
file_path.write_bytes(resp.content)
python
# Download the video
print("正在下载视频...")
resp = httpx.get(video_url)
file_path = output_dir / f"video_{int(time.time())}.mp4"
file_path.write_bytes(resp.content)

Technical Analysis

The script trusts media URLs supplied by the remote API and passes them directly to httpx.get. It does not validate the URL scheme, destination hostname, resolved IP address, or redirect destinations. It also does not configure network timeouts, verify successful HTTP status codes, stream downloads, or impose a maximum response size.

Consequently, a compromised API service, manipulated API response, or other party able to control the returned URL could direct the script to request loopback, private, link-local, or otherwise sensitive network resources. Redirects may also lead from an initially acceptable host to a prohibited destination.

Because the response is loaded completely into memory and then written to disk, an attacker-controlled endpoint can return an extremely large or indefinitely slow response. This may consume memory, disk capacity, network bandwidth, or execution time.

Attack Path

  1. The user invokes image or video generation with a valid Agnes API key.
  2. An attacker compromises the upstream API, interferes w ...[truncated 1224 chars]
Remediation
View remediation

Remediation Suggestions

  1. Require HTTPS and reject all other URL schemes.
  2. Allowlist the exact storage hostnames expected to serve generated media.
  3. Resolve destination hostnames and reject loopback, private, link-local, multicast, reserved, and unspecified addresses for both IPv4 and IPv6.
  4. Repeat destination validation after every redirect, or disable redirects entirely.
  5. Configure explicit connection, read, write, and pool timeouts.
  6. Call raise_for_status() before processing response content.
  7. Stream responses to disk rather than loading the complete body into memory.
  8. Enforce a strict maximum download size using both Content-Length, when present, and a running byte counter.
  9. Validate the returned content type and, where practical, verify the media format from trusted file signatures before saving it.
  10. Treat absent or malformed image and video URLs as API errors rather than passing them to the HTTP client.

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/generate.py:130
Finding

Predictable Output Filenames Permit Symlink-Based File Overwrite

Content
View full analysis

Vulnerability Details

File Location: scripts/generate.py, lines 130-143 and 157-158
Vulnerability Type: Predictable file creation without symlink protection
Risk Level: Medium

Vulnerable Code

python
output_dir = Path(args.output_dir)
output_dir.mkdir(exist_ok=True)

if args.mode == "image":
    url = generate_image(api_key, args.prompt, args.size, args.seed, args.image_urls)
    # Download the image
    resp = httpx.get(url)
    ext = "jpg" if b"JPEG" in resp.content else "png"
    file_path = output_dir / f"image_{int(time.time())}.{ext}"
    file_path.write_bytes(resp.content)
    print(f"图片已保存: {file_path}")
python
# Download the video
print("正在下载视频...")
resp = httpx.get(video_url)
file_path = output_dir / f"video_{int(time.time())}.mp4"
file_path.write_bytes(resp.content)
print(f"视频已保存: {file_path}")

Technical Analysis

Output filenames are based solely on the current Unix timestamp in seconds, making them predictable. Path.write_bytes() opens the destination using ordinary file-writing semantics and follows an existing symbolic link.

The script neither creates files exclusively nor verifies that the destination is a new regular file. It also accepts an existing output directory without checking its ownership, permissions, or whether the directory itself is a symbolic link.

If an attacker can write to the selected output directory, the attacker can predict the likely filename and create a symbolic link with that name before the download is saved. The subsequent write follows the link and truncates or replaces the linked target with downloaded media bytes.

Attack Path

  1. A victim runs the script using a shared or attacker-writable output directory.
  2. The attacker predicts the current timestamp and expected filename, such as video_1760000000.mp4 or the corresponding image filename.
  3. The attacker creates that path as a symbolic link to a fil ...[truncated 945 chars]
Remediation
View remediation

Remediation Suggestions

  1. Create a private output directory with permissions restricted to the current user.
  2. Reject an output directory that is a symbolic link or is owned by an unexpected user.
  3. Generate filenames using cryptographically unpredictable values rather than second-resolution timestamps.
  4. Open destination files atomically with exclusive creation so an existing path causes failure.
  5. Use no-follow semantics, such as O_NOFOLLOW where supported, to prevent symbolic-link traversal.
  6. Verify through fstat that the opened destination is a regular file before writing.
  7. Download into a securely created temporary file in the same private directory, flush and close it, and then atomically rename it to the final destination.
  8. Avoid running the media-generation helper with elevated 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 (12)

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding

The declared purpose focuses on image/video generation, but the skill also implies undeclared local file persistence and overstates support for some flows such as image-to-video in the main entry path. Description/behavior mismatch is dangerous because users and orchestrators may approve the skill for one function while it performs additional actions, especially file writes or network-mediated handling of user-provided media URLs.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
94% confidence
Finding

The skill documents use of environment variables, network access, and a bundled script that can save outputs locally, but it declares no explicit tool scope or permissions. This creates an authorization gap: a host agent may invoke capabilities broader than users or reviewers expect, increasing the risk of silent data exfiltration or unintended filesystem writes.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The trigger description is very broad and can match ordinary image/video requests, causing this skill to activate more often than intended. Over-broad activation is risky in a skill that sends prompts and media references to an external service, because user content may be transmitted off-platform without sufficiently specific user intent to use Agnes APIs.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

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

python
import httpx

response = httpx.post(
    "https://apihub.agnes-ai.com/v1/images/generations",
    headers={"Authorization": "Bearer YOUR_API_KEY"},
    json={

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

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

python
import httpx

response = httpx.post(
    "https://apihub.agnes-ai.com/v1/images/generations",
    headers={"Authorization": "Bearer YOUR_API_KEY"},
    json={

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

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

python
import httpx

response = httpx.post(
    "https://apihub.agnes-ai.com/v1/images/generations",
    headers={"Authorization": "Bearer YOUR_API_KEY"},
    json={

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

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

python
import httpx

response = httpx.post(
    "https://apihub.agnes-ai.com/v1/images/generations",
    headers={"Authorization": "Bearer YOUR_API_KEY"},
    json={

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

This Python file contains user-facing CLI text in Chinese, such as the error at L028 and additional status/help strings elsewhere, but provides no user opt-in or documented justification for enforcing that locale. Under the policy rule, forcing a specific language without choice is a natural-language policy violation.

Content

No source excerpt is available for this finding.

Tainted flow: 'task_id' from httpx.post (line 90, network input) → httpx.get (network output)

Medium
Category
Data Flow
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.

Content

Scanner excerpt · scripts/generate.py (reported line 96)May include surrounding context.

python
# Step 2: Poll for result
    print("正在等待视频生成...")
    while True:
        result = httpx.get(
            f"{API_BASE}/videos/{task_id}",
            headers=headers
        )

Tainted flow: 'url' from httpx.post (line 54, network input) → httpx.get (network output)

Medium
Category
Data Flow
Confidence
93% confidence
Finding

The script downloads a URL returned by the remote API without validating its scheme, host, or content type. If the upstream service is compromised, misconfigured, or abused, this can turn the client into an SSRF primitive that fetches arbitrary URLs, including internal resources, or downloads unexpectedly large/malicious content to disk.

Content

Scanner excerpt · scripts/generate.py (reported line 139)May include surrounding context.

python
if args.mode == "image":
        url = generate_image(api_key, args.prompt, args.size, args.seed, args.image_urls)
        # Download the image
        resp = httpx.get(url)
        ext = "jpg" if b"JPEG" in resp.content else "png"
        file_path = output_dir / f"image_{int(time.time())}.{ext}"
        file_path.write_bytes(resp.content)

Tainted flow: 'video_url' from httpx.get (line 104, network input) → httpx.get (network output)

Medium
Category
Data Flow
Confidence
95% confidence
Finding

The script performs a second GET using video_url taken from a prior network response, with no validation of destination or content. In an agent or enterprise environment, this could be exploited for SSRF, internal network probing, or forced download of hostile/oversized files if the API returns an attacker-controlled URL.

Content

Scanner excerpt · scripts/generate.py (reported line 157)May include surrounding context.

python
)
        # Download the video
        print("正在下载视频...")
        resp = httpx.get(video_url)
        file_path = output_dir / f"video_{int(time.time())}.mp4"
        file_path.write_bytes(resp.content)
        print(f"视频已保存: {file_path}")

Intent-Code Divergence

Low
Category
Not specified by scanner
Confidence
85% confidence
Finding

L188-L189 的轮询示例在任务完成后将 remixed_from_video_id 当作可直接使用的视频 URL,而该字段名按语义更像“来源视频 ID”而非结果 URL。与此同时,L225-L235 的响应示例与状态说明也延续了这一表述,导致文档意图与字段含义存在明显冲突,容易让实现者误用返回值。

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.