Back to skill

Security audit

Ai Animation Studio

Security checks for vulnerabilities and agentic risk

Overview

This looks like a real AI animation tool, but it needs review because it sends prompts to cloud services and processes downloaded media with limited validation and disclosure.

Review this before installing if you may use private stories, proprietary prompts, or sensitive media. Install only if you are comfortable with Volcengine/Doubao and edge-tts processing your content, with local ffmpeg execution, and with generated files being written or overwritten in the configured output locations.

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/full_workflow.py:88
Finding

Unvalidated Remote Media URLs Permit Server-Side Request Forgery and Resource Exhaustion

Content
View full analysis

Vulnerability Details

File Location: scripts/full_workflow.py, lines 88-93 and 141-146
Vulnerability Type: Unvalidated remote resource retrieval
Risk Level: Medium

Vulnerable Code

python
image_url = result["data"][0]["url"]

# Download image
local_path = self.output_dir / f"scene_{scene_id}.jpg"
img_response = requests.get(image_url, timeout=30)
with open(local_path, "wb") as f:
    f.write(img_response.content)
python
video_url = status["content"]["video_url"]

# Download video
local_path = self.output_dir / f"scene_{scene_id}.mp4"
video_response = requests.get(video_url, timeout=120)
with open(local_path, "wb") as f:
    f.write(video_response.content)

Technical Analysis

The workflow retrieves image and video URLs from Ark API responses and passes them directly to requests.get() without validating the URL scheme, hostname, resolved IP address, redirect chain, response status, content type, or response size.

requests follows redirects by default. Consequently, a compromised, malicious, or malformed upstream response could direct the process to loopback addresses, private network services, link-local cloud metadata endpoints, or other resources reachable from the host. Redirects can also be used to bypass a validation check if only the initial URL is inspected.

The use of response.content buffers the complete response in memory before writing it. No maximum download size or disk quota is enforced, allowing an unexpectedly large response to consume substantial memory and disk space. The downloaded content is also accepted as trusted media without checking its MIME type or file signature.

Attack Path

  1. An attacker compromises the upstream API, influences an API response, or otherwise causes the returned image_url or video_url to reference an attacker-controlled endpoint.
  2. The attacker supplies a direct internal URL or an external URL that redirect ...[truncated 1546 chars]
Remediation
View remediation

Remediation Suggestions

  1. Permit only HTTPS media URLs from an explicit allowlist of expected Ark/CDN hostnames.
  2. Resolve the hostname before connecting and reject loopback, private, link-local, multicast, unspecified, and reserved IPv4 and IPv6 ranges.
  3. Disable automatic redirects with allow_redirects=False, or validate every redirect target before following it.
  4. Call raise_for_status() before processing the response.
  5. Use stream=True and copy the response in bounded chunks while tracking a strict maximum byte count.
  6. Enforce separate download limits appropriate for images and videos.
  7. Validate the Content-Type header and verify the downloaded file's magic bytes before treating it as media.
  8. Delete partial output files when validation or download fails.
  9. Apply connection and read timeouts separately, and cap the number of redirects.
  10. Repeat the same controls for both image and video download paths.

Example hardening pattern:

python
with requests.get(
    validated_url,
    stream=True,
    timeout=(10, 30),
    allow_redirects=False
) as response:
    response.raise_for_status()
    validate_media_content_type(response.headers.get("Content-Type", ""))

    downloaded = 0
    with open(local_path, "wb") as output:
        for chunk in response.iter_content(chunk_size=64 * 1024):
            downloaded += len(chunk)
            if downloaded > MAX_MEDIA_BYTES:
                raise ValueError("Media download exceeds the permitted size")
            output.write(chunk)

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/full_workflow.py:213
Finding

FFmpeg Concat Manifest Injection Through Unescaped File Paths

Content
View full analysis

Vulnerability Details

File Location: scripts/full_workflow.py, lines 213-228
Vulnerability Type: FFmpeg concat-manifest injection
Risk Level: Medium

