Back to skill

Security audit

完美排版ocr

Security checks for vulnerabilities and agentic risk

Overview

This PDF OCR skill does what it claims, but it under-discloses that documents are uploaded to a third-party service and fetches service-provided URLs without destination validation.

Review before installing if you process confidential, regulated, legal, financial, medical, or proprietary PDFs. Use only with documents you are willing to send to the PaddleOCR cloud service, protect the PADDLEOCR_TOKEN environment variable, run in an isolated environment, and remove the work directory after use if you do not want intermediate copies retained.

Vulnerability Patterns
  • Insecure DependenciesIntroduces malicious components through unsafe dependency sources
  • 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 (3)

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/pipeline.py:119
Finding

Complete PDF Documents Are Uploaded to a Third-Party OCR Service Without Explicit Disclosure or Consent

Content
View full analysis
str: """Submit a PDF chunk to the OCR API. Returns job ID.""" headers = {"Authorization": f"bearer {TOKEN}"} data = {"model": MODEL, "optionalPayload": json.dumps(OPTIONAL_PAYLOAD)} with open(chunk_path, "rb") as f: resp = requests.post(API_URL, headers=headers, data=data, files={"file": f}, timeout=120) if resp.status_code != 200: raise RuntimeError(f"API error {resp.status_code}: {resp.text[:300]}") job_id = resp.json()["data"]["jobId"] print(f" Submitted → job {job_id}") return job_id ``` The Skill documentation describes an “OCR API” but does not explicitly explain that all pages and document contents are transmitted to `paddleocr.aistudio-app.com`: ```markdown Automated pipeline: **Split → OCR API → Layout PDF → Merge** ``` ### Technical Analysis The implementation splits the source PDF into chunks and uploads each chunk in full to a third-party cloud OCR endpoint. This transmission is functionally necessary for the selected cloud-based OCR implementation, but the Skill does not clearly identify the third-party recipient, request informed confirmation, explain potential service-side retention, or warn users not to submit confidential documents. PDFs processed by an OCR Skill may contain personal data, credentials, legal records, financial data, health information, or proprietary business material. Uploading such data crosses a significant trust boundary. The local work directory also retains complete PDF chunk ...[truncated 1526 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/pipeline.py:293
Finding

Server-Controlled Result and Image URLs Are Retrieved Without Destination Validation

Content
View full analysis
str: """Poll until the job is done. Returns JSONL download URL.""" headers = {"Authorization": f"bearer {TOKEN}"} while True: resp = requests.get(f"{API_URL}/{job_id}", headers=headers, timeout=30) resp.raise_for_status() data = resp.json()["data"] state = data["state"] if state == "done": prog = data.get("extractProgress", {}) print(f" Done — {prog.get('extractedPages', '?')} pages extracted") return data["resultUrl"]["jsonUrl"] ``` The returned URL is downloaded without validating its scheme or destination: ```python def download_results(jsonl_url: str, save_path: str) -> list[dict]: """Download (or load from cache) JSONL results. Returns list of page dicts.""" if os.path.exists(save_path): print(f" [cache] {save_path}") with open(save_path, encoding="utf-8") as f: return parse_jsonl(f.read()) resp = requests.get(jsonl_url, timeout=120) resp.raise_for_status() with open(save_path, "w", encoding="utf-8") as f: f.write(resp.text) print(f" Saved JSONL → {save_path}") return parse_jsonl(resp.text) ``` Image URLs from OCR output are also retrieved without validation: ```python _img_cache: dict = {} def fetch_image(url: str): """Download image from URL (cached). Returns PIL Image or None.""" if url in _img_cache: return _img_cache[url] try: data = urllib.request.urlopen(url, timeout=15).read() img = Image.open(io.BytesIO(data)).convert("RGB") _img_cache[url] = img return img except Exception: _im ...[truncated 3638 chars]
Remediation
View remediation

T08 · Insecure Dependencies

Note
Location
SKILL.md:38
Finding

Runtime Dependencies Are Installed Without Version or Integrity Pinning

Content
View full analysis
Remediation
View remediation
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
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (10)

Tainted flow: 'headers' from os.environ.get (line 136, credential/environment) → requests.post (network output)

Critical
Category
Data Flow
Confidence
90% confidence
Finding

Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Content

Scanner excerpt · scripts/pipeline.py (reported line 119)May include surrounding context.

python
data    = {"model": MODEL, "optionalPayload": json.dumps(OPTIONAL_PAYLOAD)}

    with open(chunk_path, "rb") as f:
        resp = requests.post(API_URL, headers=headers, data=data,
                             files={"file": f}, timeout=120)

    if resp.status_code != 200:

Tainted flow: 'headers' from os.environ.get (line 136, credential/environment) → requests.get (network output)

Critical
Category
Data Flow
Confidence
90% confidence
Finding

Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Content

Scanner excerpt · scripts/pipeline.py (reported line 139)May include surrounding context.

python
headers = {"Authorization": f"bearer {TOKEN}"}

    while True:
        resp = requests.get(f"{API_URL}/{job_id}", headers=headers, timeout=30)
        resp.raise_for_status()
        data  = resp.json()["data"]
        state = data["state"]

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
95% confidence
Finding

The skill instructs the agent to install packages, run a Python pipeline, write output and working files, and interact with an OCR API, but it declares no explicit tool scope or permission boundaries. That mismatch can cause the agent to invoke file, environment, and network capabilities without clear user-visible constraints, increasing the risk of unintended data access or transmission.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The trigger definition combines a generic file-path condition with broad phrases like "text recognition" and "layout preservation," which could match ordinary discussion about PDFs rather than a clear invocation. The description does not provide exclusions or negative examples to limit unintended activation.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
98% confidence
Finding

The skill describes an OCR pipeline that submits content to an external API and even mentions API polling, but it does not warn that PDF contents may leave the local system. Users may provide sensitive scanned documents under the assumption of local-only processing, leading to unintentional disclosure of confidential, personal, or regulated data.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The pipeline retrieves inputImage URLs from OCR result data using urllib.request.urlopen, which expands the skill from simple OCR submission into arbitrary remote fetching based on external service output. If the OCR service or its output is compromised, this can trigger server-side request forgery behavior, unexpected network access, or retrieval of internal-only resources from the environment running the skill.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The skill uploads PDF chunks to an external OCR API, which may expose sensitive document contents off-device without explicit user notice or consent. In an OCR skill this behavior is functionally expected, but it is still security-relevant because scanned PDFs often contain personal, legal, financial, or proprietary data.

Content

No source excerpt is available for this finding.

Tainted flow: 'data' from urllib.request.urlopen (line 295, 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/pipeline.py (reported line 119)May include surrounding context.

python
data    = {"model": MODEL, "optionalPayload": json.dumps(OPTIONAL_PAYLOAD)}

    with open(chunk_path, "rb") as f:
        resp = requests.post(API_URL, headers=headers, data=data,
                             files={"file": f}, timeout=120)

    if resp.status_code != 200:

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The render path downloads image URLs embedded or referenced in OCR results and inserts them into the output PDF. Because those URLs come from external OCR output, this enables attacker-influenced outbound requests and content inclusion, creating SSRF-style risk and making the local environment fetch arbitrary resources.

Content

No source excerpt is available for this finding.

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
84% confidence
Finding

The code reads a sensitive credential from the PADDLEOCR_TOKEN environment variable to authenticate outbound requests. Although this is expected for API access, there is no explicit disclosure in comments or user-facing text that the script consumes a credential from the environment.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.