Back to skill

Security audit

Zyt one click video creation

Security checks for vulnerabilities and agentic risk

Overview

This video-generation skill is mostly coherent, but it handles account credentials through an unrestricted API host override and local credential file storage that users should review before installing.

Review before installing. Use this only in a trusted runtime, leave CHANJING_API_BASE unset or pinned to the official HTTPS Chanjing API host, protect ~/.chanjing/credentials.json with owner-only permissions, and avoid putting sensitive unpublished scripts or private media into workflows unless you are comfortable sending them to the Chanjing service. Also review the default persona and ethnicity rules if demographic neutrality matters for your videos.

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

Error
Location
clients/auth.py:8
Finding

Unrestricted API Base Override Can Exfiltrate Credentials and Access Tokens

Content
View full analysis
Tuple[Optional[str], Optional[int], Optional[str]]: url = f"{API_BASE}/open/v1/access_token" body = json.dumps( { "app_id": app_id, "secret_key": secret_key, } ).encode("utf-8") req = urllib.request.Request( url, data=body, headers={"Content-Type": "application/json"}, method="POST", ) try: with urllib.request.urlopen(req, timeout=30) as resp: ``` `clients/tts_client.py:10-25`: ```python API_BASE = os.environ.get("CHANJING_API_BASE", "https://open-api.chanjing.cc") def _post_json(path: str, payload: dict[str, Any], timeout: int = 30) -> dict[str, Any]: token, err = get_token() if err: raise RuntimeError(err) def _once(current_token: str) -> dict[str, Any]: req = urllib.request.Request( f"{API_BASE}{path}", data=json.dumps(payload).encode("utf-8"), headers={"access_token": current_token, "Content-Type": "application/json"}, method="POST", ) with urllib.request.urlopen(req, timeout=timeout) as resp: ``` `clients/ai_creation_client.py:13-46`: ```python API_BASE = os.environ.get("CHANJING_API_BASE", "https://open-api.chanjing.cc") RUNNING_STATUSES = {"Queued", "Ready", "Generating"} SUCCESS_STATUSES = {"Success"} FAILED_STATUSES = {"Error", "Fail"} def _get_access_token() -> str: token, err = get_token() if err: ...[truncated 4368 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
clients/auth.py:37
Finding

Credential File Is Written Without Enforced Restrictive Permissions

Content
View full analysis
dict: if CONFIG_FILE.exists(): try: with open(CONFIG_FILE, "r", encoding="utf-8") as f: data = json.load(f) return data if isinstance(data, dict) else {} except Exception: return {} return {} def write_config(data: dict) -> None: CONFIG_DIR.mkdir(parents=True, exist_ok=True) with open(CONFIG_FILE, "w", encoding="utf-8") as f: json.dump(data, f, indent=2, ensure_ascii=False) ``` `clients/auth.py:114-150`: ```python def get_token(force_refresh: bool = False) -> Tuple[Optional[str], Optional[str]]: now = int(time.time()) cached_token = _token_cache["access_token"] expire_at = int(_token_cache["expire_at"] or 0) if (not force_refresh) and cached_token and now < expire_at - BUFFER_SECONDS: return cached_token, None data = read_config() file_token = str(data.get("access_token") or "").strip() try: file_expire = int(data.get("expire_in") or 0) except (TypeError, ValueError): file_expire = 0 if (not force_refresh) and file_token and now < file_expire - BUFFER_SECONDS: _token_cache["access_token"] = file_token _token_cache["expire_at"] = file_expire return file_token, None app_id, secret_key, err = _load_credentials() if err: return None, err token, new_expire_at, err = _request_token(app_id, secret_key) if err: return None, err _token_cache["access_token"] = token _token_cache["expire_at"] = int(new_expire_at or (now + 86400)) data["app_id"] = app_id data["secret_key"] = secret_key data["access_token"] = token data["expire_in"] = int(new_exp ...[truncated 2168 chars]
Remediation
View remediation
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • 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 (46)

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

Critical
Category
Data Flow
Confidence
90% confidence
Finding

API_BASE is taken from an environment variable and used to construct outbound requests that include the access_token header. If an attacker can influence CHANJING_API_BASE, the client can be redirected to an attacker-controlled endpoint and leak credentials or send sensitive task data to an untrusted server.

Content

Scanner excerpt · clients/ai_creation_client.py (reported line 46)May include surrounding context.

python
headers={"access_token": current_token},
            method="GET",
        )
        with urllib.request.urlopen(req, timeout=timeout) as resp:
            return json.loads(resp.read().decode("utf-8"))

    body = _once(token)

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

Critical
Category
Data Flow
Confidence
90% confidence
Finding

This POST path also uses the environment-controlled API_BASE and transmits authenticated requests containing prompts and task parameters. A malicious or misconfigured environment can redirect these requests to an attacker-controlled service, causing token exposure and unauthorized disclosure of user content.

Content

Scanner excerpt · clients/ai_creation_client.py (reported line 81)May include surrounding context.

python
headers={"access_token": current_token, "Content-Type": "application/json"},
            method="POST",
        )
        with urllib.request.urlopen(req, timeout=timeout) as resp:
            return json.loads(resp.read().decode("utf-8"))

    body = _once(token)

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

