Back to skill

Security audit

Zyt avatar

Security checks for vulnerabilities and agentic risk

Overview

The skill is mostly aligned with Chanjing avatar video creation, but it handles long-lived credentials in ways that need careful review before installation.

Install only if you trust the Chanjing account credentials and the local environment where the skill will run. Keep CHANJING_API_BASE unset unless you intentionally use a trusted HTTPS endpoint, store credentials.json in a private directory, and restrict the file to your user account because it contains reusable secrets and cached tokens.

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
scripts/_auth.py:11
Finding

Application Credentials Can Be Sent to an Unrestricted API Endpoint

Content
View full analysis

Vulnerability Details

File Location: scripts/_auth.py, lines 11 and 63-72
Vulnerability Type: Unvalidated credential transmission endpoint
Risk Level: High

Vulnerable Code

python
API_BASE = os.environ.get("CHANJING_API_BASE", "https://open-api.chanjing.cc")
python
def _request_new_token(app_id, secret_key):
    url = API_BASE + "/open/v1/access_token"
    req = urllib.request.Request(
        url,
        data=json.dumps({"app_id": app_id, "secret_key": secret_key}).encode("utf-8"),
        headers={"Content-Type": "application/json"},
        method="POST",
    )
    with urllib.request.urlopen(req, timeout=30) as resp:
        return json.loads(resp.read().decode("utf-8"))

Technical Analysis

The CHANJING_API_BASE environment variable is accepted as the authentication endpoint without validating its scheme, hostname, port, or origin. _request_new_token() subsequently transmits the long-lived app_id and secret_key to this endpoint.

Sending these credentials to the official Chanjing token endpoint is necessary for the declared functionality. However, allowing the destination to be changed arbitrarily is not required for ordinary production use and violates least-exposure principles for secrets.

Both attacker-controlled HTTPS endpoints and unencrypted HTTP endpoints are accepted. An HTTP endpoint exposes credentials to network interception, while an attacker-controlled endpoint directly receives them. This issue requires an attacker or unsafe deployment configuration to influence the process environment.

Attack Path

  1. An attacker, malicious wrapper script, compromised launcher, or unsafe deployment manifest sets CHANJING_API_BASE to an attacker-controlled URL.
  2. The user invokes a workflow that calls get_token() while no valid cached token exists.
  3. _request_new_token() appends /open/v1/access_token to the attacker-selected base URL.
  4. Th ...[truncated 856 chars]
Remediation
View remediation

Remediation Suggestions

  1. Remove CHANJING_API_BASE configurability in production and use the fixed official endpoint:
    python
    API_BASE = "https://open-api.chanjing.cc"
    
  2. If endpoint customization is necessary for testing, parse the URL with urllib.parse.urlparse and enforce:
    • HTTPS only.
    • An explicit hostname allowlist.
    • No embedded username or password.
    • No unexpected port.
    • No query string or fragment.
  3. Separate production and test configurations so production credentials cannot be used with development endpoints.
  4. Require explicit user confirmation before sending credentials to any non-default endpoint.
  5. Prevent HTTPS-to-HTTP redirect downgrade and validate the final response origin.
  6. Document that environment variables affecting credential destinations must be treated as security-sensitive configuration.
  7. Rotate the Chanjing credentials if the script has previously run with an untrusted CHANJING_API_BASE.

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/_auth.py:52
Finding

Credential and Access-Token File Permissions Are Not Enforced

Content
View full analysis

Vulnerability Details

File Location: scripts/_auth.py, lines 52-55 and 109-111
Vulnerability Type: Insecure local secret storage
Risk Level: Medium

Vulnerable Code

python
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)
python
    data["access_token"] = new_token
    data["expire_in"] = d.get("expire_in")
    write_config(data)

Technical Analysis

credentials.json stores the secret_key and cached access_token, but write_config() does not explicitly enforce owner-only permissions on either the configuration directory or the file.

For newly created paths, permissions depend on the process umask. If the umask is permissive, the directory or file may be readable by other local users. When the file already exists, opening it with mode "w" truncates and rewrites it without correcting existing permissive permissions.

The same function is also used when clearing or updating cached token data. Consequently, every rewrite preserves an insecure existing mode rather than hardening it.

Attack Path

  1. The Skill runs under a permissive umask, or credentials.json already has group-readable or world-readable permissions.
  2. Token acquisition succeeds and the returned access_token is written into the same file as app_id and secret_key.
  3. Another local user or process with filesystem access reads the configuration file.
  4. The exposed token or application credentials are reused against the Chanjing API before expiration or revocation.

Impact Assessment

Exploitation requires local filesystem access and insufficient file or directory permissions. A successful attacker can obtain the stored secret_key and potentially a currently valid access token.

The resulting remote privileges are limited to those granted to the Ch ...[truncated 207 chars]

Remediation
View remediation

