T09 · Insecure Skill Coding Practices
- Location
scripts/_auth.py:11- Finding
Environment-Controlled API Base Can Exfiltrate Application Credentials and Access Tokens
- Content
View full analysis
- Remediation
View remediation
Security audit
Security checks for vulnerabilities and agentic risk
This skill does what it says, but it handles persistent API secrets and unvalidated URLs in ways that deserve careful review before installation.
Install only if you are comfortable storing Chanjing app credentials and access tokens in ~/.chanjing/credentials.json. Do not set CHANJING_API_BASE unless you fully trust the endpoint, avoid shared machines or permissive config directories, verify callback and media URLs, and be cautious with custom download output paths because files can be overwritten.
scripts/_auth.py:11Environment-Controlled API Base Can Exfiltrate Application Credentials and Access Tokens
scripts/_auth.py:50Credential File Permissions Are Not Explicitly Restricted
scripts/download_result.py:26Unrestricted Download URL and Output Path Permit Local Resource Access and Arbitrary File Overwrite
scripts/upload_file.py:90API-Supplied Upload URL Is Trusted Without Scheme or Host Validation
The request target is built from CHANJING_API_BASE, an environment-controlled value, and the POST body contains app_id and secret_key. If this environment variable is set to an attacker-controlled endpoint, the skill will exfiltrate long-lived API credentials directly to that server over the network.
method="POST",
)
try:
with urllib.request.urlopen(req, timeout=30) as resp:
body = json.loads(resp.read().decode("utf-8"))
except Exception as e:
return None, str(e)
This section documents that access tokens and expiry are read from and written back to the same plaintext credentials file on disk. Persisting live bearer tokens in a shared file increases exposure if local filesystem permissions are weak, backups are accessible, or other skills/processes can read the same file.
## 登记摘要(英文 · ClawHub / OpenClaw)
**Primary credential (not `primaryEnv`)**: `app_id` + `secret_key` in `~/.chanjing/credentials.json` (or `$CHANJING_CONFIG_DIR/credentials.json`); `access_token` / expiry **read/written** in the same file. **`primaryEnv` is omitted** on purpose (OpenClaw uses it for a single env-injected API key; this client uses **file-based dual keys**).
**Required vs optional**: **`CHANJING_API_BASE`** is **optional** (defaults to `https://open-api.chanjing.cc`). **`CHANJING_CONFIG_DIR`** is optional. **No `ffmpeg` / `ffprobe` gate** in this skill's `metadata`—orchestration-only use of this skill does not bundle local concat.
The skill explicitly states it reads and writes credentials.json, may open a browser/login helper, uploads/downloads network content, and can trigger server-side callbacks to arbitrary user-supplied URLs. In combination, this is a sensitive capability set involving secrets, local persistence, and externally directed network flows, which is dangerous without stronger scope restriction and callback validation.
| # | 关切 | 说明 |
|---|------|------|
| **1** | **名称/能力与实现一致?主凭据是否写进登记?** | **一致**。本 skill 为蝉镜 **数字人视频合成** API 客户端(形象列表、上传、创建/轮询任务、用户明确要求时 **`download_result.py`**)。**`CHANJING_API_BASE` 不是必填**(有默认基址)。**主凭据**为 **`credentials.json`** 内 **`app_id` / `secret_key`** 及刷新后的 **`access_token`**;已在 **`description`**、**`credential_hint`**、**`metadata.openclaw.credentialModel`**、篇首英文摘要、下表 **安全与凭据** 写明。**不**声明 **`primaryEnv`**(双字段文件凭据,与单一 env Key 模型不符)。本 skill **不**将 **`ffmpeg`/`ffprobe`** 写入 `metadata.requires.bins`(与 **一键成片** 不同)。 |
| **2** | **运行时指令范围与敏感数据、任意 URL** | **符合声明目的**。脚本 **读写** **`CHANJING_CONFIG_DIR/credentials.json`**;缺凭证时可能 **浏览器** 或 **`open_login_page.py`**;向 Open API **上传/下载**;用户明确要求落盘时 **`download_result.py`** 会请求 **接口返回的媒体 URL**——请自行判断是否信任 API 主机与链接。若使用 **`create_task.py --callback <URL>`**,蝉镜服务端可能向该 URL **推送任务结果**——仅使用你信任且可接收回调的端点。 |
| **3** | **环境变量适合客户端,主凭据在文件** | **`CHANJING_CONFIG_DIR` / `CHANJING_API_BASE`** 为可选配置。**主凭据非**单一注入式 env Key:**`app_id` / `secret_key`** 经 **guard** 等写入 **`credentials.json`**(路径见 **`metadata.openclaw.credentialModel`**);**`access_token` 持久化到磁盘**,属敏感;勿提交版本库、勿在对话中回显完整密钥。 |
| **4** | **持久化与特权** | 凭据与 token 写入约定 **配置文件**;可能 **交互式登录**。**`always: false`**(默认);**不**修改其它 skill 或全局 Agent 配置。 |
This line confirms the skill reads/writes the shared credentials file, may launch browser-based login, fetches media from API-returned URLs, and supports callback delivery to a user-provided endpoint. Accepting API-returned download URLs and arbitrary callback URLs broadens the trust boundary and can enable data leakage, SSRF-like interactions, or retrieval from untrusted hosts if not strictly validated by code.
| # | 关切 | 说明 |
|---|------|------|
| **1** | **名称/能力与实现一致?主凭据是否写进登记?** | **一致**。本 skill 为蝉镜 **数字人视频合成** API 客户端(形象列表、上传、创建/轮询任务、用户明确要求时 **`download_result.py`**)。**`CHANJING_API_BASE` 不是必填**(有默认基址)。**主凭据**为 **`credentials.json`** 内 **`app_id` / `secret_key`** 及刷新后的 **`access_token`**;已在 **`description`**、**`credential_hint`**、**`metadata.openclaw.credentialModel`**、篇首英文摘要、下表 **安全与凭据** 写明。**不**声明 **`primaryEnv`**(双字段文件凭据,与单一 env Key 模型不符)。本 skill **不**将 **`ffmpeg`/`ffprobe`** 写入 `metadata.requires.bins`(与 **一键成片** 不同)。 |
| **2** | **运行时指令范围与敏感数据、任意 URL** | **符合声明目的**。脚本 **读写** **`CHANJING_CONFIG_DIR/credentials.json`**;缺凭证时可能 **浏览器** 或 **`open_login_page.py`**;向 Open API **上传/下载**;用户明确要求落盘时 **`download_result.py`** 会请求 **接口返回的媒体 URL**——请自行判断是否信任 API 主机与链接。若使用 **`create_task.py --callback <URL>`**,蝉镜服务端可能向该 URL **推送任务结果**——仅使用你信任且可接收回调的端点。 |
| **3** | **环境变量适合客户端,主凭据在文件** | **`CHANJING_CONFIG_DIR` / `CHANJING_API_BASE`** 为可选配置。**主凭据非**单一注入式 env Key:**`app_id` / `secret_key`** 经 **guard** 等写入 **`credentials.json`**(路径见 **`metadata.openclaw.credentialModel`**);**`access_token` 持久化到磁盘**,属敏感;勿提交版本库、勿在对话中回显完整密钥。 |
| **4** | **持久化与特权** | 凭据与 token 写入约定 **配置文件**;可能 **交互式登录**。**`always: false`**(默认);**不**修改其它 skill 或全局 Agent 配置。 |
The documentation states that app_id, secret_key, and access_token are persisted in credentials.json and that the token is stored on disk as sensitive cross-skill state. Plaintext persistence of both long-lived credentials and refreshed tokens in a shared file materially increases the impact of local compromise or accidental disclosure.
|---|------|------|
| **1** | **名称/能力与实现一致?主凭据是否写进登记?** | **一致**。本 skill 为蝉镜 **数字人视频合成** API 客户端(形象列表、上传、创建/轮询任务、用户明确要求时 **`download_result.py`**)。**`CHANJING_API_BASE` 不是必填**(有默认基址)。**主凭据**为 **`credentials.json`** 内 **`app_id` / `secret_key`** 及刷新后的 **`access_token`**;已在 **`description`**、**`credential_hint`**、**`metadata.openclaw.credentialModel`**、篇首英文摘要、下表 **安全与凭据** 写明。**不**声明 **`primaryEnv`**(双字段文件凭据,与单一 env Key 模型不符)。本 skill **不**将 **`ffmpeg`/`ffprobe`** 写入 `metadata.requires.bins`(与 **一键成片** 不同)。 |
| **2** | **运行时指令范围与敏感数据、任意 URL** | **符合声明目的**。脚本 **读写** **`CHANJING_CONFIG_DIR/credentials.json`**;缺凭证时可能 **浏览器** 或 **`open_login_page.py`**;向 Open API **上传/下载**;用户明确要求落盘时 **`download_result.py`** 会请求 **接口返回的媒体 URL**——请自行判断是否信任 API 主机与链接。若使用 **`create_task.py --callback <URL>`**,蝉镜服务端可能向该 URL **推送任务结果**——仅使用你信任且可接收回调的端点。 |
| **3** | **环境变量适合客户端,主凭据在文件** | **`CHANJING_CONFIG_DIR` / `CHANJING_API_BASE`** 为可选配置。**主凭据非**单一注入式 env Key:**`app_id` / `secret_key`** 经 **guard** 等写入 **`credentials.json`**(路径见 **`metadata.openclaw.credentialModel`**);**`access_token` 持久化到磁盘**,属敏感;勿提交版本库、勿在对话中回显完整密钥。 |
| **4** | **持久化与特权** | 凭据与 token 写入约定 **配置文件**;可能 **交互式登录**。**`always: false`**(默认);**不**修改其它 skill 或全局 Agent 配置。 |
### 安全与凭据(登记摘要)
This section reiterates that the access token and expiry are written back to a shared credentials file used by multiple skills. Cross-skill shared secret state raises the blast radius of any one skill defect or local read access, because compromise of one component can expose credentials for others.
| 维度 | 说明 |
|------|------|
| **主凭据** | `app_id`、`secret_key` 存于 **`credentials.json`**(默认 `~/.chanjing/`,**`CHANJING_CONFIG_DIR`** 可改目录)。经 **`chanjing-credentials-guard`** 配置。非 OpenClaw **`primaryEnv`** 模型(非单一环境变量 API Key)。 |
| **Token** | 脚本将 **`access_token`** / **`expire_in`** 写回同一文件;与 **`chanjing-credentials-guard`** 及其它蝉镜子技能**共用**该文件(磁盘持久化、跨技能状态)。 |
| **回调(可选)** | **`create_task.py --callback`**:若提供,API 可能向用户 URL 发送任务相关负载;属**出站到你方端点**的信任边界,请自行评估。 |
| **环境变量** | **`CHANJING_API_BASE`**、**`CHANJING_CONFIG_DIR`** 可选。 |
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
# 鉴权:与 chanjing-credentials-guard 使用同一配置文件(CONFIG_DIR/credentials.json)
# 无 AK/SK 时执行 open_login_page.py 打开注册/登录页
import json
import os
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
# 鉴权:与 chanjing-credentials-guard 使用同一配置文件(CONFIG_DIR/credentials.json)
# 无 AK/SK 时执行 open_login_page.py 打开注册/登录页
import json
import os
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
# 鉴权:与 chanjing-credentials-guard 使用同一配置文件(CONFIG_DIR/credentials.json)
# 无 AK/SK 时执行 open_login_page.py 打开注册/登录页
import json
import os
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
# 鉴权:与 chanjing-credentials-guard 使用同一配置文件(CONFIG_DIR/credentials.json)
# 无 AK/SK 时执行 open_login_page.py 打开注册/登录页
import json
import os
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
# 鉴权:与 chanjing-credentials-guard 使用同一配置文件(CONFIG_DIR/credentials.json)
# 无 AK/SK 时执行 open_login_page.py 打开注册/登录页
import json
import os
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
# 鉴权:与 chanjing-credentials-guard 使用同一配置文件(CONFIG_DIR/credentials.json)
# 无 AK/SK 时执行 open_login_page.py 打开注册/登录页
import json
import os
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
# 鉴权:与 chanjing-credentials-guard 使用同一配置文件(CONFIG_DIR/credentials.json)
# 无 AK/SK 时执行 open_login_page.py 打开注册/登录页
import json
import os
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
# 鉴权:与 chanjing-credentials-guard 使用同一配置文件(CONFIG_DIR/credentials.json)
# 无 AK/SK 时执行 open_login_page.py 打开注册/登录页
import json
import os
The skill declares broad capabilities in prose and metadata—reading/writing credentials files, network access, shell invocation, and possible browser launch—but does not constrain them with an explicit tool scope such as permissions or allowed-tools. This creates an overly permissive execution surface where the agent runtime may grant more capability than is necessary, increasing the chance of misuse or prompt-driven abuse.
The skill explicitly persists access_token state on disk, creating durable session material beyond a single run. Persistent authentication state is not inherently malicious, but it increases the risk of unauthorized reuse if the host is multi-user, compromised, or if the file is mishandled.
---
name: chanjing-video-compose
description: "Use Chanjing video synthesis APIs to create digital human videos from text or audio, with optional background upload, task polling, and explicit download when the user asks to save the result locally. Primary credential: credentials.json (app_id/secret_key; access_token persisted on disk—do not commit; user accepts file-based secrets). Same credentials file as chanjing-credentials-guard. Not OpenClaw primaryEnv. Default path in metadata.openclaw.credentialModel. Optional env: CHANJING_API_BASE, CHANJING_CONFIG_DIR. May invoke open_login_page.py when AK/SK missing. create_task.py optional --callback may cause the API to POST task result payloads to a user-supplied URL. This skill's scripts do not require ffmpeg/ffprobe."
author: chan-skills
binaries: []
env:
The natural-language guidance says that, absent special user requirements, the agent should '默认优先年轻、有活力的形象'. This hard-codes an age/style preference into skill behavior without user opt-in or a documented policy justification, which is a natural-language policy concern.
This is a markdown file, so SQP-2 applies to missing warnings in the skill description. The examples demonstrate uploading local files to a remote service and downloading output to the local machine, but there is no warning about transmitting user content, potential privacy implications, or local file creation.
The reference text states the skill content in Chinese ('本 skill 当前覆盖这些接口') and the examples/defaults consistently assume Chinese-language operation, but there is no indication that the user may choose another language or that the skill is intentionally region-specific. This can violate language/locale policy when a skill imposes a specific language without user opt-in.
subprocess module calls execute external commands. Without careful input validation, this enables command injection.
skills_dir = Path(__file__).resolve().parent.parent.parent
script = skills_dir / "chanjing-credentials-guard" / "scripts" / "open_login_page.py"
if script.exists():
subprocess.run([sys.executable, str(script)], check=False, timeout=5)
else:
import webbrowser
The credentials file path is derived from CHANJING_CONFIG_DIR, an environment variable, and then written without validation. If an attacker can influence the environment in which this skill runs, they may redirect credential writes to an unintended file location, causing secret leakage or overwriting sensitive files accessible to the current user.
def write_config(data):
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)
The code sends app_id and secret_key to a remote API endpoint via HTTP POST, but there is no user-facing warning, log, prompt, or explanatory comment disclosing that local credentials will be transmitted over the network. For code files, network calls that transmit sensitive data should have some visible disclosure unless the warning is documented elsewhere, which is not evident from this file alone.
The natural-language help text sets --language default to cn, which imposes a specific locale by default. The file does not explain that the skill is intentionally region-specific or ask the user to opt into a Chinese-language default, so this is a language/locale policy concern.
The script forwards user-provided task content, optional remote media URLs, and an optional callback URL to an external API with no validation or explicit confirmation. In this skill context, that creates a real data-exposure and SSRF-style risk surface: sensitive text may be sent off-platform unexpectedly, and a user-supplied callback can cause the third-party service to POST results to arbitrary destinations, including internal endpoints if the upstream service permits it.
All user-facing natural-language strings and help text are in Chinese, with no indication that language selection is optional or that the skill is intentionally limited to Chinese-speaking users. Under the stated policy, forcing a specific language without user opt-in is a natural-language policy violation.