Critical
Category
Data Flow
Confidence
90% confidence
Finding

Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Content

Scanner excerpt · clients/ai_creation_client.py (reported line 236)May include surrounding context.

python
method="GET",
    )

    with urllib.request.urlopen(req, timeout=120) as resp:
        with open(output, "wb") as handle:
            handle.write(resp.read())

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

Critical
Category
Data Flow
Confidence
95% confidence
Finding

The token request is sent to API_BASE, which is taken directly from the CHANJING_API_BASE environment variable. This allows a caller-controlled endpoint to receive app_id and secret_key, enabling credential exfiltration or redirection to an attacker-controlled service; in a skill that handles API credentials automatically, this is especially sensitive.

Content

Scanner excerpt · clients/auth.py (reported line 95)May include surrounding context.

python
)

    try:
        with urllib.request.urlopen(req, timeout=30) as resp:
            res = json.loads(resp.read().decode("utf-8"))
    except Exception as e:
        return None, None, f"获取 access_token 失败: {e}"

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

Critical
Category
Data Flow
Confidence
90% confidence
Finding

The generic request helper sends requests to URLs derived from a configurable API base without validating the destination. If an attacker can control CHANJING_API_BASE or pass crafted URLs into this helper's callers, requests containing access tokens could be redirected to an attacker-controlled endpoint, enabling credential leakage and arbitrary outbound connections.

Content

Scanner excerpt · clients/avatar_client.py (reported line 53)May include surrounding context.

python
headers=headers_to_use,
            method=method,
        )
        with urllib.request.urlopen(req, timeout=timeout) as resp:
            raw = resp.read().decode("utf-8")
            return json.loads(raw)

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

Critical
Category
Data Flow
Confidence
90% confidence
Finding

Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Content

Scanner excerpt · clients/avatar_client.py (reported line 766)May include surrounding context.

python
method="PUT",
    )

    with urllib.request.urlopen(req, timeout=120) as resp:
        if resp.status not in (200, 204):
            raise RuntimeError(f"上传失败 status={resp.status}")

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

Critical
Category
Data Flow
Confidence
90% confidence
Finding

The download helper fetches an arbitrary URL and writes the response directly to disk with no scheme, host, size, or content validation. If an attacker can influence the URL, this can be abused for SSRF-like outbound requests, downloading unexpected internal resources, or filling disk with large/untrusted content.

Content

Scanner excerpt · clients/avatar_client.py (reported line 842)May include surrounding context.

python
method="GET",
    )

    with urllib.request.urlopen(req, timeout=120) as resp:
        with open(output, "wb") as f:
            f.write(resp.read())

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

Critical
Category
Data Flow
Confidence
90% confidence
Finding

This request path also uses the configurable API base and sends the access token to the resolved host. If the base URL is altered, figure-listing requests could disclose bearer credentials and permit redirection of network traffic to unintended systems.

Content

Scanner excerpt · clients/avatar_client.py (reported line 876)May include surrounding context.

python
else:
            raise ValueError("source 只能是 'customised' 或 'common'")

        with urllib.request.urlopen(req, timeout=30) as resp:
            return json.loads(resp.read().decode("utf-8"))

    body = _once(token)

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

Critical
Category
Data Flow
Confidence
90% confidence
Finding

The request target is derived from CHANJING_API_BASE, an environment variable, and the code sends the access token in a custom header to whatever host that value specifies. If an attacker can influence the environment or deployment configuration, they can redirect authenticated requests and user content to an attacker-controlled endpoint, causing credential and data exfiltration.

