Back to skill

Security audit

医药知识图谱

Security checks for vulnerabilities and agentic risk

Overview

The skill appears to call an external medical graph API, but it stores the user's API key in a plaintext .env file and includes inconsistent references to an unrelated exam-admissions skill.

Install only if you are comfortable sending queries to the XiaoBenYang MCP API and storing that service API key in a local plaintext .env file. Prefer using a disposable or scoped API key, verify .env is not committed or shared, and review the stale gaokao/admissions references with the publisher before trusting this package for medical knowledge graph work.

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:46
Finding

API Key Stored in a Plaintext Environment File Without Enforced Access Controls

Content
View full analysis

Vulnerability Details

File Location: scripts/config.py, lines 46–65
Vulnerability Type: Plaintext credential storage with uncontrolled file permissions
Risk Level: Medium

Vulnerable Code

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

Technical Analysis

The function persistently writes the user-supplied API key into a plaintext .env file. Path.write_text() creates the file according to the process umask and does not explicitly enforce owner-only permissions. If the file already exists with permissive permissions, those permissions are retained.

The implementation also does not use an operating-system credential store, atomic secure file creation, or checks preventing the credential file from being committed to source control. Consequently, the credential may be exposed to other local accounts, backups, development tooling, or repository history.

Because the value is later used as the XBY-APIKEY authentication header when communicating with the configured API, disclosure permits reuse of the credential outside the Skill.

