Back to skill

Security audit

PaddleOCR Document Parsing V2

Security checks for vulnerabilities and agentic risk

Overview

This skill appears to do legitimate remote PaddleOCR document parsing, but it can send full documents and API tokens to arbitrary configured endpoints without strong disclosure or destination validation.

Install only if you are comfortable sending selected documents or document URLs to the configured PaddleOCR/Baidu endpoint. Use trusted HTTPS endpoints, avoid confidential or regulated files unless approved, protect and scope the API token, and be cautious with async mode because it follows a result URL provided by the OCR service.

Vulnerability Patterns
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • 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
Findings (2)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/paddleocr_parse.py:30
Finding

Unrestricted OCR endpoints can receive sensitive documents and API credentials

Content
View full analysis

Vulnerability Details

File Location: scripts/paddleocr_parse.py:30-61, scripts/paddleocr_parse.py:69-105, and scripts/paddleocr_parse.sh:221-225
Vulnerability Type: Unvalidated sensitive-data transmission destination
Risk Level: High

Vulnerable Code

python
def sync_parse(file_path: str, file_type: int, api_url: str, token: str, verbose: bool = False) -> dict:
    """Synchronous document parsing."""
    
    if file_path.startswith("http"):
        # URL mode
        payload = {
            "file": file_path,
            "fileType": file_type,
            "useDocOrientationClassify": False,
            "useDocUnwarping": False,
        }
    else:
        # Local file mode
        path = Path(file_path)
        if not path.exists():
            print(f"Error: File not found: {file_path}", file=sys.stderr)
            sys.exit(1)
        
        file_bytes = path.read_bytes()
        file_data = base64.b64encode(file_bytes).decode("ascii")
        
        payload = {
            "file": file_data,
            "fileType": file_type,
            "useDocOrientationClassify": False,
            "useDocUnwarping": False,
        }
    
    headers = {
        "Authorization": f"token {token}",
        "Content-Type": "application/json"
    }
    
    if verbose:
        print(f"Making sync request to: {api_url}", file=sys.stderr)
    
    response = requests.post(api_url, json=payload, headers=headers, timeout=600)
python
if file_path.startswith("http"):
    # URL Mode
    headers["Content-Type"] = "application/json"
    payload = {
        "fileUrl": file_path,
        "model": model,
        "optionalPayload": optional_payload
    }
    job_response = requests.post(job_url, json=payload, headers=headers)
