Back to skill

Security audit

Chanjing Tts

Security checks for vulnerabilities and agentic risk

Overview

This TTS skill appears purpose-aligned, but it needs review because stored credentials can be sent to an environment-selected endpoint and an adjacent login helper can be executed automatically.

Review before installing. Use it only in an environment where CHANJING_API_BASE is unset or pinned to the official HTTPS Chanjing API, protect ~/.chanjing/credentials.json with owner-only permissions, and inspect any adjacent chanjing-credentials-guard installation before allowing this skill to run. Do not automatically download returned audio URLs unless the destination is validated as an expected Chanjing media host.

Vulnerability Patterns
  • Tool Hijacking and SpoofingModifies or replaces tools so legitimate-looking calls execute attacker logic
  • 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
Findings (5)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/_auth.py:13
Finding
Credentials, access tokens, and user content can be redirected to an arbitrary endpoint<![CDATA[ ## Vulnerability Details **File Location**: `scripts/_auth.py:13, 83-89`; related use in `scripts/create_task.py:17, 47-54`, `scripts/list_voices.py:18, 34-36`, and `scripts/poll_task.py:18, 33-43` **Vulnerability Type**: Unrestricted security-sensitive endpoint configuration **Risk Level**: High ### Vulnerable Code ```python API_BASE = os.environ.get("CHANJING_API_BASE", "https://open-api.chanjing.cc") ``` ```python 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", ) ``` Subsequent requests use the same unrestricted API base and transmit the access token: ```python url = f"{API_BASE}/open/v1/create_audio_task" req = urllib.request.Request( url, data=json.dumps(body).encode("utf-8"), headers={"access_token": token, "Content-Type": "application/json"}, method="POST", ) ``` ### Technical Analysis The `CHANJING_API_BASE` environment variable is accepted without validation of its scheme, hostname, port, or destination address. The authentication request sends the long-lived `app_id` and `secret_key` to this endpoint. Later requests send the resulting access token and, during task creation, user-provided TTS text. An attacker who can influence the process environment or launcher configuration can redirect these requests to an attacker-controlled server. The value may also use plaintext HTTP or target a local or internal service. Allowing the authentication destination to be changed this freely exceeds the minimum network privileges required for a client dedicated to the declared Chanjing service. ### Attack Path 1. An attacker, compromised launcher, or unsafe deployment configuration sets `CHANJING_API_BASE` to an attacker-controlled endpoint. 2. The user invokes any included script while the stored token is absent or near expiration. 3. `get_token()` s ...[truncated 750 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Enforce `https://open-api.chanjing.cc` as the authentication endpoint. - If API-base overrides are operationally necessary, require explicit opt-in and restrict them to a documented allowlist of trusted HTTPS hosts. - Parse the configured URL and reject: - Schemes other than HTTPS - Embedded usernames or passwords - Unexpected ports - URL fragments - Loopback, link-local, private, multicast, and cloud metadata addresses - Keep token acquisition pinned to the official authentication host even if other API endpoints may be overridden. - Do not send credentials after cross-origin redirects; either disable redirects for authentication or revalidate every redirect destination. - Add automated tests confirming that malicious, plaintext, and internal-network API-base values are rejected. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/_auth.py:61
Finding
Plaintext credential storage does not enforce restrictive filesystem permissions<![CDATA[ ## Vulnerability Details **File Location**: `scripts/_auth.py:61-64` **Vulnerability Type**: Insecure storage of sensitive credentials **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) ``` ### Technical Analysis The configuration file contains `app_id`, `secret_key`, `access_token`, and token expiry information. The code creates or rewrites it using ordinary filesystem defaults and does not enforce owner-only permissions. The resulting permissions depend on the process umask. A permissive umask can create a file readable by other local users. If the file or configuration directory already has unsafe permissions, rewriting the file does not correct them. The direct write is also non-atomic, which can leave a truncated credential file if the process terminates during serialization. ### Attack Path 1. The Skill runs under a permissive umask or uses an existing credential file with broad read permissions. 2. A token refresh invokes `write_config()`. 3. The plaintext AK/SK and refreshed token remain in a file that another local principal can read. 4. The local attacker copies and reuses those credentials against the API. ### Impact Assessment An attacker with local filesystem access allowed by the resulting permissions could obtain the application secret and access token. This grants the attacker the same API authorization represented by those credentials, potentially enabling unauthorized TTS requests, quota consumption, and access to account-associated API resources. This issue does not itself grant operating-system privilege escalation beyond the rights of the process that can read the file. ]]>
Remediation
<![CDATA[ ## Remediation Suggestions - Create the configuration directory with mode `0700`. - Create credential files with mode `0600`, independent of the ambient umask. - Verify and correct permissions on existing directories and credential files before reading or writing them. - Write updates atomically: 1. Create a temporary file in the same directory with mode `0600`. 2. Serialize and flush the data. 3. Call `fsync()` where durability is required. 4. Atomically replace the destination with `os.replace()`. - Reject symlinked credential files or use safe file-opening flags where supported. - Prefer an operating-system credential store or keyring for long-lived application secrets when available. ]]>

T07 · Tool Hijacking and Spoofing

Error
Location
scripts/_auth.py:33
Finding
Unverified Python code from an adjacent Skill is executed automatically<![CDATA[ ## Vulnerability Details **File Location**: `scripts/_auth.py:33-48` **Vulnerability Type**: Local tool or adjacent Skill spoofing **Risk Level**: High ### Vulnerable Code ```python def _run_open_login_page(): """执行 credentials-guard 的 open_login_page.py,在默认浏览器打开注册/登录页。""" try: 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 webbrowser.open(LOGIN_URL) except Exception: try: import webbrowser webbrowser.open(LOGIN_URL) except Exception: pass ``` ### Technical Analysis When credentials are missing or rejected, the Skill computes the path of a separate adjacent Skill and executes its `open_login_page.py` merely because the file exists. The current package neither contains this script nor verifies its package identity, ownership, permissions, signature, or contents. This creates a local trust-boundary violation. A spoofed or modified adjacent directory can supply arbitrary Python code that will be executed with the same operating-system privileges as the current Skill. Opening a fixed login URL does not require execution of another Skill and can be done directly with the standard `webbrowser` module. ### Attack Path 1. An attacker gains the ability to create or alter the adjacent path `chanjing-credentials-guard/scripts/open_login_page.py`. 2. The attacker places a malicious Python program at that location. 3. The victim runs this Skill with missing credentials, or the token service returns a response that causes credentials to be treated as invalid. 4. `_run_open_login_page()` detects the file and launches it through the current Python interpreter. 5. The malicious program executes with the permissions and envir ...[truncated 467 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Remove cross-Skill script execution from the authentication failure path. - Open the fixed HTTPS login URL directly through `webbrowser.open()` after explicit user confirmation. - If integration with a credential-guard package is mandatory, call a trusted installed package API rather than locating a script by relative path. - If external execution cannot be removed, verify all of the following before execution: - Expected canonical path - Package identity and version - File ownership and permissions - Cryptographic integrity or trusted signature - Absence of writable parent directories in the trust path - Avoid performing executable side effects automatically in response to remote API errors. ]]>

T09 · Insecure Skill Coding Practices

Note
Location
scripts/poll_task.py:24
Finding
Task polling has no attempt limit or overall deadline<![CDATA[ ## Vulnerability Details **File Location**: `scripts/poll_task.py:24-62` **Vulnerability Type**: Unbounded authenticated polling **Risk Level**: Low ### Vulnerable Code ```python parser.add_argument("--interval", type=int, default=3, help="轮询间隔秒数,默认 3") args = parser.parse_args() token, err = get_token() if err: print(err, file=sys.stderr) sys.exit(1) url = f"{API_BASE}/open/v1/audio_task_state" body = json.dumps({"task_id": args.task_id}).encode("utf-8") while True: req = urllib.request.Request( url, data=body, headers={"access_token": token, "Content-Type": "application/json"}, method="POST", ) with urllib.request.urlopen(req, timeout=30) as resp: res = json.loads(resp.read().decode("utf-8")) if res.get("code") != 0: print(res.get("msg", res), file=sys.stderr) sys.exit(1) data = res.get("data", {}) status = data.get("status") if status == 9: full = data.get("full") or {} audio_url = full.get("url") if audio_url: print(audio_url) return 0 print("任务完成但无 full.url", file=sys.stderr) sys.exit(1) if status not in (1, None): err_msg = data.get("errMsg") or data.get("errReason") or f"status={status}" print(f"任务失败: {err_msg}", file=sys.stderr) sys.exit(1) time.sleep(args.interval) ``` ### Technical Analysis The polling loop has no maximum number of attempts and no overall deadline. A service that continually returns status `1` or omits the status can keep the process alive and cause authenticated requests indefinitely. The user-controlled `--interval` value is not validated. A zero interval creates rapid repeated requests, potentially exhausting an API quota. A negative value reaches `time.sleep()` and raises an uncaught exception rather than producing a controlled validation error. ### Attack Path 1. The configured API endpoint continually returns a successfu ...[truncated 685 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Add a configurable maximum attempt count and an overall wall-clock deadline. - Validate that `--interval` is positive and within a reasonable upper bound. - Use capped exponential backoff with jitter where supported by the API. - Honor server rate-limit and retry guidance. - Exit with a clear timeout error when the task does not complete within the configured deadline. - Catch network, JSON parsing, and sleep-related errors and return controlled diagnostic messages. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/poll_task.py:50
Finding
API-controlled audio URL is exposed for downstream fetching without validation<![CDATA[ ## Vulnerability Details **File Location**: `scripts/poll_task.py:50-54`; documented workflow in `SKILL.md:27, 52, 78, 94, 441-452` **Vulnerability Type**: Untrusted server-provided URL handling **Risk Level**: Medium ### Vulnerable Code ```python if status == 9: full = data.get("full") or {} audio_url = full.get("url") if audio_url: print(audio_url) return 0 ``` The documented workflow characterizes response-provided URLs as audio download locations and instructs the operator to trust the API host and returned links. ### Technical Analysis The script does not itself download the URL; it prints the value supplied in `data.full.url`. However, the declared workflow expects an agent or user to use that value for a subsequent download. The URL is not validated for scheme, host, port, redirect behavior, or destination address range. A compromised or redirected API can return a loopback URL, private-network URL, cloud metadata address, non-HTTPS URL, local-file URI, or attacker-controlled content URL. Whether non-HTTP schemes are exploitable depends on the downstream fetching tool, but passing the value forward without a trust boundary makes server-side request forgery or unsafe local resource access possible in an automated agent workflow. ### Attack Path 1. An attacker compromises the API response path or uses the unrestricted `CHANJING_API_BASE` configuration to supply a malicious response. 2. The response reports task status `9` and provides a malicious value in `data.full.url`. 3. `poll_task.py` prints the URL as the successful task result without marking or validating it. 4. An automated agent or user follows the documented workflow and passes the URL to a download utility. 5. The downstream utility accesses an internal service, local resource, metadata endpoint, or attacker-controlled server, depending on its supported schemes and network access. ### Impact Assessment The practical impact depends on the downstream ...[truncated 423 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Treat `data.full.url` as untrusted data. - Parse the URL and require HTTPS. - Allowlist official Chanjing media domains instead of accepting arbitrary hosts. - Resolve the hostname and reject loopback, link-local, private, multicast, reserved, and cloud metadata address ranges. - Apply the same validation after every redirect and limit the number of redirects. - Return structured output that explicitly identifies the field as an untrusted remote URL. - If downloading is added to the Skill, enforce response-size limits, content-type checks, download timeouts, safe destination paths, and atomic file creation. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Rogue AgentSelf-Modification, Session Persistence
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
Findings (33)

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

Critical
Category
Data Flow
Content
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)
Confidence
95% confidence
Finding
The destination for the token request is derived from CHANJING_API_BASE, and the request body contains app_id and secret_key. If an attacker can control the environment, they can redirect this authentication request to an arbitrary server and exfiltrate stored credentials.

Credential Access

High
Category
Privilege Escalation
Content
## How to Use This Skill

**前置条件(权限验证)**:执行本 Skill 前,必须先通过 **chanjing-credentials-guard** 完成 AK/SK 与 Token 校验。本 Skill 与 guard 使用同一套凭证(`~/.chanjing/credentials.json`);脚本在无凭证时会**执行 `open_login_page.py` 脚本**,在默认浏览器打开 AK/SK 注册/登录页,并提示配置命令。

### Reviewer Q&A (four items)
Confidence
84% confidence
Finding
The skill states it will execute open_login_page.py and open the default browser when credentials are missing. Triggering an auxiliary script and browser launch from a credential failure path expands the attack surface and can surprise users, especially in automated or high-trust agent environments where shell execution and external navigation should be explicit and tightly constrained.

Credential Access

High
Category
Privilege Escalation
Content
| # | Topic | Answer |
|---|-------|--------|
| **1** | **Purpose vs implementation; primary credential in registry text** | **Aligned**. This skill is a **Chanjing TTS** API client (list voices, create speech task, poll, fetch audio from **URLs in responses**). **`CHANJING_API_BASE` is not required** (default base URL). **Primary credential** is **`credentials.json`** (`app_id` / `secret_key`, refreshed **`access_token`**); stated in **`description`**, top English summary, and **Security & credentials** below. **`primaryEnv` omitted** (file-based dual keys). **No** `ffmpeg`/`ffprobe` in `metadata`. |
| **2** | **Runtime scope: secrets, arbitrary URLs** | **In scope**. Reads/writes **`CHANJING_CONFIG_DIR/credentials.json`**; may open **browser** / **`open_login_page.py`**; **HTTPS** to Open API; **downloads audio using URLs returned by the API**—**you must trust** the API host and those links. |
| **3** | **Env vars vs file-stored secrets** | **`CHANJING_CONFIG_DIR` / `CHANJING_API_BASE`** are optional. **AK/SK and token persist on disk**—sensitive; do not commit secrets; avoid echoing full keys in chat. |
| **4** | **Persistence & privilege** | Default **`always: false`**; **does not** modify other skills or global agent config. |
Confidence
95% confidence
Finding
This section explicitly says the skill reads and writes credentials.json, may open a browser, and downloads audio from URLs returned by the API, asking the operator to trust those links. Downloading and following response-supplied URLs without documented allowlisting or validation can enable SSRF-like outbound access, unintended data exfiltration targets, or delivery of malicious content if the API or its responses are compromised.

Credential Access

High
Category
Privilege Escalation
Content
| # | Topic | Answer |
|---|-------|--------|
| **1** | **Purpose vs implementation; primary credential in registry text** | **Aligned**. This skill is a **Chanjing TTS** API client (list voices, create speech task, poll, fetch audio from **URLs in responses**). **`CHANJING_API_BASE` is not required** (default base URL). **Primary credential** is **`credentials.json`** (`app_id` / `secret_key`, refreshed **`access_token`**); stated in **`description`**, top English summary, and **Security & credentials** below. **`primaryEnv` omitted** (file-based dual keys). **No** `ffmpeg`/`ffprobe` in `metadata`. |
| **2** | **Runtime scope: secrets, arbitrary URLs** | **In scope**. Reads/writes **`CHANJING_CONFIG_DIR/credentials.json`**; may open **browser** / **`open_login_page.py`**; **HTTPS** to Open API; **downloads audio using URLs returned by the API**—**you must trust** the API host and those links. |
| **3** | **Env vars vs file-stored secrets** | **`CHANJING_CONFIG_DIR` / `CHANJING_API_BASE`** are optional. **AK/SK and token persist on disk**—sensitive; do not commit secrets; avoid echoing full keys in chat. |
| **4** | **Persistence & privilege** | Default **`always: false`**; **does not** modify other skills or global agent config. |
Confidence
95% confidence
Finding
The documented runtime scope includes reading/writing persistent credentials and downloading from API-returned links, with no mention of output validation or network restrictions. In an agent setting, that combination is risky because secrets and network egress are both in scope, and a compromised or misconfigured API could direct the skill to retrieve attacker-controlled resources.

Credential Access

High
Category
Privilege Escalation
Content
# 鉴权:与 chanjing-credentials-guard 使用同一配置文件(CONFIG_DIR/credentials.json)
# 实现:AK/SK 校验、Token 校验与刷新;无 AK/SK 时执行 open_login_page.py 打开注册/登录页
import json
import os
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
# 鉴权:与 chanjing-credentials-guard 使用同一配置文件(CONFIG_DIR/credentials.json)
# 实现:AK/SK 校验、Token 校验与刷新;无 AK/SK 时执行 open_login_page.py 打开注册/登录页
import json
import os
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
# 鉴权:与 chanjing-credentials-guard 使用同一配置文件(CONFIG_DIR/credentials.json)
# 实现:AK/SK 校验、Token 校验与刷新;无 AK/SK 时执行 open_login_page.py 打开注册/登录页
import json
import os
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
# 鉴权:与 chanjing-credentials-guard 使用同一配置文件(CONFIG_DIR/credentials.json)
# 实现:AK/SK 校验、Token 校验与刷新;无 AK/SK 时执行 open_login_page.py 打开注册/登录页
import json
import os
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
# 鉴权:与 chanjing-credentials-guard 使用同一配置文件(CONFIG_DIR/credentials.json)
# 实现:AK/SK 校验、Token 校验与刷新;无 AK/SK 时执行 open_login_page.py 打开注册/登录页
import json
import os
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
# 鉴权:与 chanjing-credentials-guard 使用同一配置文件(CONFIG_DIR/credentials.json)
# 实现:AK/SK 校验、Token 校验与刷新;无 AK/SK 时执行 open_login_page.py 打开注册/登录页
import json
import os
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
# 鉴权:与 chanjing-credentials-guard 使用同一配置文件(CONFIG_DIR/credentials.json)
# 实现:AK/SK 校验、Token 校验与刷新;无 AK/SK 时执行 open_login_page.py 打开注册/登录页
import json
import os
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
# 鉴权:与 chanjing-credentials-guard 使用同一配置文件(CONFIG_DIR/credentials.json)
# 实现:AK/SK 校验、Token 校验与刷新;无 AK/SK 时执行 open_login_page.py 打开注册/登录页
import json
import os
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
# 鉴权:与 chanjing-credentials-guard 使用同一配置文件(CONFIG_DIR/credentials.json)
# 实现:AK/SK 校验、Token 校验与刷新;无 AK/SK 时执行 open_login_page.py 打开注册/登录页
import json
import os
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Lp3

Medium
Category
MCP Least Privilege
Confidence
92% confidence
Finding
The skill declares significant capabilities—environment access, reading and writing credential files, network access, and shell/browser invocation—but does not define an explicit permission or allowed-tools scope. That omission increases the chance an agent runtime grants broader access than users or reviewers expect, especially given the documented credential handling and external downloads.

Session Persistence

Medium
Category
Rogue Agent
Content
**Required vs optional**: **`CHANJING_API_BASE`** **optional** (default `https://open-api.chanjing.cc`). **`CHANJING_CONFIG_DIR`** optional. **No** `ffmpeg`/`ffprobe` in skill `metadata`.

**Purpose alignment**: **TTS** client—list voices, create task, poll, **download audio from URLs in API responses**. **Trust** the API host and returned URLs.

See **How to Use** → **Reviewer Q&A (four items)** → **Security & credentials (registry summary)**.
Confidence
94% confidence
Finding
The skill explicitly states it will download audio from URLs contained in API responses and instructs reviewers to trust the returned URLs. Treating response-supplied URLs as trusted without documented validation is dangerous because it can turn the skill into an unrestricted fetcher, enabling attacker-controlled downloads, internal network probing, or retrieval of unexpected content if the upstream service is compromised.

Session Persistence

Medium
Category
Rogue Agent
Content
1. Obtain an `access_token`, which is required for all subsequent API calls
2. List all voice IDs and select one to use
3. Call the Create Speech API, record `task_id`
4. Poll the Query Speech Status API until success, then download generated audio file using the url in response

### Obtain AccessToken
Confidence
94% confidence
Finding
The documented workflow requires polling until success and then downloading the generated audio from a URL in the response, again without any stated restriction on the destination. In this skill context, the combination of persistent credentials, network access, and response-driven downloads makes the behavior more dangerous because a single compromised API response can direct outbound traffic to unintended hosts.

Description-Behavior Mismatch

Medium
Confidence
91% confidence
Finding
The manifest describes this skill as converting text to speech via the Chanjing TTS API, with credentials stored in credentials.json. This helper also launches a separate login/registration experience in the user's browser, which is a broader user-auth/onboarding behavior not reflected in the stated skill purpose.

Context-Inappropriate Capability

Medium
Confidence
94% confidence
Finding
A text-to-speech skill is expected to read credentials and call the TTS API, but invoking subprocesses to execute another skill's script introduces a separate execution capability. That behavior is not obviously required by the manifest's stated purpose, especially since the manifest only mentions credential files and optional configuration variables.

Missing User Warnings

Medium
Confidence
92% confidence
Finding
The code automatically executes another script via subprocess and may open the user's default browser when credentials are missing or invalid. This is a system-affecting action, but the file provides no prompt, log, or other user-visible disclosure before triggering it; the returned message is only produced after the action has already occurred.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
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
            webbrowser.open(LOGIN_URL)
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Missing User Warnings

Medium
Confidence
88% confidence
Finding
The function writes access tokens and related credentials to CONFIG_DIR/credentials.json, which affects user data on disk. Although comments describe authentication behavior, there is no user-facing prompt, log, print, or other disclosure at the point of writing to inform the user that secrets and refreshed tokens are being persisted locally.

Tainted flow: 'CONFIG_FILE' from os.environ.get (line 12, credential/environment) → open (file write)

Medium
Category
Data Flow
Content
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)
Confidence
84% confidence
Finding
The credential file path is influenced by CHANJING_CONFIG_DIR from the environment and is written without validation or permission hardening. In a hostile execution environment, an attacker could redirect writes to an unintended filesystem location, causing credential leakage or overwriting sensitive files accessible to the running user.

Missing User Warnings

Medium
Confidence
84% confidence
Finding
The token refresh flow transmits app_id and secret_key to a remote API endpoint over HTTP(S). While this is plausibly part of the authentication purpose, this file contains no user-facing warning, prompt, or runtime disclosure that locally stored credentials will be sent to the service to obtain a token.

Dynamic import via __import__()

Medium
Category
Dangerous Code Execution
Content
sys.path.insert(0, str(Path(__file__).resolve().parent))
from _auth import get_token

API_BASE = __import__("os").environ.get("CHANJING_API_BASE", "https://open-api.chanjing.cc")


def main():
Confidence
75% confidence
Finding
Dynamic __import__() can load arbitrary modules at runtime, bypassing static analysis and potentially importing malicious code.

Dynamic import via __import__()

Medium
Category
Dangerous Code Execution
Content
sys.path.insert(0, str(Path(__file__).resolve().parent))
from _auth import get_token

API_BASE = __import__("os").environ.get("CHANJING_API_BASE", "https://open-api.chanjing.cc")


def main():
Confidence
75% confidence
Finding
Dynamic __import__() can load arbitrary modules at runtime, bypassing static analysis and potentially importing malicious code.

Static analysis

No suspicious patterns detected.