Back to skill

Security audit

AI短剧制作助手 | AI Short Film Producer

Security checks for vulnerabilities and agentic risk

Overview

This skill is a disclosed AI video-production workflow that uses third-party generation APIs and local FFmpeg processing, with some implementation cautions but no evidence of hidden or malicious behavior.

Install only if you are comfortable using the referenced third-party video and TTS APIs. Do not submit secrets, private scripts, regulated data, or sensitive personal information. Keep API keys out of URLs and logs, and harden any copied code with request timeouts, retry limits, trusted download hosts, archive size checks, and safe FFmpeg concat manifests before using it on untrusted media folders.

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

T09 · Insecure Skill Coding Practices

Warning
Location
references/sucuang_api.md:125
Finding

Unvalidated Remote Media URL Enables Server-Side Request Forgery and Resource Exhaustion

Content
View full analysis

Vulnerability Details

File Location: references/sucuang_api.md, lines 125–141
Vulnerability Type: Unrestricted remote URL retrieval and unbounded archive processing
Risk Level: Medium

Vulnerable Code

python
while True:
    result = requests.get(f"{BASE}/api/async/detail?id={task_id}", headers=HEADERS)
    data = result.json()["data"]
    if data["status"] == 2:  # Complete
        audio_url = data["audio_url"]
        audio_resp = requests.get(audio_url)
        tar = tarfile.open(fileobj=io.BytesIO(audio_resp.content))
        for member in tar.getmembers():
            if member.name.endswith('.mp3'):
                f = tar.extractfile(member)
                with open(output_path, 'wb') as out:
                    out.write(f.read())
        return
    elif data["status"] == -1:  # Failed
        raise Exception(f"TTS failed: {data}")
    time.sleep(3)

Technical Analysis

The audio_url value is obtained from a remote API response and passed directly to requests.get() without validating its scheme, hostname, port, or resolved IP address. If the API service, API response, or task data is compromised, an attacker can supply a URL targeting an unintended internal or local service.

The response is also accessed through audio_resp.content, which loads the entire body into memory before parsing it as a tar archive. No HTTP timeout, status validation, content-length limit, archive-size limit, member-count limit, or decompressed-size limit is enforced. A malicious endpoint could therefore return an extremely large response or a highly compressed archive and exhaust process memory, CPU, or disk capacity.

The implementation uses extractfile() and writes the selected member to a fixed output_path, so the shown code does not directly perform path traversal through archive member names. The primary risks are unrestricted URL retrieval and resource exhaustion.

Attack P

...[truncated 1430 chars]

Remediation
View remediation

Remediation Suggestions

  1. Require https:// URLs and reject embedded credentials, fragments, unsupported ports, and malformed URLs.
  2. Maintain an explicit allowlist of expected media-download hostnames.
  3. Resolve the destination before connecting and reject loopback, link-local, private, reserved, multicast, and cloud metadata address ranges. Repeat validation after redirects and DNS resolution changes.
  4. Disable redirects or validate every redirect destination.
  5. Set explicit connect and read timeouts:
    python
    requests.get(audio_url, timeout=(5, 30), allow_redirects=False, stream=True)
    
  6. Call raise_for_status() before processing the body.
  7. Stream the response while enforcing a strict maximum byte count instead of using audio_resp.content.
  8. Validate the response content type and archive signature.
  9. Limit the number of archive members, individual member size, total decompressed size, and compression ratio.
  10. Reject non-regular archive members and process only one expected MP3 member.
  11. Write to a temporary file with restrictive permissions and atomically replace the destination only after validation succeeds.

T09 · Insecure Skill Coding Practices

Warning
Location
references/production_workflow.md:258
Finding

FFmpeg Concat Manifest Injection Through Unescaped Media Filenames

Content
View full analysis

Vulnerability Details

File Location: references/production_workflow.md, lines 258–284
Vulnerability Type: Manifest injection and unsafe resource access
Risk Level: Medium

