T09 · Insecure Skill Coding Practices
- Location
scripts/call_api.py:53- Finding
API Credential Exfiltration Through a Configurable Upstream Endpoint
- Content
View full analysis
- Remediation
View remediation
Security audit
Security checks for vulnerabilities and agentic risk
The skill appears to be a timezone API wrapper, but it has mismatched Gaokao references and stores a user API key in a local plaintext .env file while forwarding requests to a configurable remote endpoint.
Install only if you trust the Xiaobenyang service and are comfortable with your API key being written to a local plaintext .env file. Review and restrict the configured base URL before use, avoid running it in shared or source-controlled directories, and prefer a version that removes the stale Gaokao references, pins dependencies, validates the API endpoint, and stores secrets in a safer location.
scripts/call_api.py:53API Credential Exfiltration Through a Configurable Upstream Endpoint
scripts/config.py:42API Key Persisted in a Plaintext File Without Enforced Access Controls
requirements.txt:1Non-Reproducible Dependencies Installed Through Open-Ended Version Ranges
The skill claims to provide timezone features, yet the document indicates API-key persistence and even references a gaokao-oriented project structure. A misleading facade around unrelated functionality is dangerous because it can conceal credential handling and external data flows that users would not reasonably expect from a time conversion skill.
The skill claims to provide timezone features, yet the document indicates API-key persistence and even references a gaokao-oriented project structure. A misleading facade around unrelated functionality is dangerous because it can conceal credential handling and external data flows that users would not reasonably expect from a time conversion skill.
This file implements a generic proxy to an upstream MCP service by accepting arbitrary mcp_id, tool_name, and params and forwarding them directly to the upstream API. In a skill advertised as timezone management, this creates a capability mismatch: if the caller can influence these fields, the skill can invoke unrelated upstream tools, potentially expanding privileges, exposing data, or triggering unsafe actions beyond the declared scope.
The call_tool method places the caller-supplied tool_name directly into the func header and forwards arbitrary JSON params, enabling invocation of any upstream tool by name. That is effectively a general remote action primitive; in the context of a timezone skill, it is unnecessarily broad and could be abused to access non-timezone functionality available to the configured API key.
The configuration is clearly inconsistent with the declared timezone-management purpose: it references a different service domain, a gaokao-oriented env prefix, and an unrelated remote MCP identifier. This mismatch strongly suggests code reuse or hidden functionality, which is dangerous because users may grant trust and credentials to a skill whose real behavior is outside its stated scope.
The docstring explicitly labels the file as configuration for a gaokao skill, contradicting the manifest's timezone-management identity. This kind of identity mismatch is a strong trust-boundary issue because it suggests the package may not do what it claims, increasing the risk of hidden or repurposed behavior.
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",
)
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 forcibly reads a specific API key from .env outside the standard settings mechanism, indicating deliberate credential collection for an unrelated service. In the context of a timezone skill, this makes secret access more suspicious because it exceeds user expectations and may facilitate unauthorized backend access.
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")
Reading a live environment variable to override the API key enables the skill to consume externally provided credentials for an unrelated backend service. In a mislabeled timezone tool, this broad secret intake increases the chance of misuse and makes hidden service coupling more dangerous.
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 persists credentials in plain text to .env, creating durable local exposure of a secret tied to an unrelated service. If the host is shared, backed up, or the repository is mishandled, the API key can be recovered and abused, making the skill substantially more dangerous than its declared timezone role suggests.
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 permissions while its documented behavior implies access to environment variables, file writes for credential persistence, and network/API calls. Missing scope boundaries increases the chance that an agent runtime grants broader capabilities than users expect, which is risky when the skill also handles secrets.
The skill instructs the agent to ask the user for an API key and persist it via set_api_key, but it does not warn the user that the credential will be stored or explain retention and protection. Collecting and storing secrets without transparent notice can lead to accidental disclosure, insecure persistence, and uninformed user consent.
The workflow example calls a school-search function unrelated to timezone conversion, showing the skill documentation is likely copied from another project. In security terms, inconsistent operational instructions make the true behavior of the skill ambiguous and can hide unintended or unauthorized tool usage.
The project structure still identifies the package as a gaokao skill, contradicting the claimed timezone-service identity. Such inconsistencies are a red flag for repackaged or mislabeled skills and make it harder for reviewers to trust what code and external services are actually involved.
The class docstring is written only in Chinese, and additional user-facing strings in the file are also Chinese-only. Under the policy, forcing a specific language without user opt-in is a natural-language policy violation unless the locale restriction is clearly documented and justified, which is not present here.
The raised error text is presented only in Chinese, which can force a specific language on users. The file does not indicate that the skill is region-specific or provide any opt-in or fallback language behavior.
The function docstring and failure message include Chinese-only text, and no language preference mechanism or scope limitation is documented. This creates a policy issue because the skill effectively enforces one language for user-facing communication.
This skill declares remote-service configuration and API credential handling even though timezone conversion normally does not require secret-backed external access. Unjustified credential collection expands the attack surface and creates a risk of covert data exfiltration, account abuse, or misleading users into supplying secrets to an unrelated backend.
The code persistently stores an API key into a local .env file, which is unnecessary for a simple timezone skill and creates long-lived credential exposure on disk. Stored secrets can be recovered by other local processes, accidentally committed to source control, or reused beyond the user's intent.
The function writes the API key to .env without any user-facing warning, confirmation, or explanation that the secret will be stored persistently. Silent credential persistence is risky because users may expect temporary use only, and the saved secret may later be exposed through filesystem access or accidental sharing.
All user-facing instructions and metadata are written in Chinese, and the document does not indicate that other languages are supported or let the user opt into a preferred language. Under the policy, forcing a specific language without opt-in can be a natural-language policy violation unless the locale restriction is clearly justified.
The dependency is specified with a lower bound only, so builds may resolve to different versions over time. This weakens reproducibility and makes it hard to determine whether a deployed version includes known security fixes or vulnerable releases.
requests>=2.31.0
pydantic>=2.7.0
pydantic-settings>=2.2.0
python-dotenv>=1.0.1
Requests has multiple published advisories, but because the manifest does not pin the package, it is impossible to verify whether the installed version is affected. In a network-facing time zone service, an HTTP client is likely to be used, so unresolved dependency versioning increases uncertainty around exposure.
Using an unpinned pydantic version allows environment-dependent resolution, which can introduce inconsistent behavior and uncertain exposure to vulnerable releases. This is a supply-chain hygiene issue rather than an immediate exploit by itself.
requests>=2.31.0
pydantic>=2.7.0
pydantic-settings>=2.2.0
python-dotenv>=1.0.1