Vulnerable Code

python
# Create concat file
concat_file = self.output_dir / "concat_list.txt"
with open(concat_file, "w") as f:
    for vf in video_files:
        f.write(f"file '{vf}'\n")

output_path = self.output_dir / output_file

cmd = [
    "ffmpeg", "-y",
    "-f", "concat",
    "-safe", "0",
    "-i", str(concat_file),
    "-c:v", "libx264",
    "-preset", "medium",
    "-c:a", "aac",
    "-movflags", "+faststart",
    str(output_path)
]

Technical Analysis

Every entry from video_files is interpolated directly into an FFmpeg concat manifest without escaping apostrophes, backslashes, carriage returns, or newline characters. Although subprocess.run() correctly receives an argument array and therefore does not introduce shell injection, the generated text file is itself interpreted as a command-like manifest by FFmpeg.

A crafted path containing a quote and newline can terminate the intended file directive and add additional concat directives. The use of -safe 0 disables FFmpeg concat demuxer's safe-filename restrictions, allowing absolute paths and otherwise unsafe resource references.

The normal bundled workflow generates predictable local filenames, reducing exposure through the default main() path. However, concat_videos() is a public method that accepts an arbitrary list of strings. It becomes exploitable if another caller, integration, or future command-line interface passes untrusted filenames to it.

Attack Path

  1. An attacker gains influence over an item passed to concat_videos(video_files, output_file).
  2. The attacker supplies a filename containing concat syntax delimiters, such as an apostrophe followed by a newline and another file directive.
  3. The method writes the crafted value ...[truncated 1262 chars]
Remediation
View remediation

Remediation Suggestions

  1. Treat every entry in video_files as untrusted input.
  2. Resolve each path to its canonical absolute path and require it to remain beneath a dedicated, approved output directory.
  3. Require each input to be an existing regular file with an approved media extension.
  4. Reject paths containing carriage returns, newlines, null bytes, or other control characters.
  5. Escape apostrophes and backslashes according to FFmpeg concat-demuxer syntax rather than applying shell-escaping rules.
  6. Avoid -safe 0 where possible. Use -safe 1 and filenames compatible with FFmpeg's safe-path rules.
  7. If absolute paths are operationally required, validate them before writing the manifest and run FFmpeg with restricted filesystem and network access.
  8. Consider using FFmpeg's filter-complex input composition or a media library that does not require generating an interpreted manifest.
  9. Create the manifest with a unique temporary filename and remove it after processing to prevent collisions between concurrent runs.

A validation step should follow this pattern:

python
approved_root = self.output_dir.resolve()
validated_files = []

for value in video_files:
    if any(char in value for char in ("\r", "\n", "\x00")):
        raise ValueError("Invalid control character in media path")

    path = Path(value).resolve(strict=True)
    if not path.is_file() or approved_root not in path.parents:
        raise ValueError("Media path is outside the approved directory")

    validated_files.append(path)

The validated paths must still be encoded using FFmpeg concat-file escaping rules before being written to the manifest.

Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • System Prompt LeakageDirect Leakage, Indirect Extraction, Tool-Based Exfiltration
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
Findings (42)

Tainted flow: 'cmd' from requests.post (line 257, network input) → subprocess.run (code execution)

Critical
Category
Data Flow
Confidence
90% confidence
Finding

External input (network, user) flows to a code execution sink. This enables remote code execution or command injection.

Content

Scanner excerpt · scripts/full_workflow.py (reported line 189)May include surrounding context.

python
str(output_path)
        ]
        
        result = subprocess.run(cmd, capture_output=True, text=True)
        
        if result.returncode == 0:
            print(f"   ✅ 已合并: {output_path}")

Tainted flow: 'cmd' from requests.post (line 257, network input) → subprocess.run (code execution)

Critical
Category
Data Flow
Confidence
90% confidence
Finding

External input (network, user) flows to a code execution sink. This enables remote code execution or command injection.

Content

Scanner excerpt · scripts/full_workflow.py (reported line 234)May include surrounding context.

