T09 · Insecure Skill Coding Practices
- Location
scripts/config.py:11- Finding
Configurable API Endpoint Can Redirect and Disclose Credentials
- Content
View full analysis
- Remediation
View remediation
Security audit
Security checks for vulnerabilities and agentic risk
The skill appears to be a real MCP registry search helper, but it stores a user API key in plaintext and can send that key to a configurable API endpoint, so it needs review before installation.
Install only if you trust the publisher and are comfortable providing a XiaoBenYang API key. Prefer using a temporary or low-privilege key, check that .env is not committed or shared, and verify that XBY_GAOKAO_BASE_URL cannot be changed to an untrusted endpoint before using the skill.
scripts/config.py:11Configurable API Endpoint Can Redirect and Disclose Credentials
scripts/config.py:43API Key Is Persisted in Plaintext Without Enforced Restrictive Permissions
The skill claims to perform registry search, but it also reads local .env files, modifies environment variables, and persists API keys and other configuration values. Secret collection and local configuration mutation are materially more sensitive than search, and the mismatch can lead to unjustified credential harvesting or unintended persistence on the host.
The skill claims to perform registry search, but it also reads local .env files, modifies environment variables, and persists API keys and other configuration values. Secret collection and local configuration mutation are materially more sensitive than search, and the mismatch can lead to unjustified credential harvesting or unintended persistence on the host.
The documentation says this is an MCP registry search tool, but the project structure and examples reference unrelated gaokao/school search code. Such cross-domain inconsistency is a strong signal that the skill may be mislabeled, copied from another project, or hiding behavior not apparent from the declared purpose, which undermines trust and safe review.
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
model_config = SettingsConfigDict(
env_prefix="XBY_GAOKAO_",
env_file=".env",
env_file_encoding="utf-8",
extra="ignore",
)
The custom model_post_init force-reads .env to extract XBY_APIKEY outside normal settings handling, which unnecessarily broadens secret-processing logic. This increases the chance of mishandling credentials, bypassing expected configuration controls, and normalizing direct secret scraping from workspace files.
default_year: int = 2025
def model_post_init(self, __context):
# 强制从 .env 文件读取 XBY_APIKEY
env_path = Path(".env")
if env_path.exists():
content = env_path.read_text(encoding="utf-8")
Opening and reading the .env file to manually parse an API key creates direct plaintext secret access in application code. In the context of a search tool, this is unnecessary secret-handling behavior and increases the risk of accidental leakage or abuse if the code path is triggered unexpectedly.
def model_post_init(self, __context):
# 强制从 .env 文件读取 XBY_APIKEY
env_path = Path(".env")
if env_path.exists():
content = env_path.read_text(encoding="utf-8")
for line in content.splitlines():
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
if line.startswith("XBY_APIKEY="):
self.api_key = line.split("=", 1)[1].strip()
break
# 如果环境变量有值,覆盖 .env 的值
env_val = os.getenv("XBY_APIKEY", "")
if env_val:
self.api_key = env_val
This function is explicitly designed to persist an API key to a local .env file, creating durable plaintext storage of a secret. In a skill whose declared role is service search, this capability is unnecessarily dangerous because it can expose credentials to other local users, accidental commits, backups, and compromised tooling.
def save_api_key_to_env(api_key: str) -> bool:
"""将API key保存到.env文件"""
try:
env_path = Path(".env")
lines = []
if env_path.exists():
lines = env_path.read_text(encoding="utf-8").splitlines()
The skill advertises executable capabilities that include environment access, file read/write, and network use, but it does not declare any explicit tool scope or permission boundaries. In an agent setting, this increases the chance of over-broad execution and makes it difficult to constrain sensitive operations such as reading or persisting secrets.
The skill instructs the agent to ask the user for a third-party API key and persist it, even though simple registry search does not obviously justify credential collection. In this context, requesting and storing secrets expands the attack surface and creates risk of credential misuse, leakage, or collection under misleading pretenses.
The skill implements persistent API key storage and mutation logic in a local .env file, which exceeds the stated purpose of a server search tool and creates unnecessary secret-handling capability. Expanding a simple search skill to write credentials increases the attack surface and can expose secrets through local file access, backups, logs, or other tooling that reads .env files.
The code can both write a credential to disk and modify the process environment, capabilities that are not directly proportional to a search-only tool. Such secret-writing behavior can lead to unintended persistence, lateral exposure to other local components, and makes compromise of the host or workspace more valuable to an attacker.
Persisting an API key to a local .env file without any explicit warning or confirmation can surprise users and leaves sensitive material on disk. Secrets stored this way may be leaked through source-control mistakes, local backups, shared workspaces, or other tools that inspect environment files.
The dependency specification uses a lower-bound version constraint rather than pinning to an exact version, which makes builds non-reproducible and can result in different environments resolving to vulnerable or incompatible releases. In a network-facing tool that relies on HTTP requests, this increases supply-chain and patch-management uncertainty even though the file itself does not prove active exploitation.
requests>=2.31.0
pydantic>=2.7.0
pydantic-settings>=2.2.0
python-dotenv>=1.0.1
The manifest does not pin requests, and the package has multiple known advisories across its release history, so it is not possible to verify from this file alone that deployed environments will avoid affected versions. In a search tool likely making outbound HTTP requests, requests is operationally relevant, which makes unverifiable version selection a meaningful supply-chain risk.
Using an unpinned pydantic version allows installation of any later release that satisfies the minimum bound, reducing reproducibility and making it hard to verify whether known vulnerable versions are excluded. This is a supply-chain hygiene issue rather than direct malicious behavior in the skill content.
requests>=2.31.0
pydantic>=2.7.0
pydantic-settings>=2.2.0
python-dotenv>=1.0.1
Because pydantic is not pinned, the file does not establish whether installed versions avoid known advisories such as regex DoS or date parsing issues. This is especially relevant for tools that may parse user-controlled input or configuration, where vulnerable parsing behavior can become reachable.
The unpinned pydantic-settings dependency means downstream installs may pull in different releases over time, including versions with security defects or breaking changes. For configuration-loading libraries, that uncertainty can affect secret handling and environment parsing behavior.
requests>=2.31.0
pydantic>=2.7.0
pydantic-settings>=2.2.0
python-dotenv>=1.0.1
The pydantic-settings package has known advisory history, and without an exact version it is impossible to confirm whether the deployed build includes a fixed release. Since settings libraries may read secrets or filesystem-backed configuration, unresolved version ambiguity can matter in real deployments.
python-dotenv is declared with only a minimum version, so environments may resolve to different package versions with different security properties. Because dotenv libraries often interact with local configuration files and secrets, lack of pinning increases exposure to supply-chain and configuration-related risk.
requests>=2.31.0
pydantic>=2.7.0
pydantic-settings>=2.2.0
python-dotenv>=1.0.1
python-dotenv has known advisories and is not pinned here, so the security posture of installed environments cannot be verified from the manifest. Given that dotenv tooling can read and modify local environment files, vulnerable versions may affect secret integrity or local file safety if those features are used.
The class docstring is written only in Chinese, and other user-facing strings in the file are also Chinese-only. This can violate a language/locale policy when a skill is expected to be language-neutral or user-selectable but does not provide any opt-in or alternative locale handling.
The raised error message "API密钥未设置,请先调用 set_api_key()" is user-facing natural language and forces a specific language without any opt-in or locale negotiation. This is a policy concern if the skill should support broader audiences or respect user language preferences.
The function docstring and returned error message "API调用失败" are natural-language outputs exposed to callers, but they are fixed to Chinese with no documented locale restriction or user choice. That can violate organizational language policy for multilingual or default-English environments.
Settings 类的文档字符串写的是“小笨羊高考Skill配置”,而当前技能清单名称和描述均指向MCP注册服务搜索。该内联文档反映出代码来源于不同用途的技能,和当前代码意图说明存在明显不一致。