Vulnerable Code

python
def concat_videos(seg_dir, output_path):
    """Concatenate all video segments"""
    segs = sorted(Path(seg_dir).glob("seg_*.mp4"))

    # Generate file list
    list_path = Path(seg_dir) / "video_list.txt"
    with open(list_path, 'w') as f:
        for seg in segs:
            f.write(f"file '{seg.absolute()}'\n")

    cmd = [
        '/opt/homebrew/bin/ffmpeg', '-y',
        '-f', 'concat', '-safe', '0',
        '-i', str(list_path),
        '-c', 'copy',
        str(output_path)
    ]
    subprocess.run(cmd, check=True)

def concat_audios(audio_dir, output_path):
    """Concatenate all TTS audio"""
    audios = sorted(Path(audio_dir).glob("*.mp3"))

    list_path = Path(audio_dir) / "audio_list.txt"
    with open(list_path, 'w') as f:
        for audio in audios:
            f.write(f"file '{audio.absolute()}'\n")

    cmd = [
        '/opt/homebrew/bin/ffmpeg', '-y',
        '-f', 'concat', '-safe', '0',
        '-i', str(list_path),
        '-c', 'copy',
        str(output_path)
    ]
    subprocess.run(cmd, check=True)

Technical Analysis

Media paths are interpolated directly into FFmpeg concat-demuxer manifests. Single quotes, backslashes, carriage returns, and newline characters in filenames are not escaped or rejected. Consequently, an attacker-controlled filename can terminate the current file directive and inject additional manifest syntax.

The FFmpeg invocation also uses -safe 0, disabling the concat demuxer's safe-path checks. This permits absolute paths and may allow protocol-based resource references supported by the installed FFmpeg build.

Using a list with subprocess.run() prevents operating-system shell injection, but it does not ...[truncated 1706 chars]

Remediation
View remediation

Remediation Suggestions

  1. Generate media filenames internally using a strict format such as seg_[0-9]{3}.mp4 and tts_[0-9]{3}.mp3.
  2. Reject paths containing quotes, backslashes, carriage returns, newlines, NUL characters, colons, or other concat control syntax.
  3. Resolve each path and verify that it remains beneath the expected trusted project directory.
  4. Do not process directories writable by untrusted users.
  5. Avoid -safe 0 where possible. Use relative, portable filenames accepted by FFmpeg safe mode.
  6. If manifest files remain necessary, implement escaping specifically according to FFmpeg concat-demuxer syntax rather than shell-escaping rules.
  7. Consider piping a validated manifest to FFmpeg or renaming/copying validated inputs into a private temporary directory with controlled filenames.
  8. Disable unnecessary FFmpeg network protocols where supported and run FFmpeg in a sandbox with restricted filesystem and network access.
  9. Create manifest files with restrictive permissions and remove them after use.

T09 · Insecure Skill Coding Practices

Note
Location
references/sucuang_api.md:61
Finding

Unbounded API Polling Can Indefinitely Exhaust Workflow Resources

Content
View full analysis

Vulnerability Details

File Location: references/sucuang_api.md, lines 61–65
Vulnerability Type: Unbounded polling and missing network timeouts
Risk Level: Low

Vulnerable Code

python
while True:
    result = requests.get(f"{BASE}/detail?id={task_id}", headers=HEADERS)
    if result.json()["data"]["status"] == 2:
        return download_video(result.json()["data"]["video_url"])
    time.sleep(5)

Technical Analysis

The result-polling loop has no maximum attempt count, deadline, cancellation condition, or handling for a terminal failure status. In addition, the HTTP request has no connect or read timeout.

If the API continually reports a pending state, the worker remains active indefinitely. If the API accepts a connection but stops responding, the individual requests.get() call can also block for an extended period. In the documented concurrent workflow, multiple stalled tasks can consume all available ThreadPoolExecutor workers.

