Back to skill

Security audit

Fund Report Extractor

Security checks for vulnerabilities and agentic risk

Overview

The skill has a coherent fund-report extraction purpose, but its script can write files outside the intended output area and processes downloaded PDFs insecurely.

Review before installing or running. Use it only in a dedicated working directory, pass normal fund codes and names without path characters, avoid shared writable folders, and prefer updating the script to use HTTPS, safe temporary files, validated filenames, size/type checks, and pinned dependencies.

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
extract.py:90
Finding
Remote PDF Files Are Downloaded over Unencrypted HTTP## Vulnerability Details **File Location**: `extract.py`, line 90 **Vulnerability Type**: Unauthenticated remote content retrieval **Risk Level**: Medium ### Vulnerable Code ```python report_id = row['报告ID'] date = row['公告日期'] pdf_url = f'http://pdf.dfcfw.com/pdf/H2_{report_id}_1.pdf' try: resp = requests.get(pdf_url, timeout=30) if len(resp.content) < 5000: continue ``` The same insecure URL scheme is documented in `SKILL.md`, line 34: ```text http://pdf.dfcfw.com/pdf/H2_{报告ID}_1.pdf ``` ### Technical Analysis The application retrieves untrusted PDF documents over plaintext HTTP and immediately processes the response with `pdfplumber` or PyMuPDF. HTTP does not provide server authentication, integrity protection, or confidentiality. A network-positioned attacker can therefore replace or modify a downloaded report. The response is accepted based only on its size. The code does not call `raise_for_status()`, validate the response content type, verify the PDF magic bytes, impose a maximum response size, or authenticate the document. PDF parsers process complex binary structures, so feeding attacker-controlled documents to them increases exposure to parser vulnerabilities and resource-exhaustion attacks. ### Attack Path 1. A user runs the extractor on a network controlled or observable by an attacker. 2. The script requests a report through `http://pdf.dfcfw.com/...`. 3. The attacker intercepts or redirects the HTTP response. 4. The attacker returns a crafted payload larger than 5,000 bytes. 5. The script writes the payload to `temp.pdf`. 6. `pdfplumber` or PyMuPDF parses the attacker-controlled file. 7. The payload corrupts extracted results, consumes excessive resources, or attempts to exploit a vulnerability in the installed parser. ### Impact Assessment An attacker can alter the generated report content and potentially cause denial of service through oversized or computat ...[truncated 286 chars]
Remediation
## Remediation Suggestions - Require an HTTPS endpoint and reject redirects that downgrade to HTTP. - Call `resp.raise_for_status()` before consuming the response. - Stream downloads while enforcing a strict maximum file size. - Validate the expected content type and require the file to begin with a valid PDF signature. - Where supported, verify reports using a trusted digest or digital signature. - Parse externally sourced PDFs in a sandbox with restricted filesystem access, network access, CPU time, and memory. - Keep PyMuPDF, pdfplumber, and their transitive parsing components updated to reviewed versions.

T09 · Insecure Skill Coding Practices

Error
Location
extract.py:164
Finding
Unvalidated Command-Line Arguments Permit Path Traversal and File Overwrite## Vulnerability Details **File Location**: `extract.py`, lines 164-174 **Vulnerability Type**: Path traversal and arbitrary file creation **Risk Level**: High ### Vulnerable Code User-controlled arguments are accepted without validation and used to construct paths: ```python def main(): parser = argparse.ArgumentParser(description='提取基金定期报告投资策略') parser.add_argument('--code', required=True, help='基金代码') parser.add_argument('--name', required=True, help='基金名称') args = parser.parse_args() output_dir = f'reports_{args.code}' process_reports(args.code, args.name, output_dir) ``` The resulting directory and files are created or overwritten at lines 82 and 121-123: ```python os.makedirs(output_dir, exist_ok=True) ``` ```python filename = f'{output_dir}/{date}.txt' with open(filename, 'w', encoding='utf-8') as f: f.write(content) ``` The user-controlled fund name is also used directly as a summary path at lines 153-155: ```python summary_file = f'{name}_投资策略汇总.txt' with open(summary_file, 'w', encoding='utf-8') as f: f.write('\n'.join(output)) ``` ### Technical Analysis The `--code` and `--name` values can contain path separators and parent-directory components. No allowlist, basename conversion, canonicalization, or output-root containment check is applied. Prefixing `--code` with `reports_` does not prevent traversal. For example, a value containing an initial directory component followed by `../` sequences can normalize outside the intended workspace. Similarly, a `--name` value such as `../../destination` causes the summary file path to resolve in a parent directory. Files are opened with mode `w`, so an existing writable file at the resolved path is truncated and replaced. ### Attack Path 1. An attacker who can influence invocation arguments supplies a `--code` or `--name` containing path separators and `..` components. 2. The application concatena ...[truncated 837 chars]
Remediation
## Remediation Suggestions - Validate fund codes against the expected format, such as a strict numeric allowlist: ```python if not re.fullmatch(r'\d{6}', args.code): parser.error('Invalid fund code') ``` - Convert the display name into a safe filename by removing path separators, control characters, `.` and `..` path components, and platform-specific reserved names. - Use `pathlib.Path` and place all generated files beneath a fixed output root. - Resolve both the root and candidate path, then verify that the candidate remains inside the root with `Path.is_relative_to()` or an equivalent check. - Use `Path` joins instead of string path concatenation. - Avoid silent overwrites. Use exclusive creation mode where appropriate or require explicit confirmation before replacing an existing file. - Treat announcement-derived filename components as untrusted and validate them as well.

T09 · Insecure Skill Coding Practices

