T09 · Insecure Skill Coding Practices
- Location
SKILL.md:39- Finding
API Key Exposed Through Documented Terminal Command
- Content
View full analysis
- Remediation
View remediation
Security audit
Security checks for vulnerabilities and agentic risk
This skill performs the advertised employee lookup, but it also handles plaintext API keys, account payments, diagnostic reporting, and an undisclosed version-check cache in ways users should review carefully.
Install only if you are comfortable using UpKuaJing's paid API for employee-profile data. Do not print or paste your API key, review any top-up/payment action before approving it, and avoid sending raw prompts, employee data, tokens, or full responses in error reports.
SKILL.md:39API Key Exposed Through Documented Terminal Command
scripts/auth.py:65Credential File Is Created Without Explicitly Restrictive Permissions
requirements.txt:1Open-Ended Dependency Version Allows Unreviewed Future Releases
The finding indicates undocumented update/version checking, cache reads/writes, and contact with version APIs unrelated to employee lookup. Even if benign, hidden network and filesystem behaviors expand the attack surface and can enable tracking, data persistence, or execution flows users did not expect from the stated function.
The finding indicates undocumented update/version checking, cache reads/writes, and contact with version APIs unrelated to employee lookup. Even if benign, hidden network and filesystem behaviors expand the attack surface and can enable tracking, data persistence, or execution flows users did not expect from the stated function.
The finding indicates undocumented update/version checking, cache reads/writes, and contact with version APIs unrelated to employee lookup. Even if benign, hidden network and filesystem behaviors expand the attack surface and can enable tracking, data persistence, or execution flows users did not expect from the stated function.
The skill instructs reading a home-directory .env file containing an API key. Direct access to local credential files is dangerous because it normalizes secret exposure and can lead to accidental disclosure of the key contents to the model, logs, or downstream tooling.
This skill requires an API key. The API key is stored in the ~/.upkuajing/.env file:
cat ~/.upkuajing/.env
Example file content:
The workflow asks the agent to check for and potentially populate credentials in ~/.upkuajing/.env, including obtaining a new key and auto-saving it locally. This combines credential discovery, creation, and persistence in a user-home path, which can expose secrets and exceeds the expectations of a simple data-lookup skill.
### **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 skill explicitly encourages manual editing and automatic saving of API keys into a local .env file. Persisting secrets in predictable plaintext locations increases the risk of credential theft by other tools, users, logs, backups, or future prompts that read local files.
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**
The file implements authentication and billing management features even though the skill is ներկայացված as an employee-profile lookup tool. This scope mismatch is dangerous because it expands the skill's authority and attack surface beyond user expectations, enabling account provisioning and financial operations that are unrelated to the declared purpose.
The code explicitly targets a local .env file for credential handling, signaling disk-based secret persistence. Storing API keys in plaintext configuration files is dangerous because such files are commonly exposed through backups, misconfigured permissions, logs, or accidental source control inclusion.
"""
申请新的 API 密钥。
"""
# 检查是否已存在 .env 文件和 API key
env_file = UPKUAJING_ENV_FILE
if env_file.exists():
Reading an existing .env file to inspect stored API keys confirms the skill relies on local plaintext secret material. This pattern is risky because any process with file access can harvest the credential, and the code normalizes secret retrieval outside stronger platform-managed secret controls.
env_file = UPKUAJING_ENV_FILE
if env_file.exists():
# 读取现有的 .env 文件
try:
with open(env_file, 'r', encoding='utf-8') as f:
content = f.read()
Writing the newly created API key directly to a .env file stores a sensitive credential in plaintext on disk. This is dangerous because it creates a durable secret artifact that may be readable by other users, captured by backups, or accidentally committed or exfiltrated.
"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 failure path instructs users to set the API key manually as an environment variable, continuing an insecure pattern of ad hoc secret handling. While environment variables can be acceptable in some deployments, treating them as a fallback without secure handling guidance increases the chance of exposure through process listings, shell history, crash reports, or CI logs.
except IOError as e:
return {
"success": False,
"message": f"API密钥申请成功,但保存到 .env 文件失败:{str(e)}。\n请手动设置环境变量 {API_KEY_ENV}。",
"envFilePath": str(env_file)
}
Creating recharge orders is unrelated to employee search and introduces a financial operation into a data-retrieval skill. That makes the skill more dangerous because a user or downstream agent invoking it for lookup purposes could unexpectedly trigger payment-related actions or expose billing endpoints.
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:
环境变量字典
This code adds an error-telemetry pathway that reports Agent/Skill execution failures to a platform endpoint, even though the stated skill purpose is overseas employee-profile lookup. Because the payload includes operational metadata and context, it creates a secondary data flow unrelated to the business function and can expose sensitive request details during failures.
The function sends requestId, requestPath, and especially free-form context to a remote '/agent/skill/error/report' endpoint without any visible filtering or justification tied to the skill’s business purpose. If context contains prompts, user data, employee data, tokens, or internal errors, those details may be exfiltrated to an external service during exception handling.
The skill declares capabilities that involve environment variables, filesystem access, and network calls, but it does not constrain tool scope with explicit permissions or allowed-tools. In practice, this gives the skill broad operational latitude to read local secrets, modify files, and contact remote services without clear least-privilege boundaries.
The trigger phrases are broad enough to activate on common recruiting, business analysis, and lead-generation requests. In this context, that increases the chance of the skill being invoked opportunistically to pull employee profile data in situations where the user did not explicitly request this specific data source or understand the privacy implications.
The skill is designed to retrieve employee profile information for recruitment and B2B lead generation but does not present a clear privacy warning or data-handling notice. Because the subject matter is employee data and decision-maker identification, the lack of notice makes misuse and over-collection more likely.
The API documentation explicitly permits uploading raw request parameters, response bodies, and exception context for debugging, which commonly contain personal data, internal identifiers, tokens, or other sensitive business information. Although it notes that sensitive fields are 'automatically desensitized,' it does not define what is scrubbed, require minimization, or warn operators not to include secrets/PII, creating a realistic risk of oversharing employee or customer data into centralized logs.
The module docstring and all user-facing help/messages in this file are written only in Chinese, which imposes a specific language on users. The policy allows fixed locale behavior only when the constraint is explicitly justified or when users are given a choice, neither of which appears here.
The code provisions new API keys and persists them locally in a .env file, which is beyond the stated recruiting/lead-generation purpose. Unnecessary credential creation and storage increase the risk of secret leakage, unauthorized reuse, and privilege expansion if the host or workspace is compromised.
Using require_auth=False for key creation establishes an unsafe default on a sensitive operation. In the context of a skill that should only perform employee-profile lookup, this makes the mismatch more dangerous by permitting unauthenticated credential provisioning through an unexpectedly broad interface.
pass # 如果读取失败,继续执行
# 不需要认证申请新密钥
response = make_request('/agent/auth/create', {}, require_auth=False)
# 检查是否申请成功
if response.get('code') != 0:
Detected: suspicious.exposed_secret_literal