Back to skill

Security audit

MinerU PDF Extractor

Security checks for vulnerabilities and agentic risk

Overview

This skill appears purpose-aligned, but it handles API tokens, uploads PDFs to a third-party service, and extracts remote ZIP results with enough scoping and validation gaps to require review before installation.

Install only if you are comfortable sending PDFs, URLs, document metadata, and extracted results through MinerU-controlled services. Use a dedicated low-privilege MinerU token, do not inherit or set MINERU_BASE_URL from untrusted environments, avoid sensitive or regulated documents unless approved, and inspect downloaded ZIP contents in a controlled directory before trusting extracted files.

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

T09 · Insecure Skill Coding Practices

Error
Location
scripts/local_file_step1_apply_upload_url.sh:27
Finding
Unvalidated API Base URL Can Redirect Bearer Credentials to an Attacker-Controlled Server<![CDATA[ ## Vulnerability Details **File Locations**: - `scripts/local_file_step1_apply_upload_url.sh:27-29,94-98` - `scripts/local_file_step3_poll_result.sh:7-9,41-43` - `scripts/online_file_step1_submit_task.sh:27-29,95-99` - `scripts/online_file_step2_poll_result.sh:23-25,62-64` **Vulnerability Type**: Credential disclosure through an unvalidated configurable endpoint **Risk Level**: High ### Vulnerable Code ```bash # Support MINERU_TOKEN or MINERU_API_KEY environment variables MINERU_TOKEN="${MINERU_TOKEN:-${MINERU_API_KEY:-}}" MINERU_BASE_URL="${MINERU_BASE_URL:-https://mineru.net/api/v4}" ``` The configured endpoint is subsequently used with the bearer credential: ```bash RESPONSE=$(curl -s -X POST "${MINERU_BASE_URL}/file-urls/batch" \ -H "Content-Type: application/json" \ -H "Authorization: Bearer ${MINERU_TOKEN}" \ -d "$JSON_PAYLOAD") ``` The same pattern is present in the polling and online-submission scripts: ```bash RESPONSE=$(curl -s -X GET "${MINERU_BASE_URL}/extract-results/batch/${BATCH_ID}" \ -H "Content-Type: application/json" \ -H "Authorization: Bearer ${MINERU_TOKEN}") ``` ```bash RESPONSE=$(curl -s -X POST "${MINERU_BASE_URL}/extract/task" \ -H "Content-Type: application/json" \ -H "Authorization: Bearer ${MINERU_TOKEN}" \ -d "$JSON_PAYLOAD") ``` ```bash RESPONSE=$(curl -s -X GET "${MINERU_BASE_URL}/extract/task/${TASK_ID}" \ -H "Content-Type: application/json" \ -H "Authorization: Bearer ${MINERU_TOKEN}") ``` ### Technical Analysis `MINERU_BASE_URL` is read directly from the process environment without validating its scheme, hostname, port, or path. Every authenticated API request sends `MINERU_TOKEN` or `MINERU_API_KEY` to this address in an `Authorization: Bearer` header. Environment-based endpoint configuration can be useful for testing or private deployments, but attaching a sensitive credential to an arbitrary endpoint violates least-trust principles. A malicious wrapper, compromise ...[truncated 1499 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Enforce the official endpoint by default and do not expose arbitrary endpoint replacement in normal operation: ```bash MINERU_BASE_URL="https://mineru.net/api/v4" ``` 2. If custom deployments must be supported, require an explicit opt-in and validate the endpoint with a strict allowlist: ```bash case "$MINERU_BASE_URL" in "https://mineru.net/api/v4") ;; *) echo "Error: Unapproved MinerU API endpoint" >&2 exit 1 ;; esac ``` 3. Require HTTPS and reject embedded credentials, unexpected ports, fragments, and malformed hosts. Use a proper URL parser where possible rather than a permissive shell regular expression. 4. Use separate credentials for custom or test endpoints. Never send the production MinerU token to an endpoint merely because it was supplied through an environment variable. 5. Add `curl` hardening such as `--proto '=https' --fail-with-body --show-error` and reasonable connection and request timeouts. 6. Document that overriding an authenticated service endpoint is security-sensitive, and avoid inheriting the variable from untrusted job or wrapper environments. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/local_file_step3_poll_result.sh:25
Finding
Unvalidated Polling Arguments Are Evaluated in Bash Arithmetic Contexts<![CDATA[ ## Vulnerability Details **File Locations**: - `scripts/local_file_step3_poll_result.sh:25-26,37,89` - `scripts/online_file_step2_poll_result.sh:41-42,57,157` **Vulnerability Type**: Bash arithmetic-expression injection **Risk Level**: High ### Vulnerable Code Local-file polling accepts arguments without numeric validation and uses them in arithmetic and command contexts: ```bash MAX_RETRIES="${2:-60}" RETRY_INTERVAL="${3:-5}" echo "=== Step 3: Poll Extraction Results ===" echo "Batch ID: $BATCH_ID" echo "Max Retries: $MAX_RETRIES" echo "Retry Interval: $RETRY_INTERVAL seconds" echo "" echo "Waiting 5 seconds for system to start processing..." sleep 5 for ((attempt=1; attempt<=MAX_RETRIES; attempt++)); do ``` It also evaluates both values in the timeout calculation: ```bash echo "❌ Polling timeout, waited $((MAX_RETRIES * RETRY_INTERVAL)) seconds" ``` The online polling script contains the equivalent pattern: ```bash OUTPUT_DIR="${2:-online_result}" MAX_RETRIES="${3:-60}" RETRY_INTERVAL="${4:-5}" # Validate directory name OUTPUT_DIR=$(validate_dirname "$OUTPUT_DIR") ``` ```bash for ((attempt=1; attempt<=MAX_RETRIES; attempt++)); do ``` ```bash echo "❌ Polling timeout, waited $((MAX_RETRIES * RETRY_INTERVAL)) seconds" ``` The retry interval is also passed to `sleep` without validation or quoting: ```bash sleep $RETRY_INTERVAL ``` ### Technical Analysis Bash arithmetic contexts do not treat variable contents as inert decimal strings. Values referenced by arithmetic expressions can themselves be interpreted as arithmetic syntax, including variable references and array subscripts. In affected Bash evaluation paths, crafted expressions involving array subscripts can cause command substitutions to be evaluated. Because `MAX_RETRIES` and `RETRY_INTERVAL` originate directly from positional arguments and are never restricted to decimal integers, an attacker who controls script arguments can supply an arithmetic expression rather than a number. ...[truncated 1827 chars]
Remediation
<![CDATA[ ## Remediation Suggestions Validate every numeric argument immediately after assignment and before any arithmetic or command use: ```bash MAX_RETRIES="${2:-60}" RETRY_INTERVAL="${3:-5}" if [[ ! "$MAX_RETRIES" =~ ^[0-9]+$ ]]; then echo "Error: max_retries must be a positive decimal integer" >&2 exit 1 fi if [[ ! "$RETRY_INTERVAL" =~ ^[0-9]+$ ]]; then echo "Error: retry_interval must be a non-negative decimal integer" >&2 exit 1 fi if (( 10#$MAX_RETRIES < 1 || 10#$MAX_RETRIES > 1000 )); then echo "Error: max_retries is outside the permitted range" >&2 exit 1 fi if (( 10#$RETRY_INTERVAL > 3600 )); then echo "Error: retry_interval is outside the permitted range" >&2 exit 1 fi ``` Apply equivalent validation to arguments 3 and 4 of `online_file_step2_poll_result.sh`. Additional hardening: - Normalize validated values to integers before reuse. - Quote the sleep operand: `sleep "$RETRY_INTERVAL"`. - Enforce upper bounds to prevent excessive loops and denial of service. - Validate `BATCH_ID` and `TASK_ID` against their expected UUID or identifier format. - Prefer `set -euo pipefail` and explicitly handle failed HTTP and parsing operations. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/local_file_step4_download.sh:71
Finding
Downloaded Result Archives Are Extracted Without Validating Individual Entry Paths<![CDATA[ ## Vulnerability Details **File Locations**: - `scripts/local_file_step4_download.sh:71-91` - `scripts/online_file_step2_poll_result.sh:100-127` **Vulnerability Type**: Unsafe archive extraction and redirect trust **Risk Level**: Medium ### Vulnerable Code The local-result script validates archive integrity but not archive entry names or types: ```bash # Download ZIP echo "📥 Downloading..." curl -L -o "$ZIP_FILENAME" "$ZIP_URL" if [ ! -f "$ZIP_FILENAME" ]; then echo "❌ Download failed" exit 1 fi # Validate ZIP file if ! unzip -t "$ZIP_FILENAME" &>/dev/null; then echo "❌ Error: Invalid ZIP file" rm -f "$ZIP_FILENAME" exit 1 fi echo "✅ Download complete: $ZIP_FILENAME ($(du -h "$ZIP_FILENAME" | cut -f1))" echo "" # Extract echo "📦 Extracting..." mkdir -p "$EXTRACT_DIR" unzip -q "$ZIP_FILENAME" -d "$EXTRACT_DIR" ``` The online-result script uses the same extraction approach: ```bash # Validate ZIP URL if [[ ! "$ZIP_URL" =~ ^https://cdn-mineru\.openxlab\.org\.cn/ ]]; then echo "❌ Error: Invalid ZIP URL from API" exit 1 fi # Download and extract echo "=== Download and Extract Results ===" mkdir -p "$OUTPUT_DIR" ZIP_NAME="result.zip" echo "📥 Downloading..." curl -L -o "${OUTPUT_DIR}/${ZIP_NAME}" "$ZIP_URL" # Validate ZIP if ! unzip -t "${OUTPUT_DIR}/${ZIP_NAME}" &>/dev/null; then echo "❌ Error: Invalid ZIP file downloaded" exit 1 fi echo "📦 Extracting..." unzip -q "${OUTPUT_DIR}/${ZIP_NAME}" -d "${OUTPUT_DIR}/extracted" ``` ### Technical Analysis `unzip -t` checks whether an archive is structurally valid and whether its compressed data passes integrity checks. It does not establish that every archive entry is safe to write. The scripts do not explicitly reject: - Absolute entry paths. - Entries containing `..` path components. - Unsafe symbolic links or link-based path traversal. - Special file types. - Excessive expansion ratios or uncompressed sizes. - Duplicate or conflicting paths. Behavior varie ...[truncated 1824 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Download into a newly created private temporary directory: ```bash TEMP_DIR=$(mktemp -d) chmod 700 "$TEMP_DIR" ``` 2. Restrict redirects. Either disable redirects or permit only HTTPS redirects to explicitly approved hosts. Validate the effective URL after transfer. 3. Enumerate archive entries before extraction and reject any entry that: - Is absolute. - Contains a `..` path component. - Resolves outside the extraction root. - Represents a symbolic link, hard link, device, FIFO, or other unsupported type. - Exceeds configured per-file or total uncompressed-size limits. 4. Prefer a maintained archive library that performs canonical-path checks before writing each entry. For every destination, resolve the parent path and verify that it remains under the canonical extraction root. 5. Extract into an empty staging directory with restrictive permissions. Only move expected files such as `full.md`, `images/`, `content_list.json`, and `layout.json` into the final output after validation. 6. Add download and resource limits, including maximum compressed size, maximum total uncompressed size, maximum number of entries, timeouts, and minimum available disk-space checks. 7. Use `curl --fail-with-body --show-error --proto '=https'` and verify the HTTP result before attempting archive validation. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (59)

Credential Access

High
Category
Privilege Escalation
Content
# 安全检查: 验证 URL 格式
    # 必须以 http:// 或 https:// 开头,以 .pdf 结尾
    # 这可以防止:
    # - 文件协议攻击 (file:///etc/passwd)
    # - JavaScript 协议攻击 (javascript:alert(1))
    # - 其他恶意协议
    if [[ ! "$url" =~ ^https?://[a-zA-Z0-9.-]+/.*\.pdf$ ]]; then
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
# 安全检查: 验证 URL 格式
    # 必须以 http:// 或 https:// 开头,以 .pdf 结尾
    # 这可以防止:
    # - 文件协议攻击 (file:///etc/passwd)
    # - JavaScript 协议攻击 (javascript:alert(1))
    # - 其他恶意协议
    if [[ ! "$url" =~ ^https?://[a-zA-Z0-9.-]+/.*\.pdf$ ]]; then
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
# 安全检查: 验证 URL 格式
    # 必须以 http:// 或 https:// 开头,以 .pdf 结尾
    # 这可以防止:
    # - 文件协议攻击 (file:///etc/passwd)
    # - JavaScript 协议攻击 (javascript:alert(1))
    # - 其他恶意协议
    if [[ ! "$url" =~ ^https?://[a-zA-Z0-9.-]+/.*\.pdf$ ]]; then
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
# 安全检查: 验证 URL 格式
    # 必须以 http:// 或 https:// 开头,以 .pdf 结尾
    # 这可以防止:
    # - 文件协议攻击 (file:///etc/passwd)
    # - JavaScript 协议攻击 (javascript:alert(1))
    # - 其他恶意协议
    if [[ ! "$url" =~ ^https?://[a-zA-Z0-9.-]+/.*\.pdf$ ]]; then
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
# 安全检查: 验证 URL 格式
    # 必须以 http:// 或 https:// 开头,以 .pdf 结尾
    # 这可以防止:
    # - 文件协议攻击 (file:///etc/passwd)
    # - JavaScript 协议攻击 (javascript:alert(1))
    # - 其他恶意协议
    if [[ ! "$url" =~ ^https?://[a-zA-Z0-9.-]+/.*\.pdf$ ]]; then
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
# 安全检查: 验证 URL 格式
    # 必须以 http:// 或 https:// 开头,以 .pdf 结尾
    # 这可以防止:
    # - 文件协议攻击 (file:///etc/passwd)
    # - JavaScript 协议攻击 (javascript:alert(1))
    # - 其他恶意协议
    if [[ ! "$url" =~ ^https?://[a-zA-Z0-9.-]+/.*\.pdf$ ]]; then
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Tool Parameter Abuse

High
Category
Tool Misuse
Content
# 安全: 解压前验证 ZIP 文件
if ! unzip -t "${OUTPUT_DIR}/result.zip" &>/dev/null; then
    echo "❌ 错误: 无效的 ZIP 文件"
    rm -f "${OUTPUT_DIR}/result.zip"
    exit 1
fi
Confidence
85% confidence
Finding
Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).

Tool Parameter Abuse

High
Category
Tool Misuse
Content
# 安全: 解压前验证 ZIP 文件
if ! unzip -t "${OUTPUT_DIR}/result.zip" &>/dev/null; then
    echo "❌ 错误: 无效的 ZIP 文件"
    rm -f "${OUTPUT_DIR}/result.zip"
    exit 1
fi
Confidence
85% confidence
Finding
Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).

Tool Parameter Abuse

High
Category
Tool Misuse
Content
# 安全: 解压前验证 ZIP 文件
if ! unzip -t "${OUTPUT_DIR}/result.zip" &>/dev/null; then
    echo "❌ 错误: 无效的 ZIP 文件"
    rm -f "${OUTPUT_DIR}/result.zip"
    exit 1
fi
Confidence
85% confidence
Finding
Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).

Tool Parameter Abuse

High
Category
Tool Misuse
Content
# 安全: 解压前验证 ZIP 文件
if ! unzip -t "${OUTPUT_DIR}/result.zip" &>/dev/null; then
    echo "❌ 错误: 无效的 ZIP 文件"
    rm -f "${OUTPUT_DIR}/result.zip"
    exit 1
fi
Confidence
85% confidence
Finding
Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).

External Script Fetching

High
Category
Supply Chain
Content
}"
fi

RESPONSE=$(curl -s -X POST "${MINERU_BASE_URL}/file-urls/batch" \
    -H "Content-Type: application/json" \
    -H "Authorization: Bearer ${MINERU_TOKEN}" \
    -d "$JSON_PAYLOAD")
Confidence
90% confidence
Finding
Remote code is downloaded and executed. This bypasses code review and could introduce malicious code.

External Script Fetching

High
Category
Supply Chain
Content
}"
fi

RESPONSE=$(curl -s -X POST "${MINERU_BASE_URL}/extract/task" \
    -H "Content-Type: application/json" \
    -H "Authorization: Bearer ${MINERU_TOKEN}" \
    -d "$JSON_PAYLOAD")
Confidence
90% confidence
Finding
Remote code is downloaded and executed. This bypasses code review and could introduce malicious code.

Lp3

Medium
Category
MCP Least Privilege
Confidence
87% confidence
Finding
The skill documents shell-based scripts and network/file-handling behavior but does not declare any tool scope such as permissions or allowed-tools. That omission can cause users or agent runtimes to execute shell actions with broader-than-expected capability, reducing transparency and weakening least-privilege controls.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The skill encourages uploading local PDFs and submitting remote PDF URLs to the external MinerU service but does not prominently warn that document contents and metadata are disclosed to a third party. In this context, users may process sensitive internal documents and unintentionally exfiltrate confidential data outside their trust boundary.

Intent-Code Divergence

Medium
Confidence
91% confidence
Finding
The documentation asserts that the skill includes input validation, sanitization, and protections against JSON injection and directory traversal, but this file does not substantiate those claims. Unverified security claims can mislead users into trusting unsafe inputs or outputs, increasing the chance that downstream script usage handles attacker-controlled paths, URLs, or JSON unsafely.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The documentation instructs users to upload local PDFs to a third-party MinerU service but does not prominently warn that document contents are being transmitted off-host to an external provider. Users may unknowingly send sensitive or regulated documents, creating confidentiality, privacy, and compliance risk in a skill specifically designed for document extraction.

Intent-Code Divergence

Medium
Confidence
89% confidence
Finding
Lines L322-L323 assert that the skill contains input validation, sanitization, and extra security checks. In the provided file, these are blanket security claims about the implementation rather than observable behavior, creating an intent/documentation divergence unless the underlying scripts demonstrably implement them.

Missing User Warnings

Medium
Confidence
93% confidence
Finding
The document presents local PDF parsing as a normal workflow but does not clearly warn that the file contents are transmitted to a third-party remote API. Users may unknowingly upload sensitive local documents, creating confidentiality, compliance, and retention risks that are especially relevant for enterprise or regulated data.

External Transmission

Medium
Category
Data Exfiltration
Content
**Command:**
```bash
curl -s -X POST "${MINERU_BASE_URL}/file-urls/batch" \
    -H "Content-Type: application/json" \
    -H "Authorization: Bearer ${MINERU_TOKEN}" \
    -d '{
Confidence
93% confidence
Finding
This command initiates a workflow that sends metadata and authorizes upload of a local document to a remote service using a bearer token, but the guide does not contextualize the security/privacy implications. In this skill's context, external transmission is expected functionality, yet it still represents a real risk because users may disclose sensitive local data to a third party without clear warning.

Natural-Language Policy Violations

Medium
Confidence
96% confidence
Finding
The guide hard-codes the `language` parameter to `ch` in the API example, which imposes a specific locale choice rather than offering a user-selected option. This is a natural-language policy concern because the document presents the setting as the default workflow rather than documenting a justified region-specific constraint.

Missing User Warnings

Medium
Confidence
89% confidence
Finding
The upload and result-download steps operationalize external transfer of document contents and processed outputs without an explicit user-facing warning about remote processing and retention implications. This increases the chance that operators handle confidential PDFs under the false assumption that processing is purely local.

External Transmission

Medium
Category
Data Exfiltration
Content
**Command:**
```bash
# Download ZIP package
curl -L -o "result.zip" \
  "YOUR_FULL_ZIP_URL_FROM_STEP3"

# Extract to folder
Confidence
90% confidence
Finding
Downloading the result ZIP from a remote URL is inherent to the service design, but it is still an external data transfer and can expose processed content to third-party infrastructure. Combined with subsequent extraction, it also expands trust to remotely supplied artifacts that may be malicious if the service or distribution path is compromised.

Natural-Language Policy Violations

Medium
Confidence
97% confidence
Finding
Within the embedded shell script, the JSON payload sets `language` to `ch` both in the `jq` path and the fallback string payload. This forces a locale choice without opt-in and is not justified in the surrounding documentation as a region-specific or compliance-driven restriction.

External Transmission

Medium
Category
Data Exfiltration
Content
fi

# Send request to MinerU API
STEP1_RESPONSE=$(curl -s -X POST "${MINERU_BASE_URL}/file-urls/batch" \
    -H "Content-Type: application/json" \
    -H "Authorization: Bearer ${MINERU_TOKEN}" \
    -d "$JSON_PAYLOAD")
Confidence
93% confidence
Finding
The scripted POST request automates transmission to a third-party API and uses a bearer token from the environment, but the guide frames this as routine without a clear privacy/security notice. In a local-file skill, this is materially relevant because users may believe the operation is local when it is not.

External Transmission

Medium
Category
Data Exfiltration
Content
mkdir -p "$OUTPUT_DIR"

# Download result ZIP
curl -L -o "${OUTPUT_DIR}/result.zip" "$ZIP_URL"

# SECURITY: Validate ZIP file before extraction
# Prevents extraction of malicious or corrupted archives
Confidence
91% confidence
Finding
The script downloads a remote ZIP result and then processes it locally, making the host trust externally provided content. Even with hostname validation and `unzip -t`, a compromised upstream could still deliver a harmful archive, so this is a meaningful supply-chain and data-transfer risk in context.

Static analysis

No suspicious patterns detected.