T09 · Insecure Skill Coding Practices
- Location
SKILL.md:37- Finding
Unsafe API Key Disclosure and Insufficient Secret File Permission Enforcement
- Content
View full analysis
- Remediation
View remediation
Security audit
Security checks for vulnerabilities and agentic risk
This appears to be a real UpKuaJing customs-data skill, but it handles API keys, paid queries, and account top-up flows in ways users should review before installing.
Install only if you are comfortable with a paid UpKuaJing API workflow. Do not let the agent print your .env file or API key into chat or logs, protect ~/.upkuajing/.env with restrictive permissions, require explicit confirmation before fee-incurring queries or top-up order creation, and avoid sending sensitive business data in error reports unless needed.
SKILL.md:37Unsafe API Key Disclosure and Insufficient Secret File Permission Enforcement
requirements.txt:1Unpinned Third-Party HTTP Dependency
The documented behavior includes remote update checks, reading local metadata, and writing a version cache file under the user's home directory, none of which are part of the stated trade-analysis purpose. This is risky because it silently adds persistence, network communication, and local state changes that broaden attack surface and can be abused for tracking, tampering, or unexpected outbound requests.
The documented behavior includes remote update checks, reading local metadata, and writing a version cache file under the user's home directory, none of which are part of the stated trade-analysis purpose. This is risky because it silently adds persistence, network communication, and local state changes that broaden attack surface and can be abused for tracking, tampering, or unexpected outbound requests.
The documented behavior includes remote update checks, reading local metadata, and writing a version cache file under the user's home directory, none of which are part of the stated trade-analysis purpose. This is risky because it silently adds persistence, network communication, and local state changes that broaden attack surface and can be abused for tracking, tampering, or unexpected outbound requests.
The skill instructs reading the entire ~/.upkuajing/.env file via cat to obtain an API key. Directly reading and potentially exposing a secrets file is dangerous because it normalizes credential access beyond the minimum necessary and may leak additional secrets present in the file to logs, model context, or downstream tools.
This skill requires an API key. The API key is stored in the ~/.upkuajing/.env file:
cat ~/.upkuajing/.env
Example file content:
The skill's setup flow directs the agent to check for and potentially manipulate a local secrets file, and to obtain a new API key that is automatically saved there. Combining credential discovery, issuance, and local persistence in a general-purpose skill increases the risk of secret leakage, unauthorized key creation, and expansion of long-lived credentials on the host.
### **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 interface (`auth.py --new_key`), the new key will be automatically saved to ~/.upkuajing/.env
Wait for user selection;
The documented workflow encourages checking a local .env file and storing newly created credentials in it, which creates unnecessary exposure of authentication material on disk. In this context the danger is elevated because the skill also performs networked, fee-incurring, and account-related actions, so compromise of the API key could enable unauthorized queries or account misuse.
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 interface (`auth.py --new_key`), the new key will be automatically saved to ~/.upkuajing/.env
Wait for user selection;
### **Account Top-up**
This file implements account provisioning, account info retrieval, pricing lookup, and recharge/payment flows inside a skill advertised as customs trade analytics. That mismatch materially expands the skill's privilege and commercial surface area beyond its stated purpose, creating an opportunity for unnecessary credential handling and financial actions that users would not reasonably expect from an analytics skill.
The function explicitly checks for and uses a local .env file to manage an API key, indicating direct credential access/handling within the skill. Handling secrets in ad hoc local files is risky because such files are often weakly permissioned, accidentally committed, or exposed through logs and support workflows.
"""
申请新的 API 密钥。
"""
# 检查是否已存在 .env 文件和 API key
env_file = UPKUAJING_ENV_FILE
if env_file.exists():
Reading the .env file contents into memory and parsing for an existing API key means the skill directly accesses stored credentials. This expands exposure of secrets to the script's execution context and any downstream error, debug, or output path that may inadvertently disclose credential material.
env_file = UPKUAJING_ENV_FILE
if env_file.exists():
# 读取现有的 .env 文件
try:
with open(env_file, 'r', encoding='utf-8') as f:
content = f.read()
The code writes the newly issued API key directly to a .env file, persisting a sensitive secret in plaintext on disk. Plaintext local persistence creates durable compromise risk from local malware, backups, shared accounts, accidental publication, or insufficient filesystem permissions.
"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")
Although this line is in an error message path, it reinforces manual environment-variable based secret handling and returns the env file path to the caller. Exposing secret storage locations can aid follow-on attacks or increase the chance that operators mishandle credentials outside protected channels.
except IOError as e:
return {
"success": False,
"message": f"API密钥申请成功,但保存到 .env 文件失败:{str(e)}。\n请手动设置环境变量 {API_KEY_ENV}。",
"envFilePath": str(env_file)
}
The recharge-order function creates payment URLs even though the skill's declared purpose is supply-chain and customs analysis. Embedding payment initiation in an unrelated skill increases the risk of deceptive monetization, accidental charges, or abuse if invoked by an agent or user who expected only data analysis behavior.
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'
# 日志开关
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
def load_env_file() -> Dict[str, str]:
"""
从 ~/.upkuajing/.env 文件加载环境变量。
Returns:
环境变量字典
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
def load_env_file() -> Dict[str, str]:
"""
从 ~/.upkuajing/.env 文件加载环境变量。
Returns:
环境变量字典
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
def load_env_file() -> Dict[str, str]:
"""
从 ~/.upkuajing/.env 文件加载环境变量。
Returns:
环境变量字典
The skill advertises executable behaviors that involve environment access, reading and writing files under the user's home directory, and making network requests, but it does not declare any explicit tool scope or permission boundaries. That creates an overbroad execution surface where a caller may invoke credential, telemetry, update-check, and billing-related actions without clear least-privilege constraints or user-visible authorization expectations.
The trigger language is broad and ambiguous, increasing the chance the skill is invoked in contexts broader than intended. Because this skill can touch credentials, billing/account flows, telemetry, and network operations, overbroad activation materially increases the chance of unintended sensitive actions being suggested or performed.
The documentation explicitly instructs sending request parameters, response payloads, and error context to a platform error-reporting endpoint, and only notes that sensitive fields are 'automatically masked' without defining scope, user notice, consent, or data-minimization controls. In a skill that processes trade/company data, these fields can easily contain customer queries, identifiers, business-sensitive records, or stack traces, creating a real privacy and confidentiality leakage risk if forwarded wholesale.
The file’s user-facing description and CLI help/messages are entirely in Chinese, which imposes a language choice on users with no opt-in or alternative locale. Under the policy, forcing a specific language without user choice is a natural-language policy violation unless clearly justified as region-specific.
The script applies for a new API key and persists it locally, but those actions are not justified by the skill's stated analytics purpose and introduce unnecessary secret lifecycle management. Unneeded credential creation and storage increase the attack surface for secret leakage, unauthorized API use, and user confusion about what the skill is allowed to do.
Using require_auth=False on the key-creation endpoint normalizes an insecure security posture for a sensitive operation. In the context of a trade-analytics skill, this is especially unjustified because credential issuance is ancillary behavior and should be more tightly controlled, not less.
pass # 如果读取失败,继续执行
# 不需要认证申请新密钥
response = make_request('/agent/auth/create', {}, require_auth=False)
# 检查是否申请成功
if response.get('code') != 0:
Using require_auth=False on the key-creation endpoint normalizes an insecure security posture for a sensitive operation. In the context of a trade-analytics skill, this is especially unjustified because credential issuance is ancillary behavior and should be more tightly controlled, not less.
pass # 如果读取失败,继续执行
# 不需要认证申请新密钥
response = make_request('/agent/auth/create', {}, require_auth=False)
# 检查是否申请成功
if response.get('code') != 0:
This code file's docstring and surrounding user-facing text are written exclusively in Chinese, and later functions emit Chinese-only messages to users. The file does not indicate that language is configurable or that the skill is intentionally restricted to a Chinese-speaking or region-specific audience, which can violate a language/locale policy requiring user opt-in or documented justification.
The manifest describes pulling and mapping customs trade data by company ID, which implies remote querying and analysis. This shared utility code also persists request/response logs under ~/.upkuajing/logs and later task-management helpers write task metadata/results to local files, introducing local data-storage behavior not reflected in the description.