Remediation Suggestions

  1. Create the configuration directory with owner-only permissions:
    python
    CONFIG_DIR.mkdir(parents=True, exist_ok=True, mode=0o700)
    os.chmod(CONFIG_DIR, 0o700)
    
  2. Create secret-bearing files with mode 0600 rather than relying on the process umask.
  3. Correct permissions on existing configuration files before reading or rewriting them.
  4. Use an atomic write pattern:
    • Create a temporary file in the same directory with mode 0600.
    • Write and flush the JSON data.
    • Call os.fsync() where durability is required.
    • Atomically replace the destination with os.replace().
    • Confirm that the final file remains mode 0600.
  5. Reject configuration files that are symbolic links or are owned by another account to reduce link-based and ownership attacks.
  6. Prefer an operating-system credential store or secret manager for long-lived secret_key values where available.
  7. Rotate credentials and revoke cached tokens if the file was previously accessible to unauthorized local users.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Rogue AgentSelf-Modification, Session Persistence
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (15)

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

Critical
Category
Data Flow
Confidence
92% confidence
Finding

The request destination is derived from CHANJING_API_BASE, so an attacker who can influence the environment can redirect app_id and secret_key to an arbitrary server. Because these are long-lived credentials, this creates a straightforward credential exfiltration path rather than a mere configuration issue.

Content

Scanner excerpt · scripts/_auth.py (reported line 73)May include surrounding context.

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

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 · SKILL.md (reported line 148)May include surrounding context.

md
## API Notes

### Access Token

Read `app_id` and `secret_key` from the local credentials file. If there is no valid token, request one from:

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 · SKILL.md (reported line 3)May include surrounding context.

md
# 鉴权:从 CONFIG_DIR/credentials.json 读取 AK/SK
# 实现:AK/SK 校验、Token 校验与刷新;默认不自动打开浏览器,仅返回登录引导信息
import json
import os

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 · SKILL.md (reported line 35)May include surrounding context.

md
# 鉴权:从 CONFIG_DIR/credentials.json 读取 AK/SK
# 实现:AK/SK 校验、Token 校验与刷新;默认不自动打开浏览器,仅返回登录引导信息
import json
import os

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 · SKILL.md (reported line 36)May include surrounding context.

md
# 鉴权:从 CONFIG_DIR/credentials.json 读取 AK/SK
# 实现:AK/SK 校验、Token 校验与刷新;默认不自动打开浏览器,仅返回登录引导信息
import json
import os

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 · scripts/_auth.py (reported line 1)May include surrounding context.

python
# 鉴权:从 CONFIG_DIR/credentials.json 读取 AK/SK
# 实现:AK/SK 校验、Token 校验与刷新;默认不自动打开浏览器,仅返回登录引导信息
import json
import os

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 · scripts/_auth.py (reported line 10)May include surrounding context.

python
# 鉴权:从 CONFIG_DIR/credentials.json 读取 AK/SK
# 实现:AK/SK 校验、Token 校验与刷新;默认不自动打开浏览器,仅返回登录引导信息
import json
import os

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 · scripts/_auth.py (reported line 18)May include surrounding context.

python
# 鉴权:从 CONFIG_DIR/credentials.json 读取 AK/SK
# 实现:AK/SK 校验、Token 校验与刷新;默认不自动打开浏览器,仅返回登录引导信息
import json
import os

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 · scripts/_auth.py (reported line 28)May include surrounding context.

python
# 鉴权:从 CONFIG_DIR/credentials.json 读取 AK/SK
# 实现:AK/SK 校验、Token 校验与刷新;默认不自动打开浏览器,仅返回登录引导信息
import json
import os

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 · scripts/_auth.py (reported line 29)May include surrounding context.

python
# 鉴权:从 CONFIG_DIR/credentials.json 读取 AK/SK
# 实现:AK/SK 校验、Token 校验与刷新;默认不自动打开浏览器,仅返回登录引导信息
import json
import os

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
95% confidence
Finding

The skill documentation describes capabilities to read environment variables, access local credential files, write configuration, and make network requests, but it does not declare any explicit tool scope or permissions boundary. This creates a least-privilege gap: an agent invoking the skill may grant broader access than reviewers or users expect, increasing the risk of unintended credential access and outbound data transfer.

Content

No source excerpt is available for this finding.

Session Persistence

Medium
Category
Rogue Agent
Confidence
60% confidence
Finding

Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Content

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

md
---
name: chanjing-avatar
description: use chanjing avatar api to create lip-sync videos by uploading source media, creating avatar tasks, and polling task status. this skill reads app_id and secret_key from ~/.chanjing/credentials.json or $CHANJING_CONFIG_DIR/credentials.json and refreshes access_token for api calls. by default it does not auto-open browser pages; it returns login guidance when credentials are missing or invalid.
metadata:
  openclaw:
    requires:

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

The function writes app_id, secret_key, and token data to ~/.chanjing/credentials.json, which is a safety-relevant file write involving credentials. In this file there is no confirmation prompt, user-facing log/print, or explicit warning comment/docstring near the write operation disclosing that credentials are being persisted locally.

Content

No source excerpt is available for this finding.

Tainted flow: 'CONFIG_FILE' from os.environ.get (line 10, credential/environment) → open (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/_auth.py (reported line 53)May include surrounding context.

python
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)

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

This function sends app_id and secret_key to the remote access_token endpoint via HTTP POST, which is a network transmission of sensitive data. The code lacks a confirmation prompt, user-facing log/print, or explicit warning comment/docstring describing that local credentials will be sent to an external service.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.