Back to skill

Security audit

Pdf Ocr Tool

Security checks for vulnerabilities and agentic risk

Overview

This OCR skill appears purpose-aligned, but its install and data-handling paths need review before use.

Review the install commands before running them. Prefer package-manager or verified installer paths instead of curl piped to sh, avoid running setup as root unless necessary, process sensitive documents only against a trusted local Ollama service, and avoid running the PDF converter in directories containing important PNG files whose names match the PDF page prefix.

Vulnerability Patterns
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
  • 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
Findings (4)

T03 · Remote Payload Retrieval and Execution

Error
Location
SKILL.md:21
Finding
Unpinned Remote Installer Scripts Are Piped Directly to a Shell<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md:21-35`; duplicated in `README.md:23-37` **Vulnerability Type**: Remote payload retrieval and immediate execution **Risk Level**: High ### Vulnerable Code ```bash # Install Ollama curl -fsSL https://ollama.com/install.sh | sh ollama pull glm-ocr:q8_0 # Install poppler-utils (for PDF to image conversion) sudo apt install poppler-utils # Debian/Ubuntu brew install poppler # macOS # Install uv package manager curl -LsSf https://astral.sh/uv/install.sh | sh ``` ### Technical Analysis The installation instructions pipe responses from mutable external URLs directly into `sh`. The downloaded content is not pinned to a version, saved for inspection, checked against a cryptographic hash, or verified using a digital signature. Installing Ollama and `uv` is relevant to the declared OCR functionality, but immediate remote-script execution is not the minimum privilege or safest mechanism necessary to install those dependencies. The effective code executed by the user can change after the Skill package has been reviewed. Although the referenced domains appear related to the declared software, their apparent legitimacy does not eliminate the risk. Compromise of the remote publication process, hosting account, DNS resolution, certificate trust chain, or upstream infrastructure could turn these commands into arbitrary code execution. The similar commands in `hooks/post-install.sh:10-14` and `hooks/install-deps.sh:37-42` are only printed as recommendations and are not themselves piped to a shell by those hooks. The directly executable commands in the documentation remain dangerous because installation instructions are part of the Skill's operational behavior. ### Attack Path 1. An attacker compromises an installer publication account, hosting endpoint, or relevant network trust dependency. 2. The response served by `https://ollama.com/install.sh` or `https://astral.sh/uv/install.sh` is modifie ...[truncated 843 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove all `curl | sh` installation instructions from `SKILL.md` and `README.md`. 2. Prefer signed operating-system packages or documented package-manager commands. 3. If a standalone installer is unavoidable: - Pin a specific released version. - Download it as a separate file. - Verify a publisher signature or a checksum committed in the reviewed Skill package. - Display or inspect the downloaded script before execution. - Execute it without administrative privileges unless a specific operation requires elevation. 4. Document the exact expected source repository and publisher identity. 5. Use reproducible installation instructions so the audited payload cannot change independently of the Skill version. A safer pattern is: ```bash curl -fL -o installer.sh "https://example.invalid/releases/vX.Y.Z/installer.sh" echo "<audited-sha256> installer.sh" | sha256sum --check - sh installer.sh ``` The version and checksum must be obtained from an independently authenticated, reviewed release record rather than from the same mutable endpoint. ]]>

T08 · Insecure Dependencies