Attack Path

  1. A user supplies an API key through the documented Skill workflow.
  2. set_api_key() invokes save_api_key_to_env().
  3. The function writes `XBY_APIKEY=&lt ...[truncated 941 chars]
Remediation
View remediation

Remediation Suggestions

  1. Prefer an operating-system credential manager or a session-only environment variable instead of persistent plaintext storage.
  2. If file persistence is unavoidable, create the credential file atomically with owner-only permissions such as 0600.
  3. Explicitly correct permissions on an existing .env file before writing secrets to it.
  4. Reject carriage returns and newline characters in the supplied key to prevent additional environment-file entries from being injected.
  5. Add .env to .gitignore and provide only a secret-free .env.example.
  6. Avoid including credentials in logs, exceptions, backups, diagnostic output, or returned API data.
  7. Document credential rotation and revocation procedures in case the file is exposed.
  8. Consider storing only a reference to a credential managed by the deployment environment rather than the credential itself.
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
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (26)

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

A second and stronger mismatch exists because the skill instructs reading/writing .env, persisting API keys, and configuring generic remote-service settings, while the project structure references a gaokao skill unrelated to medical data. This inconsistency is a red flag for repurposed or mislabeled code and raises the chance that credentials are being collected for an unexpected backend or an unintended service domain.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding

A second and stronger mismatch exists because the skill instructs reading/writing .env, persisting API keys, and configuring generic remote-service settings, while the project structure references a gaokao skill unrelated to medical data. This inconsistency is a red flag for repurposed or mislabeled code and raises the chance that credentials are being collected for an unexpected backend or an unintended service domain.

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
84% confidence
Finding

The code manually opens and parses .env to extract XBY_APIKEY, bypassing the settings framework and directly handling credential material. This increases the attack surface for secret mishandling, normalizes direct secret scraping behavior, and makes it easier for unrelated code paths to read credentials outside the documented configuration model.

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
82% confidence
Finding

Creating a Path to .env and reading its contents as raw text is part of direct credential access logic rather than standard typed configuration loading. In the context of a skill whose purpose is unrelated to credential management, this is suspicious and increases the risk that secrets are accessed more broadly than necessary.

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

Credential Access

High
Category
Privilege Escalation
Confidence
93% confidence
Finding

This function writes API keys into .env, creating long-lived plaintext credential storage on disk. If the workspace is shared, backed up, logged, or accidentally committed, the external service credential can be stolen and abused, and the risk is elevated because the skill’s stated medical knowledge graph purpose does not clearly justify such secret management behavior.

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
93% confidence
Finding

The skill declares no explicit tool/permission scope even though it handles environment variables, local file reads/writes, and outbound network access. In practice this weakens least-privilege controls and makes it harder for users or a host platform to understand that the skill can persist credentials and contact external services.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The skill instructs the model to ask the user for an API key and persist it via local configuration without warning about storage, retention, access controls, or alternative secret-handling methods. Collecting and storing credentials this way can expose sensitive tokens to logs, local files, or other components, especially when combined with file-write capability.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

L037 要求调用 scripts.tools 中的函数时,示例却是 search_schools(score='520', province='北京', category='综合'),这与本技能声明的医药知识图谱查询能力明显不一致。该文档会主动误导调用方按错误的领域和参数组织请求,属于文档意图与实际工具集合相矛盾。

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

This code sends the full params payload to an external HTTP endpoint using requests.Session.post, which may include user or system data. There is no confirmation prompt, print/log disclosure to the user, or inline warning/comment indicating that provided data will be transmitted off-system.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
99% confidence
Finding

The docstring identifies the code as a different skill ('小笨羊高考Skill配置') than the manifest’s medical knowledge graph. This mismatch is a supply-chain integrity concern because it suggests code reuse from another project or deceptive packaging, making it harder for users and reviewers to understand what the skill actually does and whether hidden functionality is present.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The skill persists an external API key to a local .env file and exposes helper functions to set and retrieve it, which is inconsistent with the declared purpose of a medical knowledge graph and expands the trust boundary unnecessarily. While not exfiltrating the secret by itself, storing credentials in plaintext can expose them to other local processes, accidental commits, or misuse by unrelated code in the skill.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The function silently writes a supplied API key into .env without any explicit warning, consent, or visibility to the user. Persisting secrets in plaintext can lead to credential leakage through source-control mistakes, backups, shared workspaces, or other local access, especially when users may assume the key is only used in memory.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
82% confidence
Finding

Most instructional and user-facing content is written in Chinese, while the file does not state that language selection is optional or limited to a justified region-specific use case. Under the policy, forcing a specific language without opt-in can be a natural-language policy violation.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Low
Category
Not specified by scanner
Confidence
88% confidence
Finding

L090 展示的项目根目录为 xiaobenyang_gaokao_skill/,这与当前“医药知识图谱”技能的声明用途明显冲突。虽然这不直接证明代码行为异常,但属于文档层面的明显错配,反映出技能说明复用了其他项目内容。

Content

No source excerpt is available for this finding.

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
95% confidence
Finding

The dependency specifier requests>=2.31.0 is unpinned, so future installs may resolve to different versions over time. That weakens build reproducibility and can unintentionally introduce a vulnerable or breaking release from the supply chain.

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
89% confidence
Finding

requests has multiple known advisories, and because the manifest does not pin an exact version, it is impossible to verify whether installed environments are affected. This uncertainty is a supply-chain security issue because different deployments may resolve to different, potentially vulnerable releases.

Content

No source excerpt is available for this finding.

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
95% confidence
Finding

pydantic>=2.7.0 allows any later version to be installed, making deployments non-reproducible and increasing supply-chain risk. If a later release contains a security flaw or incompatible change, the environment may become vulnerable without any manifest change.

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
87% confidence
Finding

pydantic has published advisories, but the open-ended version specifier prevents determining whether the deployed version is safe. That leaves the project unable to make a reliable security claim about input validation and parsing behavior provided by this library.

Content

No source excerpt is available for this finding.

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
95% confidence
Finding

pydantic-settings>=2.2.0 is not version-pinned, so installations may pull different releases at different times. This creates uncertainty around security posture and can expose the project to newly introduced vulnerable versions.

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
85% confidence
Finding

pydantic-settings has a known advisory history, and the lack of exact version pinning makes the dependency state unverifiable. If the skill reads secrets or configuration from the filesystem, an affected version could increase exposure depending on runtime usage.

Content

No source excerpt is available for this finding.

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
95% confidence
Finding

python-dotenv>=1.0.1 permits any newer version, which undermines reproducibility and can silently introduce a vulnerable dependency. In applications that load local configuration files, dependency trust and deterministic builds are especially important.

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
86% confidence
Finding

python-dotenv has known advisories, and without exact pinning there is no assurance that installed environments avoid affected releases. Since this package commonly interacts with local .env files, vulnerable versions may matter if file writes or symlinked paths are involved elsewhere in the skill.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
80% confidence
Finding

The natural-language strings and comments in this file consistently assume Chinese, including the class docstring and configuration labels. Under the policy, forcing a specific language without opt-in can be a locale/language policy issue when no choice or justification is provided.

Content

No source excerpt is available for this finding.