Back to skill

Security audit

Local GLM OCR with llama.cpp on AIPC(no API Key)

Security checks for vulnerabilities and agentic risk

Overview

This local OCR skill has a coherent purpose, but setup can download and run third-party executables and unpinned Python packages without integrity checks.

Review before installing in sensitive environments. Use the step-by-step/manual setup path, verify upstream hashes or signatures where possible, and avoid autonomous setup unless you trust ggml-org, conda-forge, HuggingFace, ModelScope, and the Python package dependency chain.

Vulnerability Patterns
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
  • Insecure DependenciesIntroduces malicious components through unsafe dependency sources
  • 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
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (2)

T03 · Remote Payload Retrieval and Execution

Warning
Location
ocr_setup.py:150
Finding

Downloaded Executables Are Used Without Cryptographic Integrity Verification

Content
View full analysis
None: """Download and extract the llama.cpp Vulkan prebuilt binary.""" tag = tag or LLAMA_TAG llama_dir = os.path.join(ocr_dir, "llama.cpp") url = ( f"https://github.com/ggml-org/llama.cpp/releases/download/{tag}" f"/llama-{tag}-bin-win-vulkan-x64.zip" ) print(f"Downloading llama.cpp {tag} ...") zip_path = os.path.join(tempfile.gettempdir(), "llama-vulkan.zip") urllib.request.urlretrieve(url, zip_path) os.makedirs(llama_dir, exist_ok=True) with zipfile.ZipFile(zip_path, "r") as zf: zf.extractall(llama_dir) os.remove(zip_path) print("LLAMA_INSTALL=DONE") ``` The extracted executable is subsequently launched by `ocr_run.py`: ```python SERVER_EXE = os.path.join(LLAMA_DIR, "llama-server.exe") server_proc = subprocess.Popen( server_args, stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.DEVNULL, creationflags=_CREATE_NO_WINDOW, ) ``` The Skill documentation also instructs users to download and execute Miniforge installers without checking a digest or signature: ```powershell $mf = "$env:TEMP\Miniforge3-Windows-x86_64.exe" Invoke-WebRequest -Uri "https://github.com/conda-forge/miniforge/releases/latest/download/Miniforge3-Windows-x86_64.exe" -OutFile $mf Start-Process $mf -ArgumentList "/S /D=$env:USERPROFILE\miniforge3" -Wait Remove-Item $mf ``` ```bash MF_URL="https://github.com/conda-forge/miniforge/releases/latest/download/Miniforge3-Linux-x86_64.sh" MF_INSTALLER="/tmp/Miniforge3-install.sh" curl -fsSL "$MF_URL" -o "$MF_INSTALLER" bash "$MF_INSTALLER" -b -p "$HOME/miniforge3" rm -f "$MF_INSTALLER" ``` ### Technica ...[truncated 2483 chars]
Remediation
View remediation

T08 · Insecure Dependencies

Warning
Location
ocr_setup.py:190
Finding

Unpinned Python Packages Are Installed Dynamically at Runtime

Content
View full analysis
None: """Download GLM-OCR model files. source ∈ {'hf', 'ms'}.""" model_dir = os.path.join(ocr_dir, "models", "GLM-OCR-GGUF") os.makedirs(model_dir, exist_ok=True) files = ["GLM-OCR-Q8_0.gguf", "mmproj-GLM-OCR-Q8_0.gguf"] if source == "ms": subprocess.run( [sys.executable, "-m", "pip", "install", "modelscope", "-q"], check=True ) from modelscope.hub.file_download import model_file_download for fname in files: print(f"Downloading {fname} ...") model_file_download("ggml-org/GLM-OCR-GGUF", file_path=fname, local_dir=model_dir) else: subprocess.run( [sys.executable, "-m", "pip", "install", "huggingface_hub", "-q"], check=True ) from huggingface_hub import hf_hub_download for fname in files: print(f"Downloading {fname} ...") hf_hub_download( repo_id="ggml-org/GLM-OCR-GGUF", filename=fname, local_dir=model_dir ) ``` ### Technical Analysis The setup process invokes pip with bare package names. No reviewed versions, dependency constraints, hashes, lockfile, isolated environment, or package-index restrictions are specified. As a result, the installed package and its transitive dependency graph are selected dynamically at setup time. Python package installation can execute package build hooks and other installation-time logic. Imported package code then runs in the setup process. A compromised upstream release or dependency can therefore introduce code that was not included in, or visible during, this Skill audit. The package names are legitimate and are obtained through normal pip resolution, so there is no evidence of ...[truncated 1418 chars]
Remediation
View remediation
Vulnerability Patterns
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • 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
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
Findings (14)

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding

The skill’s declared purpose is OCR, but it also instructs the agent to download and execute external binaries, install Miniforge, fetch large model files, and modify local configuration/state. That mismatch is security-relevant because users or policy systems may grant permission expecting only local OCR, while the skill actually expands its capabilities into software installation and network retrieval.

Content

