T09 · Insecure Skill Coding Practices
- Location
scripts/tools.py:88- Finding
Unredacted User Reasoning Is Transmitted to an External Service
- Content
View full analysis
- Remediation
View remediation
Security audit
Security checks for vulnerabilities and agentic risk
This skill is a Review item because it sends detailed reasoning content to a third-party API and stores the required API key in a local plaintext .env file with incomplete scoping and privacy disclosure.
Install only if you are comfortable sending the tool's reasoning inputs to Xiaobenyang's API and storing the API key in a local .env file. Do not submit secrets, regulated data, private code, or confidential business context through this skill unless the publisher documents retention, deletion, endpoint controls, and safer credential storage.
scripts/tools.py:88Unredacted User Reasoning Is Transmitted to an External Service
scripts/config.py:9Configurable API Origin Can Receive API Credentials and User Data
scripts/config.py:43API Key Is Persisted in a Plaintext File Without Enforced Access Permissions
The documentation claims iterative reasoning functionality, but the described implementation instead reads/writes a local .env file, manages XBY_APIKEY, and stores connection settings for an external service. That hidden configuration-management and secret-persistence behavior materially expands the attack surface and can expose secrets or modify local state under the guise of a harmless thought tool.
The documentation claims iterative reasoning functionality, but the described implementation instead reads/writes a local .env file, manages XBY_APIKEY, and stores connection settings for an external service. That hidden configuration-management and secret-persistence behavior materially expands the attack surface and can expose secrets or modify local state under the guise of a harmless thought tool.
The configuration is clearly for a different product/domain ('小笨羊高考', XBY_GAOKAO, xiaobenyang.com) than the declared sequential-thinking service. This mismatch is dangerous because it can cause the skill to silently connect to an unrelated external service and route user data or credentials outside the expected trust boundary, which is especially concerning for an MCP service whose stated purpose does not require this backend.
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 code explicitly and forcibly reads a specific API key from .env during post-initialization, bypassing normal settings abstractions and coupling the skill to local secret files. In the context of a mismatched skill, this increases the risk of unintended credential harvesting or use of locally stored secrets for an unrelated external service.
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 hardcoded Path('.env') for manual secret access is part of an unnecessary credential-access workflow in a skill that does not obviously need such behavior. This strengthens the possibility of unauthorized or non-transparent use of developer-local credentials, especially given the unrelated service branding and external endpoint.
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 dedicated to persisting API credentials into a .env file, creating durable local secret storage for a service whose stated purpose does not justify it. If exploited through local file access, repo inclusion, backups, or support bundles, the credential could be stolen and used against the linked third-party service.
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 even though the documented/project capabilities include environment access, local file read/write, and outbound network use. In an agent setting, missing scope declarations increases the chance of over-privileged execution, unexpected data access, and unreviewed secret handling, especially because the skill also instructs collecting and persisting an API key.
The skill is presented primarily in Chinese and labels requirements as mandatory, while not stating that users may interact in another language or choose their preferred locale. Under the policy, forcing a specific language without opt-in is a natural-language policy concern unless the locale restriction is explicitly justified.
The document simultaneously describes the skill as a sequential-thinking tool and mandates asking for and saving an API key, which signals conflicting implementation intent. That inconsistency raises the risk of covert credential collection or at minimum confusing secret-handling behavior in a context where users would not expect authentication or persistent storage.
The project structure and workflow reference unrelated gaokao-school-selection modules and examples, which indicates copy-paste drift and documentation inconsistency. In security terms, this can mislead reviewers about what code is actually being invoked, hide unintended external integrations, and cause operators to trust the skill under false assumptions.
This module reads, stores, and updates a third-party API key even though the advertised skill is a sequential-thinking service, not an external API proxy. Unnecessary credential management expands the attack surface, increases the chance of secret exposure, and suggests hidden external service dependence that users would not reasonably expect from the manifest.
The function writes the supplied API key directly into a local .env file and updates process environment state without any user-facing confirmation, warning, or consent flow. Persisting secrets to disk by default can expose them through backups, repository accidents, local file disclosure, or shared workstation access.
The tool docstring explicitly instructs the agent to generate, verify, and repeat chain-of-thought reasoning, which encourages disclosure and external handling of internal reasoning steps. In an MCP/tool setting, this can cause sensitive hidden reasoning or intermediate analysis to be transmitted via tool arguments or API calls, increasing prompt leakage and data exposure risk.
The dependency is specified with a lower bound only (requests>=2.31.0), which makes builds non-reproducible and allows future installs to pull in unexpected versions. This increases supply-chain risk and makes it hard to verify whether a deployed environment includes a vulnerable or incompatible release.
requests>=2.31.0
pydantic>=2.7.0
pydantic-settings>=2.2.0
python-dotenv>=1.0.1
requests has multiple known advisories, and because the manifest does not pin a specific version, it is impossible to verify from this file alone whether the installed release is affected. In a network-capable service, uncertainty around an HTTP client library is meaningful because vulnerable versions could expose credentials, TLS/session handling, or request-processing weaknesses.
The dependency is unpinned (pydantic>=2.7.0), so installations may resolve to different versions over time. That weakens reproducibility and can expose the project to newly introduced vulnerable releases or breaking behavior without any code changes.
requests>=2.31.0
pydantic>=2.7.0
pydantic-settings>=2.2.0
python-dotenv>=1.0.1
pydantic has known advisories, but the unpinned requirement prevents verification of whether deployed instances use a safe version. Given this skill likely processes structured input, a vulnerable parsing or validation library could affect availability or input-handling safety if an affected version is resolved.
Using pydantic-settings>=2.2.0 without an exact pin means the installed version is not deterministic. This can lead to accidental adoption of insecure or incompatible versions and complicates incident response when trying to determine affected deployments.
requests>=2.31.0
pydantic>=2.7.0
pydantic-settings>=2.2.0
python-dotenv>=1.0.1
pydantic-settings has at least one advisory, and the open version specifier makes the installed version unverifiable. If the service reads secrets or configuration from the filesystem, an affected version could increase risk around secret source handling depending on runtime usage.
The requirement python-dotenv>=1.0.1 is not pinned, so package resolution may select any later release. This creates avoidable supply-chain uncertainty and makes it difficult to prove that known-bad versions are not being installed.
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 dependency means the manifest does not demonstrate that a safe version will be installed. Because dotenv libraries often interact with local configuration files, vulnerable versions can matter if the application loads or modifies .env files in attacker-influenced paths.
The class docstring contains a Chinese-only description, which may indicate a fixed language choice without offering the user a locale or language option. Under the policy, language constraints should be optional or clearly justified rather than implicitly enforced.