Back to skill

Security audit

doc-holmes

Security checks for vulnerabilities and agentic risk

Overview

The skill is mostly transparent about PDF translation, but batch mode can process linked PDFs outside the folder the user selected and send their text to an external translation endpoint.

Review before installing. Do not run batch mode on directories that other people or automated systems can write to until symlink containment is fixed. Use only endpoints approved for the documents you translate, because PDF text, OCR text, and optional seeded terms may be sent to that service under your key. Prefer the direct/offline mode for sensitive Tier-A documents when layout needs allow it.

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
scripts/doc_holmes/runner.py:43
Finding

Batch PDF discovery follows symlinks outside the authorized input directory

Content
View full analysis

Vulnerability Details

File Location: scripts/doc_holmes/runner.py:43-50, 150, 160-173, 290-296
Vulnerability Type: Symlink-based path escape and unauthorized document disclosure
Risk Level: Medium

Complete Code Snippet

python
def list_pdfs(root: str) -> list:
    """铁律①②:realpath 收集 + 排除目录。"""
    real_root = os.path.realpath(root)
    found = []
    for dirpath, dirnames, filenames in os.walk(real_root):
        dirnames[:] = [d for d in dirnames if d not in EXCLUDE_DIRS]
        for name in filenames:
            if name.lower().endswith(".pdf"):
                found.append(os.path.join(dirpath, name))
    return sorted(found)
python
pdfs = list_pdfs(input_dir)
python
def _one(pdf: str) -> dict:
    real = os.path.realpath(pdf)
    digest = sha256_of(real)
    rel = os.path.relpath(real, os.path.realpath(input_dir))
    ...
    rep = triage_mod.triage_pdf(real) if do_triage else None
python
eng = translate_pdf(
    real, file_out, base_url, api_key, model,
    page_count=rep.page_count if rep else None,
    timeout_s=per_file_timeout_s,
    lang_in=eff_lang_in, lang_out=lang_out, qps=qps, extra_args=extra,
    openai_timeout=openai_timeout)

Technical Analysis

Batch discovery accepts every directory entry whose name ends in .pdf. It does not reject symbolic links or verify that each entry's resolved path remains beneath real_root.

The worker subsequently calls os.path.realpath(pdf), follows the symlink, and uses the resolved path in sha256_of(), triage_pdf(), and translate_pdf(). No commonpath or equivalent containment check is performed after resolution.

Consequently, a symlink located inside the selected batch directory can refer to a PDF outside that directory. The external file is then treated as an authorized batch input. Its content is parsed locally and passed to the translation engine, which sends document text to the user-configured OpenAI-compatible endpoint as part of the do ...[truncated 2169 chars]

Remediation
View remediation

Remediation Suggestions

  1. Resolve every candidate and enforce containment before any file operation:

    python
    real_root = os.path.realpath(root)
    candidate = os.path.join(dirpath, name)
    resolved = os.path.realpath(candidate)
    
    try:
        if os.path.commonpath([real_root, resolved]) != real_root:
            continue
    except ValueError:
        # Different drives on Windows or otherwise incomparable paths.
        continue
    
  2. Reject symbolic-link inputs explicitly when links are not required:

    python
    if os.path.islink(candidate):
        continue
    
  3. Apply the check again immediately before hashing, parsing, or translation to reduce time-of-check/time-of-use exposure.

  4. Where supported, open files relative to a trusted directory descriptor using no-follow semantics such as O_NOFOLLOW, then process the verified file descriptor rather than reopening an attacker-controlled pathname.

  5. Record rejected links and out-of-root paths in the audit log so users can diagnose skipped inputs without silently broadening the authorized scope.

  6. Add regression tests covering:

    • A symlinked PDF targeting a file outside the batch root.
    • A symlink targeting a PDF inside the root.
    • Nested symlink paths.
    • Windows paths on different drives.
    • Replacement of a validated entry between discovery and processing.
Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Rogue AgentSelf-Modification, Session Persistence
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
Findings (51)

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding

Handling non-PDF office documents via LibreOffice subprocesses and staging copies in a home-directory area extends the skill beyond the narrow PDF-only expectation. This broadens the attack surface to additional file formats and local file movement, which can surprise users and create data exposure or integrity risks if not clearly constrained.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
94% confidence
Finding

