Back to skill

Security audit

大都会博物馆

Security checks for vulnerabilities and agentic risk

Overview

This museum-data skill needs review because it requires and stores a XiaoBenYang API key and sends requests through a configurable third-party MCP service that is not clearly aligned with the advertised Met Museum purpose.

Install only if you are comfortable giving this skill a XiaoBenYang API key, having it stored in plaintext in the workspace .env file, and routing museum queries through the XiaoBenYang MCP service. Review or change the endpoint and credential-storage behavior before using it with any valuable or reusable API key.

Vulnerability Patterns
  • Insecure DependenciesIntroduces malicious components through unsafe dependency sources
  • 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
Findings (3)

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/config.py:43
Finding

API Key Persisted in a Plaintext File Without Secure File Controls

Content
View full analysis

Vulnerability Details

File Location: scripts/config.py:43-64
Vulnerability Type: Plaintext credential storage and unsafe file handling
Risk Level: Medium

python
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 supplied API key is stored in plaintext in a working-directory-relative .env file. The implementation does not create the file with restrictive permissions, validate existing permissions, reject symbolic links, or perform an atomic write.

Because Path(".env") is resolved relative to the process working directory, the destination may differ depending on how the Skill is launched. If an attacker can prepare that directory, an existing .env symbolic link can redirect the credential write to another file writable by the Skill process.

The credential is also copied into the process environment, making it accessible to code running within the same process and potentially to child processes created later.

Attack Path

  1. An attacker obtains read access to the working directory or prepares a malicious .env symbolic link before Skill execution.
  2. The Skill asks the user for an XBY_APIKEY.
  3. set_api_key() invokes the persistence logic.
  4. The key is written in plaintext without restrictive permission enforcement, or the symbolic link redirects the write.
  5. The attacker reads the key from the resulting file or causes an unintended file to be overwritten within the process's existing filesystem p ...[truncated 377 chars]
Remediation
View remediation

Remediation Suggestions

  • Prefer session-only credential handling or an operating-system credential manager rather than plaintext persistence.
  • Obtain explicit user consent before persisting a credential.
  • Resolve the credential file against a fixed, trusted application directory instead of the current working directory.
  • Reject symbolic links and verify that any existing destination is a regular file owned by the expected user.
  • Create the file atomically with owner-only permissions, such as mode 0600.
  • Avoid copying the key into the process environment unless required.
  • Ensure .env is excluded from source control, backups, logs, and diagnostic bundles.
  • Support credential rotation and deletion.

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/call_api.py:52
Finding

Configurable Upstream Endpoint Can Receive the API Key

Content
View full analysis

Vulnerability Details

File Location: scripts/config.py:10-16, scripts/call_api.py:52-70
Vulnerability Type: Credential exfiltration through an unrestricted configurable endpoint
Risk Level: Medium

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

base_url: str = "https://mcp.xiaobenyang.com"
python
url = f"{settings.base_url}/api"
mcp_id = mcp_id or settings.mcp_id

api_key = get_api_key()
if not api_key:
    raise UpstreamError("API key is not configured")

headers = {
    "XBY-APIKEY": api_key,
    "func": tool_name,
    "mcpid": mcp_id,
    "Content-Type": "application/json",
}

resp = self._session.post(
    url=url,
    headers=headers,
    data=json.dumps(params),
    timeout=settings.timeout_seconds,
)

Technical Analysis

SettingsConfigDict permits configuration fields to be overridden through prefixed environment variables and the .env file. Consequently, base_url can be replaced through configuration such as XBY_GAOKAO_BASE_URL.

The API client constructs its destination directly from settings.base_url and attaches the user's XBY-APIKEY header without validating the scheme or requiring the hostname to match the intended XiaoBenYang service. The HTTP session supports both HTTP and HTTPS adapters, so a configured plain-HTTP endpoint is also accepted.

TLS certificate verification remains enabled by default for HTTPS requests, but this does not protect the key when the configured hostname itself is attacker-controlled or when an HTTP URL is supplied.

