Back to skill

Security audit

ebook-to-md

Security checks for vulnerabilities and agentic risk

Overview

This skill mostly does what it claims, but it can upload documents to Baidu and then fetch unvalidated URLs returned in OCR output, which creates a review-worthy network and privacy risk.

Install only if you are comfortable sending converted documents and images to Baidu OCR. Avoid using it on sensitive, regulated, or confidential files unless you trust the provider and run it in a network-restricted environment; the URL-fetching behavior should be reviewed or patched before use on untrusted documents.

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/ebook_to_md.py:230
Finding
Unrestricted Fetching of Server-Supplied URLs Enables SSRF<![CDATA[ ## Vulnerability Details **File Location**: `scripts/ebook_to_md.py:230-269` and `scripts/ebook_to_md.py:437-466` **Vulnerability Type**: Server-Side Request Forgery (SSRF) and unrestricted resource retrieval **Risk Level**: Medium ### Vulnerable Code ```python def _download_markdown(markdown_url: str) -> str: resp = requests.get(markdown_url) resp.raise_for_status() return resp.text def _download_parse_result_json(parse_result_url: str) -> dict: resp = requests.get(parse_result_url) resp.raise_for_status() return json.loads(resp.content.decode("utf-8", errors="replace")) def _detect_image_mime(raw: bytes) -> str: if raw[:2] == b"\xff\xd8": return "image/jpeg" if raw[:8] == b"\x89PNG\r\n\x1a\n": return "image/png" if raw[:6] in (b"GIF87a", b"GIF89a"): return "image/gif" if raw[:2] == b"BM": return "image/bmp" if len(raw) > 12 and raw[:4] == b"RIFF" and raw[8:12] == b"WEBP": return "image/webp" return "image/jpeg" def _fetch_image_raw(url: str): resp = requests.get(url) resp.raise_for_status() raw = resp.content mime = _detect_image_mime(raw) ext_map = { "image/jpeg": ".jpg", "image/png": ".png", "image/gif": ".gif", "image/bmp": ".bmp", "image/webp": ".webp", } return raw, ext_map.get(mime, ".jpg") ``` The image-fetching functions are subsequently invoked for URLs extracted from downloaded Markdown: ```python def _inline_images_as_base64(md_content: str) -> str: def repl(m): url = m.group(1).strip() try: return "![]({})".format(_fetch_image_as_base64(url)) except Exception as e: return m.group(0) + " <!-- Download failed: {} -->".format(e) return IMG_SRC_PATTERN.sub(repl, md_content) def _inline_images_as_local(md_content: str, output_path: Path) -> str: out_dir = output_path.parent images_dir = output_path.stem + "_image ...[truncated 3929 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. **Allowlist trusted destinations** - Permit only HTTPS URLs on explicitly approved Baidu-owned result and asset hosts. - Do not accept arbitrary hosts merely because the original API request went to Baidu. 2. **Validate every URL** - Parse URLs with `urllib.parse.urlsplit`. - Reject unsupported schemes, embedded credentials, malformed ports, fragments where inappropriate, and non-HTTPS URLs. - Reject loopback, private, link-local, multicast, reserved, and unspecified IP ranges using the `ipaddress` module. 3. **Defend against DNS rebinding** - Resolve the hostname before connecting and validate every returned address. - Ensure the connection is made to a validated address while preserving correct TLS hostname verification. - Revalidate the destination after every redirect. 4. **Restrict redirects** - Disable automatic redirects or process them manually. - Apply the same scheme, hostname, port, and resolved-address validation to every redirect target. - Enforce a low redirect limit. 5. **Apply resource controls** - Set explicit connection and read timeouts. - Stream responses rather than reading them into memory at once. - Enforce maximum response sizes for Markdown, JSON, and images. - Validate `Content-Type` and image signatures before embedding or writing content. 6. **Constrain image sources** - Prefer image URLs explicitly returned as structured fields by the trusted OCR API. - Do not automatically fetch arbitrary `<img src>` values from generated Markdown. - If arbitrary image retrieval is required, expose it as an opt-in mode with a clear security warning and strict network isolation. 7. **Add security tests** - Test rejection of loopback, RFC1918, link-local, IPv6 private, and metadata-service addresses. - Test redirects from an allowed host to a prohibited host. - Test DNS rebinding scenarios and oversized or slow responses. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
Findings (23)

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding
A description-behavior mismatch is dangerous because users may approve execution based on the stated purpose while the underlying code performs materially different actions. Here, the mismatch is especially concerning because the declared use of Baidu OCR and document conversion does not align with the reported behavior, which suggests generating sample files and using different tooling, creating room for deceptive or unintended operations.

Lp3

Medium
Category
MCP Least Privilege
Confidence
91% confidence
Finding
The skill advertises capabilities that imply reading files, writing output, using environment secrets, invoking shell tools, and making network requests, but it declares no explicit tool scope or permission boundaries. In an agent setting, this weakens reviewability and can allow the skill to access more resources than users or operators would reasonably expect.

Vague Triggers

Medium
Confidence
89% confidence
Finding
The manifest description says 'Use when 扫描PDF转Markdown、pdf ocr、图像识别、电子书转Markdown、ebook to markdown.' Several phrases, especially '图像识别' and 'pdf ocr', are broad and could overlap with many unrelated OCR or image-recognition requests, and the file does not provide exclusion conditions or negative examples to narrow activation scope.

Natural-Language Policy Violations

Medium
Confidence
88% confidence
Finding
Forcing a specific OCR provider without user opt-in can expose potentially sensitive document contents to a third-party service without informed consent. In this skill's context, inputs may include scanned PDFs, images, and ebooks, which often contain personal, financial, or proprietary information, making silent provider lock-in more risky.

Context-Inappropriate Capability

Medium
Confidence
89% confidence
Finding
The skill is described as converting PDF/PNG/JPEG/MOBI/EPUB to Markdown using Baidu OCR, but this helper script creates test fixtures and spawns the external `ebook-convert` command to generate a MOBI file. Executing a local subprocess is not an obvious requirement of the stated conversion-to-Markdown capability and represents an additional capability beyond the manifest's purpose.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
print("请先运行 create_epub_fixture()")
        return
    try:
        subprocess.run(
            ["ebook-convert", str(epub), str(mobi)],
            capture_output=True, text=True, timeout=30
        )
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Intent-Code Divergence

Medium
Confidence
94% confidence
Finding
The module docstring explicitly states 'Uses Baidu OCR only,' but the implementation does more than plain OCR: it calls Baidu Paddle-VL document parser task APIs and, for ebook formats, runs Calibre's `ebook-convert` subprocess. This is an active contradiction between the documentation and the implemented processing pipeline.

Description-Behavior Mismatch

Medium
Confidence
95% confidence
Finding
The manifest/module description says the skill uses Baidu OCR only, which implies the conversion pipeline is limited to Baidu-backed processing. In practice, MOBI/EPUB inputs are first converted to PDF by spawning the external Calibre `ebook-convert` tool before OCR, so the actual behavior includes a separate local conversion dependency beyond Baidu OCR.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
raise ValueError("仅支持 mobi、epub 格式: {}".format(ext))

    try:
        result = subprocess.run(
            ["ebook-convert", str(path), "--version"],
            capture_output=True,
            text=True,
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
fd, tmp_pdf = tempfile.mkstemp(suffix=".pdf")
    os.close(fd)
    try:
        result = subprocess.run(
            ["ebook-convert", str(path), tmp_pdf],
            capture_output=True,
            text=True,
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
Image OCR requests send raw user-provided image data to Baidu without any explicit user-facing notice in this implementation. Because the skill accepts arbitrary images and base64 blobs, users may inadvertently expose sensitive personal or business information to a third party.

Tainted flow: 'data' from requests.post (line 221, network input) → requests.post (network output)

Medium
Category
Data Flow
Content
"detect_direction": "true",
            "paragraph": "true",
        }
        resp = requests.post(url, params=params, headers=headers, data=data)
        resp.raise_for_status()
        return resp.json()
    except Exception as e:
Confidence
90% confidence
Finding
This path sends image content to Baidu OCR over the network, which creates the same confidentiality risk as document upload for PDFs. Because the skill accepts local files and raw base64 image input, users may unknowingly transmit sensitive data to a third party if the interface does not clearly disclose that behavior.

Missing User Warnings

Medium
Confidence
97% confidence
Finding
The code submits complete file contents to an external OCR/parser API without any visible consent gate, warning, redaction step, or policy check. In the context of a conversion skill that may process private books, scans, IDs, or contracts, this is a meaningful data-exposure vulnerability even if the remote service is legitimate.

Tainted flow: 'data' from requests.post (line 221, network input) → requests.post (network output)

Medium
Category
Data Flow
Content
params = {"access_token": access_token}
    headers = {"Content-Type": "application/x-www-form-urlencoded"}
    data = {"file_data": file_data, "file_name": file_name}
    resp = requests.post(PADDLE_VL_SUBMIT_URL, params=params, headers=headers, data=data)
    if resp.status_code != 200:
        raise RuntimeError("提交任务失败: HTTP {} - {}".format(resp.status_code, resp.text))
    j = resp.json()
Confidence
91% confidence
Finding
The skill uploads the full local document content, base64-encoded, to Baidu's remote document parser service. In a document-conversion skill, this is contextually expected, but it is still a real confidentiality/privacy issue because sensitive user documents leave the local environment without an explicit consent or warning flow in the code.

Tainted flow: 'data' from requests.post (line 221, network input) → requests.post (network output)

Medium
Category
Data Flow
Content
params = {"access_token": access_token}
    headers = {"Content-Type": "application/x-www-form-urlencoded"}
    data = {"task_id": task_id}
    resp = requests.post(PADDLE_VL_QUERY_URL, params=params, headers=headers, data=data)
    if resp.status_code != 200:
        raise RuntimeError("查询失败: HTTP {}".format(resp.status_code))
    j = resp.json()
Confidence
65% confidence
Finding
Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.

Context-Inappropriate Capability

Medium
Confidence
96% confidence
Finding
The script trusts image URLs embedded in Baidu parser output and fetches them server-side with requests.get, then inlines or stores the results. That creates an SSRF-style network pivot and privacy risk: a compromised or malicious upstream response could cause the host running the skill to make arbitrary outbound requests to internal or sensitive endpoints.

Missing User Warnings

Medium
Confidence
88% confidence
Finding
The tests invoke the OCR path using Baidu-backed processing, which will send fixture document or image contents to a third-party service when the required credentials are present. In a testing context this is still a real privacy and data-governance risk because developers or CI may run these tests without an explicit warning, consent step, or safeguards limiting inputs to non-sensitive data.

Natural-Language Policy Violations

Low
Confidence
87% confidence
Finding
The visible instructional/content text is presented in Chinese, and the file does not indicate that this language choice is optional or tied to a documented region-specific purpose. Under the language/locale policy, natural-language content that imposes a specific language without user opt-in can be a policy concern.

Natural-Language Policy Violations

Low
Confidence
91% confidence
Finding
The markdown content is entirely in Chinese and provides no indication that users can choose another language or that the skill is intentionally limited to a Chinese-speaking context. Under the policy, language constraints should be opt-in or clearly justified when they restrict locale or language.

Natural-Language Policy Violations

Low
Confidence
92% confidence
Finding
This markdown file presents all instructional/content text in a single language and does not indicate that the user can choose a preferred language or locale. Under the stated policy, forcing a specific language without user opt-in is a natural-language policy concern.

Natural-Language Policy Violations

Low
Confidence
76% confidence
Finding
The natural-language description states 'Uses Baidu OCR only,' and the implementation later rejects other backends. This imposes a provider-specific constraint without presenting it as an explicit user choice or documenting why that restriction is required.

Context-Inappropriate Capability

Low
Confidence
78% confidence
Finding
The skill loads `.env` files and reads `BAIDU_OCR_API_KEY` and `BAIDU_OCR_SECRET_KEY` from environment variables to operate. While practical, credential loading is an additional capability not stated in the manifest's user-facing purpose, which presents the skill simply as a format converter.

Natural-Language Policy Violations

Low
Confidence
71% confidence
Finding
Multiple docstrings and assertions specify '仅百度 OCR' and require error text containing '百度', while the file's natural-language content is entirely Chinese with no opt-in or explanation of a locale/language restriction. Under the policy rule, forcing a specific language or locale without user choice can be a natural-language policy violation.

Static analysis

No suspicious patterns detected.