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

Content
View full analysis
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) + " ".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
View remediation
` 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.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

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.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
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.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
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.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
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.

Content

No source excerpt is available for this finding.

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · fixtures/create_fixtures.py (reported line 124)May include surrounding context.

python
print("请先运行 create_epub_fixture()")
        return
    try:
        subprocess.run(
            ["ebook-convert", str(epub), str(mobi)],
            capture_output=True, text=True, timeout=30
        )

Intent-Code Divergence

Medium
Category
Not specified by scanner
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.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
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.

Content

No source excerpt is available for this finding.

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · scripts/ebook_to_md.py (reported line 71)May include surrounding context.

python
raise ValueError("仅支持 mobi、epub 格式: {}".format(ext))

    try:
        result = subprocess.run(
            ["ebook-convert", str(path), "--version"],
            capture_output=True,
            text=True,

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · scripts/ebook_to_md.py (reported line 88)May include surrounding context.

python
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,

Missing User Warnings

Medium
Category
Not specified by scanner
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.

Content

No source excerpt is available for this finding.

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

Medium
Category
Data Flow
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.

Content

Scanner excerpt · scripts/ebook_to_md.py (reported line 158)May include surrounding context.

python
"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:

Missing User Warnings

Medium
Category
Not specified by scanner
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.

Content

No source excerpt is available for this finding.

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

Medium
Category
Data Flow
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.

Content

Scanner excerpt · scripts/ebook_to_md.py (reported line 204)May include surrounding context.

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

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

Medium
Category
Data Flow
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.

Content

Scanner excerpt · scripts/ebook_to_md.py (reported line 222)May include surrounding context.

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

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
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.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
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.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
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.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
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.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
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.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
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.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Low
Category
Not specified by scanner
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.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
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.

Content

No source excerpt is available for this finding.