else:
    # Local File Mode
    path = Path(file_path)
    if not path.exists():
        print(f"Error: File no
...[truncated 3125 chars]
Remediation
View remediation

Remediation Suggestions

  1. Require https:// for PADDLEOCR_API_URL, PADDLEOCR_JOB_URL, and all derived service URLs. Reject plaintext HTTP.
  2. Restrict destinations to documented PaddleOCR/Baidu domains or a configurable administrator-approved hostname allowlist.
  3. Parse URLs with a standard URL parser and reject embedded credentials, fragments, unexpected ports, malformed hosts, and non-HTTP schemes.
  4. Resolve destination hosts and reject loopback, private, link-local, multicast, and cloud-metadata address ranges unless an administrator has explicitly approved a private deployment.
  5. Revalidate the destination after DNS resolution and on every redirect, or disable redirects entirely.
  6. Display a clear confirmation or policy notice before uploading a local document, identifying the exact destination hostname.
  7. Use narrowly scoped, short-lived API tokens and rotate any token suspected of having been sent to an untrusted endpoint.
  8. Add connection and read timeouts to asynchronous requests, consistent with the synchronous request.
  9. Document that Base64 does not protect document confidentiality and that users must only configure trusted HTTPS endpoints.

T05 · Unauthorized Access and Privilege Escalation

Warning
Location
scripts/paddleocr_parse.py:139
Finding

Server-controlled result URL permits server-side request forgery

Content
View full analysis

Vulnerability Details

File Location: scripts/paddleocr_parse.py:139-145
Vulnerability Type: Server-side request forgery through an unvalidated result URL
Risk Level: Medium

Vulnerable Code

python
elif state == 'done':
    extracted = data['extractProgress']['extractedPages']
    print(f"Status: done ({extracted} pages extracted)", file=sys.stderr)
    jsonl_url = data['resultUrl']['jsonUrl']
    break
python
# Fetch JSONL results
if jsonl_url:
    jsonl_response = requests.get(jsonl_url)
    jsonl_response.raise_for_status()
    
    lines = jsonl_response.text.strip().split('\n')
    results = []

Technical Analysis

Once an asynchronous OCR job reports completion, the Skill extracts data.resultUrl.jsonUrl from the remote server's response and passes that value directly to requests.get. No scheme, hostname, port, resolved address, or redirect validation is performed.

A compromised, malicious, or incorrectly configured OCR endpoint can therefore instruct the machine running the Skill to issue a GET request to an arbitrary URL. Potential targets include loopback services, private-network hosts, link-local services, and cloud instance metadata endpoints.

The result request has no explicit timeout. Consequently, an attacker-controlled destination can also cause the Skill to wait for an extended period, creating a denial-of-service condition.

Attack Path

  1. The user configures a malicious or compromised asynchronous OCR endpoint in PADDLEOCR_JOB_URL, or a legitimate endpoint becomes compromised.
  2. The user submits an asynchronous OCR job.
  3. During polling, the endpoint returns a completed job response containing an attacker-selected value in data.resultUrl.jsonUrl.
  4. The attacker sets that value to an internal target such as a loopback administration interface, a private-network service, or a cloud metadata address.
  5. The Skill executes `requests ...[truncated 1230 chars]
Remediation
View remediation

Remediation Suggestions

  1. Accept result URLs only over HTTPS.
  2. Restrict result URLs to the expected PaddleOCR result-storage domains or to a documented trusted-host allowlist.
  3. Resolve the hostname before connecting and reject loopback, private, link-local, multicast, unspecified, and cloud-metadata address ranges.
  4. Disable automatic redirects for result downloads, or independently validate every redirect target before following it.
  5. Protect against DNS rebinding by validating the address actually used for the connection, not only the original hostname.
  6. Reject embedded credentials, unexpected ports, nonstandard schemes, and malformed URLs.
  7. Add explicit connection and read timeouts, along with a maximum response-size limit.
  8. Prefer an API design in which the trusted job endpoint returns the result directly or returns a signed URL constrained to a known storage domain.
  9. Validate the response content type and JSONL schema before processing it.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (14)

Tainted flow: 'payload' from pathlib.Path.read_bytes (line 50, file read) → requests.post (network output)

High
Category
Data Flow
Confidence
80% confidence
Finding

File contents flow to a network sink. This may indicate data exfiltration of sensitive files.

Content

Scanner excerpt · scripts/paddleocr_parse.py (reported line 65)May include surrounding context.

python
if verbose:
        print(f"Making sync request to: {api_url}", file=sys.stderr)
    
    response = requests.post(api_url, json=payload, headers=headers, timeout=600)
    
    if response.status_code != 200:
        print(f"Error: HTTP {response.status_code}", file=sys.stderr)

Tainted flow: 'payload' from pathlib.Path.read_bytes (line 50, file read) → requests.post (network output)

High
Category
Data Flow
Confidence
80% confidence
Finding

File contents flow to a network sink. This may indicate data exfiltration of sensitive files.

Content

Scanner excerpt · scripts/paddleocr_parse.py (reported line 99)May include surrounding context.

python
"model": model,
            "optionalPayload": optional_payload
        }
        job_response = requests.post(job_url, json=payload, headers=headers)
    else:
        # Local File Mode
        path = Path(file_path)

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/paddleocr_parse.sh (reported line 29)May include surrounding context.

sh
-h, --help                   Show this help message

ENVIRONMENT:
    PADDLEOCR_ACCESS_TOKEN       Required: API access token
    PADDLEOCR_API_URL            Required: Sync mode endpoint URL
    PADDLEOCR_JOB_URL            Required for async: Async endpoint URL
    PADDLEOCR_MODEL              Optional: Model name [default: PaddleOCR-VL-1.5]

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
95% confidence
Finding

The skill advertises capabilities that imply environment access, local file read/write, shell execution, and outbound network use, but it does not declare any explicit tool scope such as permissions or allowed-tools. That creates a transparency and least-privilege gap: users and hosting platforms cannot easily constrain or review what the skill may do, increasing the risk of unintended command execution, data access, or exfiltration through the OCR workflow.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

The skill describes parsing local documents and URLs via PaddleOCR's API but does not clearly warn users that provided files and referenced URLs are transmitted to an external third-party service. This can lead to unintentional disclosure of sensitive documents, internal URLs, or regulated data because users may reasonably assume processing is local from the workflow examples alone.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

When a local file is provided, the script reads its full contents and transmits them to a remote OCR API without a clear non-verbose warning or confirmation at the point of upload. This can expose sensitive document contents to an external service if a user assumes processing is local or overlooks the environment-based endpoint configuration.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
80% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · scripts/paddleocr_parse.py (reported line 65)May include surrounding context.

python
if verbose:
        print(f"Making sync request to: {api_url}", file=sys.stderr)
    
    response = requests.post(api_url, json=payload, headers=headers, timeout=600)
    
    if response.status_code != 200:
        print(f"Error: HTTP {response.status_code}", file=sys.stderr)

External Transmission

Medium
Category
Data Exfiltration
Confidence
80% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · scripts/paddleocr_parse.py (reported line 99)May include surrounding context.

python
"model": model,
            "optionalPayload": optional_payload
        }
        job_response = requests.post(job_url, json=payload, headers=headers)
    else:
        # Local File Mode
        path = Path(file_path)

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

Async mode uploads the local file directly to a remote job service with no prominent disclosure beyond the general tool description. This increases the risk of accidental disclosure of sensitive documents because users may not appreciate that the file itself, not just metadata, is being sent to a third party.

Content

No source excerpt is available for this finding.

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

python
}
        with open(file_path, "rb") as f:
            files = {"file": f}
            job_response = requests.post(job_url, headers=headers, data=data, files=files)
    
    if verbose:
        print(f"Response status: {job_response.status_code}", file=sys.stderr)

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

The script writes the full OCR request payload, including raw document contents or a document URL, to a temporary file on disk before transmission. This creates an unnecessary at-rest exposure window where sensitive document data may be readable by other local processes, backups, or forensic recovery, especially if the temp directory is shared or permissions are weak.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
70% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · scripts/paddleocr_parse.sh (reported line 223)May include surrounding context.

sh
}
trap cleanup EXIT

response=$(curl -s -X POST "$PADDLEOCR_API_URL" \
    -m 600 \
    --fail-with-body \
    -H "Authorization: token $PADDLEOCR_ACCESS_TOKEN" \

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The default behavior sends document contents or a supplied URL to a remote OCR API, but the interface does not prominently warn users that potentially sensitive documents leave the local environment. In a document-parsing skill, this is a real privacy and data-handling risk because users may process confidential files without understanding they are being transmitted to a third-party service.

Content

No source excerpt is available for this finding.

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
76% confidence
Finding

The script writes parsed OCR output to a user-specified file path when --output is provided. Although this behavior is implied by the argument name, there is no explicit warning or explanatory comment near the write operation itself.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.