Back to skill

Security audit

a-share-daily-report-publish

Security checks for vulnerabilities and agentic risk

Overview

This skill is mostly coherent as an A-share report generator, but it needs review because it turns external market data into local HTML without escaping and includes actionable stock-trading guidance.

Review before installing. Use it only in a trusted working directory with trusted market-data sources, treat the HTML report as untrusted until output escaping is fixed, and do not treat its trading sections as personalized financial advice.

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 (2)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/generate.py:681
Finding
Stored HTML and Script Injection Through Unescaped Report Data## Vulnerability Details **File Location**: `scripts/generate.py`, lines 681-694 **Vulnerability Type**: Stored HTML injection / cross-site scripting **Risk Level**: High ### Vulnerable Code ```python def _concepts_html(concepts): """Format concept labels for HTML display.""" if not concepts: return '' parts = [p.strip() for p in concepts.split('.') if p.strip()] if not parts: return '' html = f'<span style="color:#d29922;font-weight:600;">{parts[0]}</span>' for p in parts[1:]: html += f' <span style="color:#8b949e;font-size:11px;background:#21262d;padding:1px 6px;border-radius:3px;">{p}</span>' return html ``` The same unsafe interpolation pattern is used throughout report generation. For example, fields such as stock names, codes, reasons, tags, market summaries, source labels, and date labels are inserted directly into HTML fragments without contextual escaping. ### Technical Analysis Values loaded from `report_data.json` are treated as trusted HTML even though the documented workflow populates them from external market-data services. `_concepts_html()` places each concept string directly between HTML tags. It does not call `html.escape()`, sanitize markup, or validate the field against a restricted character set. An attacker who can influence a remote data response, the intermediate JSON file, or an upstream connector can supply markup such as: ```json { "concepts": "MarketTheme.<img src=x onerror=\"alert(document.domain)\">" } ``` This becomes active markup in the generated report: ```html <span style="color:#d29922;font-weight:600;">MarketTheme</span> <span style="..."><img src=x onerror="alert(document.domain)"></span> ``` Because the output is an HTML file intended to be delivered and opened by a user, the payload executes when the report is viewed in a browser. The static template does not establish a Content Security Policy th ...[truncated 1625 chars]
Remediation
## Remediation Suggestions 1. Escape every untrusted value at the point where it is inserted into HTML: ```python from html import escape def html_text(value): return escape(str(value), quote=True) ``` Apply this helper to `concepts`, stock names, codes, reasons, labels, summaries, dates, source names, and every other string obtained from JSON or external services. 2. Correct `_concepts_html()` as follows: ```python from html import escape def _concepts_html(concepts): if not concepts: return '' parts = [p.strip() for p in str(concepts).split('.') if p.strip()] if not parts: return '' result = ( '<span style="color:#d29922;font-weight:600;">' f'{escape(parts[0])}</span>' ) for part in parts[1:]: result += ( ' <span style="color:#8b949e;font-size:11px;' 'background:#21262d;padding:1px 6px;border-radius:3px;">' f'{escape(part)}</span>' ) return result ``` 3. Prefer an auto-escaping template engine such as Jinja2 rather than assembling HTML with f-strings. Only explicitly mark constant, developer-authored fragments as safe. 4. Validate structured fields. For example, stock codes should match an expected character pattern, dates should be parsed as dates, and numeric values should reject non-numeric input. 5. Add a restrictive Content Security Policy, preferably via an HTTP header when hosted. For standalone reports, a suitable meta policy can provide partial defense in depth: ```html <meta http-equiv="Content-Security-Policy" content="default-src 'none'; style-src 'unsafe-inline'; img-src data:;"> ``` 6. Add automated tests containing payloads such as `<script>`, `<img onerror>`, quotation marks, ampersands, and closing tags, and verify that the output contains encoded text rath ...[truncated 26 chars]

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/generate.py:1363
Finding
Unvalidated Date Controls a Predictable Output File Path## Vulnerability Details **File Location**: `scripts/generate.py`, lines 1363-1366 **Vulnerability Type**: Unsafe file-path construction and symlink-following file write **Risk Level**: Medium ### Vulnerable Code ```python # Write output date = data.get('date', datetime.now().strftime('%Y-%m-%d')) out_path = f"a-share-report-{date}.html" with open(out_path, 'w', encoding='utf-8') as f: f.write(html) ``` ### Technical Analysis The `date` value is loaded from attacker-influenceable JSON and concatenated directly into a filesystem path. The value is not parsed as a date, checked for path separators, normalized, or constrained to remain in a designated output directory. In addition, `open(..., 'w')` follows symbolic links and truncates existing files. Output names are predictable, so another local process or user with write access to the working directory can pre-create the expected output path as a symbolic link. A traversal payload is conditional because the fixed `a-share-report-` prefix becomes part of the first path component. Nevertheless, slash-containing values can address nested paths where matching directories exist, and a prepared directory structure can permit `..` traversal. The deterministic symlink-overwrite path is more direct: if the attacker can prepare the working directory, the script follows a symlink and overwrites its target. The write occurs with the privileges of the user running the skill. ### Attack Path A concrete symlink attack is: 1. The attacker determines the report date, for example `2026-09-16`. 2. In a shared or attacker-writable working directory, the attacker creates a symbolic link: ```bash ln -s /home/victim/.config/example.conf \ a-share-report-2026-09-16.html ``` 3. The victim runs the generator in that directory with a JSON document whose `date` is `2026-09-16`. 4. Python opens the predictable path in write mode and follows the symbolic link. ...[truncated 956 chars]
Remediation
## Remediation Suggestions 1. Parse and strictly validate the date rather than using it as an arbitrary string: ```python raw_date = data.get("date", datetime.now().strftime("%Y-%m-%d")) parsed_date = datetime.strptime(raw_date, "%Y-%m-%d") safe_date = parsed_date.strftime("%Y-%m-%d") ``` 2. Write only to a trusted, explicitly created output directory and verify containment: ```python output_dir = os.path.abspath("./reports") os.makedirs(output_dir, mode=0o700, exist_ok=True) out_path = os.path.abspath( os.path.join(output_dir, f"a-share-report-{safe_date}.html") ) if os.path.commonpath([output_dir, out_path]) != output_dir: raise ValueError("Invalid output path") ``` 3. Refuse to overwrite existing files or symbolic links. On platforms supporting it, use `os.open()` with `O_CREAT | O_EXCL | O_NOFOLLOW`: ```python flags = os.O_WRONLY | os.O_CREAT | os.O_EXCL if hasattr(os, "O_NOFOLLOW"): flags |= os.O_NOFOLLOW fd = os.open(out_path, flags, 0o600) with os.fdopen(fd, "w", encoding="utf-8") as output: output.write(html) ``` 4. Do not generate reports in shared or untrusted working directories. Ensure the output directory is owned by the executing user and is not writable by other users. 5. If replacing an existing report is required, write to a securely created temporary file in the same trusted directory and use an atomic rename after verifying the destination is not a symlink.
Vulnerability Patterns
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • 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 (9)

