Back to skill

Security audit

Pdf To Markdown

Security checks for vulnerabilities and agentic risk

Overview

This local PDF converter is mostly aligned with its purpose, but it needs review because crafted filenames can be expanded into Python code during fallback processing.

Review before installing if you may process PDFs from untrusted sources or filenames supplied by others. The skill does not show exfiltration or persistence beyond local files, but it should be fixed to pass paths into Python as data rather than generating Python source, and users should understand that converted content and command history are stored locally.

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

Error
Location
scripts/script.sh:89
Finding

Arbitrary Python Code Execution Through Unsafe Filename Interpolation

Content
View full analysis

Vulnerability Details

File Location: scripts/script.sh:89-126, scripts/script.sh:247-258, scripts/script.sh:502-510, and scripts/script.sh:566-578
Vulnerability Type: Python source-code injection through unquoted shell heredocs
Risk Level: High

Vulnerable Code

The PDF extraction fallback directly inserts the user-supplied PDF path into Python source:

bash
_extract_with_python() {
    local file="$1"
    python3 <<PYEOF
import sys
text = ""

# Try PyPDF2 first
try:
    from PyPDF2 import PdfReader
    reader = PdfReader("$file")
    for page in reader.pages:
        t = page.extract_text()
        if t:
            text += t + "\n\n"
    if text.strip():
        print(text)
        sys.exit(0)
except ImportError:
    pass
except Exception as e:
    print(f"  PyPDF2 error: {e}", file=sys.stderr)

# Try pdfminer.six
try:
    from pdfminer.high_level import extract_text as pdfminer_extract
    text = pdfminer_extract("$file")
    if text.strip():
        print(text)
        sys.exit(0)
except ImportError:
    pass
except Exception as e:
    print(f"  pdfminer error: {e}", file=sys.stderr)

# Basic fallback: try to read raw text streams from PDF
try:
    import re
    with open("$file", "rb") as f:
        raw = f.read()

The page-count fallback contains the same unsafe interpolation:

bash
if command -v python3 &>/dev/null; then
    python3 <<PYEOF
try:
    from PyPDF2 import PdfReader
    r = PdfReader("$file")
    print(len(r.pages))
except Exception:
    # Fallback: count /Type /Page occurrences
    import re
    with open("$file", "rb") as f:
        data = f.read()
    count = len(re.findall(rb'/Type\s*/Page[^s]', data))
    print(count if count > 0 else "?")
PYEOF

The metadata fallback also inserts the PDF path into executable Python source:

bash
python3 <<PYEO
...[truncated 3853 chars]
Remediation
View remediation

Remediation Suggestions

Never interpolate paths or other external values into generated Python source. Pass each value as a command-line argument or environment variable, and quote the heredoc delimiter to disable shell expansion.

For example, replace the extraction pattern with:

bash
python3 - "$file" <<'PYEOF'
import sys

file_path = sys.argv[1]

from PyPDF2 import PdfReader
reader = PdfReader(file_path)
PYEOF

Apply the same design to the page-count and metadata routines.

For JSON export, pass every path and metadata value separately:

bash
python3 - "$latest" "$base" "$json_out" <<'PYEOF'
import datetime
import json
import sys

latest, base, json_out = sys.argv[1:4]

with open(latest, encoding="utf-8") as source_file:
    content = source_file.read()

data = {
    "source": base + ".pdf",
    "format": "markdown",
    "content": content,
    "exported": datetime.datetime.now().isoformat(),
}

with open(json_out, "w", encoding="utf-8") as output_file:
    json.dump(data, output_file, indent=2, ensure_ascii=False)
PYEOF

Additional hardening should include:

  1. Quote every heredoc delimiter used for static Python code.
  2. Treat filenames, configured directories, and derived basenames as untrusted data.
  3. Add regression tests using filenames containing quotes, backslashes, spaces, Unicode characters, shell metacharacters, and line breaks.
  4. Validate that all supported commands process unusual filenames without changing generated program syntax.
  5. Run PDF parsing in a restricted process or sandbox when processing files from untrusted sources.
  6. Review future embedded-language invocations for the same source-generation pattern.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • 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
Findings (22)

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding

The declared purpose says PDF-to-Markdown conversion, but the observed behavior reportedly includes undeclared comparison/diff and structured entity extraction while also lacking actual PDF parsing behavior. A mismatch between advertised and actual behavior is dangerous because users may invoke the skill expecting narrow document conversion while it instead processes content in broader, potentially privacy-impacting ways.

Content

No source excerpt is available for this finding.

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
scripts/script.sh md

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
scripts/script.sh md

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
scripts/script.sh md

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
scripts/script.sh md

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
scripts/script.sh md

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
scripts/script.sh md

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
scripts/script.sh md

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
scripts/script.sh md

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
scripts/script.sh md

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
scripts/script.sh md

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
scripts/script.sh md

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
scripts/script.sh md

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
scripts/script.sh md

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
scripts/script.sh md

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
scripts/script.sh md

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
scripts/script.sh md

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
88% confidence
Finding

The skill exposes code-related capabilities such as environment access and file writing but does not declare any tool scope or permissions boundaries in the manifest. This creates ambiguity for operators and orchestration systems, increasing the risk that the skill can write files or use environment-derived data in ways users did not explicitly authorize.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The description uses broad invocation terms like extracting text, summarizing documents, and fixing formatting issues, which can cause the skill to be auto-selected for many generic document tasks beyond strict PDF conversion. Over-broad triggering is risky because it can route sensitive documents into a skill with file-write capability and unclear behavioral boundaries.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The script persists converted document content under a user-specific output directory and also appends command activity to a history log, but it does not clearly warn users that potentially sensitive PDF contents and usage metadata will be stored locally. In a document-processing skill, PDFs often contain confidential business, legal, financial, or personal information, so silent persistence increases the risk of unintended exposure to other local users, backups, sync tools, or later exfiltration from disk.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
90% confidence
Finding

This shell skill presents its primary help content in Chinese and hard-codes bilingual output labels and messages throughout the commands, indicating a fixed locale behavior rather than adapting to user preference. The policy allows locale constraints only when documented and justified or when the user is given an explicit choice, which is not present here.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
83% confidence
Finding

The file is primarily written in Chinese, with only brief English glosses, and does not indicate that users may choose another language or that the skill is intended only for a Chinese-language context. Under the policy, forcing a specific language without opt-in can be a natural-language locale violation.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.