Back to skill

Security audit

Private Document AI with OpenVINO

Security checks for vulnerabilities and agentic risk

Overview

The skill appears to be a real local document AI workflow, but its unauthenticated persistent HTTP service, broad local file-path handling, mutable dependency install, and generated remote-code notebook cells need careful review before installation.

Install only on a trusted single-user machine. Keep the HTTP host on 127.0.0.1, prefer --server-kind ipc or --no-server when possible, shut the server down after use, keep output directories private, and delete artifacts containing sensitive documents. Review generated notebooks before running them, remove trust_remote_code=True unless required, and pin model/dependency versions in a virtual environment.

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

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/http_parse_server.py:72
Finding

Unauthenticated HTTP Parsing Service Permits Arbitrary Local Path Selection and Service Shutdown

Content
View full analysis
dict[str, Any]: payload = runtime.status() payload.update({"server": "http", "server_version": SERVER_VERSION}) return payload @app.post("/warmup") def warmup(payload: dict[str, Any] | None = None) -> dict[str, Any]: payload = payload or {} try: runtime.ensure_loaded(debug=bool(payload.get("debug"))) except Exception: return {"ok": False, "message": runtime.last_error, "server": "http"} result = runtime.status() result.update({"server": "http", "server_version": SERVER_VERSION}) return result @app.post("/parse") def parse(payload: dict[str, Any]) -> dict[str, Any]: result = runtime.parse(payload) result["server"] = "http" result["server_version"] = SERVER_VERSION status = result.get("status") if isinstance(status, dict): status["server_transport"] = "http" status["service_version"] = SERVER_VERSION return result @app.post("/shutdown") def shutdown() -> dict[str, Any]: def _exit_later() -> None: os._exit(0) threading.Timer(0.2, _exit_later).start() return {"ok": True, "state": "shutting_down", "server": "http", "pid": os.getpid()} ``` The parsing runtime consumes request-controlled absolute paths without authorization or approved-root validation: ```python def parse(self, payload: dict[str, Any]) -> dict[str, Any]: client, model_dir = self.ensure_loaded(debug=bool(payload.get("debug"))) config = ParseConfig( file=Path(str(payload["file"])).expanduser().resolve(), out=Path(str(payload["out"])).expanduser().resolve(), ...[truncated 3097 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/parse_client.py:32
Finding

Configurable Plaintext HTTP Client Can Disclose Sensitive Document Path Metadata to a Remote Host

Content
View full analysis
dict[str, Any]: url = f"{HTTP_BASE_URL}{path}" body = None headers = {"Accept": "application/json"} if payload is not None: body = json.dumps(payload, ensure_ascii=False).encode("utf-8") headers["Content-Type"] = "application/json" req = urllib.request.Request(url, data=body, headers=headers, method=method) with urllib.request.urlopen(req, timeout=timeout_s) as resp: text = resp.read().decode("utf-8") return json.loads(text) if text.strip() else {"ok": True} ``` ```python response = _request( "parse", payload={ "file": str(Path(ns.file).expanduser().resolve()), "out": str(Path(ns.out).expanduser().resolve()), "mode": ns.mode, "engine_version": ns.engine_version, "max_pages": ns.max_pages, "language_hint": ns.language_hint, "debug": ns.debug, }, start=True, timeout_s=ns.connect_timeout, server_kind=ns.server_kind, ) ``` ### Technical Analysis The client sends absolute input and output paths in a JSON request over HTTP. The default host is `127.0.0.1`, which is consistent with local operation, but the destination is controlled through `LOCAL_DOCUMENT_AI_HTTP_HOST`. The client does not ensure that the configured host is loopback, does not use TLS, and does not authenticate the se ...[truncated 1561 chars]
Remediation
View remediation

T08 · Insecure Dependencies

Warning
Location
requirements.txt:5
Finding

Mutable Unhashed Runtime Dependencies Create a Supply-Chain Execution Risk

Content
View full analysis
=2026.1.0,<2027 mineru-vl-utils>=0.2.7 pypdfium2 Pillow # Local service layer for the default app-like experience. fastapi>=0.115,<1 uvicorn>=0.30,<1 ``` The installer resolves and upgrades these mutable requirements: ```python def choose_install_command(requirements: Path, vendor_dir: Path) -> list[str]: uv_exe = shutil.which("uv") if uv_exe: return [ uv_exe, "pip", "install", "--upgrade", "--target", str(vendor_dir), "-r", str(requirements), ] return [ sys.executable, "-m", "pip", "install", "--upgrade", "--target", str(vendor_dir), "-r", str(requirements), ] ``` ```python cmd = choose_install_command(requirements, vendor_dir) print(f"Installing local runtime with: {' '.join(cmd)}") proc = subprocess.run(cmd, env=env) ``` ### Technical Analysis Several dependencies have broad lower bounds or no version constraints, and none are protected by package hashes. The use of `--upgrade` means an installation performed later can resolve to a materially different dependency set from the one reviewed. Python package installation executes package build and installation logic with the privileges of the invoking user. If an upstream package, transitive dependency, package index, or distribution artifact is compromised, the resulting code can execute during installation or when imported by the Skill. No evidence was found that the listed package names are intentional typosquats or currently maliciou ...[truncated 1016 chars]
Remediation
View remediation

T08 · Insecure Dependencies

Warning
Location
scripts/transform_doc_to_code.py:984
Finding

Generated Notebook Enables Execution of Remote Model Repository Code

Content
View full analysis
Remediation
View remediation
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
Findings (43)

Tainted flow: 'req' from os.environ.get (line 163, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Confidence
91% confidence
Finding

The client builds HTTP_BASE_URL from environment-controlled host/port and then sends parse, warmup, status, and shutdown requests to that address with no authentication or allowlist. In this skill's context, that is more dangerous because document-parsing requests include resolved local file and output paths, so an attacker controlling environment variables could redirect requests to an unintended service or remote endpoint and influence or observe document-processing operations.

Content

Scanner excerpt · scripts/parse_client.py (reported line 164)May include surrounding context.

python
body = json.dumps(payload, ensure_ascii=False).encode("utf-8")
        headers["Content-Type"] = "application/json"
    req = urllib.request.Request(url, data=body, headers=headers, method=method)
    with urllib.request.urlopen(req, timeout=timeout_s) as resp:
        text = resp.read().decode("utf-8")
    return json.loads(text) if text.strip() else {"ok": True}

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding

The declared description centers on a document parsing skill running MinerU 2.5 on OpenVINO GenAI for Intel hardware, keeping a local service warm, and producing structured JSON/Markdown outputs with invoice-field extraction. The supplied code chunk does not perform parsing or model execution at all. Instead, it consumes already-produced artifacts such as parsed.json, normalized/structured outputs, layout JSON files, tables, and optional generated code files, then builds HTML reports and visualization images. It also includes substantial functionality for previewing generated application/code artifacts in a 'to-code' workflow, which is materially different from the declared purpose. While reporting can be a supporting utility within a larger document AI project, this specific chunk's primary behavior is report generation and artifact visualization, not the declared document parsing/model-serving functionality.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding

The declared description is for a local document-parsing AI pipeline on Intel hardware, centered on ingesting raw documents and extracting structured outputs. The actual code is a downstream transformation utility that assumes a canonical parsed JSON already exists and converts that structured representation into app/code artifacts. Its primary purpose is document-to-code/UI scaffolding, not document parsing or invoice extraction. It also includes an additional undeclared capability—generating OpenVINO/Jupyter notebook scaffolds with model loading, conversion, and Gradio demo code—which is materially different from the stated purpose. Therefore the description does not accurately represent this code chunk.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding

The declared description presents a full local document AI system centered on Intel/OpenVINO inference and raw document parsing. The supplied code chunk is only a transformation/export stage over canonical parsed JSON. It performs schema validation, extraction of tables/entities/KV pairs, summary construction, traceability generation, and optional requested-field packaging. While the field-extraction aspect partially aligns with the description's structured invoice-field output, the primary purpose and capabilities in this code are materially narrower and different from the declared end-to-end parsing/model-service functionality. Therefore this chunk does not accurately represent the declared description.

Content

No source excerpt is available for this finding.

YARA rule 'agent_skill_prompt_injection_hidden_instructions': Prompt injection or hidden instructions embedded in AI agent skill text [agent_skills]

High
Category
YARA Match
Confidence
80% confidence
Finding

YARA rule matched a hack tool or exploit indicator (offensive tools, reconnaissance, privilege escalation, or exploit frameworks).

Content

Scanner excerpt · SKILL.md (reported line 3)May include surrounding context.

md
---
name: local-document-ai-openvino
description: Private local document AI for Intel hardware. Parse PDFs, invoices, screenshots, and diagrams with MinerU 2.5 on OpenVINO GenAI, keep the model warm in a local service, and output structured JSON/Markdown with user-defined invoice fields.
---

# Private Document AI with OpenVINO

Turn local PDFs, invoices, screenshots, and diagrams into one of two useful outcomes:

1. `to-data`: classify the document and extract structured fields, tables, and JSON, including user-requested key fields.
2. `to-code`: turn screenshots, forms, and architecture diagrams into code or Jupyter notebook scaffolds.

Everything runs locally and is built for Intel CPU/GPU acceleration with OpenVINO GenAI.
The default device is `CPU` for workshop stability.

Possible Typosquatting: 'uvicorn' resembles popular package 'gunicorn'

High
Category
Supply Chain
Confidence
70% confidence
Finding

Package name closely resembles a popular package, suggesting possible typosquatting. Attackers publish malicious packages with similar names to trick developers into installing them.

Content

No source excerpt is available for this finding.

Env Variable Harvesting

High
Category
Data Exfiltration
Confidence
80% confidence
Finding

Code enumerates, copies, or searches environment variables for secrets. Bulk environment access can collect credentials unrelated to the skill's stated purpose.

Content

Scanner excerpt · scripts/data_enrichment.py (reported line 287)May include surrounding context.

python
clean = normalize_whitespace(value)
    if hinted_entity_type in {"email", "phone", "amount", "date", "invoice_number", "code", "organization", "text"}:
        return hinted_entity_type if hinted_entity_type != "organization" else "text"
    if any(token in key_hint for token in ["code", "number", "serial", "编号", "代码", "票据", "发票", "流水号"]):
        return "code"
    if any(token in key_hint for token in ["amount", "price", "cost", "total", "tax", "due", "金额", "合计", "价税", "支付"]):
        return "amount"

Env Variable Harvesting

High
Category
Data Exfiltration
Confidence
80% confidence
Finding

Code enumerates, copies, or searches environment variables for secrets. Bulk environment access can collect credentials unrelated to the skill's stated purpose.

Content

Scanner excerpt · scripts/data_enrichment.py (reported line 289)May include surrounding context.

python
clean = normalize_whitespace(value)
    if hinted_entity_type in {"email", "phone", "amount", "date", "invoice_number", "code", "organization", "text"}:
        return hinted_entity_type if hinted_entity_type != "organization" else "text"
    if any(token in key_hint for token in ["code", "number", "serial", "编号", "代码", "票据", "发票", "流水号"]):
        return "code"
    if any(token in key_hint for token in ["amount", "price", "cost", "total", "tax", "due", "金额", "合计", "价税", "支付"]):
        return "amount"

Env Variable Harvesting

High
Category
Data Exfiltration
Confidence
80% confidence
Finding

Code enumerates, copies, or searches environment variables for secrets. Bulk environment access can collect credentials unrelated to the skill's stated purpose.

Content

Scanner excerpt · scripts/data_enrichment.py (reported line 291)May include surrounding context.

python
clean = normalize_whitespace(value)
    if hinted_entity_type in {"email", "phone", "amount", "date", "invoice_number", "code", "organization", "text"}:
        return hinted_entity_type if hinted_entity_type != "organization" else "text"
    if any(token in key_hint for token in ["code", "number", "serial", "编号", "代码", "票据", "发票", "流水号"]):
        return "code"
    if any(token in key_hint for token in ["amount", "price", "cost", "total", "tax", "due", "金额", "合计", "价税", "支付"]):
        return "amount"

Env Variable Harvesting

High
Category
Data Exfiltration
Confidence
80% confidence
Finding

Code enumerates, copies, or searches environment variables for secrets. Bulk environment access can collect credentials unrelated to the skill's stated purpose.

Content

Scanner excerpt · scripts/data_enrichment.py (reported line 984)May include surrounding context.

python
note_text = cleanup_ocr_text(half[3].strip()) if len(half) > 3 else ""
    if not any([name, quantity_text, amount_text, note_text]):
        return None
    if any(token in name for token in ["金额合计", "其他信息"]):
        return None
    quantity, unit = parse_quantity_unit(quantity_text)
    amount = find_first_amount(amount_text)

Env Variable Harvesting

High
Category
Data Exfiltration
Confidence
60% confidence
Finding

Code enumerates, copies, or searches environment variables for secrets. Bulk environment access can collect credentials unrelated to the skill's stated purpose.

Content

Scanner excerpt · scripts/install_local_runtime.py (reported line 50)May include surrounding context.

python
vendor_dir.mkdir(parents=True, exist_ok=True)
    tmp_dir.mkdir(parents=True, exist_ok=True)

    env = os.environ.copy()
    env["TMP"] = str(tmp_dir)
    env["TEMP"] = str(tmp_dir)
    env.setdefault("PYTHONUTF8", "1")

Env Variable Harvesting

High
Category
Data Exfiltration
Confidence
60% confidence
Finding

Code enumerates, copies, or searches environment variables for secrets. Bulk environment access can collect credentials unrelated to the skill's stated purpose.

Content

Scanner excerpt · scripts/parse_client.py (reported line 91)May include surrounding context.

python
vendor_dir.mkdir(parents=True, exist_ok=True)
    tmp_dir.mkdir(parents=True, exist_ok=True)

    env = os.environ.copy()
    env["TMP"] = str(tmp_dir)
    env["TEMP"] = str(tmp_dir)
    env.setdefault("PYTHONUTF8", "1")

Env Variable Harvesting

High
Category
Data Exfiltration
Confidence
60% confidence
Finding

Code enumerates, copies, or searches environment variables for secrets. Bulk environment access can collect credentials unrelated to the skill's stated purpose.

Content

Scanner excerpt · scripts/parse_client.py (reported line 110)May include surrounding context.

python
vendor_dir.mkdir(parents=True, exist_ok=True)
    tmp_dir.mkdir(parents=True, exist_ok=True)

    env = os.environ.copy()
    env["TMP"] = str(tmp_dir)
    env["TEMP"] = str(tmp_dir)
    env.setdefault("PYTHONUTF8", "1")

Env Variable Harvesting

High
Category
Data Exfiltration
Confidence
60% confidence
Finding

Code enumerates, copies, or searches environment variables for secrets. Bulk environment access can collect credentials unrelated to the skill's stated purpose.

Content

Scanner excerpt · scripts/run_skill.py (reported line 253)May include surrounding context.

python
vendor_dir.mkdir(parents=True, exist_ok=True)
    tmp_dir.mkdir(parents=True, exist_ok=True)

    env = os.environ.copy()
    env["TMP"] = str(tmp_dir)
    env["TEMP"] = str(tmp_dir)
    env.setdefault("PYTHONUTF8", "1")

Context-Inappropriate Capability

High
Category
Not specified by scanner
Confidence
98% confidence
Finding

The notebook target generates executable cells that fetch and load remote Hugging Face models using trust_remote_code=True, which can execute arbitrary code supplied by the model repository during notebook use. In the context of a document-transformation skill, embedding remote code execution pathways is unjustified and especially dangerous because it converts untrusted document processing into a code-executing supply-chain workflow.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
96% confidence
Finding

The skill advertises and instructs use of shell execution, file read/write, environment-variable control, and network-backed installation/download steps, but it does not declare any tool scope or permission boundaries. In an agent setting, this increases the chance that the skill is granted broader capabilities than users expect, enabling unintended local command execution, artifact creation, dependency installation, or external downloads.

Content

No source excerpt is available for this finding.

Dynamic import via __import__()

Medium
Category
Dangerous Code Execution
Confidence
75% confidence
Finding

Dynamic import() can load arbitrary modules at runtime, bypassing static analysis and potentially importing malicious code.

Content

Scanner excerpt · scripts/check_env.py (reported line 52)May include surrounding context.

python
if not module_available(name):
        return False, None
    try:
        __import__(name)
    except Exception as exc:
        return False, str(exc)
    return True, None

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
89% confidence
Finding

The code exports extracted document content, including table rows and traceability data, to CSV files on disk without any built-in minimization, masking, consent check, or warning. In the context of invoice and medical-document processing, those rows can contain highly sensitive financial, identity, and health information, so local plaintext export materially increases confidentiality and compliance risk if files are retained, copied, or accessed by other local users/processes.

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 · scripts/install_local_runtime.py (reported line 58)May include surrounding context.

python
cmd = choose_install_command(requirements, vendor_dir)
    print(f"Installing local runtime with: {' '.join(cmd)}")
    proc = subprocess.run(cmd, env=env)
    if proc.returncode != 0:
        return proc.returncode

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 · scripts/install_local_runtime.py (reported line 65)May include surrounding context.

python
if os.name == "nt":
        username = os.environ.get("USERNAME")
        if username:
            subprocess.run(
                [
                    "icacls",
                    str(vendor_dir),

Tainted flow: 'username' from os.environ.get (line 63, credential/environment) → subprocess.run (code execution)

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/install_local_runtime.py (reported line 65)May include surrounding context.

python
if os.name == "nt":
        username = os.environ.get("USERNAME")
        if username:
            subprocess.run(
                [
                    "icacls",
                    str(vendor_dir),

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 · scripts/parse_client.py (reported line 95)May include surrounding context.

python
env.setdefault("PYTHONIOENCODING", "utf-8")
    env.setdefault("PYTHONUTF8", "1")
    creationflags = getattr(subprocess, "CREATE_NO_WINDOW", 0) if os.name == "nt" else 0
    subprocess.Popen(
        [_pythonw_executable(), str(SERVER_SCRIPT), "--log", str(log_path)],
        cwd=str(BASE_DIR),
        env=env,

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 · scripts/parse_client.py (reported line 114)May include surrounding context.

python
env.setdefault("PYTHONIOENCODING", "utf-8")
    env.setdefault("PYTHONUTF8", "1")
    creationflags = getattr(subprocess, "CREATE_NO_WINDOW", 0) if os.name == "nt" else 0
    subprocess.Popen(
        [
            _pythonw_executable(),
            str(HTTP_SERVER_SCRIPT),

Tainted flow: 'HTTP_HOST' from os.environ.get (line 32, credential/environment) → subprocess.Popen (code execution)

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/parse_client.py (reported line 114)May include surrounding context.

python
env.setdefault("PYTHONIOENCODING", "utf-8")
    env.setdefault("PYTHONUTF8", "1")
    creationflags = getattr(subprocess, "CREATE_NO_WINDOW", 0) if os.name == "nt" else 0
    subprocess.Popen(
        [
            _pythonw_executable(),
            str(HTTP_SERVER_SCRIPT),

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

This code renders source pages, extracted text, structured records, and generated previews, then writes them to result_report.html, code_preview.html, and report_assets. Although these file writes are central to the script's purpose, there is no confirmation prompt or explicit user-facing disclosure that potentially sensitive document contents will be duplicated into browsable local artifacts.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.