T09 · Insecure Skill Coding Practices
- Location
scripts/config.py:52- Finding
API Key Persisted in a Plaintext File Without Enforced Access Restrictions
- Content
View full analysis
- Remediation
View remediation
Security audit
Security checks for vulnerabilities and agentic risk
The skill appears to be an API-backed e-bike image detector, but it has stale unrelated Gaokao configuration and saves an API key in plaintext, so users should review it before installing.
Install only if you are comfortable using the Xiaobenyang remote API, uploading image content or image URLs to that service, and storing XBY_APIKEY in a plaintext .env file. Prefer a scoped/revocable key, avoid sensitive images, and ask the publisher to remove the Gaokao remnants, document data handling, and lock dependencies.
scripts/config.py:52API Key Persisted in a Plaintext File Without Enforced Access Restrictions
requirements.txt:1Dependencies Are Not Locked to Reviewed Versions
The skill claims to perform electric-bicycle detection, yet the described code path reads/writes API keys, accesses environment variables, and includes unrelated remote-service configuration fields such as base_url and mcp_id. That indicates the skill is effectively a generic remote-service wrapper, which can mislead users into providing secrets and data under false expectations about functionality and scope.
The skill claims to perform electric-bicycle detection, yet the described code path reads/writes API keys, accesses environment variables, and includes unrelated remote-service configuration fields such as base_url and mcp_id. That indicates the skill is effectively a generic remote-service wrapper, which can mislead users into providing secrets and data under false expectations about functionality and scope.
The file’s configuration and identifiers clearly target an unrelated gaokao/XBY API integration rather than the declared electric-bicycle image detection skill. This mismatch is a strong supply-chain integrity issue: users may install a skill expecting local/object-detection behavior while it is actually wired to an external service with different data flows and trust boundaries.
Docstrings and comments describe a gaokao skill, contradicting the manifest’s electric bicycle detection intent. This inconsistency undermines provenance and reviewability, and in the context of a skill package it increases the risk that hidden or unintended capabilities are being shipped under a misleading description.
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",
)
This code explicitly opens and parses the .env file to extract a specific API key outside the normal settings abstraction. That custom secret-reading path bypasses safer centralized handling, makes secret access less transparent, and is more dangerous in this skill because the external credential is unrelated to the declared purpose.
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")
The existence check gates direct reading of a local .env file that may contain sensitive credentials, reinforcing a custom secret-access path. In isolation it is minor, but as part of the surrounding logic it contributes to unsafe and unjustified credential handling within a misleading skill package.
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 expressly designed to save an API key into a .env file, creating durable local storage of sensitive credentials. Persisting secrets in a project-local plaintext file significantly increases risk of credential theft through source-control mistakes, local compromise, backups, or unintended redistribution of the skill package.
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 declares no explicit tool scope or permission boundaries despite requiring environment access, file read/write, and network connectivity. In an agent setting, this expands the blast radius: the model is instructed to collect a secret from the user, persist it to disk, and invoke remote services without any declared least-privilege constraints.
The skill’s natural-language instructions, headings, and user-facing guidance are entirely in Chinese, and there is no indication that users may interact in another language or opt into this locale. Under the policy rule, forcing a specific language without user choice is a natural-language policy violation unless the locale constraint is documented and justified.
L037 的示例调用是 search_schools(...),而该技能声明的唯一用途是电动自行车检测;这不是简单的文档不全,而是把完全不同领域的工具调用示例放进了当前技能说明中。结合 L091 的项目目录名 xiaobenyang_gaokao_skill/,文档明显残留了另一个“高考/学校查询”技能的内容,主动误导了实现意图。
The skill includes functionality to persist and manage an external API credential even though that need is not justified by the stated image-detection purpose. Unnecessary secret handling expands the attack surface, can lead to credential leakage via local files or process environment, and is especially concerning given the broader manifest/code mismatch.
The code writes the API key into a local .env file and mirrors it into the process environment without any user-facing consent, warning, or storage-hardening. This can expose credentials through filesystem access, accidental check-in, backups, logs, or other code running in the same process environment.
The function sends a user-supplied image URL to an external API endpoint via call_api, but this file provides no disclosure, consent handling, or indication that image content may leave the local environment. Because images can contain personal or sensitive information, undisclosed third-party transmission creates a real privacy and data-governance risk even if the functionality is expected.
This function transmits raw base64-encoded image data to an external API, which is a direct transfer of potentially sensitive image content. The lack of explicit warning or consent in the code path increases privacy risk, since users may not realize full image contents are being exfiltrated to a remote processor.
L091 将项目根目录写为 xiaobenyang_gaokao_skill/,但整份清单和工具说明都在描述电动自行车检测。这种文档内容会让读者误解代码所属能力和边界,属于文档意图与当前技能实际定位相冲突。
The dependency uses a lower-bound version specifier instead of an exact pin, which makes builds non-reproducible and can silently pull in different releases over time. This increases supply-chain risk and makes it impossible to verify whether a known-vulnerable or breaking version of requests will be installed in a given environment.
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 published advisories, so there is no way to determine from this file whether the deployed version is affected. This uncertainty is a real supply-chain weakness because vulnerable versions could be installed depending on resolution time and environment.
Using pydantic>=2.7.0 allows any newer version to be installed, which weakens reproducibility and complicates assurance that only vetted versions are used. In practice this can expose deployments to newly introduced vulnerabilities or incompatible releases without code changes.
requests>=2.31.0
pydantic>=2.7.0
pydantic-settings>=2.2.0
python-dotenv>=1.0.1
Because pydantic is not pinned and has known advisories, the requirements file cannot guarantee that an unaffected version will be installed. This leaves security status unverifiable and can result in accidental deployment of a vulnerable release.
The unpinned pydantic-settings dependency permits uncontrolled upgrades at install time. That creates a supply-chain hygiene issue because the actual installed version may vary across systems and may include vulnerable or behavior-changing releases.
requests>=2.31.0
pydantic>=2.7.0
pydantic-settings>=2.2.0
python-dotenv>=1.0.1
The unpinned pydantic-settings dependency has at least one advisory associated with it, but this manifest does not establish whether the installed version is affected. That ambiguity is risky in environments that may process local configuration or secrets, because vulnerable versions could be introduced without visibility.
python-dotenv is specified with a minimum version only, so installations are not deterministic and may resolve to unexpected future releases. This can increase exposure to known or newly introduced package flaws and makes incident triage harder.
requests>=2.31.0
pydantic>=2.7.0
pydantic-settings>=2.2.0
python-dotenv>=1.0.1
python-dotenv has known advisories, and the lack of an exact version pin means the actual installed release cannot be verified from the manifest. This is a supply-chain control gap that could allow a vulnerable version to be installed, especially in automated or fresh deployments.