Back to skill

Security audit

香港开放数据访问服务

Security checks for vulnerabilities and agentic risk

Overview

This skill is advertised as Hong Kong government open-data access, but it relies on an unrelated XiaoBenYang MCP service and stores that service's API key in plaintext.

Review this skill carefully before installing. It may be useful only if you intentionally want to use XiaoBenYang as a proxy for DATA.GOV.HK data and are comfortable giving it an API key. Do not reuse a sensitive key, keep the .env file out of source control, and prefer a version that directly accesses DATA.GOV.HK or stores credentials in a safer secret store.

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

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/config.py:46
Finding
API Key Persisted in a Predictable Plaintext File Without Explicit Permission Controls<![CDATA[ ## Vulnerability Details **File Location**: `scripts/config.py`, lines 46-63 **Vulnerability Type**: Plaintext sensitive-data storage with insufficient file-permission controls **Risk Level**: Medium ### Vulnerable Code ```python def save_api_key_to_env(api_key: str) -> bool: """Persist the API key to the .env file.""" 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"Failed to save API key: {e}") return False ``` ### Technical Analysis The supplied API key is written in plaintext to `.env` in the process's current working directory. The implementation does not create the file with an explicit restrictive mode, inspect or repair the permissions of an existing file, or ensure that the selected path is private to the current user. For a newly created file, effective permissions depend on the host process's umask. If `.env` already exists, `Path.write_text()` preserves its existing permissions, which may allow access by other local users or processes. The predictable working-directory location also increases the chance that the file is copied into backups, included in an archive, exposed through a development environment, or accidentally committed to source control. The same secret is also copied into the process environment. Child processes launched after this assignment may inherit the value, expanding the number of ...[truncated 1106 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Avoid persistent storage by default. Keep the API key in memory or obtain it from a preconfigured environment variable. 2. If persistence is required, use the operating system's credential manager or keyring rather than a plaintext file. 3. If a file must be used: - Store it in a dedicated per-user configuration directory. - Create it atomically with mode `0600`. - Reject symbolic links and unexpected file types. - Check and repair permissions on existing files before reading or writing them. - Avoid relying solely on the process umask. 4. Do not propagate the key into `os.environ` unless child-process inheritance is explicitly required. 5. Add `.env` to `.gitignore` and packaging exclusion rules. 6. Document where the credential is stored, which third-party endpoint receives it, and how the user can revoke or delete it. 7. Provide a credential-removal function and recommend immediate rotation after suspected exposure. ]]>

T08 · Insecure Dependencies

Note
Location
requirements.txt:1
Finding
Unbounded Dependency Version Ranges Permit Unreviewed Future Releases<![CDATA[ ## Vulnerability Details **File Location**: `requirements.txt`, lines 1-4 **Vulnerability Type**: Non-reproducible dependency resolution and supply-chain exposure **Risk Level**: Low ### Vulnerable Code ```text requests>=2.31.0 pydantic>=2.7.0 pydantic-settings>=2.2.0 python-dotenv>=1.0.1 ``` ### Technical Analysis All dependencies use minimum-version constraints without upper bounds or exact pins. Consequently, two installations performed at different times can resolve to different dependency versions, including future major or otherwise incompatible releases that were not reviewed with this project. No lock file or integrity hashes are present in the audited project. Package authenticity therefore depends entirely on the configured package index and TLS trust at installation time. If an accepted dependency or transitive dependency is compromised, its maintainer account is taken over, or the package index used by the environment is malicious, a later installation may automatically select the affected release. This finding does not establish that any currently named dependency is malicious. The vulnerability is the project's failure to constrain and verify the dependency artifacts it installs. ### Attack Path 1. A future release satisfying one of the open-ended constraints is published to the configured package index. 2. The release is compromised, malicious, or incompatible, or its distribution account is taken over. 3. A user or deployment system installs the project without a previously reviewed lock file. 4. The resolver selects the new release because it satisfies the `>=` constraint. 5. Malicious package installation or runtime code executes with the privileges of the installer or Agent process. 6. Depending on those privileges, the package could read the persisted API key, alter API requests, access Agent-readable files, or modify the runtime environment. ### Impact Assessment The maximum impact depends on the privileges used to inst ...[truncated 557 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Resolve and pin reviewed exact versions for direct and transitive dependencies. 2. Generate and commit a lock file appropriate for the deployment workflow. 3. Record cryptographic hashes for every accepted distribution and install with hash verification, such as pip's `--require-hashes`. 4. Use a controlled package index or approved internal mirror. 5. Run dependency vulnerability and provenance checks in continuous integration. 6. Review and deliberately update dependency pins on a scheduled basis rather than accepting new releases automatically. 7. Install dependencies in an isolated, least-privileged virtual environment or container. ]]>
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 (34)

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
The documented behavior includes storing an API key in local configuration and references unrelated xiaobenyang/gaokao artifacts, which is inconsistent with a narrowly described open-data access skill. Hidden credential-management and third-party coupling create a trust-boundary problem: a user expecting simple public-data retrieval may instead authorize local secret persistence and calls to an unrelated service.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
The documented behavior includes storing an API key in local configuration and references unrelated xiaobenyang/gaokao artifacts, which is inconsistent with a narrowly described open-data access skill. Hidden credential-management and third-party coupling create a trust-boundary problem: a user expecting simple public-data retrieval may instead authorize local secret persistence and calls to an unrelated service.

Intent-Code Divergence

High
Confidence
99% confidence
Finding
The workflow example and project structure reference unrelated gaokao/school-search functionality, indicating copy-paste residue or repurposed tooling that does not match the declared Hong Kong open-data purpose. This materially raises risk because it suggests the skill may invoke unintended tools, target the wrong backend, or mishandle user input and credentials in ways not disclosed to the user.

Description-Behavior Mismatch

High
Confidence
97% confidence
Finding
The implementation does not access DATA.GOV.HK directly as the skill metadata claims; instead it acts as a generic client to an unrelated remote MCP endpoint controlled by a configurable base URL. This mismatch is dangerous because it can mislead users and hosting platforms into granting trust, data access, or execution opportunities to a service whose real behavior is broad remote proxying to a third party.

Description-Behavior Mismatch

High
Confidence
98% confidence
Finding
The configuration is clearly inconsistent with the declared Hong Kong open-data service: it references an unrelated '小笨羊高考' service, custom base URL, MCP ID, and a separate API-key flow. This kind of service-identity mismatch is dangerous because it can silently route user requests or credentials to an unrelated external service, violating user expectations and potentially enabling data exfiltration or deceptive behavior.

Credential Access

High
Category
Privilege Escalation
Content
model_config = SettingsConfigDict(
        env_prefix="XBY_GAOKAO_",
        env_file=".env",
        env_file_encoding="utf-8",
        extra="ignore",
    )
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
model_config = SettingsConfigDict(
        env_prefix="XBY_GAOKAO_",
        env_file=".env",
        env_file_encoding="utf-8",
        extra="ignore",
    )
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
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")
Confidence
90% confidence
Finding
This code forcibly reads XBY_APIKEY directly from a local .env file outside the standard settings flow, specifically targeting a named credential for an unrelated service. In the context of an advertised public open-data skill, this bespoke secret-reading behavior is suspicious and increases the risk of silently harvesting or reusing stored credentials without clear user awareness.

Credential Access

High
Category
Privilege Escalation
Content
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
Confidence
91% confidence
Finding
Reading XBY_APIKEY from the environment as a fallback/override continues the suspicious custom credential path for an unrelated service. In this skill context, the danger is not generic environment-variable use, but that the skill appears to solicit and consume secrets for a third-party endpoint inconsistent with the claimed government open-data functionality.

Credential Access

High
Category
Privilege Escalation
Content
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()
Confidence
94% confidence
Finding
This function is explicitly dedicated to saving an API key into .env, creating persistent plaintext credential storage on disk. In the context of a supposedly public-data skill with mismatched service identity, that behavior materially increases the risk of credential leakage and suggests undeclared dependence on another service.

Lp3

Medium
Category
MCP Least Privilege
Confidence
89% confidence
Finding
The skill declares no explicit tool scope or permission boundaries while its documented/project capabilities imply access to environment variables, local file read/write, and network calls. In a skill that also handles API keys, missing scope declarations increases the chance of overbroad execution and makes it harder for users or hosting platforms to constrain credential and filesystem access.

Natural-Language Policy Violations

Medium
Confidence
89% confidence
Finding
The skill name and description are presented only in Chinese, and the instructions consistently direct operation in Chinese while the underlying tools support multiple language codes (`en`, `tc`, `sc`). There is no explicit user opt-in or language-choice mechanism, which can violate a language/locale policy requiring user choice.

Description-Behavior Mismatch

Medium
Confidence
96% confidence
Finding
Requiring an API key from an unrelated external site for a skill presented as access to government open data is a strong supply-chain and phishing-style trust concern. Users may be induced to obtain, enter, and persist credentials for a service that is not necessary for the claimed functionality, creating unnecessary exposure and confusion about where data and secrets are sent.

Intent-Code Divergence

Medium
Confidence
94% confidence
Finding
The class names and docstrings identify this as a '小笨羊MCP API' client, directly contradicting the manifest's claim that the skill is for Hong Kong open data access. Such inconsistencies are a strong indicator of repackaged or deceptive code, which raises the likelihood that reviewers and users are being misled about what remote system is actually contacted and what data or actions are involved.

Context-Inappropriate Capability

Medium
Confidence
94% confidence
Finding
The client accepts arbitrary tool names and free-form parameters, then forwards them in headers and JSON body to a remote service for execution. In the context of a skill advertised as simple open-data browsing, this is an unnecessary expansion of capability that could enable hidden actions, unauthorized data handling, or abuse of the host's trust boundary via remotely selected functions.

Natural-Language Policy Violations

Medium
Confidence
78% confidence
Finding
The skill's natural-language description string is written only in Chinese, which may indicate a fixed language/locale assumption. Because the file provides no user choice or documented locale constraint, this can conflict with a policy requiring language selection or opt-in.

Context-Inappropriate Capability

Medium
Confidence
95% confidence
Finding
The code persistently stores a custom API key in a local .env file even though the stated purpose is access to a government open-data portal, which normally should not require secret handling in this manner. In this context, secret persistence is suspicious because it expands the attack surface, creates long-lived local credential exposure, and may conceal that the skill is actually calling a third-party service.

Missing User Warnings

Medium
Confidence
93% confidence
Finding
The function writes the API key to .env without any warning, consent prompt, or disclosure that the credential will be stored on disk and copied into the process environment. This is dangerous because users may reasonably believe they are providing a temporary token, while the code creates persistent plaintext storage that may later be exposed through local file access, backups, or source-control mistakes.

Unpinned Dependencies

Low
Category
Supply Chain
Content
requests>=2.31.0
pydantic>=2.7.0
pydantic-settings>=2.2.0
python-dotenv>=1.0.1
Confidence
97% confidence
Finding
The dependency on requests is specified with a lower bound only, which allows future installs to resolve to any newer release, including versions with breaking changes or newly introduced security issues. This weakens supply-chain reproducibility and makes it impossible to verify that deployments are using a known-safe version.

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
92% confidence
Finding
Requests has multiple published advisories, but the manifest does not pin an exact version, so there is no reliable way to determine whether deployed environments are affected. This is dangerous because the package could resolve to a vulnerable version depending on install time and resolver behavior.

Unpinned Dependencies

Low
Category
Supply Chain
Content
requests>=2.31.0
pydantic>=2.7.0
pydantic-settings>=2.2.0
python-dotenv>=1.0.1
Confidence
96% confidence
Finding
The pydantic requirement is unpinned, so different environments may install different versions over time. This creates non-reproducible builds and can silently introduce vulnerable or incompatible releases into the skill's runtime.

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
90% confidence
Finding
Pydantic has known advisories, and the lack of exact version pinning means the security posture of the deployed package cannot be verified from this manifest alone. This creates avoidable uncertainty around whether input-validation code may rely on a vulnerable release.

Unpinned Dependencies

Low
Category
Supply Chain
Content
requests>=2.31.0
pydantic>=2.7.0
pydantic-settings>=2.2.0
python-dotenv>=1.0.1
Confidence
95% confidence
Finding
Using pydantic-settings with only a minimum version allows the package manager to select any later version, including releases that may contain security regressions or advisories. In a service that loads configuration from environment or secrets sources, this uncertainty increases supply-chain risk.

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
89% confidence
Finding
The manifest does not pin pydantic-settings, so even though advisories exist, it is impossible to confirm whether an installation will use an affected or fixed version. For a service that may load configuration and secrets-related settings, unverifiable dependency state is a supply-chain weakness.

Unpinned Dependencies

Low
Category
Supply Chain
Content
requests>=2.31.0
pydantic>=2.7.0
pydantic-settings>=2.2.0
python-dotenv>=1.0.1
Confidence
95% confidence
Finding
The python-dotenv package is not pinned, so installations may drift to different versions with differing security properties. Because dotenv-related packages often interact with local configuration files, version ambiguity can increase the risk of pulling in a vulnerable release.

Static analysis

No suspicious patterns detected.