Back to skill

Security audit

Bank Card Recognition OCR - 银行卡识别

Security checks for vulnerabilities and agentic risk

Overview

The skill appears to perform bank-card OCR as advertised, but it uploads sensitive card images to a third-party API and has a path-handling flaw that could upload unintended local files.

Review before installing. Use this only if you are comfortable sending bank-card images and OCR results to JisuAPI, and only for cards you are authorized to process. Run it from a narrow folder containing only the intended image, avoid symlinks, do not store full card numbers unless necessary, and review the provider's privacy and retention terms.

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

Error
Location
bankcardcognition.py:35
Finding
Symlink Bypass Enables Unauthorized Local File Disclosure to a Third-Party API## Vulnerability Details **File Location**: `bankcardcognition.py`, lines 35–44, 71–87, and 92–98 **Vulnerability Type**: Symlink-based path containment bypass and sensitive-file disclosure **Risk Level**: High ### Vulnerable Code ```python norm = os.path.normpath(user_path) if norm.startswith("..") or norm == "..": return { "error": "invalid_path", "message": f"Path traversal is not allowed for '{field}'", } base = os.getcwd() full = os.path.join(base, norm) return {"error": None, "path": full, "relative": norm} ``` ```python path = safe["path"] if not os.path.isfile(path): return {"pic": None, "error": f"File not found: {safe['relative']}"} try: with open(path, "rb") as f: raw = f.read() except Exception as e: return {"pic": None, "error": f"Failed to read file: {e}"} try: encoded = base64.b64encode(raw).decode("utf-8") except Exception as e: return {"pic": None, "error": f"Failed to base64-encode file: {e}"} ``` ```python params = {"appkey": appkey} data = {"pic": pic_base64} try: resp = requests.post(BANKCARD_URL, params=params, data=data, timeout=20) ``` ### Technical Analysis The path validator prevents absolute paths and lexical traversal through `..`, but it does not resolve symbolic links before determining whether the requested file remains inside the current working directory. Both `os.path.isfile()` and `open()` follow symbolic links. A relative path that appears to be contained within the working directory can therefore reference a symlink whose target is an arbitrary readable file outside that directory. The target's contents are read in full, base64-encoded, and submitted as the `pic` field to the configured JisuAPI endpoint. Encoding the selected bank-card image and sending it to the documented OCR provider are necessary for the Skill's declared cloud OCR functionality. The vulnerability is not the use ...[truncated 1752 chars]
Remediation
## Remediation Suggestions 1. Resolve the canonical working directory and candidate path with `os.path.realpath()` or `pathlib.Path.resolve()`. 2. Verify canonical containment using `os.path.commonpath()` rather than string-prefix checks: ```python base = os.path.realpath(os.getcwd()) candidate = os.path.realpath(os.path.join(base, norm)) try: if os.path.commonpath([base, candidate]) != base: return { "error": "invalid_path", "message": f"Resolved path escapes the working directory for '{field}'", } except ValueError: return { "error": "invalid_path", "message": f"Invalid path for '{field}'", } ``` 3. Reject symbolic links explicitly with `os.path.islink()` when symlink support is unnecessary. For stronger protection against check-to-use races on supported systems, open the file with `os.open()` and `O_NOFOLLOW`, then read it through the returned descriptor. 4. Validate the opened object with `fstat()` and ensure that it is a regular file. 5. Enforce the documented file-size limit before reading the complete file into memory. 6. Validate the file's actual image signature and permit only expected image formats before upload; do not rely solely on its extension. 7. Recheck containment and file identity at open time to minimize time-of-check/time-of-use race conditions. 8. Run the Skill under a dedicated low-privilege account with access only to the intended upload directory. 9. Clearly inform users that selected images are transferred to a third-party OCR provider and avoid retaining or logging full card numbers or image contents.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
Findings (6)

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.

Lp3

Medium
Category
MCP Least Privilege
Confidence
94% confidence
Finding
The skill requires network access and an API key via environment variables, but it does not declare an explicit tool/permission scope. This weakens transparency and policy enforcement, making it easier for an agent or user to invoke a capability that sends sensitive bank card images to an external service without a clearly stated permission boundary.

Missing User Warnings

Medium
Confidence
97% confidence
Finding
This skill processes highly sensitive financial data: bank card images and extracted card numbers. The description explains functionality and API setup, but it does not prominently warn upfront that uploaded/local card images and OCR results are sent to a third-party provider, which can lead to users disclosing regulated or sensitive data without informed consent.

External Transmission

Medium
Category
Data Exfiltration
Content
import requests


BANKCARD_URL = "https://api.jisuapi.com/bankcardcognition/recognize"


def _normalize_local_path(user_path: str, field: str) -> Dict[str, Any]:
Confidence
88% confidence
Finding
The code is designed to transmit user-supplied bank card image data to an external API endpoint. While HTTPS is used and the transmission is part of intended functionality, sending sensitive financial imagery to a third party introduces privacy and data-handling risk, especially without strong transparency, consent, and vendor trust documentation.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The skill sends a full bank card image, which can contain highly sensitive financial data, to a third-party OCR service without any explicit user warning, consent flow, or minimization. In this skill context, the external transmission is central to functionality, which makes the issue more dangerous because users may assume local OCR while their card data is actually disclosed off-device to an external provider.

Natural-Language Policy Violations

Low
Confidence
83% confidence
Finding
The natural-language documentation in the file includes Chinese text for the API description and operational comments, while the rest of the interface is partly English. This imposes a language expectation without explicitly offering user choice or justifying a locale-specific constraint.

Static analysis

No suspicious patterns detected.