Back to skill

Security audit

General Text Recognition OCR - 通用文字识别

Security checks for vulnerabilities and agentic risk

Overview

This OCR skill does what it claims, but it can upload local file contents to a third-party OCR API with insufficient user-facing warning and weak file-boundary checks.

Install only if you are comfortable sending selected images to JisuAPI. Do not use it on screenshots or files containing passwords, personal records, internal documents, or regulated data unless you have confirmed that external OCR processing is acceptable. The publisher should add explicit consent language and harden path, symlink, file-type, and size validation.

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
generalrecognition.py:20
Finding
Symlink Bypass Allows Unauthorized Local File Disclosure to External OCR Service<![CDATA[ ## Vulnerability Details **File Location**: `generalrecognition.py`, lines 20–104 **Vulnerability Type**: Insufficient path containment and file-type validation **Risk Level**: Medium ### Vulnerable Code ```python def _normalize_local_path(user_path: str, field: str) -> Dict[str, Any]: """ 规范化并限制本地文件路径,只允许在当前工作目录及其子目录内读取。 禁止绝对路径和目录穿越(包含 ..),避免被恶意提示利用读取任意系统文件。 """ if not user_path: return { "error": "invalid_param", "message": f"field '{field}' is empty", } if os.path.isabs(user_path): return { "error": "invalid_path", "message": f"Absolute path is not allowed for '{field}'", } 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} def _build_pic_base64(req: Dict[str, Any]) -> Dict[str, Any]: """ 从请求中获取 base64 图片内容: - 若提供 pic 字段(base64 字符串),直接使用; - 若提供 path/image/file,则从本地文件读取并转为 base64。 """ pic = req.get("pic") if pic: return {"pic": str(pic), "error": None} path_raw = req.get("path") or req.get("image") or req.get("file") if not path_raw: return { "pic": None, "error": "Either 'pic' (base64) or 'path/image/file' is required", } safe = _normalize_local_path(str(path_raw).strip(), "path") if safe["error"]: return {"pic": None, "error": safe["message"]} 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: ...[truncated 3744 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Resolve the working directory and candidate file to canonical paths before opening the file: ```python from pathlib import Path base = Path.cwd().resolve() candidate = (base / user_path).resolve(strict=True) try: candidate.relative_to(base) except ValueError: raise ValueError("File must remain inside the working directory") ``` 2. Reject symbolic links explicitly when they are unnecessary for the OCR workflow. Perform checks using safely opened file descriptors where possible to reduce time-of-check/time-of-use race conditions. 3. Verify that the target is a regular file and inspect its actual file signature rather than trusting its extension. Permit only the image formats supported by the OCR provider, such as JPEG and PNG. 4. Enforce a strict byte-size limit before loading the complete file into memory. Read no more than the configured maximum plus one byte and reject oversized input. 5. Keep the external endpoint fixed to the documented HTTPS origin and clearly notify users that selected image contents are transmitted to a third-party OCR provider. 6. Consider requiring the caller to provide a file through a dedicated attachment or sandbox mechanism instead of accepting arbitrary filesystem paths. 7. Add tests covering: - Absolute paths. - `..` traversal. - Symlinks to files outside the working directory. - Nested symlink chains. - Non-image regular files. - Oversized files. - Files replaced between validation and opening. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • 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
Findings (7)

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.

Missing User Warnings

High
Confidence
98% confidence
Finding
The documentation does not warn that uploaded image contents are sent to a third-party OCR API, despite the skill being designed around external processing. This is dangerous because users may provide screenshots containing credentials, personal data, financial records, or internal documents without informed consent, leading to privacy and compliance exposure.

Lp3

Medium
Category
MCP Least Privilege
Confidence
92% confidence
Finding
The skill metadata declares required environment access and the workflow clearly depends on outbound API calls, but the manifest does not explicitly scope or disclose those capabilities via permissions or allowed-tools. This weakens auditability and consent controls, making it easier for a caller or platform to invoke a networked, secret-using skill without clear governance.

Vague Triggers

Medium
Confidence
91% confidence
Finding
The invocation guidance uses broad phrasing like extracting text from an image or screenshot without clear exclusions, which can cause the agent to over-trigger on sensitive screenshots or documents. Because this skill transmits content to a third-party OCR provider, ambiguous routing increases the chance of unintended disclosure of private data.

External Transmission

Medium
Category
Data Exfiltration
Content
import requests


OCR_URL = "https://api.jisuapi.com/generalrecognition/recognize"


def _normalize_local_path(user_path: str, field: str) -> Dict[str, Any]:
Confidence
93% confidence
Finding
The hardcoded external OCR endpoint shows that this skill is designed to transmit image data to a remote service. In the context of an OCR skill, this is expected behavior, but it still creates a real data-exfiltration/privacy risk if users are not clearly informed that local files or provided images are sent off-host.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The skill transmits user-supplied image content to a third-party OCR service, but the code contains no mechanism to disclose that image data leaves the local environment or to obtain explicit user consent. This is dangerous because screenshots and images often contain sensitive data such as credentials, personal information, or internal documents, and users may reasonably assume OCR is performed locally unless clearly told otherwise.

Missing User Warnings

Low
Confidence
80% confidence
Finding
The code reads the JISU_API_KEY environment variable, which is a sensitive credential, but provides no explanatory comment or user-facing disclosure beyond failing when it is absent. Under the rule, access to sensitive environment variables should have some visible explanation or documentation unless already clearly disclosed elsewhere.

Static analysis

No suspicious patterns detected.