Back to skill

Security audit

PDF 知识提取技能

Security checks for vulnerabilities and agentic risk

Overview

The skill mostly matches its PDF conversion purpose, but it includes under-scoped remote fetching and an undocumented script that reads another OpenClaw workspace path.

Review carefully before installing. Use this only in an isolated virtual environment with trusted local PDFs or trusted URLs, avoid running it on hosts with access to sensitive internal network services, and remove or disable decode_files.py unless you explicitly need that workspace metadata utility. Pin dependencies before installation.

Vulnerability Patterns
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • 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
Findings (4)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/pdf2md.py:7
Finding
Unrestricted Remote PDF Retrieval Enables Server-Side Request Forgery and Resource Exhaustion<![CDATA[ ## Vulnerability Details **File Location**: `scripts/pdf2md.py`, lines 7-12 **Vulnerability Type**: Server-side request forgery, unsafe network access, and unbounded response buffering **Risk Level**: High ### Vulnerable Code ```python def download(u): if u.startswith('http'): r = requests.get(u, timeout=60, headers={'User-Agent':'Mozilla/5.0'}) return r.content with open(u, 'rb') as f: return f.read() ``` ### Technical Analysis The source URL is accepted directly from the command line and passed to `requests.get()` without validating its destination. The implementation does not: - Restrict requests to approved hosts. - Require HTTPS. - Resolve and reject loopback, private, link-local, multicast, or reserved IP addresses. - Validate redirect destinations. - Limit the maximum response size. - Verify the HTTP status using `raise_for_status()`. - Verify the response content type or PDF file signature. The check `u.startswith('http')` is not a meaningful destination security control. Moreover, `requests` follows redirects by default, so even an initially trusted public URL could redirect the request to an internal address. The complete response is accessed through `r.content`, causing it to be buffered in memory. It is subsequently written to a temporary file for PDF processing. A malicious or compromised endpoint can therefore return an extremely large response and consume substantial memory and disk space. ### Attack Path 1. An attacker supplies a URL to `scripts/pdf2md.py`, either directly or through a system that exposes this conversion function. 2. The URL points to a loopback, private-network, link-local, or attacker-controlled service. Alternatively, it points to a public endpoint that redirects to such a destination. 3. The process sends the request using its own network privileges. 4. If the target returns a PDF, its text can be extracted and included in the generated Markdown output. 5. If the target retu ...[truncated 969 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Permit only `https://` URLs unless insecure HTTP access is explicitly required. 2. Maintain an explicit allowlist of trusted hostnames. 3. Resolve the hostname before connecting and reject loopback, private, link-local, multicast, unspecified, and reserved addresses for both IPv4 and IPv6. 4. Disable automatic redirects or validate every redirect target using the same hostname and IP-address controls. 5. Use streaming retrieval with a strict maximum download size: ```python with requests.get( url, stream=True, timeout=(5, 60), allow_redirects=False, headers={"User-Agent": "pdf-skills/1.0"}, ) as response: response.raise_for_status() ``` 6. Validate `Content-Length` when present and independently count streamed bytes so chunked responses cannot bypass the limit. 7. Accept only expected content types and verify that the downloaded data begins with a valid PDF signature. 8. Apply outbound firewall or sandbox controls so the process cannot reach internal or metadata networks. 9. Handle download and parsing failures without leaving partial output files. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/pdf2md.py:14
Finding
Predictable Temporary PDF Path Permits a Local Symlink Race<![CDATA[ ## Vulnerability Details **File Location**: `scripts/pdf2md.py`, lines 14-18 **Vulnerability Type**: Insecure temporary-file creation and time-of-check/time-of-use race **Risk Level**: Medium ### Vulnerable Code ```python def extract(data): import fitz tmp = tempfile.mktemp(suffix='.pdf') with open(tmp, 'wb') as f: f.write(data) ``` ### Technical Analysis `tempfile.mktemp()` returns a currently unused pathname but does not securely create or reserve the file. There is a race between generation of the pathname and the subsequent call to `open(tmp, 'wb')`. On a shared system, another local process operating under a different account may create a symbolic link or file at that pathname before it is opened. Because regular `open()` follows symbolic links, the downloaded PDF bytes could be written to a different file selected by the attacker. The eventual cleanup in the function's `finally` block does not prevent exploitation because the unsafe write has already occurred. It may also remove the attacker's symlink rather than addressing the overwritten target. ### Attack Path 1. The victim starts PDF conversion on a shared host. 2. The code obtains a predictable, unreserved path from `tempfile.mktemp()`. 3. Before the following `open()` call, a local attacker creates a symbolic link at that path pointing to another file. 4. The victim process opens the symbolic link in write mode. 5. Attacker-controlled or downloaded PDF data truncates and overwrites the target to the extent permitted by the victim process's filesystem privileges. 6. The temporary pathname is later removed, but the target file remains modified. Successful exploitation requires local access to the relevant temporary directory and the ability to win the race. ### Impact Assessment The attacker may overwrite any file writable by the account running the conversion process. Potential consequences include data corruption, service disruption, or modification of user c ...[truncated 209 chars]
Remediation
<![CDATA[ ## Remediation Suggestions Replace `tempfile.mktemp()` with an API that atomically creates the file: ```python def extract(data): import fitz fd, tmp = tempfile.mkstemp(suffix=".pdf") try: with os.fdopen(fd, "wb") as f: f.write(data) with fitz.open(tmp) as doc: # Process pages here. ... finally: try: os.unlink(tmp) except FileNotFoundError: pass ``` Alternatively, use `tempfile.NamedTemporaryFile(delete=False, suffix=".pdf")` and write through the already-created file object. Additional hardening should include: - Running the converter under a dedicated, non-privileged account. - Using a private temporary directory with restrictive permissions. - Avoiding reopening the temporary pathname where processing can instead use a securely created descriptor or supported in-memory input. - Catching only expected cleanup exceptions rather than using a bare `except`. ]]>

T08 · Insecure Dependencies

Warning
Location
SKILL.md:102
Finding
Unpinned Third-Party Dependency Installation Creates Supply-Chain Exposure<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md`, lines 102-106; `README.md`, lines 11-15 **Vulnerability Type**: Unpinned and non-reproducible dependency installation **Risk Level**: Medium ### Vulnerable Code From `SKILL.md`: ```markdown ## 安装依赖 ```bash pip install requests pymupdf PyYAML ``` ``` From `README.md`: ```markdown ### 1. 安装依赖 ```bash pip install requests pymupdf PyYAML ``` ``` ### Technical Analysis The installation instructions request mutable package names without exact versions, integrity hashes, or a lock file. Consequently, installations performed at different times can resolve to different package releases. Python package installation may execute package build or installation logic. If a configured package index, mirror, package release, or transitive dependency is compromised, following these instructions can introduce attacker-controlled code before the skill is run. The absence of hashes also prevents users from verifying that retrieved distributions match reviewed artifacts. `PyYAML` is listed as a required dependency, but no reviewed executable script imports it. This unnecessary package increases the supply-chain attack surface without supporting observed runtime functionality. ### Attack Path 1. A user follows the documented `pip install` command. 2. `pip` resolves the latest compatible versions from the user's configured index or mirror. 3. A compromised release, dependency, mirror, or substituted artifact is selected. 4. Malicious package build or installation code executes with the privileges of the user running `pip`. 5. The installed package may execute again when imported by the conversion scripts. This finding does not establish that the named packages are malicious. The risk arises from mutable, unverifiable resolution and the unnecessary dependency. ### Impact Assessment A compromised dependency can execute arbitrary code with the privileges of the installing user. That code could access files, ...[truncated 268 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove `PyYAML` unless a verified runtime need is introduced. 2. Define reviewed, exact dependency versions in a requirements or project metadata file. 3. Generate a lock file that includes all transitive dependencies. 4. Require artifact hashes, for example through `pip install --require-hashes -r requirements.txt`. 5. Prefer binary wheels from a trusted package index and document the expected index source. 6. Install dependencies inside an isolated virtual environment under a non-privileged account. 7. Use automated dependency scanning and regularly review pinned updates rather than resolving mutable latest versions at installation time. 8. Keep installation instructions synchronized with the lock file instead of duplicating an unpinned command in multiple documents. ]]>

T05 · Unauthorized Access and Privilege Escalation

Warning
Location
decode_files.py:4
Finding
Undocumented Script Reads Metadata from an External OpenClaw Workspace<![CDATA[ ## Vulnerability Details **File Location**: `decode_files.py`, lines 4-17 **Vulnerability Type**: Out-of-scope local workspace access and information disclosure **Risk Level**: Medium ### Vulnerable Code ```python d = r'C:\Users\Administrator\.openclaw\workspace\ioc-kms-dataset\yanghu' files = [x for x in os.listdir(d) if x[:2].isdigit() and x.endswith('.md')] print('Found %d files' % len(files), file=sys.stderr) for f in sorted(files): fp = os.path.join(d, f) with open(fp, 'r', encoding='utf-8', errors='replace') as fh: content = fh.read(500) m = re.search(r'title:\s*"([^"]+)"', content) title = m.group(1) if m else '(no title)' m2 = re.search(r'pages_total:\s*(\d+)', content) pages = m2.group(1) if m2 else '?' m3 = re.search(r'total_chars:\s*(\d+)', content) chars = m3.group(1) if m3 else '?' print('[%s] %s | P=%s C=%s' % (f[:2], title[:50], pages, chars)) ``` ### Technical Analysis The script contains a hard-coded absolute path into an Administrator user's OpenClaw workspace. It enumerates matching Markdown files, reads the first 500 characters of every file, extracts document titles and metadata, and prints those values. This behavior is outside the documented PDF-conversion and rule-extraction workflow. The file is also omitted from the directory structures documented in `README.md` and `SKILL.md`. It does not request a path from the user, validate that the target directory is within the project, or obtain explicit approval before reading the separate workspace. The code executes at module scope. Therefore, importing `decode_files.py` triggers the filesystem access immediately rather than exposing a side-effect-free library interface. ### Attack Path 1. A user, automation process, or another Python module executes or imports `decode_files.py`. 2. The script accesses the hard-coded OpenClaw workspace using the current process's filesystem privileges. 3. It enumerates numbered Markdown files and r ...[truncated 892 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove `decode_files.py` if it is an accidental development or diagnostic artifact. 2. If the functionality is required, accept the input directory through an explicit command-line argument rather than using an Administrator-specific absolute path. 3. Require explicit user approval before accessing a directory outside the project or approved output root. 4. Resolve and validate the selected path against an allowlisted base directory. 5. Place execution inside a `main()` function protected by: ```python if __name__ == "__main__": main() ``` 6. Avoid printing document metadata unless requested, and ensure logs do not expose sensitive titles. 7. Document the script, its accessed data, and its permission requirements. 8. Run the utility with a least-privileged account that cannot read unrelated Agent workspaces. ]]>
Vulnerability Patterns
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • System Prompt LeakageDirect Leakage, Indirect Extraction, Tool-Based Exfiltration
  • 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
Findings (15)

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
The declared description centers on PDF ingestion and knowledge extraction workflows. The supplied code does not open or parse PDFs, convert content to Markdown, repair CJK spacing, or extract knowledge from document bodies. Instead, it merely enumerates existing .md files in a specific hardcoded directory and prints a few metadata values from their headers. This is a materially different and much narrower behavior than the declared purpose, so it is a clear mismatch.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding
该代码块的主要功能是对Markdown文本进行规则抽取,而不是处理PDF。声明描述强调这是一个“PDF知识提取技能”,并列出四项功能,其中前两项(PDF转Markdown、CJK间距修复)在代码中完全没有体现;输入参数也是.md文件路径,说明实际资源访问对象与声明不一致。虽然规则提取与部分行业标准文档分析相关,和声明中的第(3)项有明显重合,但整体描述的核心能力范围大于代码实际行为,且关键PDF相关能力缺失,因此构成描述与行为不匹配。

Tp4

High
Category
MCP Tool Poisoning
Confidence
92% confidence
Finding
Part of the description is accurate: the script does convert PDF text to Markdown and includes CJK spacing cleanup. It also has some industry-standard-specific handling through filename/URL pattern matching for standard IDs and categories. However, the more substantive claimed capabilities—'knowledge rule batch extraction' and broader structured knowledge extraction/analysis—are not present in this code. The script processes a single PDF input, extracts plain text per page, adds simple metadata, and outputs Markdown. That is a narrower behavior than the declared purpose, so this is a description-behavior mismatch.

Direct Prompt Extraction

High
Category
System Prompt Leakage
Content
if rule not in rules[rtype]:
                            rules[rtype].append(rule)
    
    return rules

def rules_to_markdown(rules, title, standard_id):
    """将规则转为Markdown格式"""
Confidence
85% confidence
Finding
Skill contains instructions that could directly expose system prompts, internal rules, or hidden instructions to users or external parties.

Natural-Language Policy Violations

Medium
Confidence
85% confidence
Finding
All user-facing documentation in this file is presented only in Chinese, with no indication that the skill supports other languages or that Chinese-only usage is an intentional, justified regional constraint. Under the stated policy, forcing a specific language without user opt-in can be a natural-language policy violation.

Lp3

Medium
Category
MCP Least Privilege
Confidence
86% confidence
Finding
The skill advertises operations that imply shell execution, filesystem reads/writes, and network access, but it does not declare any explicit tool scope or permission boundaries. In an agent environment, this can cause the runtime to grant broader capabilities than users expect, increasing the chance of unintended file access, arbitrary downloads, or command execution through downstream scripts.

Natural-Language Policy Violations

Medium
Confidence
88% confidence
Finding
The manifest description and title are written entirely in Chinese and present the skill as a Chinese/PDF knowledge extraction workflow, but they do not state that the skill is limited to Chinese-language users or provide any language-choice option. Under the policy, forcing a specific language without user opt-in is a natural-language locale violation.

Context-Inappropriate Capability

Medium
Confidence
82% confidence
Finding
The manifest describes PDF text extraction, CJK spacing repair, rule extraction, and structured processing, but this script is specifically tied to harvesting URLs from a fixed external CDN path and orchestrating conversion by spawning another Python script. External source coupling and subprocess orchestration are implementation capabilities beyond the user-stated document-processing purpose in the manifest.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
fname = url.split('/')[-1]
    print("  Converting: %s ..." % fname[:50])
    try:
        r = subprocess.run(cmd, capture_output=True, timeout=180)
        if r.returncode == 0:
            sz = os.path.getsize(out) // 1024 if os.path.exists(out) else 0
            print("  [OK] %s_%s.md (%dKB)" % (seq, sid, sz))
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Natural-Language Policy Violations

Medium
Confidence
95% confidence
Finding
The script's user-facing messages are presented in Chinese, and elsewhere it emits Chinese report headings and Chinese default metadata values. Because this is a general-purpose extractor script and the file does not state that it is limited to a Chinese-only or region-specific workflow, the hard-coded locale behavior appears to enforce a specific language without user opt-in.

Description-Behavior Mismatch

Medium
Confidence
91% confidence
Finding
The manifest describes a PDF knowledge-extraction skill for processing PDF files, but this implementation accepts a URL and performs network retrieval with requests.get before processing. Network fetching is a broader behavior than local PDF extraction and is not mentioned in the manifest description.

Context-Inappropriate Capability

Medium
Confidence
87% confidence
Finding
This script can retrieve content from any URL beginning with 'http', which introduces a general network access capability. The manifest focuses on extracting text, fixing CJK spacing, and structuring industry-standard PDF documents; it does not state that the skill should browse or fetch remote resources.

Missing User Warnings

Low
Confidence
91% confidence
Finding
The usage examples show the tool accepting HTTPS URLs as input, which implies outbound network requests to fetch remote PDF content. The README does not include any user-facing warning about network access, remote fetching, or associated privacy/integrity considerations.

Natural-Language Policy Violations

Low
Confidence
83% confidence
Finding
The file begins with a Chinese-only natural-language docstring describing the script. Under the policy rule for language/locale, forcing a specific language without offering user choice or documenting a justified locale restriction can be a policy concern.

Missing User Warnings

Low
Confidence
88% confidence
Finding
The script can make outbound network requests and write a generated Markdown file to disk without an explicit warning or confirmation, which may surprise users in agent-driven or automated contexts. While not a direct exploit primitive, this can cause unintended network access or overwrite an existing file path supplied on the command line, making the behavior risky in environments where side effects must be explicit.

Static analysis

No suspicious patterns detected.