Back to skill

Security audit

exam-bank

Security checks for vulnerabilities and agentic risk

Overview

The skill does what it claims, but it can send complete exam documents and extracted question-bank content to configurable network services without strong scoping, transport safeguards, or clear sensitivity warnings.

Install only if you trust the configured OCR service and DeepSeek for the exam materials you process. Use HTTPS or an isolated trusted internal OCR endpoint, avoid shared/cloud archive folders for confidential papers, and do not run auto_run.py on proprietary or restricted exams unless sending OCR text to DeepSeek is acceptable.

Vulnerability Patterns
  • 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
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (1)

T09 · Insecure Skill Coding Practices

Warning
Location
parse_ocr.py:26
Finding
Unrestricted Transmission of Complete Documents to Plaintext-Capable, Caller-Controlled Endpoints<![CDATA[ ## Vulnerability Details **File Locations**: - `parse_ocr.py:26-40, 68` - `auto_run.py:15-16, 43-47, 76-77, 103` - `write_excel.py:8, 25, 33` **Vulnerability Type**: Sensitive data transmission to an unrestricted endpoint with plaintext HTTP support **Risk Level**: Medium ### Vulnerable Code #### `parse_ocr.py:26-40` ```python boundary = uuid.uuid4().hex with open(path, "rb") as f: data = f.read() body = (f"--{boundary}\r\n" f'Content-Disposition: form-data; name="file"; filename="{fname}"\r\n' f"Content-Type: application/octet-stream\r\n\r\n").encode() + data + f"\r\n--{boundary}--\r\n".encode() req_url = f"{url}/parse" if sync else f"{url}/parse-async" if owner and not sync: req_url += "?" + urllib.parse.urlencode({"owner": owner}) req = urllib.request.Request( req_url, data=body, method="POST", headers={"Content-Type": f"multipart/form-data; boundary={boundary}"}, ) try: with urllib.request.urlopen(req, timeout=timeout) as resp: return resp.status, json.loads(resp.read().decode("utf-8")) ``` #### `parse_ocr.py:68` ```python ap.add_argument("--url", default=os.environ.get("EXAM_OCR_URL", "http://exam-ocr:8000")) ``` #### `auto_run.py:15-16, 43-47` ```python DEFAULT_URL = os.environ.get("EXAM_OCR_URL", "http://exam-ocr:8000") API_URL = "https://api.deepseek.com/chat/completions" ``` ```python def ocr(url, pdf): with open(pdf, "rb") as f: r = requests.post(f"{url}/parse", files={"file": (os.path.basename(pdf), f, "application/octet-stream")}, timeout=600) ``` #### `auto_run.py:76-77` ```python def write_excel(url, records, out): r = requests.post(f"{url}/write-excel", json={"schema": SCHEMA, "records": records}, timeout=120) ``` #### `auto_run.py:103` ```python ap.add_argument("--url", default=DEFAULT_URL) ``` #### `write_excel.py:25, 33` ```python ap.add_argument("--url", default=os.environ.get("EXAM_OCR_URL", "http://exam-ocr ...[truncated 3251 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. **Require encrypted transport** - Accept only `https://` OCR URLs by default. - Permit `http://` only for explicitly approved loopback or isolated container-network hosts. - Fail closed when an unsupported scheme is supplied. 2. **Restrict destination hosts** - Introduce an explicit OCR host allowlist. - Compare the parsed hostname and port against approved configuration. - Reject URLs containing embedded credentials, fragments, unexpected paths, or non-HTTP schemes. - Resolve and validate destinations carefully if private-network restrictions are required. 3. **Add an explicit plaintext opt-in** - If compatibility requires HTTP, require a flag such as `--allow-insecure-http`. - Display a clear warning that the complete document or record set will be transmitted without transport encryption. 4. **Authenticate the OCR service** - Support an API token or mutual TLS. - Avoid placing credentials in URL query strings. - Store credentials in protected environment variables or a secret manager. 5. **Reduce transmitted data** - Generate Excel files locally where practical, avoiding retransmission of extracted records. - Send only the minimum document pages or fields needed for the requested operation. - Avoid unnecessary retention of uploaded files and OCR results on the service. 6. **Validate remote responses** - Enforce expected response content types and maximum response sizes. - Validate OCR JSON structure before processing it. - Validate downloaded Excel content before writing it as the final output. 7. **Improve user disclosure** - State clearly before execution that complete source documents are sent to the OCR service. - State that one-click mode also sends OCR text to DeepSeek. - Document the service's retention, access-control, and deletion requirements. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • 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
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
Findings (19)

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding
声明描述的是一个“真题 PDF/图片 → OCR 解析 → 题库 Excel”的核心转换技能;但代码块 `archive.py` 的功能是将已有产物复制到调用方指定的归档目录,属于交付物归档/文件管理的辅助步骤。它不调用任何 OCR 服务,不处理 PDF/图片内容,不抽取题目,也不生成 Excel。根据评估标准,这属于主目的显著不同,且代码执行了声明未体现的文件归档能力,因此应判定为不匹配。

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
声明描述的是一个完整的“试卷转题库”技能:接收 PDF/图片,调用内网 exam-ocr 服务做 OCR,再抽取并生成题库 Excel。给出的代码块却只是一个辅助校验工具 check_missing.py,用于检查已经抽取好的 JSON 题目记录是否存在漏题,并可参考 OCR markdown 中的题数说明做数量比对。它不调用任何 OCR 服务,不处理 PDF/图片,不抽取题目为 Excel,也不体现 17 列表头导出能力。虽然该脚本可能属于整体流程中的辅助质检环节,但就该代码块本身而言,其主要行为与声明的核心用途明显不一致,因此应判定为描述与代码行为不匹配。

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding
声明描述的是一个完整的“试卷转题库”流程:OCR 解析后还要抽取成固定 17 列表头的题库 Excel。但这段代码只负责调用 exam-ocr 服务做文件解析,并把返回的 markdown 写入本地文件。代码中没有任何 Excel 生成、表头映射、题目字段结构化抽取、入库转换等逻辑。因此其行为只覆盖了声明中的前半段 OCR 解析步骤,未覆盖核心的“转题库 Excel”能力,属于实质性描述不符。代码访问内网 OCR 服务这一点与声明一致,但主输出和主要能力明显更窄。

Missing User Warnings

Medium
Confidence
92% confidence
Finding
The README instructs users to send exam PDFs/images to an OCR service and optionally use a DeepSeek API key for automated processing, but it does not clearly warn that exam contents may be transmitted to external or separate network services. Because exam materials can contain sensitive or proprietary content, this omission can lead users to disclose data without informed consent, especially since the skill is explicitly designed around service-to-service transfer.

Lp3

Medium
Category
MCP Least Privilege
Confidence
89% confidence
Finding
The skill documents access to environment variables, network endpoints, and file-writing/archiving behavior, but it declares no explicit tool scope or permission boundaries. In an agent setting, that omission weakens reviewability and can let the skill invoke broader capabilities than users expect, especially when it can contact OCR services and write or copy files to chosen locations.

Missing User Warnings

Medium
Confidence
97% confidence
Finding
The skill instructs users to provide a DeepSeek API key for automated extraction but does not clearly warn that exam PDFs/OCR text and derived question content may be sent to an external model service. This creates a data exfiltration and compliance risk, especially if the source exams are proprietary, licensed, or internally restricted.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The skill encourages archiving original PDFs plus OCR markdown, JSON extractions, and validation reports to arbitrary local, NAS, or cloud-synced locations without warning about sensitivity or access control. These artifacts may contain copyrighted exam material, answer keys, and structured content that becomes easier to search, copy, and leak once broadly archived.

External Transmission

Medium
Category
Data Exfiltration
Content
import sys, os, re, json, argparse, requests

DEFAULT_URL = os.environ.get("EXAM_OCR_URL", "http://exam-ocr:8000")
API_URL = "https://api.deepseek.com/chat/completions"
SCHEMA = ["课程", "知识点", "题型", "题目内容", "选项A", "选项B", "选项C", "选项D", "选项E",
          "正确答案", "难度", "状态", "是否真题", "是否模拟试卷", "是否考前押题", "年份", "解析"]
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Missing User Warnings

Medium
Confidence
96% confidence
Finding
The extract function sends the full OCR text, which may contain sensitive exam content or personal data, to DeepSeek's external API. Although this is functionally intended, the script does not provide a strong runtime consent/warning boundary, so users may inadvertently exfiltrate confidential internal documents off-box.

External Transmission

Medium
Category
Data Exfiltration
Content
"response_format": {"type": "json_object"},
    }
    h = {"Authorization": f"Bearer {api_key}", "Content-Type": "application/json"}
    r = requests.post(API_URL, json=body, headers=h, timeout=300)
    r.raise_for_status()
    content = r.json()["choices"][0]["message"]["content"]
    data = json.loads(content)
Confidence
97% confidence
Finding
This POST request transmits OCR-derived document contents and metadata to an external internet service. In the context of exam-bank conversion for potentially internal or copyrighted materials, the main risk is data leakage and compliance/privacy breach rather than code execution.

External Transmission

Medium
Category
Data Exfiltration
Content
def write_excel(url, records, out):
    r = requests.post(f"{url}/write-excel", json={"schema": SCHEMA, "records": records}, timeout=120)
    r.raise_for_status()
    with open(out, "wb") as f:
        f.write(r.content)
Confidence
80% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Natural-Language Policy Violations

Medium
Confidence
94% confidence
Finding
The file’s natural-language instructions and user-facing behavior are entirely specified in Chinese, including usage, parsing expectations, and output descriptions. This appears to impose a specific language/locale without opt-in or justification, which matches the language/locale policy-violation criterion.

Missing User Warnings

Medium
Confidence
93% confidence
Finding
The script automatically uploads the full contents of a local PDF/image to a network OCR service, which can expose sensitive exam materials or personal data if the operator does not realize a network transfer is occurring. In this skill’s context, the destination defaults to an internal service, which reduces but does not remove risk because internal endpoints still expand the data exposure boundary and the URL can be overridden.

Missing User Warnings

Medium
Confidence
90% confidence
Finding
The script sends the full JSON-derived `records` payload to `/write-excel` over HTTP, which may include user or system data. Although the module docstring describes usage, it does not clearly warn that record contents are transmitted to a remote service, and there is no confirmation prompt or user-facing notice before the request.

External Transmission

Medium
Category
Data Exfiltration
Content
if isinstance(records, dict):
        records = records.get("records", [])
    payload = {"schema": SCHEMA, "records": records}
    r = requests.post(f"{a.url}/write-excel", json=payload, timeout=120)
    r.raise_for_status()
    out = a.out or (a.json_path + ".xlsx")
    with open(out, "wb") as f:
Confidence
80% confidence
Finding
The script sends the full extracted record set to a network service endpoint specified by argument or environment variable, with no authentication, allowlist enforcement, or transport-security guarantees. In this skill context, exam content may be sensitive or proprietary, so a misconfigured or attacker-controlled URL could cause unintended data exfiltration to an untrusted host.

Missing User Warnings

Low
Confidence
82% confidence
Finding
The archive section explains how to copy generated Excel files and source papers into a user-specified archive directory, but it does not clearly warn that potentially sensitive source and output files will be duplicated to another location. This can increase accidental data exposure, especially if the target is a shared NAS, synced cloud folder, or broadly accessible project directory.

Missing User Warnings

Low
Confidence
83% confidence
Finding
The code saves OCR output to the path provided by --ocr-md, which is a file write of potentially sensitive exam text. Although this behavior is optional, there is no adjacent comment, prompt, or user-facing warning explaining that the OCR content will be persisted to disk.

Natural-Language Policy Violations

Low
Confidence
86% confidence
Finding
All natural-language instructions, usage notes, and help context in the module docstring are Chinese-only. This effectively forces a specific language for users of the skill without offering an alternative or documenting that the tool is intended only for a Chinese-speaking context.

Natural-Language Policy Violations

Low
Confidence
82% confidence
Finding
All user-facing documentation strings in the file are written in Chinese, which can impose a language constraint on users without opt-in or explanation. The policy allows locale constraints when documented and justified, but no such justification or language alternative is provided here.

Static analysis

No suspicious patterns detected.