T09 · Insecure Skill Coding Practices
- Location
scripts/config.py:47- Finding
API Key Persisted in an Unprotected Plaintext Environment File
- Content
View full analysis
- Remediation
View remediation
Security audit
Security checks for vulnerabilities and agentic risk
This looks like an API-backed image recognition skill, but it needs Review because it stores API keys in plaintext and has mismatched, under-scoped configuration that can affect where credentials and images are sent.
Review before installing. Use only non-sensitive images unless you accept sending them to Xiaobenyang's API, avoid running it in shared workspaces, check for XBY_GAOKAO_BASE_URL overrides, and rotate the API key if the .env file may have been exposed. The publisher should remove the Gaokao copy-paste remnants, validate the API origin, and replace plaintext .env persistence with a safer credential mechanism.
scripts/config.py:47API Key Persisted in an Unprotected Plaintext Environment File
scripts/call_api.py:49Configurable API Destination Can Receive Authentication Credentials and Image Data
The skill claims to be an image-recognition skill, but the documentation references .env persistence, environment mutation, sensitive credential handling, and even configuration associated with a different 'gaokao' skill package. That inconsistency strongly suggests copy-paste reuse or mispackaging, which can lead to secret leakage, unintended behavior, and reviewers approving a skill under false assumptions.
The skill claims to be an image-recognition skill, but the documentation references .env persistence, environment mutation, sensitive credential handling, and even configuration associated with a different 'gaokao' skill package. That inconsistency strongly suggests copy-paste reuse or mispackaging, which can lead to secret leakage, unintended behavior, and reviewers approving a skill under false assumptions.
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 post-init hook forcefully reads XBY_APIKEY from .env, implementing custom secret extraction logic outside normal configuration handling. This increases risk by normalizing plaintext secret use and making the skill actively seek credentials from local storage, which is unnecessary and suspicious for the stated object-recognition 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")
Creating a Path to .env here is part of the custom credential access flow used to retrieve secrets from local plaintext storage. In this skill context, that behavior is unjustified and contributes to credential exposure risks if the file is readable by other users, included in backups, or mishandled in development workflows.
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 image-recognition skill includes code to persist, retrieve, and globally manage API credentials, which is unrelated to the declared functionality and expands the attack surface. In the context of a simple recognition skill, local credential management is an unjustified capability that can expose secrets to other local processes, accidental commits, or reuse by unrelated code.
The function explicitly writes the API key into a local .env file, creating plaintext secret persistence on disk. For a vision labeling skill, this is more dangerous because users would not reasonably expect local secret storage, increasing risk of credential leakage through filesystem access, backups, logs, or source-control mistakes.
This function is dedicated to saving an API key into .env, directly enabling plaintext local secret persistence. Because the declared skill is only for image label recognition, this credential-storage capability is unnecessary and materially increases the chance of secret disclosure or misuse.
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 requests and appears to use sensitive capabilities such as environment access, local file read/write, and network access, but does not declare any explicit tool scope or permission boundaries. This weakens reviewability and least-privilege enforcement, making it easier for a seemingly simple image-recognition skill to exercise broader capabilities than users would expect.
The skill asks users to provide image URLs or base64 image data but does not clearly warn that this content will be transmitted to an external API service. This is a privacy and data-handling risk because users may submit sensitive images or internal URLs without informed consent, especially given the skill's benign-sounding image-labeling purpose.
The docstring states the configuration is for a different skill ('高考') than the declared image-recognition skill, indicating code reuse or mismatch between manifest and implementation. That inconsistency undermines trust, makes review harder, and increases the likelihood that hidden or unintended functionality—such as unrelated credential handling—has been carried over.
The code persists an API key to .env without any user-facing notice, consent flow, or storage warning. Even if the key is legitimately needed, silently storing credentials in plaintext can surprise users and lead to unsafe operational handling, especially in consumer or shared environments.
This function sends a user-supplied image URL to an external API for processing, but the file provides no user-facing disclosure, consent flow, or privacy warning. Images and image URLs can contain sensitive personal or internal data, so silent transmission to a third party creates a real privacy and data-handling risk.
This function transmits raw base64-encoded image content to an external API without any visible warning or consent mechanism in the file. Because the full image bytes are forwarded directly, this may expose sensitive visual content or embedded metadata to a third party without adequate transparency.
The skill documentation says this skill is for image object recognition, and the listed tools are general_recognition and general_recognition_for_data_base64. However, the mandatory workflow example tells the model to call scripts.tools.search_schools(...), which belongs to a different domain and contradicts the stated toolset and intent of this skill.
The manifest and the rest of the file describe a 万物识别 image-labeling skill, but the project structure block identifies the package as xiaobenyang_gaokao_skill, which suggests a gaokao/school-related skill. This is an active documentation contradiction rather than a mere omission, and it can mislead agents about the skill's true scope and tooling.
The dependency is specified with a lower bound only, so builds may resolve to different versions over time, including versions with known security defects or breaking behavior. In a skill that likely makes outbound HTTP requests for image recognition, leaving requests unpinned weakens supply-chain control and makes it harder to verify whether vulnerable versions are excluded.
requests>=2.31.0
pydantic>=2.7.0
pydantic-settings>=2.2.0
python-dotenv>=1.0.1
Requests has multiple known advisories, but because the manifest does not pin a version, it is impossible to verify from this file whether the installed release contains the fixes. In a network-facing image recognition skill, any vulnerable HTTP client version could affect credential handling, TLS behavior, or request safety depending on usage elsewhere in the code.
Using pydantic>=2.7.0 without an exact version allows non-deterministic installs and makes it unclear which security fixes are actually present at deployment time. Because pydantic often processes untrusted input, unresolved version drift can expose the skill to parser or validation bugs in later or intermediary releases.
requests>=2.31.0
pydantic>=2.7.0
pydantic-settings>=2.2.0
python-dotenv>=1.0.1
Pydantic has known advisories and the current requirement does not establish which concrete version is installed, so the deployment may inadvertently include a vulnerable release. This is especially relevant if the skill validates untrusted API input or configuration data, where parsing flaws can become denial-of-service or validation-bypass issues.
The pydantic-settings package is also unpinned, so environments may install different releases with different security properties. For configuration-loading components, this creates uncertainty around protections for secrets and file-handling behavior.
requests>=2.31.0
pydantic>=2.7.0
pydantic-settings>=2.2.0
python-dotenv>=1.0.1
The advisory status of pydantic-settings cannot be verified because no exact version is pinned. If the skill relies on settings or secret-loading features, a vulnerable version could affect configuration integrity or secret file handling.
python-dotenv>=1.0.1 permits any later release, preventing reproducible builds and making it difficult to prove that vulnerable versions are not deployed. Since dotenv tooling reads and may modify environment/config files, version ambiguity can matter for file-handling and secrets exposure risks.
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 unpinned requirement means the actual installed version cannot be validated from this manifest. If dotenv file manipulation features are used, vulnerable versions may enable unsafe file writes or symlink-related abuse in certain environments.