Back to skill

Security audit

mineru-agent-free

Security checks for vulnerabilities and agentic risk

Overview

This skill has a coherent document-parsing purpose, but it uploads user documents to a third-party service and trusts service-provided transfer URLs without enough user-facing warning or destination validation.

Review before installing if you may parse confidential, regulated, or internal documents. Use only files and URLs you are comfortable sending to MinerU and its upload storage, avoid links containing secrets or pre-signed tokens, and prefer adding a user confirmation plus URL allowlisting/timeouts before routine use.

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/mineru_parse.py:102
Finding

Unvalidated Service-Provided URLs Enable Arbitrary Outbound Transfers

Content
View full analysis

Vulnerability Details

File Location: scripts/mineru_parse.py, lines 37-38 and 102-106
Vulnerability Type: Unvalidated remote upload and download destinations
Risk Level: Medium

Vulnerable Code

python
md_url = data["markdown_url"]
md_resp = requests.get(md_url)
python
file_url = result["data"]["file_url"]

# Upload the file using the returned URL.
with open(file_path, "rb") as f:
    put_resp = requests.put(file_url, data=f)

Technical Analysis

The script directly trusts file_url and markdown_url received from the MinerU API. It does not validate their schemes, hostnames, ports, or resolved destinations before issuing requests. The requests library also follows redirects by default, allowing an initially acceptable URL to redirect to an untrusted destination.

In file mode, this trust boundary is particularly sensitive because the script opens the user-selected local file and sends its complete contents to the destination specified by file_url. If the upstream service or its response path were compromised, an attacker could substitute an attacker-controlled upload URL and receive the document.

The same issue affects markdown_url: a malicious response could cause the process to contact an arbitrary network destination and consume attacker-controlled content. These requests also lack explicit connect and read timeouts, and the Markdown response is loaded without a size limit. The polling timeout therefore does not guarantee that an individual network request cannot hang indefinitely.

This finding does not demonstrate malicious behavior by the current MinerU service. Exploitation requires control over, or compromise of, the API response path or a redirect destination trusted by the API.

Attack Path

  1. A user invokes the script with --file and selects a local document.
  2. The script sends a task request to the MinerU API.
  3. A compromised or malicious API resp ...[truncated 1010 chars]
Remediation
View remediation

Remediation Suggestions

  1. Require https for every upload and download URL.
  2. Validate hostnames against an explicit list of documented MinerU and object-storage domains. Use exact hostname or properly bounded suffix comparisons rather than substring matching.
  3. Reject URLs containing embedded credentials, unexpected ports, malformed hostnames, or unsupported schemes.
  4. Disable automatic redirects with allow_redirects=False. If redirects are required, validate every redirect destination before following it.
  5. Consider resolving destinations and rejecting loopback, link-local, private, multicast, and other prohibited IP ranges when access to internal network resources is not required.
  6. Configure explicit connect and read timeouts for every requests.get, requests.post, and requests.put call.
  7. Use raise_for_status() and validate the API response schema before reading task IDs or transfer URLs.
  8. Stream the Markdown download and enforce a maximum response size instead of loading an unrestricted response into memory.
  9. Document the approved upload providers and expected URL patterns so destination validation can be maintained when the service configuration changes.
  10. Clearly notify users that selected documents are transferred to a third-party parsing service before uploading sensitive material.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
  • 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 (13)

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
90% confidence
Finding

The skill invokes a script that performs network access to a third-party API and can write output files, but the manifest does not declare any explicit tool scope or permissions boundaries. This weakens reviewability and consent because the agent may use sensitive capabilities without clear, least-privilege documentation for users or operators.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The skill advertises a third-party parsing API but does not clearly warn that document URLs and uploaded files are transmitted to an external service. Users may unknowingly send confidential documents, internal links, or regulated data off-platform, creating privacy, confidentiality, and compliance risks.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

The skill instructs the agent to parse files directly from conversation context without any privacy or data-handling notice. In practice, this can cause users to share attachments expecting local handling while the agent silently uploads them to a third party, increasing the chance of unauthorized disclosure.

Content

No source excerpt is available for this finding.