Content

Scanner excerpt · clients/tts_client.py (reported line 25)May include surrounding context.

python
headers={"access_token": current_token, "Content-Type": "application/json"},
            method="POST",
        )
        with urllib.request.urlopen(req, timeout=timeout) as resp:
            return json.loads(resp.read().decode("utf-8"))

    res = _once(str(token))

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

Critical
Category
Data Flow
Confidence
90% confidence
Finding

This GET path has the same issue: CHANJING_API_BASE controls the destination, and the code attaches the access token header to that request. A tampered environment can therefore cause token leakage and exposure of request metadata to an attacker-controlled server.

Content

Scanner excerpt · clients/tts_client.py (reported line 50)May include surrounding context.

python
headers={"access_token": current_token},
            method="GET",
        )
        with urllib.request.urlopen(req, timeout=timeout) as resp:
            return json.loads(resp.read().decode("utf-8"))

    body = _once(str(token))

Tp4

High
Category
MCP Tool Poisoning
Confidence
89% confidence
Finding

声明描述的是一个“一键成片”的完整短视频生成能力,范围包括脚本/文案、分镜、数字人口播、AI 画面混剪等多环节能力。但当前代码块实际只封装了对 /open/v1/ai_creation/task 相关接口的访问,主要能力是提交 creation_type=4 的视频生成任务、轮询状态并下载输出结果。从参数和实现看,更接近单一的 AI 视频生成客户端,而不是完整成片流水线。代码中也没有出现 TTS、数字人、文案生成、分镜生成或多素材混剪逻辑。因此,代码实际行为显著窄于声明用途,属于描述与实现不一致。

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

声明描述的是一个面向最终用户的一键短视频生成 skill,核心功能应围绕内容生成、镜头编排、TTS 合成、数字人视频生成和 AI 视频合成展开。但提供的代码块仅为 tests/test_id_resolver.py 测试文件,主要职责是为测试解析 audio_man、person_id、figure_type:先从内嵌默认值或测试配置文件读取,缺失时调用 list_voices() 和 list_figures() 拉取可用资源 ID。该代码既不接收用户选题/口播稿来生成内容,也不发起完整渲染链路,更没有执行成片输出。因此,这段代码的实际行为与声明的主要用途存在明显不一致。

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
90% confidence
Finding

声明描述的是一个面向最终用户的一键短视频生成技能,核心应是接收选题或口播稿并执行内容生成与视频合成。而给出的代码片段只是测试文件,主要验证底层渲染辅助函数和测试 ID 解析逻辑是否正确,并不实现所宣称的完整成片生成流程。虽然测试中涉及到与 TTS、数字人、AI 视频参数相关的辅助函数,能侧面说明项目可能包含这些能力,但该片段本身的实际用途与声明的主要目的明显不一致,因此应判定为描述与代码行为不匹配。

Content

No source excerpt is available for this finding.

Credential Access

High
Category
Privilege Escalation
Confidence
89% confidence
Finding

The skill explicitly instructs access to local credential material from ~/.chanjing/credentials.json or a path controlled by CHANJING_CONFIG_DIR. Reading local secrets is sensitive because a compromised or overly broad skill can exfiltrate API credentials or use attacker-controlled environment/path overrides to load unintended credential files.

Content

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

md
## 3. 前置条件

- **鉴权**:当前 skill 通过本地配置文件读取凭证:默认 `~/.chanjing/credentials.json`,可用 `CHANJING_CONFIG_DIR` 指向其它目录(读取 `$CHANJING_CONFIG_DIR/credentials.json`);API 域名默认 `https://open-api.chanjing.cc`,可用 `CHANJING_API_BASE` 覆盖。缺凭证或凭证无效时可能打开蝉镜官网登录页并提示重新配置。
- **Plan/Script/分镜**:本地 Agent 逻辑,**无需**外部 LLM API key(本 skill 必选路径不依赖外部 LLM)  
- **本机**:`ffmpeg`、`ffprobe`  
- **`run_render`**:当前版本主渲染链已内聚实现,**不要求**外部 `chanjing-tts` / `chanjing-video-compose` / `chanjing-ai-creation` 目录存在,也**不再依赖** `CHAN_SKILLS_DIR` 指向包含其它 skill 的仓库根

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · clients/auth.py (reported line 10)May include surrounding context.

