T09 · Insecure Skill Coding Practices
- Location
SKILL.md:39- Finding
API Key Exposure Through Terminal and Agent Output
- Content
View full analysis
- Remediation
View remediation
Security audit
Security checks for vulnerabilities and agentic risk
This skill appears to support the advertised paid business-person lookup, but it needs review because it handles API keys unsafely and includes account, billing, and recharge actions beyond a simple lookup.
Review this skill before installing. Use it only if you trust the Upkuajing service and are comfortable with paid API calls, account lookups, and recharge-order creation. Do not run the documented cat command on the .env file or paste API keys into chat; prefer a protected environment variable or secret store and restrict ~/.upkuajing permissions. Confirm every paid query, recharge, new-key, and error-report action explicitly, and avoid sending raw personal data in error reports.
SKILL.md:39API Key Exposure Through Terminal and Agent Output
scripts/auth.py:61API Key File Is Created Without Explicit Owner-Only Permissions
scripts/auth.py:18New-Key Operation Can Destructively Truncate Existing Configuration
requirements.txt:1Unbounded Third-Party Dependency Reduces Supply-Chain Reproducibility
The skill is presented as a targeted personnel-history lookup, but it also describes local credential access, generic outbound API usage, local task/result handling, and possible request/response logging. Those side capabilities expand the data exposure surface beyond the stated business purpose and can lead to credential leakage or collection of unrelated sensitive artifacts.
The skill is presented as a targeted personnel-history lookup, but it also describes local credential access, generic outbound API usage, local task/result handling, and possible request/response logging. Those side capabilities expand the data exposure surface beyond the stated business purpose and can lead to credential leakage or collection of unrelated sensitive artifacts.
The skill is presented as a targeted personnel-history lookup, but it also describes local credential access, generic outbound API usage, local task/result handling, and possible request/response logging. Those side capabilities expand the data exposure surface beyond the stated business purpose and can lead to credential leakage or collection of unrelated sensitive artifacts.
The skill is presented as a targeted personnel-history lookup, but it also describes local credential access, generic outbound API usage, local task/result handling, and possible request/response logging. Those side capabilities expand the data exposure surface beyond the stated business purpose and can lead to credential leakage or collection of unrelated sensitive artifacts.
Referencing the local .env file as the place to obtain the API key is a concrete credential-access pattern. In this context, the danger is elevated because the skill text operationalizes secret retrieval for the agent, increasing the chance that credentials are exfiltrated, echoed back to users, or exposed in logs.
使用此技能需要API密钥。API密钥保存在 ~/.upkuajing/.env 文件中:
cat ~/.upkuajing/.env
文件内容示例:
The skill instructs checking for the API key in ~/.upkuajing/.env and supports writing a newly obtained key back into that file. That combines credential discovery with local secret persistence, expanding the risk of exposure, unauthorized modification, and accidental leakage through future file reads or backups.
### **未设置API密钥**
请先检查 `~/.upkuajing/.env` 文件是否有 UPKUAJING_API_KEY;
如果未设置 UPKUAJING_API_KEY API密钥,请提示并让用户选择:
1. 用户有,由用户提供(手动添加到 ~/.upkuajing/.env 文件)
2. 用户没有,你可使用接口进行申请(`auth.py --new_key`),申请到新密钥后,会自动保存到 ~/.upkuajing/.env
等待用户选择;
This section explicitly authorizes applying for a new API key and auto-saving it into a plaintext local .env file. In a skill whose stated purpose is person-history lookup, that is unnecessarily sensitive functionality and increases the blast radius from simple data access to credential issuance and storage compromise.
请先检查 `~/.upkuajing/.env` 文件是否有 UPKUAJING_API_KEY;
如果未设置 UPKUAJING_API_KEY API密钥,请提示并让用户选择:
1. 用户有,由用户提供(手动添加到 ~/.upkuajing/.env 文件)
2. 用户没有,你可使用接口进行申请(`auth.py --new_key`),申请到新密钥后,会自动保存到 ~/.upkuajing/.env
等待用户选择;
### **账户充值**
This script implements API key issuance, account inspection, recharge-order creation, and pricing lookup, which materially exceeds the declared skill purpose of retrieving personnel experience data. In an agent-skill context, hidden billing and credential-management capabilities expand the attack surface and can be abused to provision access or incur charges unrelated to the advertised function.
The function explicitly checks for and manages an API key in a .env file, indicating local credential handling inside the skill. In agent environments, local file-based secret storage increases exposure to accidental disclosure, weak file permissions, and downstream leakage through logs, backups, or tool chaining.
"""
申请新的 API 密钥。
"""
# 检查是否已存在 .env 文件和 API key
env_file = UPKUAJING_ENV_FILE
if env_file.exists():
The code reads the entire .env file to inspect existing credentials, which confirms direct access to locally stored secrets. Handling secrets as plain text broadens the blast radius if the host is shared, compromised, or if subsequent code accidentally exposes the file content.
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 script writes the issued API key directly to a .env file in plaintext. Plaintext persistence of newly created credentials is dangerous because other local users, processes, backups, or accidental repository inclusion can expose the secret and enable unauthorized API use.
"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")
Recharge-order creation and pricing-query functionality are not necessary to fulfill the stated business purpose of building contact employment timelines. In a hosted or agent-executed environment, such monetization features can be triggered unexpectedly, causing financial loss or facilitating unauthorized service consumption.
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:
环境变量字典
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 declares capabilities that imply environment access, local file read/write, and network use, but it does not declare any explicit tool scope or allowed-tools boundary. That weakens least-privilege controls and makes it harder for a host agent to constrain credential access, local file modification, and outbound requests to only what is necessary.
The skill name includes a 'zh' locale marker and the description and operating instructions are entirely in Chinese, but the file does not offer any user language choice or explain that the skill is intentionally region- or language-specific. This can violate language/locale policy because it effectively forces one language without user opt-in.
The instructions explicitly tell the operator to read a local .env file containing the API key and even provide the command and path. In an agent setting, that creates a direct credential-exposure path: the model may retrieve and surface secrets from local storage when only a service invocation was intended.
The skill includes account-management and monetization operations such as new API key application, account info lookup, and recharge order creation, which are not necessary for simply querying a person's employment history. In context, that makes the skill more dangerous because a user seeking lookup functionality could unintentionally trigger billing or credential lifecycle actions.
The API documentation explicitly allows reporting raw request parameters, response data, and exception context, and only mentions automatic masking without requiring callers to minimize or manually review sensitive content before submission. In this skill context, the data may include personal employment-history information and agent-side request/response payloads, so error reports can become a secondary channel for leaking personal, confidential, or regulated data into centralized logs.
This code’s user-facing natural-language descriptions and messages are written exclusively in Chinese, and there is no indication that users can opt into another language. Under the policy for natural-language constraints, forcing a specific language without user choice is a violation unless the locale limitation is clearly documented and justified.
The code can request a new API key and persist it locally, which is a privileged credential-management capability beyond the core data-retrieval task. Even if intended for convenience, embedding credential provisioning in the skill increases the chance of unauthorized account creation, secret sprawl, and misuse in automated environments.
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