Handling non-PDF office documents via LibreOffice subprocesses and staging copies in a home-directory area extends the skill beyond the narrow PDF-only expectation. This broadens the attack surface to additional file formats and local file movement, which can surprise users and create data exposure or integrity risks if not clearly constrained.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding

Handling non-PDF office documents via LibreOffice subprocesses and staging copies in a home-directory area extends the skill beyond the narrow PDF-only expectation. This broadens the attack surface to additional file formats and local file movement, which can surprise users and create data exposure or integrity risks if not clearly constrained.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding

Handling non-PDF office documents via LibreOffice subprocesses and staging copies in a home-directory area extends the skill beyond the narrow PDF-only expectation. This broadens the attack surface to additional file formats and local file movement, which can surprise users and create data exposure or integrity risks if not clearly constrained.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding

Handling non-PDF office documents via LibreOffice subprocesses and staging copies in a home-directory area extends the skill beyond the narrow PDF-only expectation. This broadens the attack surface to additional file formats and local file movement, which can surprise users and create data exposure or integrity risks if not clearly constrained.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

Handling non-PDF office documents via LibreOffice subprocesses and staging copies in a home-directory area extends the skill beyond the narrow PDF-only expectation. This broadens the attack surface to additional file formats and local file movement, which can surprise users and create data exposure or integrity risks if not clearly constrained.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The skill description and operational instructions are presented in Chinese throughout the README, with no indication that users may choose another language or that the skill is intentionally limited to a Chinese-only audience for compliance or regional reasons. This creates a natural-language locale constraint that can violate language-choice policy when not explicitly justified.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
85% confidence
Finding

The skill declares broad capabilities including environment access, file I/O, networking, and subprocess execution, but does not publish an explicit permission or allowed-tools scope. That makes the trust boundary unclear and increases the chance an agent grants more authority than the user expects, especially because the workflow handles local files and API keys.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
88% confidence
Finding

The skill explicitly supports sending extracted PDF text and OCR-recovered text to user-configured external endpoints for translation and proofreading. This is dangerous in contexts involving confidential, regulated, or proprietary documents, because sensitive content may leave the local environment and be processed by third-party services.

Content

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

export DOC_HOLMES_OPENAI_API_KEY= # created by you on that platform

text

SiliconFlow (`https://api.siliconflow.cn/v1`) or any OpenAI-compatible endpoint works the same way.

> ⚠️ About GLM **Coding Plan** subscription endpoints (`/api/coding/paas/v4`): per the official FAQ, the plan only covers designated coding tools. Calls from other tools do NOT consume the plan quota - they are billed per-token against your account balance, and accounts shared across multiple people may face subscription restrictions. If you still want to connect your subscription, set `DOC_HOLMES_ALLOW_CODING_ENDPOINT=1` (one-time confirmation).

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

该 markdown/manifest 文件在触发词中列出“全文翻译”“文档翻译”“指南翻译”等泛化短语,这些表述并未限定为 PDF、学术文献或特定上下文,容易与普通翻译请求重叠。虽然同一行也包含较具体的 PDF 场景描述,但触发词本身缺少排除条件或负例,仍可能造成非预期调用。

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
95% confidence
Finding

The skill explicitly supports sending document text and OCR-derived text to a user-configured external OpenAI-compatible endpoint. Even though this behavior is disclosed and uses user-supplied credentials, it still creates a real data-exposure risk because sensitive PDF contents may leave the local environment and be processed by third-party services.

Content

Scanner excerpt · SKILL_ZH.md (reported line 54)May include surrounding context.

export DOC_HOLMES_OPENAI_API_KEY=<你在该平台创建的key>

text

SiliconFlow(`https://api.siliconflow.cn/v1`)或任意 OpenAI 兼容端点同理。

> ⚠️ 关于 GLM **Coding Plan** 订阅端点(`/api/coding/paas/v4`):官方 FAQ 明确该套餐仅限指定编码工具使用;在其他工具中调用**不享套餐额度、按量扣除账户余额**,多人共用同一订阅会被视为不当使用并限制订阅权益。确认知晓后仍要接入:设 `DOC_HOLMES_ALLOW_CODING_ENDPOINT=1`(一次性确认)。

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
62% confidence
Finding

The title and surrounding documentation are entirely in Chinese, which may indicate a fixed language choice for the skill documentation. No nearby text offers an alternative language or explains that the skill is intentionally limited to a Chinese-speaking audience or region-specific context.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The document instructs users to configure third-party OpenAI-compatible translation endpoints with their own API keys, but it does not clearly disclose that document contents may be transmitted off-host to external providers. In a PDF translation skill, the payload can include sensitive document text such as medical, legal, or proprietary content, so omission of privacy and data-handling warnings can lead to unintentional data exposure.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

