T09 · Insecure Skill Coding Practices
- Location
scripts/auth.py:60- Finding
API Key Stored Without Explicit Restrictive File Permissions
- Content
View full analysis
- Remediation
View remediation
Security audit
Security checks for vulnerabilities and agentic risk
This SMS skill is not clearly malicious, but it needs review because it can send paid texts and stores or accepts its account key in risky ways.
Review before installing. Use this only for lawful, consent-based SMS workflows, avoid passing the API key on the command line, restrict permissions on ~/.upkuajing/.env if you use it, and be careful not to include phone numbers, message bodies, or secrets in error reports unless necessary.
scripts/auth.py:60API Key Stored Without Explicit Restrictive File Permissions
scripts/auth.py:20Existing API Key Prefix Disclosed in Program Output
scripts/sms_send.py:91API Key Accepted Through Command-Line Arguments
requirements.txt:1Unbounded and Unhashed Third-Party Dependency
The skill performs remote version checks, reads local metadata, writes a version cache, and emits upgrade notices, but those behaviors are not apparent from the top-level purpose statement. Undisclosed network and filesystem behaviors are security-relevant because they expand data exposure and persistence beyond user expectations.
The skill performs remote version checks, reads local metadata, writes a version cache, and emits upgrade notices, but those behaviors are not apparent from the top-level purpose statement. Undisclosed network and filesystem behaviors are security-relevant because they expand data exposure and persistence beyond user expectations.
The skill performs remote version checks, reads local metadata, writes a version cache, and emits upgrade notices, but those behaviors are not apparent from the top-level purpose statement. Undisclosed network and filesystem behaviors are security-relevant because they expand data exposure and persistence beyond user expectations.
The skill performs remote version checks, reads local metadata, writes a version cache, and emits upgrade notices, but those behaviors are not apparent from the top-level purpose statement. Undisclosed network and filesystem behaviors are security-relevant because they expand data exposure and persistence beyond user expectations.
The skill performs remote version checks, reads local metadata, writes a version cache, and emits upgrade notices, but those behaviors are not apparent from the top-level purpose statement. Undisclosed network and filesystem behaviors are security-relevant because they expand data exposure and persistence beyond user expectations.
The skill performs remote version checks, reads local metadata, writes a version cache, and emits upgrade notices, but those behaviors are not apparent from the top-level purpose statement. Undisclosed network and filesystem behaviors are security-relevant because they expand data exposure and persistence beyond user expectations.
The skill performs remote version checks, reads local metadata, writes a version cache, and emits upgrade notices, but those behaviors are not apparent from the top-level purpose statement. Undisclosed network and filesystem behaviors are security-relevant because they expand data exposure and persistence beyond user expectations.
The skill instructs reading and populating credentials from a local .env file and can automatically create and persist a new API key. Credential discovery, creation, and storage are sensitive operations; if the skill is over-permissioned or compromised, it could expose or misuse long-lived secrets for paid messaging and account actions.
### **API Key Not Set**
First check if the `~/.upkuajing/.env` file has UPKUAJING_API_KEY;
If UPKUAJING_API_KEY is not set, prompt the user to choose:
1. User has one: User provides it (manually add to ~/.upkuajing/.env file)
2. User doesn't have one: You can apply using the API (`auth.py --new_key`), the new key will be automatically saved to ~/.upkuajing/.env
Wait for user selection;
This is the same credential-handling pattern in operational flow form: the skill checks for a local secret, asks the user to provide one, or automatically generates and saves one to disk. In context, that is particularly sensitive because the key authorizes chargeable SMS activity and related account operations, making credential theft or silent persistence materially harmful.
First check if the `~/.upkuajing/.env` file has UPKUAJING_API_KEY;
If UPKUAJING_API_KEY is not set, prompt the user to choose:
1. User has one: User provides it (manually add to ~/.upkuajing/.env file)
2. User doesn't have one: You can apply using the API (`auth.py --new_key`), the new key will be automatically saved to ~/.upkuajing/.env
Wait for user selection;
### **Account Top-up**
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
"""
申请新的 API 密钥。
"""
# 检查是否已存在 .env 文件和 API key
env_file = UPKUAJING_ENV_FILE
if env_file.exists():
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
"""
申请新的 API 密钥。
"""
# 检查是否已存在 .env 文件和 API key
env_file = UPKUAJING_ENV_FILE
if env_file.exists():
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
"""
申请新的 API 密钥。
"""
# 检查是否已存在 .env 文件和 API key
env_file = UPKUAJING_ENV_FILE
if env_file.exists():
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
"""
申请新的 API 密钥。
"""
# 检查是否已存在 .env 文件和 API key
env_file = UPKUAJING_ENV_FILE
if env_file.exists():
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
"""
申请新的 API 密钥。
"""
# 检查是否已存在 .env 文件和 API key
env_file = UPKUAJING_ENV_FILE
if env_file.exists():
The script writes a newly issued API key directly into a plaintext .env file under the user's home directory. Plaintext credential storage increases the risk of local disclosure through weak filesystem permissions, backups, accidental inclusion in support bundles, or other tools that automatically ingest .env files.
"envFilePath": str(env_file)
}
# 保存到 .env 文件
try:
with open(env_file, 'w', encoding='utf-8') as f:
f.write(f"{API_KEY_ENV}={api_key}\n")
The error path reveals the exact environment variable name and file path used for API key storage, which lowers the barrier for local attackers or malicious software seeking credentials. Combined with plaintext .env storage, this makes credential discovery and harvesting easier.
except IOError as e:
return {
"success": False,
"message": f"API密钥申请成功,但保存到 .env 文件失败:{str(e)}。\n请手动设置环境变量 {API_KEY_ENV}。",
"envFilePath": str(env_file)
}
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
API_BASE_URL = "https://openapi.upkuajing.com"
API_KEY_ENV = "UPKUAJING_API_KEY"
UPKUAJING_DIR = Path.home() / '.upkuajing'
UPKUAJING_ENV_FILE = UPKUAJING_DIR / '.env'
UPKUAJING_LOGS_DIR = UPKUAJING_DIR / 'logs'
# 日志开关
The skill declares powerful capabilities through metadata and instructions—network access, environment-variable access, and local file reads/writes—without an explicit tool-scope or permission boundary. That makes it harder for reviewers and runtime policy to constrain what the skill may do, increasing the risk of overbroad access to local secrets and unintended outbound actions.
Broad trigger phrases increase the chance that the skill activates in contexts where the user did not intend to invoke a mass-messaging tool. In a messaging skill with billing, credential use, and outbound communication, accidental activation can lead to privacy issues, unwanted costs, or inappropriate message sending workflows.
The skill handles phone numbers, message content, delivery status, and potentially two-way replies, but the description lacks privacy, consent, and regulatory warnings. In the SMS context, this is more dangerous because telecom marketing often implicates personal data, anti-spam rules, opt-in/opt-out obligations, and cross-border compliance requirements.
The documentation instructs agents to send request parameters, response payloads, and exception context to an error-reporting endpoint, which can easily include personal data, credentials, message content, phone numbers, or other sensitive operational data. Although it states that sensitive fields are 'automatically desensitized,' it does not define the scope, guarantees, or limits of that masking, and it provides no explicit warning to minimize or avoid sending sensitive content in free-form fields such as context.
The documentation describes bulk SMS sending, recipient phone number handling, message content, and delivery/reporting data without any notice about privacy, consent, lawful basis, opt-out handling, or misuse risks. In a mass-texting and cross-border outreach context, this omission increases the chance of unauthorized messaging, personal data misuse, and noncompliant processing of telecom and marketing data.
The manifest describes an SMS campaign tool focused on sending bulk SMS, handling two-way replies, and monitoring delivery reports. This file is dedicated to account/auth management and payments ('API 密钥申请、充值等功能'), which are operational account capabilities not described in the stated skill behavior.
The module title and description are written only in Chinese, and the CLI help/output throughout the file is likewise Chinese-only. This imposes a specific language on users without opt-in, which matches the language/locale policy violation category for natural-language content.
Tool defaults are unsafe or overly permissive (e.g. disabled TLS verification, no authentication, world-writable permissions). Unsafe defaults widen the attack surface.
pass # 如果读取失败,继续执行
# 不需要认证申请新密钥
response = make_request('/agent/auth/create', {}, require_auth=False)
# 检查是否申请成功
if response.get('code') != 0:
Detected: suspicious.exposed_secret_literal