Back to skill

Security audit

AI短剧制作工作流 v1.5

Security checks for vulnerabilities and agentic risk

Overview

ClawScan marks this skill suspicious because it found unvalidated network destinations that could expose an API key or trigger unsafe downloads; scanner coverage was partial.

Consider the reported network and credential risks before installing. The scans inspected all listed components, but reference resolution was partial, so the overall evidence is incomplete; the clean AIG result does not rule out the issues reported by Skillspector.

Vulnerability Patterns
  • 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
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
Vulnerability Patterns
  • Output HandlingUnvalidated Output Injection, Cross-Context Output, Unbounded Output
  • 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
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (17)

Tainted flow: 'req' from os.environ.get (line 90, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Confidence
97% confidence
Finding

Bearer authentication to the intended Ark endpoint is legitimate, but this helper attaches ARK_API_KEY to any supplied URL without validating its destination. In particular, polling trusts the api value loaded from a state file, so an attacker-supplied or modified state file can redirect authenticated requests to an attacker endpoint and disclose the key.

Content

Scanner excerpt · ai-short-drama-production/scripts/seedance_client.py (reported line 91)May include surrounding context.

python
def http_json(url: str, api_key: str, method="GET", body=None, timeout=120):
    data = json.dumps(body, ensure_ascii=False).encode("utf-8") if body is not None else None
    req = urllib.request.Request(url, data=data, headers={"Authorization": f"Bearer {api_key}", "Content-Type": "application/json"}, method=method)
    with urllib.request.urlopen(req, timeout=timeout) as r:
        return json.loads(r.read().decode("utf-8", "replace"))

Tainted flow: 'video_url' from os.environ.get (line 185, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Confidence
95% confidence
Finding

The static finding incorrectly attributes video_url to the environment; it actually comes from the task-status response, and this download request does not include the API key. Nevertheless, fetching that URL without scheme or destination validation permits attacker-controlled responses to trigger internal-network requests or file:// reads that are saved as downloads.

Content

Scanner excerpt · ai-short-drama-production/scripts/seedance_client.py (reported line 190)May include surrounding context.

python
rec["video_url"] = video_url
                    if download_dir and not rec.get("video_path"):
                        target = download_dir / f"{cid}.mp4"
                        with urllib.request.urlopen(urllib.request.Request(video_url, headers={"User-Agent": "Mozilla/5.0"}), timeout=180) as r, open(target, "wb") as f:
                            while True:
                                chunk = r.read(1024 * 1024)
                                if not chunk: break

Tainted flow: 'req' from os.environ.get (line 90, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Confidence
97% confidence
Finding

Sending ARK_API_KEY to the legitimate provider is expected, but cmd_poll takes the destination API from the loaded state file without validating it before attaching the credential. An attacker-supplied or modified state file containing an attacker-controlled API endpoint and a pending task can therefore steal the key when polled; polling does not require the paid-submit confirmation flags.

Content

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

python
def http_json(url: str, api_key: str, method="GET", body=None, timeout=120):
    data = json.dumps(body, ensure_ascii=False).encode("utf-8") if body is not None else None
    req = urllib.request.Request(url, data=data, headers={"Authorization": f"Bearer {api_key}", "Content-Type": "application/json"}, method=method)
    with urllib.request.urlopen(req, timeout=timeout) as r:
        return json.loads(r.read().decode("utf-8", "replace"))

Tainted flow: 'video_url' from os.environ.get (line 185, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Confidence
96% confidence
Finding

The static analysis incorrectly attributes video_url to an environment variable: it actually comes from the API response, and this request does not contain ARK_API_KEY. Nevertheless, downloading that unvalidated URL permits server-side request forgery or local-file reads through supported URL schemes if the response is attacker-controlled; additionally, an unvalidated task ID can make the adjacent output path escape the download directory and overwrite writable files.

Content

Scanner excerpt · scripts/seedance_client.py (reported line 190)May include surrounding context.

python
rec["video_url"] = video_url
                    if download_dir and not rec.get("video_path"):
                        target = download_dir / f"{cid}.mp4"
                        with urllib.request.urlopen(urllib.request.Request(video_url, headers={"User-Agent": "Mozilla/5.0"}), timeout=180) as r, open(target, "wb") as f:
                            while True:
                                chunk = r.read(1024 * 1024)
                                if not chunk: break

The submission workflow embeds local reference images without disclosing their transmission to Seedance.

Medium
Category
Output Handling
Confidence
95% confidence
Finding

L54 instructs submission with --embed-local-refs, which sends local reference-image content to the external generation provider. The example requires payment confirmation but does not warn that local images leave the user's system; confirming the reference-image plan does not explicitly disclose that privacy impact.

Content

No source excerpt is available for this finding.

Skill declares no tool scope ('permissions' or 'allowed-tools') but code capabilities were detected: env, file_read, file_write, network.

Medium
Category
MCP Least Privilege
Confidence
70% confidence
Finding

Without declared permissions the skill's intent is opaque and cannot be validated.

Content

No source excerpt is available for this finding.

Subtitle rules assume Chinese output without a user language choice.

Medium
Category
Output Handling
Confidence
90% confidence
Finding

The mandatory subtitle rules specify Chinese characters rather than making the limit conditional on the project's language. This generic voice and subtitle reference provides neither a language opt-in nor a justification for restricting subtitles to Chinese.

Content

No source excerpt is available for this finding.

Generic text-in-post rules impose a Chinese-specific subtitle requirement.

Medium
Category
Output Handling
Confidence
90% confidence
Finding

The instruction requires subtitles to contain 10–18 Chinese characters per line without conditioning that requirement on the user's selected language. The file describes a general production workflow and does not document a Chinese-only scope or offer language selection.

Content

No source excerpt is available for this finding.

Tainted flow: 'state' from pathlib.Path.read_text (line 159, file read) → pathlib.Path.write_text (file write)

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 · ai-short-drama-production/scripts/seedance_client.py (reported line 39)May include surrounding context.

python
def save_state(path: Path, state: dict) -> None:
    path.parent.mkdir(parents=True, exist_ok=True)
    path.write_text(json.dumps(state, ensure_ascii=False, indent=2), encoding="utf-8")


def task_summary(task: dict) -> dict:

The project template selects Simplified Chinese (zh-CN) without an explicit user locale choice.

Medium
Category
Output Handling
Confidence
90% confidence
Finding

The reusable project template assigns a specific language and locale rather than leaving it for user selection. No opt-in or region-specific justification is documented in this source.

Content

No source excerpt is available for this finding.

Paid submission can transmit local reference assets without an explicit privacy warning.

Medium
Category
Output Handling
Confidence
86% confidence
Finding

The submission workflow supports embedding local reference files into requests sent to the generation provider. Although it requires paid-generation approval and an explicit embedding flag, it does not warn that prompts and embedded assets leave the user's system; financial approval does not establish informed approval of that disclosure.

Content

No source excerpt is available for this finding.

The generic subtitle rules impose a Chinese-language constraint without user opt-in.

Medium
Category
Output Handling
Confidence
94% confidence
Finding

The subtitle instruction specifies Chinese characters rather than a language-neutral or project-selected limit. This generic reference provides no language choice or justification for applying that constraint to every project.

Content

No source excerpt is available for this finding.

Strict mode promises to reject missing references but still emits text-only generation tasks.

Medium
Category
Excessive Agency
Confidence
99% confidence
Finding

In scripts/build_seedance_tasks.py, L040 documents --strict as failing on a missing prompt, duration, or reference, but L098–L099 only warn when references are absent, even with strict mode enabled. The task is subsequently appended and written, contradicting the documented reference requirement and allowing production tasks without the character-consistency inputs the caller explicitly requested strict mode to enforce.

Content

No source excerpt is available for this finding.

Human-readable workload reports are hardcoded in Chinese without a language choice.

Medium
Category
Output Handling
Confidence
93% confidence
Finding

The Markdown renderer fixes report headings, labels, and units in Chinese regardless of the user's language. The command-line interface offers JSON as an alternative format but no locale selection, and the script's general-purpose description does not justify a Chinese-only report.

Content

No source excerpt is available for this finding.

Tainted flow: 'state' from pathlib.Path.read_text (line 159, file read) → pathlib.Path.write_text (file write)

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/seedance_client.py (reported line 39)May include surrounding context.

python
def save_state(path: Path, state: dict) -> None:
    path.parent.mkdir(parents=True, exist_ok=True)
    path.write_text(json.dumps(state, ensure_ascii=False, indent=2), encoding="utf-8")


def task_summary(task: dict) -> dict:

The reusable project template selects Simplified Chinese without an explicit user language choice.

Low
Category
Output Handling
Confidence
86% confidence
Finding

L08 supplies zh-CN as the project's language rather than leaving language selection to the user. This source does not document a Chinese-only scope or require confirmation of the locale before the template is used.

Content

No source excerpt is available for this finding.

Strict mode promises to reject missing references but still emits text-only tasks.

Low
Category
Excessive Agency
Confidence
99% confidence
Finding

The --strict help at L040 explicitly promises failure on a missing prompt, duration, or reference. However, L098–L099 only append a warning when references are absent, even in strict mode, allowing a task without the promised reference validation to be written.

Content

No source excerpt is available for this finding.