Back to skill

Security audit

vbt-report

Security checks for vulnerabilities and agentic risk

Overview

This appears to be a legitimate A-share backtesting report skill, but it has unsafe HTML and filename handling that could let crafted stock data execute browser code or write reports outside the intended output folder.

Install only in an isolated environment and run it on stock lists you trust. Keep outputs in a dedicated folder, avoid serving generated reports from an authenticated or privileged web origin, and consider fixing filename sanitization, HTML escaping, pinned dependencies, and offline Plotly bundling before production use.

Vulnerability Patterns
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
  • 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/batch_report.py:318
Finding
Stored HTML and JavaScript Injection in Generated Reports<![CDATA[ ## Vulnerability Details **File Location**: `scripts/batch_report.py:318,432,475-509`; related unsafe report interpolation in `scripts/report.py:547-579,725` **Vulnerability Type**: Stored cross-site scripting through unescaped report data **Risk Level**: High ### Vulnerable Code ```python # scripts/batch_report.py import json rows_json = json.dumps(rows_data, ensure_ascii=False) ``` ```html <script> // Data is inserted directly into an executable script context. var ROWS = {rows_json}; ``` ```javascript var sigText = r.signal || ''; var sigDisplay = sigText ? ('<td class="' + sigCss + '">' + sigText + '</td>') : '<td style="color:var(--text-dim)">-</td>'; html += '<tr style="display:table-row" data-sig="' + (r.sig_cat || '') + '">' + '<td><strong>' + r.code + '</strong></td>' + '<td>' + r.name + '</td>' + sigDisplay + '<td style="color:' + vbtC + ';font-weight:600;">' + vbtV + '</td>' + '<td>' + r.best_name + '</td>' + '<td>' + statusCell + '</td>' + '</tr>'; document.getElementById('table-body').innerHTML = html; ``` The individual report generator contains a second unsafe HTML interpolation path: ```python # scripts/report.py label = f"{ticker} {stock_name}" if stock_name else ticker html = f"""<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>{label} - VectorBT 向量化回测分析报告</title> ... <div class="header"> <h1>{label} - VectorBT 向量化回测分析报告</h1> ... </body> </html>""" with open(out_path, 'w', encoding='utf-8') as f: f.write(html) ``` ### Technical Analysis Stock names, signal text, report filenames, strategy names, and other values originating from CSV/XLS input or external market-data services are embedded into generated HTML without context-sensitive escaping. `json.dumps()` produces valid JSON, but it does not make arbitrary strings safe inside an HTML `<script>` element. In particular, an attacker-contro ...[truncated 2010 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Avoid building HTML with string concatenation and `innerHTML`. Create DOM elements and assign untrusted values through `textContent`. 2. Assign link destinations through validated DOM properties and reject values that are not expected local report filenames. 3. Escape all server-generated HTML values with `html.escape(value, quote=True)` according to their HTML text or attribute context. 4. When placing JSON in an inline script, escape at least `<`, `>`, `&`, U+2028, and U+2029. Prefer placing JSON in a non-executable element and parsing its `textContent`. 5. Validate tickers against the documented six-digit A-share format. 6. Treat names and signals from CSV files and external APIs as untrusted data. 7. Add a restrictive Content Security Policy that disallows inline scripts and event handlers. Use a nonce or external local script where scripting is required. 8. Add regression tests using payloads containing HTML tags, quotes, event handlers, `</script>`, and encoded variants. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/batch_report.py:153
Finding
Output Directory Traversal Through Unsanitized Stock Names<![CDATA[ ## Vulnerability Details **File Location**: `scripts/batch_report.py:153-154,626-627` **Vulnerability Type**: Path traversal and unintended file write **Risk Level**: High ### Vulnerable Code ```python def _run_stock_worker(params): code, name, signal, score, advice, data_dir, output_dir = params report_file = f"{code}_{name}_vbt_report.html" if name else f"{code}_vbt_report.html" report_path = os.path.join(output_dir, report_file) try: from report import get_data, BacktestEngine, generate_report, guess_stock_name results, all_pfs, df = backtest_single(code, name, data_dir) best = results[0] stock_name_zh = guess_stock_name(code) generate_report(code, stock_name_zh, df, results, all_pfs, report_path) ``` The same unsafe filename construction is used when processing existing reports: ```python for st in stocks: code = st['code'] name = st['name'] signal = st.get('signal', '') score = st.get('score', 0) advice = st.get('advice', '') report_file = f"{code}_{name}_vbt_report.html" if name else f"{code}_vbt_report.html" report_path = os.path.join(args.output, report_file) ``` The destination is eventually written by `scripts/report.py`: ```python with open(out_path, 'w', encoding='utf-8') as f: f.write(html) ``` ### Technical Analysis The stock `name` is obtained from CSV/XLS/XLSX input or the `--stocks` argument and is inserted directly into a filename. No validation rejects directory separators, traversal components, rooted paths, reserved names, or platform-specific path syntax. `os.path.join(output_dir, report_file)` does not guarantee that the resulting path remains within `output_dir`. Traversal components in `name` can cause the normalized destination to refer to a different location. The write operation then creates or truncates the selected file using the process's existing filesystem privileges. The behavior exceeds the minimum privileges required ...[truncated 1452 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Do not use stock names in report filenames. Prefer a filename based only on a strictly validated six-digit ticker, such as `000001_vbt_report.html`. 2. If names must be retained, replace every character outside a narrow allowlist with `_`, including all path separators and platform-specific reserved characters. 3. Resolve and validate the final destination before writing: ```python from pathlib import Path base = Path(output_dir).resolve() filename = f"{validated_code}_vbt_report.html" destination = (base / filename).resolve() if destination.parent != base: raise ValueError("Report path escapes the output directory") ``` 4. Validate ticker values independently rather than trusting values produced by `zfill()`. 5. Use exclusive creation or an explicit overwrite policy where accidental replacement is not required. 6. Reject absolute paths, `..` components, null bytes, Windows drive prefixes, UNC syntax, and both `/` and `\` in all filename metadata. 7. Run report generation under an account that has write access only to the dedicated output directory. ]]>

T08 · Insecure Dependencies

Warning
Location
SKILL.md:37
Finding
Unpinned Third-Party Dependency Installation<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md:37` **Vulnerability Type**: Unpinned dependency and software supply-chain exposure **Risk Level**: Medium ### Vulnerable Code ```bash pip install vectorbt pandas numpy plotly mootdx tdxpy requests akshare openpyxl ``` ### Technical Analysis The documented installation command installs packages without exact versions, hashes, a lock file, or an explicitly trusted package index. Consequently, the code reviewed during this audit may run against different dependency versions each time it is installed. Python package installation can execute package build logic, and imported packages execute module-level code with the privileges of the Python process. A compromised future release, dependency-resolution change, or malicious package served through an untrusted index could therefore introduce code that is not present in the audited repository. The dependencies are broadly relevant to the declared backtesting and reporting functionality. The issue is not unnecessary package inclusion, but the absence of reproducible and integrity-verified dependency resolution. ### Attack Path 1. A direct or transitive package release is compromised, replaced, or otherwise becomes malicious. 2. A user follows the unpinned installation command at a later date. 3. `pip` resolves the affected package version from the configured package index. 4. Malicious build or installation logic may execute during installation. 5. Malicious module-level code may also execute when the report scripts import the package. 6. The code runs with the privileges and data access of the user running the Skill. ### Impact Assessment A compromised dependency can obtain arbitrary Python code execution in the installation or report-generation environment. Depending on the execution account, this may permit: - Reading files accessible to the user. - Modifying generated reports or local data. - Accessing environment variables and application cre ...[truncated 280 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Pin every direct dependency to a reviewed exact version. 2. Generate a lock file that also fixes transitive dependency versions. 3. Require package hashes, for example through a hashed requirements file and `pip install --require-hashes`. 4. Document and enforce the trusted package index instead of inheriting arbitrary user or system index configuration. 5. Install dependencies in an isolated virtual environment or container using a non-privileged account. 6. Regularly scan the resolved dependency graph for known vulnerabilities and package ownership changes. 7. Review dependency updates before regenerating the lock file. 8. Where practical, separate optional network-data providers into explicit extras so users do not install components they do not need. ]]>

T03 · Remote Payload Retrieval and Execution

Warning
Location
scripts/report.py:513
Finding
Generated Reports Retrieve Executable JavaScript From an External CDN<![CDATA[ ## Vulnerability Details **File Location**: `scripts/report.py:513` **Vulnerability Type**: Runtime external script retrieval and viewer metadata disclosure **Risk Level**: Medium ### Vulnerable Code ```python def make_portfolio_chart(pf, name, price, IC): ... fig.update_xaxes(gridcolor='#30363d') fig.update_yaxes(gridcolor='#30363d') return fig.to_html(include_plotlyjs='cdn', full_html=False) ``` ### Technical Analysis `include_plotlyjs='cdn'` instructs Plotly to generate HTML that obtains the Plotly JavaScript runtime from an external content-delivery network when the report is opened. The executable content used by the final report is therefore not completely contained in the audited project and can differ after the Skill has been reviewed. Opening the generated report also causes the viewer's browser to contact the external host. This discloses normal connection metadata such as the viewer's IP address, user agent, request time, and potentially referrer information, depending on browser policy and deployment. The report generator legitimately needs Plotly JavaScript for interactive charts, but network retrieval at viewing time is not the minimum-privilege implementation. A pinned local copy can provide the same declared functionality without a browser-side network request. ### Attack Path 1. The Skill generates an HTML report containing a CDN reference for Plotly. 2. A victim opens the report while connected to a network. 3. The browser contacts the external CDN and requests executable JavaScript. 4. The CDN observes request metadata and returns the script. 5. If the CDN, dependency distribution path, or network trust boundary is compromised, modified JavaScript executes in the report's browser context. ### Impact Assessment The immediate confirmed impact is disclosure of limited browser and network metadata whenever a generated report is opened. The design also introduces a remote executable-content trust dependency. If ...[truncated 409 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Bundle a reviewed, pinned Plotly JavaScript file with the generated reports or inline the exact reviewed version. 2. Ensure the local Plotly asset is version-controlled or integrity-checked during packaging. 3. If CDN loading must remain available, make it an explicit opt-in mode and clearly disclose the resulting network request. 4. Pin the external resource to a versioned URL and apply Subresource Integrity where the generated integration supports it. 5. Configure a Content Security Policy that restricts `script-src` to the minimum required locations. 6. Set an appropriate referrer policy to limit metadata disclosure. 7. Provide an offline report mode as the secure default. ]]>
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 (7)

Lp3

Medium
Category
MCP Least Privilege
Confidence
94% confidence
Finding
The skill advertises and instructs use of capabilities that read local files, write HTML output, and make outbound network requests, but it does not declare any explicit tool scope or permission boundaries. In an agent setting, this can lead to overbroad execution where a caller is not clearly informed that running the skill may access local market data directories and contact external hosts automatically.

Missing User Warnings

Medium
Confidence
91% confidence
Finding
The markdown explicitly states that the report generator will automatically read local `.day` files and connect to external services, including a hard-coded TDX server and public APIs, without an explicit warning about those side effects. This is risky because users may trigger unintended data access or outbound connections simply by following normal usage instructions, which can leak metadata, violate policy, or surprise sandboxed environments.

Natural-Language Policy Violations

Low
Confidence
77% confidence
Finding
The natural-language content of the skill is overwhelmingly Chinese, including the description, usage guidance, and parameter explanations, with no indication that users can choose another language. Under the language/locale policy rule, forcing a single language without opt-in can be a policy issue unless the locale restriction is explicitly justified.

Missing User Warnings

Low
Confidence
87% confidence
Finding
This code generates per-stock HTML reports at report_path, and later the script writes index.html to the output directory. Although file generation is part of the script's purpose, there is no visible disclosure that existing files in the output location may be replaced or modified, and no confirmation or warning is shown before these writes occur.

Natural-Language Policy Violations

Low
Confidence
94% confidence
Finding
The generated document sets lang="zh-CN" and all user-facing text in the script is Chinese, with no opt-in or alternate locale path. Under the policy, forcing a specific language without user choice is a natural-language locale violation unless clearly documented as region-specific.

Missing User Warnings

Low
Confidence
80% confidence
Finding
The script unconditionally creates the output directory with os.makedirs and later writes an index HTML file there. There is no explicit user warning in the code or docstring that running the script will modify the filesystem beyond the implied reporting behavior.

Natural-Language Policy Violations

Low
Confidence
95% confidence
Finding
This code file contains a natural-language locale setting in the generated HTML (`lang="zh-CN"`) and the surrounding report content is written in Chinese. Under the policy rule, forcing a specific language/locale without user opt-in is a natural-language policy violation.

Static analysis

No suspicious patterns detected.