python
API_BASE = os.environ.get("CHANJING_API_BASE", "https://open-api.chanjing.cc")
CONFIG_DIR = Path(os.environ.get("CHANJING_CONFIG_DIR", Path.home() / ".chanjing"))
CONFIG_FILE = CONFIG_DIR / "credentials.json"
BUFFER_SECONDS = 300
LOGIN_URL = "https://www.chanjing.cc/openapi/login"

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · clients/auth.py (reported line 72)May include surrounding context.

python
API_BASE = os.environ.get("CHANJING_API_BASE", "https://open-api.chanjing.cc")
CONFIG_DIR = Path(os.environ.get("CHANJING_CONFIG_DIR", Path.home() / ".chanjing"))
CONFIG_FILE = CONFIG_DIR / "credentials.json"
BUFFER_SECONDS = 300
LOGIN_URL = "https://www.chanjing.cc/openapi/login"

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · tests/test_id_resolver.py (reported line 130)May include surrounding context.

python
API_BASE = os.environ.get("CHANJING_API_BASE", "https://open-api.chanjing.cc")
CONFIG_DIR = Path(os.environ.get("CHANJING_CONFIG_DIR", Path.home() / ".chanjing"))
CONFIG_FILE = CONFIG_DIR / "credentials.json"
BUFFER_SECONDS = 300
LOGIN_URL = "https://www.chanjing.cc/openapi/login"

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · tests/test_run_render_stub.py (reported line 119)May include surrounding context.

python
API_BASE = os.environ.get("CHANJING_API_BASE", "https://open-api.chanjing.cc")
CONFIG_DIR = Path(os.environ.get("CHANJING_CONFIG_DIR", Path.home() / ".chanjing"))
CONFIG_FILE = CONFIG_DIR / "credentials.json"
BUFFER_SECONDS = 300
LOGIN_URL = "https://www.chanjing.cc/openapi/login"

Env Variable Harvesting

High
Category
Data Exfiltration
Confidence
60% confidence
Finding

Code enumerates, copies, or searches environment variables for secrets. Bulk environment access can collect credentials unrelated to the skill's stated purpose.

Content

Scanner excerpt · scripts/run_render.py (reported line 145)May include surrounding context.

python
capture_output=True,
        text=True,
        timeout=timeout,
        env={**os.environ, **(env or {})},
    )
    if r.returncode != 0:
        msg = (r.stderr or r.stdout or "").strip() or f"exit {r.returncode}"

Natural-Language Policy Violations

High
Category
Not specified by scanner
Confidence
97% confidence
Finding

The file hard-codes a default ethnicity profile of 'East Asian / Asian face' whenever a person may appear and the prompt does not uniquely specify another ethnicity. This creates systematic demographic steering without user consent, causing biased or discriminatory outputs and potentially misrepresenting subjects in generated media at scale.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

High
Category
Not specified by scanner
Confidence
96% confidence
Finding

The historical-content rules instruct the agent to infer Chinese/East Asian identity from context and then enforce East Asian facial features and Chinese historical styling even without explicit user choice. Inferring and imposing protected attributes from ambiguous narrative context can produce culturally biased, exclusionary, or inaccurate depictions, especially in an automated one-click video creation workflow.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The README explicitly describes sending user-provided content to remote TTS, avatar, and AI video generation services and writing generated artifacts to local files, but it does not clearly warn users that their inputs may be transmitted to external services or that the skill will create/overwrite files on disk. In an agent setting, missing disclosure can cause unintentional exfiltration of sensitive scripts/topics and unexpected filesystem side effects, especially when users assume processing is local-only.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
85% confidence
Finding

The skill documents capabilities that require environment access, local file reads/writes, network calls, and shell execution, but it does not declare any explicit tool scope or permission boundaries. In an agent environment, this weakens least-privilege controls and makes it easier for the skill to invoke sensitive operations without transparent policy enforcement.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

Line L060 instructs the agent to prioritize avatars with signals such as 青年/少女/小哥哥/小姐姐/学生/元气/青春/年轻 by default, unless the topic or user explicitly asks for a different persona. This is a natural-language policy concern because it enforces a demographic/style preference by default rather than offering the user a neutral choice or opt-in.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

Line L140 states that the agent must compare candidates but also sets a default preference for young digital humans. This embeds a demographic preference into the skill's natural-language instructions without user opt-in, which fits the policy category for organizational natural-language violations.

Content

No source excerpt is available for this finding.