Back to skill

Security audit

柏林公共服务查询服务

Security checks for vulnerabilities and agentic risk

Overview

This skill needs review because a Berlin-services lookup skill asks for and stores a Xiaobenyang API key in plaintext and sends it to a configurable third-party endpoint.

Install only after confirming that Xiaobenyang is the intended and trusted provider for the Berlin service data. Do not provide a valuable or reused API key unless plaintext .env storage is acceptable in your environment, and prefer a version that removes local secret write-back, pins dependencies, and locks the credential-bearing endpoint to the intended host.

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/call_api.py:48
Finding
API credential disclosure through a configurable upstream endpoint<![CDATA[ ## Vulnerability Details **File Location**: `scripts/config.py:10-17`; `scripts/call_api.py:48-72` **Vulnerability Type**: Credential disclosure through an untrusted configurable endpoint **Risk Level**: Medium ### Vulnerable Code `scripts/config.py:10-17`: ```python model_config = SettingsConfigDict( env_prefix="XBY_GAOKAO_", env_file=".env", env_file_encoding="utf-8", extra="ignore", ) # API配置 base_url: str = "https://mcp.xiaobenyang.com" ``` `scripts/call_api.py:48-72`: ```python def call_tool( self, mcp_id: str, tool_name: str, params: dict[str, Any], ) -> HttpResult: """调用小笨羊MCP工具""" 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密钥未设置,请先调用 set_api_key()") headers = { "XBY-APIKEY": api_key, "func": tool_name, "mcpid": mcp_id, "Content-Type": "application/json", } # data = {k: str(v) if v is not None else "" for k, v in params.items()} t0 = time.time() try: resp = self._session.post( url=url, headers=headers, data=json.dumps(params), timeout=settings.timeout_seconds, ) ``` ### Technical Analysis The upstream `base_url` is a security-sensitive setting because every API request sends the user's `XBY-APIKEY` credential to that destination. Pydantic Settings permits this value to be overridden through the `XBY_GAOKAO_BASE_URL` environment variable or the working-directory `.env` file. The request logic constructs a URL directly from this configurable value and attaches the API key without validating the URL scheme, hostname, port, or origin. Consequently, an attacker who can influence the process environment or place a crafted `.env` file in the working directory can redirect authenticated requests to an attacker-controlled server. TLS does not prevent this attack if the subst ...[truncated 1742 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. **Make the credential destination immutable.** Hardcode the trusted API origin if endpoint customization is not required: ```python TRUSTED_API_ORIGIN = "https://mcp.xiaobenyang.com" url = f"{TRUSTED_API_ORIGIN}/api" ``` 2. **If configuration is required, validate the parsed URL before attaching credentials.** Enforce an exact HTTPS origin allowlist: ```python from urllib.parse import urlparse TRUSTED_HOSTS = {"mcp.xiaobenyang.com"} parsed = urlparse(settings.base_url) if ( parsed.scheme != "https" or parsed.hostname not in TRUSTED_HOSTS or parsed.username is not None or parsed.password is not None or parsed.port not in (None, 443) ): raise UpstreamError("Untrusted API endpoint") ``` 3. **Do not expose `base_url` through the ordinary `.env` configuration source.** Separate security-sensitive trust configuration from user-editable settings. 4. **Validate the final request URL immediately before transmission.** This provides defense in depth against future URL-construction or redirect-related changes. 5. **Restrict redirects for credential-bearing requests.** Use `allow_redirects=False`, or validate every redirect destination before forwarding the credential. 6. **Protect persisted credentials.** Prefer an operating-system credential store. If `.env` storage remains necessary, create the file with owner-only permissions such as mode `0600`, use a dedicated configuration directory, and exclude it from source control. ]]>
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 (29)

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding
This finding indicates the skill manages and persists an API key for an unrelated service and targets mcp.xiaobenyang.com rather than an obvious Berlin-government data source. That creates a trust-boundary violation: sensitive user credentials are being collected and stored for a remote service whose role is inconsistent with the advertised function.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding
This finding indicates the skill manages and persists an API key for an unrelated service and targets mcp.xiaobenyang.com rather than an obvious Berlin-government data source. That creates a trust-boundary violation: sensitive user credentials are being collected and stored for a remote service whose role is inconsistent with the advertised function.

Description-Behavior Mismatch

High
Confidence
98% confidence
Finding
The skill's documentation claims a Berlin services workflow, but the API-key acquisition flow and metadata reference Xiaobenyang/gaokao-related infrastructure. Such semantic inconsistency is a strong indicator of repurposed or misleading skill content, which can trick users into supplying credentials under false pretenses.

Intent-Code Divergence

High
Confidence
99% confidence
Finding
The example invocation shows a school-search function with gaokao-style parameters even though the skill is presented as a Berlin public-services query service. That contradiction suggests the routing guidance may direct the model toward unintended or broader capabilities, undermining least surprise and enabling misuse of the skill as a generic proxy.

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
88% confidence
Finding
The post-init hook forcibly reads .env and extracts XBY_APIKEY manually, bypassing normal typed configuration flow and explicitly targeting a credential. In a skill context, custom secret scraping from local files is unnecessary and increases the chance of unintended credential ingestion from the workspace.

Credential Access

High
Category
Privilege Escalation
Content
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():
Confidence
88% confidence
Finding
Creating a direct Path('.env') reference for credential retrieval contributes to explicit local secret access behavior. In an otherwise public-service query skill, this local file access is suspicious because it seeks secrets from the workspace rather than using approved runtime secret sources.

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
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Description-Behavior Mismatch

High
Confidence
97% confidence
Finding
The module includes functionality to persist an API key into a local .env file and expose helper functions to set and retrieve it, which is unrelated to a public Berlin services lookup skill. Persisting secrets to project-local plaintext storage increases the chance of accidental disclosure through source control, logs, backups, or later file reads by other components.

Context-Inappropriate Capability

High
Confidence
96% confidence
Finding
The code implements local credential write-back for XBY_APIKEY despite the advertised skill being a public information retrieval service. This expands the attack surface by allowing sensitive credentials to be stored on disk without necessity, making compromise easier if the workspace is accessible to users, other skills, or packaging processes.

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
95% confidence
Finding
The function explicitly saves an API key to .env, creating plaintext credential storage on disk. This is dangerous because .env files are commonly copied, committed, or exposed through debugging and packaging, and the behavior is especially unjustified for a public administrative lookup skill.

Lp3

Medium
Category
MCP Least Privilege
Confidence
92% confidence
Finding
The skill exposes capabilities consistent with environment access, local file read/write, and network access, yet the manifest provides no explicit tool scope or permissions boundary. In a skill that also asks the model to collect and persist API keys, this lack of declared scope increases the chance of overbroad execution and hidden side effects.

Natural-Language Policy Violations

Medium
Confidence
86% confidence
Finding
The skill name, description, workflow, and operational instructions are written entirely in Chinese, with no indication that users may choose another language or that Chinese is a required locale. For a Berlin public-services lookup skill, this reads as a language constraint imposed by the skill rather than a documented, justified regional compliance requirement.

Ssd 3

Medium
Confidence
96% confidence
Finding
The skill explicitly instructs the model to solicit an API key from the user during normal conversation and persist it locally. Collecting secrets through general chat flow and writing them to a local `.env` file increases the risk of accidental disclosure, insecure storage, exfiltration by other components, and user confusion about who is receiving the credential.

Intent-Code Divergence

Medium
Confidence
95% confidence
Finding
A project structure labeled `xiaobenyang_gaokao_skill` conflicts with the stated Berlin public-services purpose. While naming alone is not proof of exploitability, in this context it reinforces the evidence that the package may be repackaged from unrelated code and could mislead reviewers about provenance and actual behavior.

Description-Behavior Mismatch

Medium
Confidence
94% confidence
Finding
清单描述该技能用于搜索和检索柏林当局提供的公共服务信息,但此文件只实现了一个面向任意 mcp_id、tool_name 和 params 的通用上游 API 调用器,没有任何与柏林行政服务数据、查询范围限制或特定资源端点相关的约束。这样的实现语义上更像一个泛化的远程工具代理,而非专用的柏林公共服务查询能力。

Missing User Warnings

Medium
Confidence
91% confidence
Finding
This code accesses a sensitive credential via get_api_key() and sends it in the XBY-APIKEY request header during a network call. While the module has internal comments/docstrings and logging, none of them clearly disclose to the user that a secret will be used and transmitted to an external service.

Intent-Code Divergence

Medium
Confidence
97% confidence
Finding
The comments and naming indicate this file belongs to a different '小笨羊高考' skill and uses unrelated XBY_GAOKAO/XBY_APIKEY identifiers, which is inconsistent with the declared Berlin public service purpose. Such provenance mismatch is a supply-chain red flag because repurposed code can conceal undeclared network dependencies, inappropriate secret handling, or functionality not reviewed for this skill's threat model.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
The API key is silently persisted to .env with no user warning, consent, or indication that a plaintext secret is being written to disk. Even if intended for convenience, this can surprise operators and cause long-lived credential exposure in developer environments, containers, snapshots, or repositories.

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 dependency list uses a lower-bound specifier (`requests>=2.31.0`) rather than an exact pinned version, which makes builds non-reproducible and can pull in unexpected releases over time. In a server-side skill, this increases supply-chain uncertainty and complicates verification against known vulnerable or breaking versions.

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
The manifest does not pin `requests`, and the package has multiple published advisories, so it is impossible to verify from this file whether deployments will use an affected version. In a network-facing service, an unresolved `requests` version is more concerning because HTTP client behavior can directly affect credential handling, TLS verification, and request security.

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
94% confidence
Finding
`pydantic>=2.7.0` is unpinned, so installations may resolve to different versions depending on when and where the skill is deployed. This weakens reproducibility and can introduce vulnerable or incompatible releases without code changes.

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
Because `pydantic` is not pinned, the effective installed version cannot be matched reliably against known advisories. For a service likely to parse external input, uncertainty around a validation library can expose the deployment to denial-of-service or parsing flaws present in some releases.

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
94% confidence
Finding
`pydantic-settings>=2.2.0` allows any newer version, which creates uncertainty about the actual installed package and its security posture. For infrastructure-facing code that may load secrets or environment configuration, this increases supply-chain risk even if no exploit is visible in this file alone.

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
86% confidence
Finding
`pydantic-settings` has known advisories, and the absence of an exact version means the deployed build may resolve to a vulnerable release without visibility in the manifest. This is relevant in a service context because settings libraries may process secrets paths and filesystem-backed configuration.

Static analysis

No suspicious patterns detected.