Attack Path

  1. An attacker or compromised deployment modifies the process environment or the Skill's .env configuration.
  2. The attacker sets XBY_GAOKAO_BASE_URL to an attacker-controlled HTTP or HTTPS server.
  3. The user supplies an API key and invokes a museum tool.
  4. `cal ...[truncated 595 chars]
Remediation
View remediation

Remediation Suggestions

  • Pin credential-bearing requests to the exact expected origin, such as https://mcp.xiaobenyang.com.
  • Parse the URL and reject non-HTTPS schemes, embedded credentials, unexpected ports, and unapproved hostnames.
  • Do not attach XBY-APIKEY until the final request destination has passed origin validation.
  • If custom upstream endpoints are a legitimate requirement, require explicit user approval and use credentials scoped specifically to each endpoint.
  • Prevent untrusted project-local .env files from silently changing security-sensitive destinations.
  • Add tests confirming that HTTP URLs, lookalike domains, alternate ports, redirects to other origins, and user-information URL syntax are rejected.
  • Disable or carefully validate cross-origin redirects for requests carrying authentication headers.

T08 · Insecure Dependencies

Note
Location
requirements.txt:1
Finding

Dependencies Are Not Pinned to Reproducible Versions

Content
View full analysis

Vulnerability Details

File Location: requirements.txt:1-4
Vulnerability Type: Unbounded dependency resolution and supply-chain exposure
Risk Level: Low

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

Technical Analysis

Every dependency uses only a minimum-version constraint. There is no lockfile, exact version pin, or package-integrity hash. Separate installations can therefore resolve to different future versions that were not reviewed with this project.

This does not establish that any currently named package is malicious. It creates a supply-chain weakness in which an unexpected, compromised, or incompatible future release may be installed automatically.

Attack Path

  1. A future dependency release is compromised, malicious, or behaviorally incompatible.
  2. A user or deployment system installs the project using the current requirements file.
  3. The resolver selects that future release because it satisfies the lower-bound constraint.
  4. Package installation or subsequent import executes or exposes the unexpected behavior with the permissions of the installing or running process.

Impact Assessment

Impact depends on the behavior of the resolved package and the privileges of the installation or runtime environment. A malicious dependency could access files, environment variables, and the stored API key or perform network operations with the user's privileges. The current repository does not itself demonstrate such exploitation, so the finding is rated Low.

Remediation
View remediation

Remediation Suggestions

  • Pin dependencies to reviewed, exact versions.
  • Generate and commit a lockfile appropriate to the deployment workflow.
  • Require package hashes during installation, for example through a hash-locked requirements file.
  • Use automated vulnerability and dependency-update scanning.
  • Review updates before merging them rather than automatically accepting every future compatible version.
  • Install dependencies from a trusted package index and disable unneeded alternate indexes.
  • Rebuild lockfiles regularly so security patches can be adopted in a controlled and reproducible manner.
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 (27)

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

The declared purpose is harmless museum data retrieval, but the instructions and project structure introduce credential collection, persistence to .env, environment mutation, and references to an unrelated gaokao/xiaobenyang service. This mismatch is a strong indicator of deceptive packaging: a user may consent to a low-risk public-data skill while actually enabling secret capture or unrelated external service interaction.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

High
Category
Not specified by scanner
Confidence
98% confidence
Finding

The manifest says this skill queries and retrieves open collection data for the Metropolitan Museum, but this file defines settings for a different service: the docstring names '小笨羊高考Skill', the environment prefix is 'XBY_GAOKAO_', and the defaults point to 'mcp.xiaobenyang.com'. This indicates the implemented behavior/configuration is for another product entirely, not the declared museum-data purpose.

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

This function explicitly writes the API key into a plaintext .env file, creating durable local secret exposure. If the workspace is shared, backed up, synced, logged, or accidentally committed, the credential can be compromised and reused to access the associated backend service.

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

The skill declares no explicit tool scope or permission boundaries even though its documented/project capabilities imply environment access, file read/write, and network use. In an agent setting, missing scope declarations increase the chance of over-privileged execution and make it harder to constrain misuse or review whether credential handling and network access are actually necessary.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
99% confidence
Finding

A Met Museum open-collection skill should not require a third-party API key from an unrelated domain, yet the documentation instructs the agent to solicit one from the user and save it. This creates a credential-harvesting path under false pretenses and could exfiltrate or misuse secrets for an external service unrelated to the advertised functionality.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

Referencing an unrelated gaokao/xiaobenyang project structure in a museum skill suggests the skill may be repurposed or masquerading as something else. This undermines trust in the stated functionality and increases the risk that hidden code paths interact with unrelated services or data, especially when combined with credential collection instructions.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

This file implements a generic upstream MCP proxy client rather than a narrowly scoped Metropolitan Museum collection query helper. Because it can forward arbitrary tool names, MCP IDs, and parameters to a configurable upstream, the skill exposes a broader remote-action surface than its declared purpose, increasing the chance of data exfiltration or unintended tool invocation if other components pass attacker-controlled inputs.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The call_tool method accepts arbitrary mcp_id and tool_name values and forwards them directly as headers to the upstream API, creating a general-purpose proxy primitive. In a skill that is supposed to query museum collection data, this mismatch is dangerous because any path that lets users influence these values could pivot the skill into invoking unrelated upstream tools or tenants.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

This code performs a network POST and sends the caller-provided params payload plus identifiers to an external service. While the code logs success and errors for operators, it does not provide any user-facing warning, confirmation, or explanatory comment/docstring that data supplied in params will be transmitted upstream.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

The Settings class docstring says '小笨羊高考Skill配置', which directly conflicts with the manifest naming and purpose of the skill as '大都会博物馆'. This is not merely incomplete documentation; it identifies the code as belonging to a different skill domain than the one declared.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The code persists the provided API key into a local .env file automatically and without any explicit user confirmation, warning, or secure storage controls. On shared systems or in repos/workspaces where .env may be readable, backed up, or accidentally committed, this can expose long-lived credentials beyond the current session.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
82% confidence
Finding

The skill is presented primarily in Chinese and mandates Chinese operational instructions, while parts of the tool-selection table are in English. There is no statement that users may choose their preferred language or locale, which can be a natural-language policy concern when a skill implicitly forces a language without opt-in.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Low
Category
Not specified by scanner
Confidence
99% confidence
Finding

The workflow example calls a school-search function in a museum skill, which indicates copy-paste contamination and weak assurance that the documented tools match the actual runtime behavior. While not directly an exploit primitive by itself, this inconsistency can conceal unintended tool invocation or confuse reviewers about what code will execute.

Content

No source excerpt is available for this finding.

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
93% confidence
Finding

The dependency is specified with a minimum version only, which allows future installs to resolve to different releases over time. This weakens build reproducibility and makes it harder to verify whether a vulnerable or breaking version of requests is being pulled into the skill.

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 the package, there is no reliable way to determine whether deployed environments are using a fixed or vulnerable release. In a skill that performs external queries, an HTTP client library is directly exposed to untrusted network interaction, which slightly increases the practical security relevance.

Content

No source excerpt is available for this finding.

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
93% confidence
Finding

Using an unpinned pydantic version means deployments may silently consume newer releases with different security posture or behavior. This creates supply-chain uncertainty and prevents auditors from determining the exact package version in use.

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

Pydantic has published advisories, and the lack of version pinning makes the actual risk impossible to verify. Since pydantic is often used on untrusted input, unresolved version ambiguity can leave the skill exposed to parser or validation bugs present in some releases.

Content

No source excerpt is available for this finding.

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
92% confidence
Finding

The pydantic-settings dependency is not pinned, so installations are not reproducible and may resolve to releases with undiscovered or known flaws. This is especially relevant for configuration-handling libraries, which often interact with environment variables and secrets.

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

The manifest does not specify an exact pydantic-settings version, so environments may install releases affected by known issues without visibility. Because this package can participate in secrets and configuration loading, version uncertainty has supply-chain and configuration-security implications.

Content

No source excerpt is available for this finding.

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
92% confidence
Finding

An unpinned python-dotenv dependency allows uncontrolled version drift across environments. Because this library handles local configuration files and environment loading, unexpected vulnerable versions could increase the risk of local file or configuration-related issues.

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

Python-dotenv has advisories related to file-handling behavior, and without a pinned version it is not possible to verify whether the installed release includes fixes. In software that may read local environment files during setup or deployment, this ambiguity can enable avoidable file or configuration risks.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.