Warning
Location
hooks/install-deps.sh:7
Finding
Installation Hook Retrieves Mutable Dependency Metadata Without Integrity Verification<![CDATA[ ## Vulnerability Details **File Location**: `hooks/install-deps.sh:7-29, 44-62`; related dependency definitions in `pyproject.toml:35-49` **Vulnerability Type**: Unverified and non-reproducible dependency acquisition **Risk Level**: Medium ### Vulnerable Code ```bash SKILL_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" SKILL_NAME="pdf-ocr-tool" GITHUB_USER="nala0222" GITHUB_REPO="pdf-ocr-tool" GITHUB_BRANCH="master" # Function to copy file from local or GitHub copy_file() { local file=$1 local local_path="${SKILL_DIR}/${file}" local github_url="https://raw.githubusercontent.com/${GITHUB_USER}/${GITHUB_REPO}/refs/heads/${GITHUB_BRANCH}/${file}" if [ -f "${local_path}" ]; then echo "✅ Found local ${file}" cp "${local_path}" "${SKILL_DIR}/.tmp_${file}" return 0 else echo "⚠️ Local ${file} not found, trying GitHub..." if curl -sLf "${github_url}" -o "${SKILL_DIR}/.tmp_${file}"; then echo "✅ Downloaded ${file} from GitHub" return 0 else echo "❌ Failed to get ${file} from both local and GitHub" return 1 fi fi } cd "${SKILL_DIR}" echo "📋 Copying dependency files..." copy_file "pyproject.toml" || exit 1 copy_file "uv.lock" || exit 1 if [ ! -d ".venv" ]; then uv venv fi source .venv/bin/activate uv sync ``` The package dependencies also use broad lower bounds: ```toml dependencies = [ "requests>=2.31.0", "pillow>=10.0.0", ] ``` ### Technical Analysis The audited project does not contain `uv.lock`. Consequently, running the installation hook attempts to download that file from the mutable `master` branch of a hard-coded GitHub repository. The download has no commit pin, checksum, or signature verification. There is also a provenance inconsistency: `_meta.json:2` identifies the package owner as `tsukisama9292`, while the installation hook trusts GitHub user `nala0222`. The available files do not e ...[truncated 2210 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Generate, audit, and bundle `uv.lock` in the Skill package. 2. Remove the GitHub fallback from the installation hook. 3. Install dependencies strictly from the reviewed lockfile and fail closed if it is absent or invalid. 4. Pin dependency artifacts and hashes where supported. 5. If remote retrieval is indispensable: - Pin a full immutable Git commit rather than `master`. - Verify a checksum or digital signature included in the reviewed package. - Download into a private temporary directory. - Do not consume the file unless verification succeeds. 6. Reconcile and document the relationship between the package publisher and the hard-coded source repository. 7. Correct the hook logic so it does not download files that `uv sync` will ignore. 8. Avoid broad lower-bound-only dependencies for production installation; use a reviewed lockfile to establish exact transitive versions. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
utils/ollama_client.py:21
Finding
Complete Document Images May Be Transmitted to Arbitrary Hosts Over Plaintext HTTP<![CDATA[ ## Vulnerability Details **File Location**: `utils/ollama_client.py:21-41, 70-81, 107-126`; arbitrary host input exposed at `ocr_tool.py:199-207` **Vulnerability Type**: Plaintext transmission of potentially sensitive document contents **Risk Level**: Medium ### Vulnerable Code ```python def __init__( self, host: str = "localhost", port: str = "11434", model: str = "glm-ocr:q8_0", timeout: int = 120, max_retries: int = 3, retry_delay: float = 1.0 ): self.host = host self.port = port self.model = model self.timeout = timeout self.max_retries = max_retries self.retry_delay = retry_delay self.base_url = f"http://{host}:{port}" @staticmethod def encode_image_to_base64(image_path: str) -> str: with open(image_path, "rb") as f: return base64.b64encode(f.read()).decode("utf-8") # If an image is present, add its complete encoded contents if image_path: try: image_base64 = self.encode_image_to_base64(image_path) payload["images"] = [image_base64] except Exception as e: return f"❌ 無法讀取圖片:{e}" resp = requests.post( f"{self.base_url}/api/generate", json=payload, timeout=self.timeout ) ``` The destination can be supplied through the CLI: ```python parser.add_argument( "--host", default="localhost", help=f"Ollama 主機位置 (預設:localhost)" ) parser.add_argument( "--port", default="11434", help=f"Ollama 端口 (預設:11434)" ) ``` ### Technical Analysis The client always constructs an `http://` URL and does not support authenticated HTTPS. OCR input images—including complete PDF page renders and cropped regions—are base64-encoded and included in the request body. The default destination is localhost, which limits exposure in the normal configuration. However, the public `--host` option accepts arbitrary remote destinations without requiring explicit confirmation or warning that document contents will leave the local system. Plain HTTP ...[truncated 1264 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Restrict the default client to loopback addresses such as `127.0.0.1` and `::1`. 2. Reject non-loopback hosts unless the user supplies an explicit remote-processing opt-in flag. 3. Display a clear warning and confirmation before sending document data to a remote destination. 4. Add HTTPS support with strict certificate and hostname verification. 5. Support authentication for remote Ollama-compatible endpoints. 6. Avoid silently falling back from HTTPS to HTTP. 7. Document exactly what data is transmitted, including complete page images and cropped regions. 8. Consider an allowlist for approved hosts in managed deployments. 9. Redact credentials and sensitive response bodies from connection-error messages and logs. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
utils/pdf_utils.py:20
Finding
Broad Output Globbing Can Cause Deletion of Unrelated PNG Files<![CDATA[ ## Vulnerability Details **File Location**: `utils/pdf_utils.py:20-51`; deletion occurs at `ocr_tool.py:79-103` **Vulnerability Type**: Unsafe temporary-file creation and cleanup **Risk Level**: Medium ### Vulnerable Code ```python def pdf_to_images( pdf_path: str, output_prefix: Optional[str] = None, dpi: int = 150 ) -> List[str]: pdf_path = Path(pdf_path) if output_prefix is None: output_prefix = pdf_path.stem # Output is written beside the source PDF output_dir = pdf_path.parent output_base = str(output_dir / output_prefix) cmd = [ "pdftoppm", "-png", "-r", str(dpi), str(pdf_path), output_base ] subprocess.run( cmd, check=True, capture_output=True, text=True ) # Broadly includes every pre-existing matching PNG images = sorted(glob.glob(f"{output_base}*.png")) return images ``` The caller derives a predictable prefix and deletes every returned match: ```python images = pdf_to_images( pdf_path, output_prefix=Path(pdf_path).stem + "_page" ) for i, img_path in enumerate(images, 1): analysis = analyzer.analyze_page( img_path, page_number=i, auto_detect=auto_detect ) processor.process_page_analysis(analysis) analyses.append(analysis) try: os.remove(img_path) except: pass ``` ### Technical Analysis Generated page images are placed in the same directory as the source PDF with a predictable name. The conversion function then uses the pattern `<output_base>*.png` rather than tracking files created by the current `pdftoppm` process. For an input named `report.pdf`, the caller uses `report_page` as the output prefix. Any pre-existing file such as `report_page_notes.png` or `report_page_backup.png` matches the glob, is returned as if it were a generated page, and is subsequently deleted by `ocr_tool.py`. The broad ex ...[truncated 1379 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Create a unique private directory using `tempfile.TemporaryDirectory()`. 2. Direct `pdftoppm` output into that directory rather than beside the source PDF. 3. Enumerate generated files only inside the unique directory. 4. Delete the temporary directory as a unit after processing. 5. Do not use a broad glob in a user-controlled or shared directory. 6. If output must remain beside the PDF, record the directory contents before conversion and process only newly created regular files with the exact expected numeric naming format. 7. Reject symbolic links when processing temporary page images. 8. Replace broad cleanup exception handling with specific exceptions and report cleanup failures. A secure structure would be: ```python with tempfile.TemporaryDirectory(prefix="pdf-ocr-") as temp_dir: output_base = str(Path(temp_dir) / "page") subprocess.run( ["pdftoppm", "-png", "-r", str(dpi), str(pdf_path), output_base], check=True, capture_output=True, text=True, ) images = sorted(Path(temp_dir).glob("page-*.png")) # Process files before the temporary directory is automatically removed. ``` ]]>
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
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Output HandlingUnvalidated Output Injection, Cross-Context Output, Unbounded Output
Findings (65)

External Script Fetching

High
Category
Supply Chain
Content
```bash
# Install Ollama
curl -fsSL https://ollama.com/install.sh | sh
ollama pull glm-ocr:q8_0

# Install poppler-utils (for PDF to image conversion)
Confidence
97% confidence
Finding
The README instructs users to execute a remote installation script directly with `curl ... | sh`, which grants immediate code execution from a network source without review or integrity verification. If the upstream site, transport path, or script content is compromised, users can suffer arbitrary code execution on their machine.

Chaining Abuse

High
Category
Tool Misuse
Content
```bash
# Install Ollama
curl -fsSL https://ollama.com/install.sh | sh
ollama pull glm-ocr:q8_0

# Install poppler-utils (for PDF to image conversion)
Confidence
96% confidence
Finding
The `| sh` chaining pattern is dangerous because it converts downloaded content directly into shell commands without an opportunity for inspection, sandboxing, or validation. In an installation context, this materially increases the likelihood and impact of supply-chain compromise or malicious script substitution.

Chaining Abuse

High
Category
Tool Misuse
Content
brew install poppler            # macOS

# Install uv package manager
curl -LsSf https://astral.sh/uv/install.sh | sh
```

### Install via ClawHub (Recommended)
Confidence
94% confidence
Finding
This second `| sh` pattern has the same issue: a remote network response is executed immediately as shell code. Even if the source is reputable, the practice is unsafe by default and especially problematic in third-party skill documentation that users may follow without scrutiny.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding
The declared description describes a full OCR-based PDF/image-to-Markdown conversion skill with content classification capabilities. The actual code chunk is only a minimal tests package initializer and version declaration, which is unrelated to the declared functionality. This is a material mismatch in primary purpose, not merely an implementation detail.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding
The declared description promises an intelligent PDF/image to Markdown converter with OCR-based content detection. The supplied code does not implement conversion, OCR, Markdown generation, or document structure detection. Instead, it only offers supporting image-processing helpers such as crop, resize, save, size detection, and base64 encoding. While such utilities could support a larger OCR pipeline, this chunk by itself does not match the declared primary purpose and exposes capabilities not mentioned in the description.

Tp4

High
Category
MCP Tool Poisoning
Confidence
94% confidence
Finding
The supplied code chunk is limited to low-level PDF preprocessing and inspection utilities. It invokes external tools (pdftoppm, pdfinfo) to rasterize PDFs, count pages, extract metadata, and validate files. While PDF-to-image conversion could support a larger OCR pipeline, this code does not implement the declared primary functionality of converting PDFs or images into Markdown, nor does it perform intelligent content detection or use Ollama GLM-OCR. Therefore the description materially overstates and misrepresents what this code chunk actually does.

External Script Fetching

High
Category
Supply Chain
Content
```bash
# Install Ollama
curl -fsSL https://ollama.com/install.sh | sh
ollama pull glm-ocr:q8_0

# Install poppler-utils (for PDF to image conversion)
Confidence
97% confidence
Finding
Piping a remote script directly into `sh` executes unreviewed code from the network with the user's privileges. If the hosting site, network path, or script content is compromised, this becomes an immediate arbitrary code execution and supply-chain compromise vector.

Chaining Abuse

High
Category
Tool Misuse
Content
```bash
# Install Ollama
curl -fsSL https://ollama.com/install.sh | sh
ollama pull glm-ocr:q8_0

# Install poppler-utils (for PDF to image conversion)
Confidence
96% confidence
Finding
The `| sh` construct chains network retrieval directly into shell execution, removing any opportunity for user inspection or validation before running code. In a skill installation context, this is especially risky because users may copy-paste it verbatim and unknowingly execute attacker-controlled payloads.

Chaining Abuse

High
Category
Tool Misuse
Content
brew install poppler            # macOS

# Install uv package manager
curl -LsSf https://astral.sh/uv/install.sh | sh
```

### 2. Install with uv (Recommended)
Confidence
95% confidence
Finding
This second `| sh` usage has the same unsafe pattern: direct execution of network-fetched code without validation. Even if common in developer tooling, it normalizes insecure installation behavior and increases exposure to supply-chain compromise.

Unvalidated Output Injection

High
Category
Output Handling
Content
from pathlib import Path

# Process PDF (auto mode)
subprocess.run([
    "python", "skills/pdf-ocr-tool/ocr_tool.py",
    "--input", "/path/to/document.pdf",
    "--output", "/tmp/result.md",
Confidence
85% confidence
Finding
Model output is used without validation or sanitization. Unvalidated output injected into downstream contexts (SQL, shell, HTML) enables injection attacks and arbitrary code execution.

External Script Fetching

High
Category
Supply Chain
Content
# Check if Ollama is installed
if ! command -v ollama &> /dev/null; then
    echo "❌ ollama is not installed. Please install ollama first:"
    echo "   curl -fsSL https://ollama.com/install.sh | sh"
    exit 1
fi
Confidence
90% confidence
Finding
Remote code is downloaded and executed. This bypasses code review and could introduce malicious code.

Direct Prompt Extraction

High
Category
System Prompt Leakage
Content
RegionType.FIGURE: get_prompt("figure"),
            RegionType.MIXED: get_prompt("mixed"),
        }
        return prompt_map.get(region_type, get_prompt("mixed"))
    
    def _crop_region(
        self,
Confidence
85% confidence
Finding
Skill contains instructions that could directly expose system prompts, internal rules, or hidden instructions to users or external parties.

Direct Prompt Extraction

High
Category
System Prompt Leakage
Content
RegionType.FIGURE: get_prompt("figure"),
            RegionType.MIXED: get_prompt("mixed"),
        }
        return prompt_map.get(region_type, get_prompt("mixed"))
    
    def _crop_region(
        self,
Confidence
85% confidence
Finding
Skill contains instructions that could directly expose system prompts, internal rules, or hidden instructions to users or external parties.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The README describes OCR processing via Ollama but does not clearly warn users that document contents are transmitted to a local or remote Ollama service endpoint during processing. This can cause unintended disclosure of sensitive PDFs or images, especially when `--host` is configured to point at a non-local service.

Rp1

Medium
Category
MCP Rug Pull
Confidence
70% confidence
Finding
npx commands without a version suffix (e.g. @1.0.0) create a rug-pull risk if the upstream server is compromised and publishes a malicious update.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
### Missing pdftoppm

```bash
sudo apt install poppler-utils  # Debian/Ubuntu
brew install poppler            # macOS
```
Confidence
70% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
### Missing pdftoppm

```bash
sudo apt install poppler-utils  # Debian/Ubuntu
brew install poppler            # macOS
```
Confidence
70% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Lp3

Medium
Category
MCP Least Privilege
Confidence
89% confidence
Finding
The skill advertises and demonstrates capabilities involving file access, network access, and shell execution, but does not declare any explicit tool scope or permissions boundary. This increases the risk of over-privileged execution because operators and agents cannot easily constrain what the skill is allowed to do before installation or use.

Missing User Warnings

Medium
Confidence
92% confidence
Finding
The skill documentation does not clearly warn that user-provided PDFs or images may be transmitted to an OCR service via Ollama for processing. This is a data-handling transparency issue that can lead users to expose sensitive local documents without understanding the privacy boundary.

Rp1

Medium
Category
MCP Rug Pull
Confidence
82% confidence
Finding
Using `npx clawhub install pdf-ocr-tool` without a pinned package version makes installation dependent on whatever package version is current at execution time. That creates a supply-chain risk where a compromised or maliciously updated package could be fetched and run unexpectedly.

Session Persistence

Medium
Category
Rogue Agent
Content
# Clone or download skill
git clone <repo> ~/.openclaw/workspace/skills/pdf-ocr-tool

# Create virtual environment and install dependencies
cd ~/.openclaw/workspace/skills/pdf-ocr-tool
uv venv
source .venv/bin/activate
Confidence
60% confidence
Finding
Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Natural-Language Policy Violations

Medium
Confidence
96% confidence
Finding
Comments, docstrings, prompts, and printed messages in this file are written in Chinese only, with no indication that users can choose another language. This creates a locale/language policy concern because the skill effectively enforces one language without explicit user preference or justified regional scope.

Missing User Warnings

Medium
Confidence
91% confidence
Finding
This code sends the page image to `self.ollama_client.generate(...)` for analysis, which is a network/model call that may transmit user document data. While there is exception logging, there is no confirmation prompt, user-facing disclosure, or explanatory comment/docstring warning that page contents are being sent for external processing.

Missing User Warnings

Medium
Confidence
93% confidence
Finding
After cropping, the code submits the temporary image file to `self.ollama_client.generate(...)`, which may transmit sensitive page-region content for remote processing. The surrounding code lacks any confirmation prompt, disclosure comment, or documentation warning about this data transmission.

Context-Inappropriate Capability

Medium
Confidence
96% confidence
Finding
The install hook downloads dependency definition files from a remote GitHub URL when local files are absent, introducing a supply-chain risk during installation. Because those fetched files control what packages are installed, a compromised repository, branch, account, or network path could cause execution of attacker-chosen dependencies or build steps.

Static analysis

No suspicious patterns detected.