python
str(output_path)
        ]
        
        result = subprocess.run(cmd, capture_output=True, text=True)
        
        if result.returncode == 0:
            print(f"   ✅ 已合并: {output_path}")

Tainted flow: 'cmd' from requests.post (line 257, network input) → subprocess.run (code execution)

Critical
Category
Data Flow
Confidence
90% confidence
Finding

External input (network, user) flows to a code execution sink. This enables remote code execution or command injection.

Content

Scanner excerpt · scripts/full_workflow.py (reported line 272)May include surrounding context.

python
str(output_path)
        ]
        
        result = subprocess.run(cmd, capture_output=True, text=True)
        
        if result.returncode == 0:
            print(f"   ✅ 已合并: {output_path}")

Direct Prompt Extraction

High
Category
System Prompt Leakage
Confidence
85% confidence
Finding

Skill contains instructions that could directly expose system prompts, internal rules, or hidden instructions to users or external parties.

Content

Scanner excerpt · resource_index.py (reported line 133)May include surrounding context.

python
"prompt": row[1]
            })
    
    return prompts

def load_ai_prompts():
    """加载AI生图指令"""

Direct Prompt Extraction

High
Category
System Prompt Leakage
Confidence
85% confidence
Finding

Skill contains instructions that could directly expose system prompts, internal rules, or hidden instructions to users or external parties.

Content

Scanner excerpt · resource_index.py (reported line 156)May include surrounding context.

python
"prompt": row[1]
            })
    
    return prompts

def load_ai_prompts():
    """加载AI生图指令"""

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
84% confidence
Finding

文档标题与主体说明均以中文呈现,且示例交互、选项与流程说明默认要求中文使用,没有看到提供多语言选项或征求用户语言偏好的机制。若组织要求避免未经用户选择即固定语言/locale,这构成自然语言层面的政策风险。

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

L010 的触发词列表包含“生成动画”“动画制作”等宽泛短语,这些表达可覆盖大量普通创作请求,缺少更明确的适用边界或排除条件。文档也未说明哪些上下文不应触发该技能,因此存在误触发风险。

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

The skill sends user-provided story content and media prompts to external services such as 豆包 API and performs local media processing with ffmpeg, but the documentation does not clearly warn users that their content may be transmitted to third parties or that local CPU/storage-intensive processing will occur. This creates a real privacy and operational transparency risk, especially if users submit sensitive text, copyrighted material, or large workloads without informed consent.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

User-facing docstrings, CLI descriptions, and status messages are presented only in Chinese, with no indication that another language can be selected. That creates a language/locale constraint without opt-in or documented justification, which matches the policy-violation category.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The skill sends user-supplied prompts to an external script that likely interacts with a remote media-generation service, but there is no explicit notice, consent flow, or data-handling disclosure. In this context, prompts and referenced media URLs may contain sensitive or proprietary content, so silent transmission creates a real privacy and data-governance risk.

Content

No source excerpt is available for this finding.

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · create_animation.py (reported line 57)May include surrounding context.

python
"--size", size
        ]
        
        result = subprocess.run(cmd, capture_output=True, text=True, encoding='utf-8')
        
        if result.returncode != 0:
            print(f"  [ERROR] 图片生成失败: {result.stderr[:100]}")

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · create_animation.py (reported line 85)May include surrounding context.

python
"--size", size
        ]
        
        result = subprocess.run(cmd, capture_output=True, text=True, encoding='utf-8')
        
        if result.returncode != 0:
            print(f"  [ERROR] 图片生成失败: {result.stderr[:100]}")

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

Line L025 accesses the ARK_API_KEY environment variable, which is a sensitive credential source. Although the code raises an error if it is missing, there is no comment, docstring, or user-facing warning explaining that the skill consumes API credentials.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
80% 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 · scripts/full_workflow.py (reported line 76)May include surrounding context.