Line L32 states that outputs will automatically include a bilingual Chinese-English notice page, and the document as a whole is written as normative behavior without user opt-in. This creates a locale/language policy concern because the skill imposes a specific language choice rather than offering it or documenting a region-specific requirement.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

The module docstring describing the skill is entirely in Chinese, which can function as a language-specific instruction surface for users or maintainers. The file does not indicate that Chinese is optional, selectable, or required for a region-specific purpose, so this appears to force a locale without opt-in.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
89% confidence
Finding

This CSV maps all terms to Chinese and marks the target language as zh for every entry, which is a natural-language locale constraint embedded in the file. Because no user choice or justification is present in this file, it can violate policy guidance against forcing a specific language without opt-in.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The module docstring explicitly states '全部中文帮助', indicating the command-line help is provided only in Chinese. This is a natural-language locale policy concern because the skill imposes a specific language on users without offering a choice or documenting an opt-in mechanism.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The manifest consistently positions the skill as operating on PDFs, including triage of PDF quality tiers and PDF translation outputs. However, the translate command also accepts .docx, .pptx, and .ppt inputs and converts them to PDF before processing, which expands actual behavior beyond the declared PDF-only scope.

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/doc_holmes/cli.py (reported line 451)May include surrounding context.

python
gpu = "未检测到"
    try:
        import subprocess as sp
        r = sp.run(["nvidia-smi", "-L"], capture_output=True, text=True, timeout=10)
        if r.returncode == 0 and r.stdout.strip():
            gpu = r.stdout.strip().splitlines()[0]
    except Exception:

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The manifest describes a PDF translation skill with triage, translation, batch processing, audit, and direct extract/apply workflows. This CLI also exposes standalone merge, split, and estimate commands that perform general PDF manipulation and sizing functions not described in the manifest's stated purpose or triggers.

Content

No source excerpt is available for this finding.

Session Persistence

Medium
Category
Rogue Agent
Confidence
69% confidence
Finding

The setup flow persists endpoint credentials to a local env file for future use, which creates a credential-at-rest exposure if the file is stored in an unsafe location, inherits weak permissions, or is later read by other local users/processes. Although the code comments indicate 0600 permissions, this CLI accepts a user-supplied --env-file, so the safety depends on how validate_and_write enforces secure creation and path handling.

Content

Scanner excerpt · scripts/doc_holmes/cli.py (reported line 609)May include surrounding context.

python
def cmd_setup(args) -> int:
    from .setup_wizard import status_report, validate_and_write
    from .endpoints import EndpointError
    if args.base_url or args.api_key:
        try:

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The top-level docstring repeatedly describes the tool as producing translated output in Chinese, and the code defaults lang_out to "zh" in extraction. This is a natural-language locale policy concern because the skill imposes a specific language choice by default without presenting a user choice or explaining a region-specific requirement.

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/doc_holmes/docx_output.py (reported line 30)May include surrounding context.

python
staged_src = os.path.join(staging, os.path.basename(pdf_path))
    shutil.copyfile(pdf_path, staged_src)
    try:
        proc = subprocess.run(
            [lo_bin, "--headless", "--convert-to", "docx",
             "--outdir", staging, staged_src],
            capture_output=True, text=True, timeout=timeout)

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · scripts/doc_holmes/endpoints.py (reported line 29)May include surrounding context.

python
"OpenAI 兼容端点 + key(三选一):\n"
    "  A) 智谱开放平台官方 API(推荐):https://open.bigmodel.cn/api/paas/v4\n"
    "     glm-4.5-flash 免费档;在 open.bigmodel.cn 注册并创建你自己的 key\n"
    "  B) SiliconFlow 等聚合平台免费档(https://api.siliconflow.cn/v1)\n"
    "  C) 任意 OpenAI 兼容端点\n"
    "设置方式(二选一):\n"
    "  1) 环境变量:\n"

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

The default parameters set lang_out to "zh", and surrounding natural-language comments/docstrings are in Chinese, indicating a default behavior that enforces a specific output locale. Under the stated policy, forcing a language without explicit user choice or clear justification is a locale policy concern.

Content

No source excerpt is available for this finding.