Tainted flow: 'task_id' from requests.post (line 104, network input) → requests.get (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/mineru_parse.py (reported line 37)May include surrounding context.

python
"""轮询查询解析结果,返回 Markdown 文本或 None。"""
    start = time.time()
    while time.time() - start < timeout:
        resp = requests.get(f"{BASE_URL}/parse/{task_id}")
        result = resp.json()
        data = result.get("data", {})
        state = data.get("state", "unknown")

Tainted flow: 'md_url' from requests.get (line 44, network input) → requests.get (network output)

Medium
Category
Data Flow
Confidence
94% confidence
Finding

The script fetches markdown_url directly from remote API output and performs a second GET without validating the URL. If the API response is malicious or compromised, the client can be induced to make arbitrary outbound requests, potentially exposing metadata endpoints or downloading attacker-controlled content.

Content

Scanner excerpt · scripts/mineru_parse.py (reported line 46)May include surrounding context.

python
if state == "done":
            md_url = data["markdown_url"]
            print(f"[{elapsed}s] 解析完成", file=sys.stderr)
            md_resp = requests.get(md_url)
            md_resp.encoding = "utf-8"
            return md_resp.text

External Transmission

Medium
Category
Data Exfiltration
Confidence
89% confidence
Finding

The script transmits user-supplied document URLs to an external service for remote retrieval and parsing. In this skill's context, external transmission is expected, but it still matters because URLs may contain sensitive query strings, internal resource locations, or pre-signed access tokens that the user may not realize are being shared.

Content

Scanner excerpt · scripts/mineru_parse.py (reported line 70)May include surrounding context.

python
if page_range:
        data["page_range"] = page_range

    resp = requests.post(f"{BASE_URL}/parse/url", json=data)
    result = resp.json()
    if result.get("code") != 0:
        print(f"提交失败: {result.get('msg')}", file=sys.stderr)

Tainted flow: 'data' from requests.get (line 39, 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/mineru_parse.py (reported line 70)May include surrounding context.

python
if page_range:
        data["page_range"] = page_range

    resp = requests.post(f"{BASE_URL}/parse/url", json=data)
    result = resp.json()
    if result.get("code") != 0:
        print(f"提交失败: {result.get('msg')}", file=sys.stderr)

External Transmission

Medium
Category
Data Exfiltration
Confidence
97% confidence
Finding

This request initiates the remote file-processing workflow and, together with the subsequent PUT, causes local document contents and metadata to be transmitted off-host. In a document-parsing tool this is functionally intended, but it is still security-relevant because users may process confidential files without an explicit disclosure of the external transfer.

Content

Scanner excerpt · scripts/mineru_parse.py (reported line 98)May include surrounding context.

python
if page_range:
        data["page_range"] = page_range

    resp = requests.post(f"{BASE_URL}/parse/file", json=data)
    result = resp.json()
    if result.get("code") != 0:
        print(f"获取上传链接失败: {result.get('msg')}", file=sys.stderr)

Tainted flow: 'data' from requests.get (line 39, 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/mineru_parse.py (reported line 98)May include surrounding context.

python
if page_range:
        data["page_range"] = page_range

    resp = requests.post(f"{BASE_URL}/parse/file", json=data)
    result = resp.json()
    if result.get("code") != 0:
        print(f"获取上传链接失败: {result.get('msg')}", file=sys.stderr)

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
98% confidence
Finding

This script sends local file contents to an external service but does not provide an explicit privacy or data-transmission warning at the point of upload. In a document-parsing skill, users may unknowingly transmit confidential PDFs, Office files, or images to a third party, creating a real data exposure risk.

Content

No source excerpt is available for this finding.

Tainted flow: 'file_url' from requests.post (line 105, network input) → requests.put (network output)

Medium
Category
Data Flow
Confidence
95% confidence
Finding

The script uploads the full local file to a file_url obtained from a remote API without validating the destination host or warning the user about the trust boundary. If the service, DNS, or upstream response is compromised, sensitive local documents could be exfiltrated to an attacker-controlled endpoint.

Content

Scanner excerpt · scripts/mineru_parse.py (reported line 110)May include surrounding context.

python
# 2. PUT 上传文件到 OSS
    with open(file_path, "rb") as f:
        put_resp = requests.put(file_url, data=f)
        if put_resp.status_code not in (200, 201):
            print(f"文件上传失败, HTTP {put_resp.status_code}", file=sys.stderr)
            return None

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
89% confidence
Finding

The usage text states the parsing language defaults to 'ch', and the code also uses 'ch' as the default language parameter. This imposes a specific language/locale by default rather than prompting the user to choose or clearly obtaining opt-in.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
91% confidence
Finding

The CLI defines '--language' with a default of 'ch', which means the skill selects a specific language automatically if the user does not choose one. Under the language/locale policy, this is a locale choice made without explicit user opt-in.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.