Back to skill

Security audit

护照识别OCR

Security checks for vulnerabilities and agentic risk

Overview

This passport OCR skill is purpose-aligned overall, but it warrants Review because it handles passport data and API keys with weak privacy and secret-storage controls.

Install only if you trust the Xiaobenyang service with passport images and extracted identity data. Use a low-privilege API key, avoid running from shared or untrusted working directories, check any .env file before use, prefer temporary/session-only key handling, and rotate the key if it may have been written somewhere exposed.

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

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/config.py:44
Finding
API Key Persisted in an Insecure Plaintext File<![CDATA[ ## Vulnerability Details **File Location**: `scripts/config.py:44-61` **Vulnerability Type**: Plaintext credential storage and unsafe file handling **Risk Level**: Medium ### Vulnerable Code ```python def save_api_key_to_env(api_key: str) -> bool: 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 The supplied API key is persisted in plaintext in a `.env` file located relative to the process's current working directory. The implementation does not enforce owner-only permissions, verify that the destination is a regular file, reject symbolic links, or perform an atomic file replacement. The default permissions of `Path.write_text()` are affected by the process umask. In an insufficiently restricted environment, the resulting file may be readable by other local accounts. Because the path is relative and symbolic links are followed, an attacker able to prepare the working directory could create `.env` as a symbolic link to another writable file. Calling `set_api_key()` would then rewrite that target with attacker-influenced content. Plaintext persistence also increases the chance that the credential will be copied into backups, build contexts, support archives, or source-control commits. ### Attack Path 1. An attacker obtains local access to the directory from wh ...[truncated 1186 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Avoid persistent storage by default. Keep the API key in process memory or obtain it from an existing secret-management facility. 2. If persistence is required, use an operating-system credential store or a dedicated secret manager rather than a project-level `.env` file. 3. Store configuration under a fixed, user-private directory rather than the current working directory. 4. Create the directory with owner-only permissions and create the secret file with mode `0600`. 5. Reject symbolic links and verify that existing destinations are regular files owned by the expected user. 6. Write to a securely created temporary file in the same directory, set restrictive permissions, flush it, and atomically replace the destination. 7. Add `.env` to version-control, packaging, backup, and diagnostic-export exclusions. 8. Support key revocation and rotation, and document that previously stored keys should be rotated if file exposure is suspected. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/call_api.py:51
Finding
Environment-Controlled API Endpoint Can Exfiltrate Credentials and Passport Data<![CDATA[ ## Vulnerability Details **File Location**: `scripts/config.py:13-19`; `scripts/call_api.py:51-67` **Vulnerability Type**: Unvalidated security-sensitive endpoint configuration **Risk Level**: High ### Vulnerable Code ```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" mcp_id: str = "1820705335657482" api_key: str = "" ``` ```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密钥未设置,请先调用 set_api_key()") 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` assigns the `XBY_GAOKAO_` prefix to environment-based settings. Consequently, `XBY_GAOKAO_BASE_URL` can override the trusted default endpoint. The request client uses the resulting value directly without validating the URL scheme or enforcing an allowlist of trusted origins. Every request includes the user's API key in the `XBY-APIKEY` header. Request parameters contain either a passport image URL or the Base64-encoded passport image itself. If an attacker can influence the Skill's environment or `.env` settings before initialization, the attacker can redirect this information to an arbitrary server. The client also mounts an adapter for plain HTTP and does not reject a non-HTTPS `base_url`. An attacker-controlled endpoint can therefore receive credentials and sensitive identity-document data over an unencrypted connection. ### Attack Path 1. An attacker gains the ability to influence the Skill process environment or its settings source, such as deployment variables or a prepared `.env` file. 2. The attacker set ...[truncated 1254 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Do not permit production callers to override the API origin through ordinary environment variables. 2. Hard-code the expected origin, or enforce an exact allowlist containing only `https://mcp.xiaobenyang.com`. 3. Parse the configured URL and reject non-HTTPS schemes, embedded credentials, unexpected ports, fragments, and unapproved hostnames. 4. Compare the normalized hostname rather than using prefix or substring checks. 5. Validate the destination again immediately before transmission so later configuration changes cannot bypass initialization checks. 6. Separate development endpoint overrides from production builds and require an explicit, secure development-mode flag. 7. Never send the API key after a cross-origin redirect. Disable redirects or independently validate every redirect destination. 8. Obtain informed user consent before transmitting passport data and clearly disclose the receiving service, transport protections, and retention policy. 9. Rotate any API key that may have been used while the endpoint configuration was untrusted. ]]>
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 (30)

Tp2

High
Category
MCP Tool Poisoning
Confidence
85% confidence
Finding
Mixing characters from multiple Unicode scripts in a single identifier is a common technique to create visually ambiguous tool names.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
The documented behavior includes reading and writing configuration, persisting API keys, and maintaining unrelated service configuration, which exceeds the narrowly declared OCR purpose. This mismatch is especially dangerous in a passport-processing skill because users may not expect local secret storage or unrelated project components to be involved when handling identity documents.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding
The documented behavior includes reading and writing configuration, persisting API keys, and maintaining unrelated service configuration, which exceeds the narrowly declared OCR purpose. This mismatch is especially dangerous in a passport-processing skill because users may not expect local secret storage or unrelated project components to be involved when handling identity documents.

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
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
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
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
90% confidence
Finding
This function writes the API key in plaintext to a local .env file, creating a durable credential exposure surface on disk. In the context of a passport OCR skill, handling highly sensitive identity documents alongside weak secret storage increases operational risk, because compromise of the key could enable unauthorized backend use and access to document-processing services.

Lp3

Medium
Category
MCP Least Privilege
Confidence
90% confidence
Finding
The skill appears to require environment access, file read/write, and network access, but it does not declare any explicit tool scope or permissions. This weakens reviewability and least-privilege controls, making it easier for a skill handling sensitive passport data and API keys to access more capabilities than users may expect.

Missing User Warnings

Medium
Confidence
96% confidence
Finding
The skill processes extremely sensitive identity data, including passport number, name, nationality, dates, and MRZ content, yet it does not warn users that this data may be sent to an external API or explain privacy risks. In this context, missing disclosure and consent mechanisms are dangerous because users may unknowingly transmit regulated personal data to third parties.

Missing User Warnings

Medium
Confidence
87% confidence
Finding
This code retrieves a credential via get_api_key() and sends it in the XBY-APIKEY header during a network POST request. Although the code logs request success and failure, those logs are not user-facing warnings and there is no confirmation prompt, comment, or docstring disclosing that a secret and request data will be sent to an external service.

Description-Behavior Mismatch

Medium
Confidence
95% confidence
Finding
技能清单描述聚焦于护照OCR与MRZ解析,但这段代码不仅加载配置,还提供了设置、持久化和全局更新API key的能力。对于一个宣称执行本地识别/解析的技能而言,管理并落盘外部服务凭证属于额外行为,超出了描述中声明的功能范围。

Context-Inappropriate Capability

Medium
Confidence
92% confidence
Finding
save_api_key_to_env 会读写 .env 文件并修改 os.environ,这是一种配置/凭证管理能力,而不是护照字段识别本身所必需的能力。对一个OCR技能而言,这类本地持久化秘密的能力缺乏清单描述中的直接正当性。

Missing User Warnings

Medium
Confidence
94% confidence
Finding
The function silently persists an API key into a local .env file and also places it into the process environment without any user-facing warning or confirmation. This can expose credentials to other local users, backups, source-control mistakes, or support bundles, especially because .env files are often weakly protected.

Missing User Warnings

Medium
Confidence
93% confidence
Finding
This function sends passport images or OCR inputs to an external API via call_api, which involves highly sensitive identity-document data such as passport number, name, nationality, and birth date. The file provides no user-facing warning, consent flow, minimization, retention notice, or indication of transport/privacy safeguards, which increases the risk of privacy violations, regulatory noncompliance, and accidental exposure of PII.

Intent-Code Divergence

Low
Confidence
96% confidence
Finding
L037 要求调用本技能的 `scripts.tools` 工具时,示例却是 `scripts.tools.search_schools(...)`,而本文件声明的实际工具只有护照 OCR 相关函数。这不是单纯信息缺失,而是文档对“应调用什么工具”给出了与技能用途不一致的明确指示,容易误导代理实现。

Intent-Code Divergence

Low
Confidence
93% confidence
Finding
L091 将项目结构根目录写为 `xiaobenyang_gaokao_skill/`,但整个文件其余部分都描述的是“护照识别OCR”技能。这属于文档层面的主动误导,暗示该技能代码来源或用途与当前声明不一致。

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 specification uses a lower-bound range (`requests>=2.31.0`) instead of pinning an exact version, which makes builds non-reproducible and can unintentionally pull in vulnerable or breaking releases over time. In a skill that may process passport OCR data, supply-chain uncertainty increases risk because sensitive personal data could be exposed if a bad dependency version is installed.

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
93% confidence
Finding
`requests` has multiple known advisories, and because the manifest does not pin a version, there is no reliable way to determine whether deployed environments avoid affected releases. This creates a real supply-chain exposure: a resolver could install a vulnerable version that enables credential leakage, TLS verification issues, or other network-related weaknesses.

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
`pydantic>=2.7.0` is not pinned to a specific release, so different installations may resolve to different versions with different security properties. This weakens reproducibility and can introduce vulnerable transitive behavior without any code change in the skill itself.

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
89% confidence
Finding
`pydantic` has published advisories, and the unpinned requirement prevents verification that installed versions are outside affected ranges. If vulnerable versions are resolved, issues such as denial of service through crafted input may become reachable, which matters for an OCR-related skill that may parse untrusted document data.

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
`pydantic-settings>=2.2.0` allows any newer version to be installed, making it impossible to guarantee that tested and deployed environments use the same package release. This is a supply-chain hygiene issue that becomes more important when the application may handle configuration or secrets related to OCR processing.

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
88% confidence
Finding
`pydantic-settings` has at least one known advisory, and the current range-based dependency means deployment could resolve to an affected release without visibility. Since settings libraries may read secrets and filesystem-backed configuration, vulnerable versions can create configuration or secret-handling risks.

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
`python-dotenv>=1.0.1` is unpinned, so future installs may pick up versions with newly introduced vulnerabilities or unsafe behavior changes. Because dotenv libraries often interact with local configuration and secrets, version drift can increase the risk of secret exposure or unsafe file handling.

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
90% confidence
Finding
`python-dotenv` has known advisories involving unsafe file handling, and the lack of a pinned version makes it impossible to verify whether installations avoid affected releases. In an application environment that may load secrets from `.env` files, this uncertainty can lead to secret exposure or arbitrary file overwrite risks if vulnerable versions are installed.

Static analysis

No suspicious patterns detected.