python
"size": "1920x1080"
        }
        
        response = requests.post(
            f"{BASE_URL}/images/generations",
            headers=self.headers,
            json=payload,

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The requests at L076-L080 and subsequent API calls send prompts and related content to a remote service. While the script prints progress, it does not clearly disclose to users that their text and media data are being sent off-device to third-party endpoints.

Content

No source excerpt is available for this finding.

Tainted flow: 'payload' from requests.post (line 102, network input) → requests.post (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/full_workflow.py (reported line 76)May include surrounding context.

python
"size": "1920x1080"
        }
        
        response = requests.post(
            f"{BASE_URL}/images/generations",
            headers=self.headers,
            json=payload,

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The script writes images, audio, concat lists, and output videos to disk, and ffmpeg commands at multiple points use the -y flag to overwrite existing files automatically. Although saved-file messages are printed after completion, there is no prior warning that existing outputs may be replaced and many local files will be created.

Content

No source excerpt is available for this finding.

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

Medium
Category
Data Flow
Confidence
84% confidence
Finding

The script downloads a URL returned by an external service without validating the destination host, scheme, or content type. If the upstream API or its response is compromised, this can turn the script into an SSRF-style fetcher or cause it to retrieve unexpected internal or malicious resources and store them locally.

Content

Scanner excerpt · scripts/full_workflow.py (reported line 91)May include surrounding context.

python
# 下载图片
        local_path = self.output_dir / f"scene_{scene_id}.jpg"
        img_response = requests.get(image_url, timeout=30)
        with open(local_path, "wb") as f:
            f.write(img_response.content)

External Transmission

Medium
Category
Data Exfiltration
Confidence
80% 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 · scripts/full_workflow.py (reported line 114)May include surrounding context.

python
}
        
        # 创建任务
        response = requests.post(
            f"{BASE_URL}/contents/generations/tasks",
            headers=self.headers,
            json=payload,

Tainted flow: 'payload' from requests.post (line 102, network input) → requests.post (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/full_workflow.py (reported line 114)May include surrounding context.

python
}
        
        # 创建任务
        response = requests.post(
            f"{BASE_URL}/contents/generations/tasks",
            headers=self.headers,
            json=payload,

Tainted flow: 'task_id' from requests.post (line 124, network input) → requests.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/full_workflow.py (reported line 131)May include surrounding context.

python
print(f"   等待生成...")
        while True:
            time.sleep(5)
            status_response = requests.get(
                f"{BASE_URL}/contents/generations/tasks/{task_id}",
                headers=self.headers,
                timeout=30

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

Medium
Category
Data Flow
Confidence
85% confidence
Finding

The script blindly downloads a video URL supplied by an external API and writes it to disk without validation. In a compromised or malicious-service scenario, this can be abused to fetch arbitrary URLs, download oversized content, or store malicious media that is later processed by ffmpeg, increasing exposure.

Content

Scanner excerpt · scripts/full_workflow.py (reported line 144)May include surrounding context.

python
# 下载视频
                local_path = self.output_dir / f"scene_{scene_id}.mp4"
                video_response = requests.get(video_url, timeout=120)
                with open(local_path, "wb") as f:
                    f.write(video_response.content)

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
89% confidence
Finding

At L189 and later subprocess.run invocations, the script launches ffmpeg and ffprobe on the user's system. These operations are central to the workflow, but the file lacks any comment, docstring notice, or startup disclosure that external binaries will be executed.

Content

No source excerpt is available for this finding.

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · scripts/full_workflow.py (reported line 247)May include surrounding context.

python
print(f"添加BGM背景音乐...")
        
        # 获取视频时长
        result = subprocess.run(
            ["ffprobe", "-v", "error", "-show_entries", "format=duration",
             "-of", "json", video_path],
            capture_output=True,

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · create_animation.py (reported line 170)May include surrounding context.

python
video_path
        ]
        
        result = subprocess.run(cmd, capture_output=True, text=True)
        data = json.loads(result.stdout)
        stream = data["streams"][0]

Static analysis

No suspicious patterns detected.