Lp3

Medium
Category
MCP Least Privilege
Confidence
91% confidence
Finding
The skill instructs the agent to create `report_data.json`, run `generate.py`, and deliver a generated HTML file, which implies file read/write behavior, yet it declares no explicit tool scope or permission boundaries. In an agent environment, missing scope declarations can let the skill inherit broader filesystem capabilities than necessary, increasing the risk of unintended file access or overwrite if the workflow or surrounding tooling is abused.

Vague Triggers

Medium
Confidence
94% confidence
Finding
The trigger phrases include broad terms like '市场复盘' and '大盘复盘', which can match ordinary market-commentary requests beyond the user’s intent to invoke this specific skill. Overly broad activation can cause the skill to run unexpectedly, leading to unnecessary external data access, file generation, or script execution in contexts where the user only wanted a general discussion.

Natural-Language Policy Violations

Medium
Confidence
95% confidence
Finding
The document declares `lang="zh-CN"`, and the entire rendered content is written in Simplified Chinese, which imposes a specific language/locale on all users. Under the policy, this is a natural-language locale restriction unless the skill offers language choice or clearly documents a justified region-specific scope, which is not present here.

Description-Behavior Mismatch

Medium
Confidence
94% confidence
Finding
The template goes well beyond neutral market reporting and embeds concrete stock-picking, entry/avoidance guidance, and next-day trading plans. In an agent skill advertised as a daily report generator, this creates a behavioral steering risk: users may rely on AI-generated recommendations as actionable financial advice without suitability checks, jurisdictional compliance, or clear separation between data reporting and opinion.

Context-Inappropriate Capability

Medium
Confidence
96% confidence
Finding
The template contains direct investment advice and risk-management instructions such as which stocks to buy/avoid, when to add exposure, stop-loss rules, and target trading setups. This is dangerous because it can cause users to take real financial actions based on unverified automated output, increasing legal/compliance exposure and risk of user harm from unsuitable or misleading recommendations.

Natural-Language Policy Violations

Medium
Confidence
95% confidence
Finding
The module description and all generated natural-language report content are hard-coded in Chinese, indicating a fixed language/locale behavior. The policy requires either user choice/opt-in or a clearly documented justification for the locale restriction; this file contains neither.

Context-Inappropriate Capability

Medium
Confidence
97% confidence
Finding
The manifest describes a generator for daily A-share review reports with market data modules, but this code produces explicit next-day trading guidance such as '重点接力', '分歧低吸', and '坚决规避'. That is a prescriptive advisory capability rather than just generating a recap/report, and it is not clearly justified by the stated purpose.

Context-Inappropriate Capability

Medium
Confidence
96% confidence
Finding
This section outputs concrete investment guidance like position sizing, stop-loss rules, and trading discipline recommendations. Those behaviors go beyond assembling a daily HTML market report from collected data and amount to investment-advice functionality not expressed in the manifest.

Missing User Warnings

Low
Confidence
83% confidence
Finding
This code performs a file write to a new HTML report on disk, but the write operation itself has no nearby comment/docstring warning the user that a local file will be created. For code files, file writes can merit a finding when there is no direct disclosure around the action, and the main function does not explicitly warn before writing.

Static analysis

No suspicious patterns detected.