No source excerpt is available for this finding.

Lp1

High
Category
MCP Least Privilege
Confidence
75% confidence
Finding

The skill uses 'env' capability that is not listed in its permissions. This may indicate deceptive intent or missing permission declarations.

Content

No source excerpt is available for this finding.

Lp1

High
Category
MCP Least Privilege
Confidence
90% confidence
Finding

The code spawns an external executable (llama-server.exe), which is a shell/process-execution capability and should be declared. Even though the use appears legitimate for local OCR, undeclared process-launch capability expands trust assumptions and could be abused if the working directory or binaries are tampered with.

Content

No source excerpt is available for this finding.

Lp1

High
Category
MCP Least Privilege
Confidence
90% confidence
Finding

The code spawns an external executable (llama-server.exe), which is a shell/process-execution capability and should be declared. Even though the use appears legitimate for local OCR, undeclared process-launch capability expands trust assumptions and could be abused if the working directory or binaries are tampered with.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

Claiming 'no cloud API calls' while directing downloads from GitHub, HuggingFace, ModelScope, and Miniforge can mislead users and reviewers into treating the skill as fully local/offline. Even if inference is local, the install path still performs network access and introduces supply-chain and consent risks through downloaded binaries and models.

Content

No source excerpt is available for this finding.

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · ocr_run.py (reported line 93)May include surrounding context.

python
]

print(f"[INFO] Starting llama-server on port {port} ...", file=sys.stderr)
server_proc = subprocess.Popen(
    server_args,
    stdout=subprocess.PIPE,
    stderr=subprocess.PIPE,

Internal Network Request

Medium
Category
Server-Side Request Forgery
Confidence
70% confidence
Finding

Code issues a request to a loopback, link-local, or private-range host. This can reach internal services not meant to be exposed and is a common SSRF pivot.

Content

Scanner excerpt · ocr_run.py (reported line 140)May include surrounding context.

python
"max_tokens": 4096,
    }).encode("utf-8")

    req = urllib.request.Request(
        f"http://127.0.0.1:{port}/v1/chat/completions",
        data=payload,
        headers={"Content-Type": "application/json"},

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · ocr_setup.py (reported line 99)May include surrounding context.

python
print("LLAMA_STATUS=MISSING")
        return "MISSING", 0

    result = subprocess.run([server_exe, "--version"], capture_output=True, text=True)
    output = result.stdout + result.stderr
    m = re.search(r"version:\s*(\d+)", output)
    if m:

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The manifest describes the skill as running locally with no cloud API calls, but this setup script fetches llama.cpp binaries from GitHub over the network. Although installation/download can be part of local model setup, the broad 'no cloud API calls' claim does not match code that contacts remote hosting services.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The script downloads OCR model files from ModelScope or Hugging Face and even installs client packages to do so. This is a meaningful remote-service dependency that contradicts a plain reading of 'no cloud API calls,' even if inference itself is local afterward.

Content

No source excerpt is available for this finding.

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
87% confidence
Finding

The setup script installs a Python package from the network at runtime, which executes arbitrary package installation logic from a remote registry and expands the trust boundary significantly. If the package, dependency chain, index configuration, or TLS/proxy environment is compromised, running setup can result in arbitrary code execution on the host.

Content

Scanner excerpt · ocr_setup.py (reported line 197)May include surrounding context.

python
files = ["GLM-OCR-Q8_0.gguf", "mmproj-GLM-OCR-Q8_0.gguf"]

    if source == "ms":
        subprocess.run(
            [sys.executable, "-m", "pip", "install", "modelscope", "-q"], check=True
        )
        from modelscope.hub.file_download import model_file_download

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
87% confidence
Finding

This line performs an on-demand pip installation from an external package source, introducing a remote code execution and supply-chain risk during setup. Even though the command is not shell-invoked, pip installation executes package and dependency code under the user's privileges.

Content

Scanner excerpt · ocr_setup.py (reported line 205)May include surrounding context.

python
print(f"Downloading {fname} ...")
            model_file_download("ggml-org/GLM-OCR-GGUF", file_path=fname, local_dir=model_dir)
    else:
        subprocess.run(
            [sys.executable, "-m", "pip", "install", "huggingface_hub", "-q"], check=True
        )
        # Uncomment the next line to use the HF mirror (recommended in China):

Intent-Code Divergence

Low
Category
Not specified by scanner
Confidence
76% confidence
Finding

The cached-installation guidance says that on every subsequent invocation the assistant should read the cache first and attempt the fast path before setup. However, the 'Execution flow (do not skip steps)' section later presents a rigid sequence beginning with pre-flight and setup-oriented steps, creating contradictory operator guidance about whether setup checks are mandatory on later runs.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
91% confidence
Finding

The comment and conditional logic explicitly force a specific output encoding on Windows by reconfiguring stdout and stderr to UTF-8. This is a natural-language locale/encoding policy choice applied unconditionally, and the file does not offer the user a choice or explain a required regional justification.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.