Error
Location
extract.py:98
Finding
Predictable Shared Temporary File Enables Symlink and Race Attacks## Vulnerability Details **File Location**: `extract.py`, lines 98-104 **Vulnerability Type**: Unsafe temporary-file handling **Risk Level**: High ### Vulnerable Code ```python # 保存临时文件 with open('temp.pdf', 'wb') as f: f.write(resp.content) # 尝试两种提取方式 full_text = None # 方法1: pdfplumber try: full_text = extract_with_pdfplumber('temp.pdf') except: pass ``` Cleanup later uses the same predictable path at line 130: ```python os.remove('temp.pdf') ``` ### Technical Analysis Every invocation uses the fixed relative filename `temp.pdf`. In a shared or attacker-writable working directory, an attacker can pre-create this path as a symbolic link. Opening it with `wb` follows the link and truncates the linked target before writing PDF data. The predictable path also creates a time-of-check/time-of-use race. Another local process can replace the file between writing and parsing, causing the extractor to parse different content. Concurrent legitimate invocations can overwrite, parse, or remove one another's temporary files. Cleanup is not placed in a `finally` block. Exceptions before `os.remove()` can leave untrusted PDF data behind for later runs. ### Attack Path 1. The attacker obtains write access to the extractor's working directory. 2. Before execution, the attacker creates `temp.pdf` as a symbolic link to a file writable by the victim account. 3. The victim runs the extractor. 4. `open('temp.pdf', 'wb')` follows the symbolic link and truncates the target. 5. The script writes downloaded PDF data into the target file. 6. Alternatively, the attacker races file replacement after the write and supplies a malicious PDF for parsing. ### Impact Assessment A local attacker can overwrite any file writable by the process account when the working-directory and symbolic-link preconditions are met. This can cause data destruction, configuration corruption, or indirect code execution if an ...[truncated 225 chars]
Remediation
## Remediation Suggestions - Replace the fixed filename with `tempfile.NamedTemporaryFile` or `tempfile.TemporaryDirectory`. - Create temporary files atomically with restrictive permissions. - Pass the generated unique path directly to the parser. - Perform cleanup in a `finally` block or rely on a temporary-directory context manager. - Prefer in-memory parsing where safely supported and subject to input-size limits. - Run untrusted document parsers with least privilege and in an isolated working directory inaccessible to other users.

T08 · Insecure Dependencies

Note
Location
README.md:31
Finding
Dependencies Are Installed Without Version or Integrity Pinning## Vulnerability Details **File Location**: `README.md`, lines 31-35 **Vulnerability Type**: Unpinned third-party dependency installation **Risk Level**: Low ### Vulnerable Code ```markdown ## 依赖 ```bash pip install akshare pymupdf pdfplumber pandas requests ``` ``` Equivalent installation guidance appears in `SKILL.md`, lines 111-114: ```markdown ## 依赖库 ```bash pip install akshare pymupdf pdfplumber pandas requests ``` ``` ### Technical Analysis The project instructs users to install mutable package names without exact versions, hashes, or a lock file. As a result, separate installations can resolve to different direct and transitive dependency versions. Although the listed package names are consistent with imports in the script and no typosquatted package was identified, the installation process does not authenticate a reviewed dependency set. A compromised release, malicious transitive dependency, or unexpected incompatible update can be retrieved after the Skill itself has been audited. Python packages may execute code during installation or when imported. Consequently, dependency compromise can affect the process before normal extraction logic begins. ### Attack Path 1. A user follows the documented `pip install` command. 2. The package resolver selects the latest versions available at installation time. 3. A selected direct or transitive release has been compromised or contains a newly introduced vulnerability. 4. The package executes code during installation or import, or exposes the extractor to a vulnerable parsing path. 5. The malicious or vulnerable dependency operates with the privileges of the user performing installation or running the script. ### Impact Assessment A compromised dependency can execute arbitrary code with the installing or running user's privileges, access files and credentials available to that account, and alter extraction results. The current repository does n ...[truncated 137 chars]
Remediation
## Remediation Suggestions - Provide a reviewed lock file containing exact direct and transitive versions. - Use hash-verified installation, such as pinned requirements with `--require-hashes`. - Install from a documented trusted package index. - Automate dependency vulnerability and provenance checks. - Review and deliberately update pinned dependencies on a controlled schedule. - Use an isolated virtual environment and avoid installing project dependencies with administrative privileges.
Vulnerability Patterns
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
Findings (5)

Natural-Language Policy Violations

Medium
Confidence
93% confidence
Finding
The entire skill documentation is presented only in Chinese, including headings, parameter explanations, examples, and warnings. Under the policy rule, forcing a specific language without user opt-in is a natural-language policy violation unless the locale restriction is explicitly justified, which this README does not do.

Natural-Language Policy Violations

Low
Confidence
93% confidence
Finding
The documented instructions, examples, and headings all assume Chinese without any opt-in or locale justification. The policy requires flagging language or locale constraints when a skill forces a specific language without user choice or an explicit documented reason.

Missing User Warnings

Low
Confidence
88% confidence
Finding
This markdown file documents outputs including a raw report directory and a summary text file, which indicates local data storage. Under the markdown criteria for SQP-2, the description should include a user-facing warning when behavior affects user data or filesystem state, but the file only lists outputs without any caution about disk usage, overwrite behavior, or retention of downloaded reports.

Natural-Language Policy Violations

Low
Confidence
94% confidence
Finding
This code file contains natural-language strings that require Chinese comprehension for basic use, including the module description and usage example. Under the policy, forcing a specific language without user opt-in or justification is a natural-language policy concern.

Natural-Language Policy Violations

Low
Confidence
92% confidence
Finding
The argparse description and help strings are only in Chinese, which constrains users to a single language without offering alternatives. The file does not indicate that this language restriction is optional or justified as a region-specific requirement.

Static analysis

No suspicious patterns detected.