Attack Path

  1. A malicious, compromised, or malfunctioning API endpoint accepts submitted jobs.
  2. The endpoint continually returns a non-complete status, or accepts polling connections without completing the HTTP response.
  3. The client enters the unconditional while True loop.
  4. Because no deadline or maximum retry count exists, the worker never terminates.
  5. Repeating this condition across concurrent tasks consumes the worker pool and prevents legitimate jobs from completing.
  6. Continuous polling may also generate unnecessary network traffic and API charges.

Impact Assessment

The principal impact is denial of service against the media-production workflow. Stalled jobs can occupy all worker threads, delay project completion, and consume local memory, sockets, network capacity, or paid API quota.

The vulnerability does not provide filesystem access, code execution, or privilege escalation. Its scope is primarily availability and oper ...[truncated 13 chars]

Remediation
View remediation

Remediation Suggestions

  1. Add explicit connect and read timeouts to every HTTP request.
  2. Establish a maximum polling duration and attempt count.
  3. Handle success, failure, cancellation, expiration, and unknown status values explicitly.
  4. Use exponential backoff with bounded jitter rather than polling forever at a fixed interval.
  5. Allow external cancellation when the user aborts the project.
  6. Raise a controlled exception after the retry budget is exhausted.
  7. Apply a global concurrency limit and per-task deadline.
  8. Log task identifiers and terminal reasons without logging API credentials.
  9. A hardened structure should resemble:
    python
    deadline = time.monotonic() + 300
    delay = 2
    
    while time.monotonic() < deadline:
        response = requests.get(url, headers=HEADERS, timeout=(5, 20))
        response.raise_for_status()
        status = response.json()["data"]["status"]
    
        if status == 2:
            return handle_success(response)
        if status in {-1, 3, 4}:
            raise RuntimeError(f"Task ended with status {status}")
    
        time.sleep(delay)
        delay = min(delay * 2, 30)
    
    raise TimeoutError("API task did not complete within five minutes")
    
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
Findings (6)

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The activation examples are broad enough to capture common requests like 'make me a short video' or 'turn this copy into a video,' which can cause the skill to engage by default in situations where a more privacy-preserving or user-confirmed workflow would be appropriate. In this skill, automatic engagement is more concerning because the workflow is designed to send user prompts and script text to third-party APIs and trigger local media-processing steps, increasing the chance of unintended data disclosure or unexpected external actions.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
98% confidence
Finding

The skill documents external API use for video and TTS generation but does not clearly warn users that their scripts, prompts, and dialogue text will be transmitted to third-party services. This creates a meaningful privacy and compliance risk, especially if users provide sensitive drafts, proprietary story ideas, personal data, or regulated content under the assumption that processing is local.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The entire workflow, headings, instructions, examples, and output conventions are written solely in Chinese, with no indication that users may choose another language or locale. Under the stated policy, a skill that imposes a specific language without opt-in is a natural-language policy violation unless the locale restriction is documented and justified.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
70% 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 · references/sucuang_api.md (reported line 58)May include surrounding context.

md
def submit_shot(shot):
    # 提交生成任务
    resp = requests.post(url, headers=HEADERS, json=params)
    task_id = resp.json()["data"]["id"]
    # 轮询直到完成
    while True:

External Transmission

Medium
Category
Data Exfiltration
Confidence
70% 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 · references/sucuang_api.md (reported line 122)May include surrounding context.

md
def generate_tts(text, voice_id, output_path):
    payload = {"text": text, "voice_id": voice_id, "speed": 1.0}
    resp = requests.post(f"{BASE}/api/async/audio_tts", headers=HEADERS, json=payload)
    task_id = resp.json()["data"]["id"]

    while True:

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
88% confidence
Finding

The sample prompt guidance says '英文描述效果更稳定', and the pitfalls section repeats that English prompts perform better than Chinese. This steers users toward a specific language by default without opt-in or a documented policy reason, which fits the language/locale policy category.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.