Back to skill

Security audit

stock-monitor-lite

Security checks for vulnerabilities and agentic risk

Overview

This is a coherent Chinese stock-monitoring skill, but it ships private-looking portfolio cost bases and can repeatedly send those figures in reports, so it needs user review before installation.

Review and replace the bundled watchlist before use, especially the cost values. Only enable the recurring OpenClaw task and channel push if you are comfortable sending stock symbols, names, and profit/loss reports through the configured notification channel and querying Sina/Eastmoney for market/news data. Treat the generated trading suggestions as informational only.

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

Warning
Location
config.json:8
Finding
Plaintext Portfolio Data and Cost Bases Embedded in Configuration and Source Code<![CDATA[ ## Vulnerability Details **File Location**: `config.json:8-315`; duplicated fallback data in `stock_monitor.py:38-151`; portfolio values included in generated messages at `stock_monitor.py:894-899` **Vulnerability Type**: Plaintext sensitive financial data exposure **Risk Level**: Medium ### Vulnerable Code ```json "watchlist": [ { "code": "002050", "name": "三花智控", "market": "sz", "type": "individual", "cost": 48.59, "alerts": { "cost_pct_above": 15.0, "cost_pct_below": -12.0, "change_pct_above": 4.0, "change_pct_below": -4.0, "volume_surge": 2.0, "ma_monitor": true, "rsi_monitor": true, "macd_monitor": true, "bollinger_monitor": true, "obv_monitor": true, "atr_monitor": true, "gap_monitor": true, "trailing_stop": true } } ] ``` The same type of data is embedded in the configuration-loading fallback: ```python def load_watchlist(): """Load the watchlist from the configuration file.""" try: with open('config.json', 'r', encoding='utf-8') as f: config = json.load(f) return config.get('config', {}).get('watchlist', []) except Exception as e: logging.error(f"Failed to read configuration file: {e}") return [ { "code": "002050", "name": "三花智控", "market": "sz", "type": "individual", "cost": 48.59, "alerts": { "cost_pct_above": 15.0, "cost_pct_below": -12.0, "change_pct_above": 4.0, "change_pct_below": -4.0, "volume_surge": 2.0, "ma_monitor": True, "rsi_monitor": True, "gap_monitor": True, "trailing_stop": True } }, # Additional positions and cost bases are embedded ...[truncated 2805 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove all real or private-looking portfolio entries and acquisition costs from distributable files. 2. Ship a separate example configuration containing clearly fictitious values, such as `config.example.json`. 3. Store the operational watchlist in a user-owned configuration file outside the package or repository. 4. Resolve the configuration path explicitly rather than relying on the current working directory: ```python from pathlib import Path config_path = Path(__file__).resolve().parent / "config.json" ``` 5. Fail closed when the operational configuration cannot be loaded. Do not silently substitute a private-looking fallback portfolio: ```python def load_watchlist(): config_path = Path(__file__).resolve().parent / "config.json" try: with config_path.open("r", encoding="utf-8") as f: config = json.load(f) except (OSError, json.JSONDecodeError) as exc: raise RuntimeError("Unable to load the stock-monitor configuration") from exc return config.get("config", {}).get("watchlist", []) ``` 6. Restrict configuration-file permissions to the account running the monitor. 7. Make inclusion of cost bases in generated messages opt-in and redact them by default. 8. Ensure notification channels and retained logs have appropriate access controls and retention policies. 9. Add schema validation so malformed or unintended configuration values cannot silently enter the reporting pipeline. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
stock_monitor.py:791
Finding
Unescaped Remote News Titles Inserted into HTML-Formatted Reports<![CDATA[ ## Vulnerability Details **File Location**: Remote input acquired at `stock_monitor.py:720-729`; unsafe report interpolation at `stock_monitor.py:791-795` **Vulnerability Type**: Untrusted content injection into formatted output **Risk Level**: Medium ### Vulnerable Code Remote titles are accepted from the Eastmoney response without content validation or sanitization: ```python url = f"https://searchapi.eastmoney.com/api/suggest/get" params = { "input": name, "type": 14, "count": limit } try: resp = self.session.get(url, params=params, timeout=10) data = resp.json() news_list = [] for item in data.get("QuotationCodeTable", {}).get("Data", []): news_list.append({ "title": item.get("Title", ""), "url": item.get("Url", ""), "time": item.get("ShowTime", "") }) ``` The remote title is then concatenated directly into a report that otherwise uses HTML formatting: ```python if news_list: report += "\n<b>Latest updates:</b>\n" for n in news_list[:2]: report += f"• {n.get('title', 'Untitled')[:30]}...\n" ``` The report also deliberately contains HTML elements: ```python report = f"""📊 <b>{name} ({code}) In-depth analysis</b> 💰 <b>Price movement:</b> • Current: {price_data.get('price', 'N/A')} ({price_data.get('change_pct', 0):+.2f}%) • Triggered: {', '.join([a[1] for a in alerts])} """ ``` ### Technical Analysis News titles originate from a remote HTTP API and therefore cross an external trust boundary. They are inserted into a report without HTML escaping, character filtering, or a clearly enforced plain-text output mode. The report contains `<b>` elements, demonstrating that it is intended for, or may be consumed by, an HTML-capable notification renderer. If the upstream service is compromised, its response is manipulated, or an attacker can influence a returned title, markup in the title may be interpreted by the downstream channel instead of being d ...[truncated 2208 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Escape every remote string before inserting it into HTML-formatted output: ```python from html import escape if news_list: report += "\n<b>Latest updates:</b>\n" for news in news_list[:2]: title = escape(str(news.get("title", "Untitled"))[:30], quote=True) report += f"• {title}...\n" ``` 2. Prefer a plain-text report format when rich formatting is not required. 3. Check HTTP success before parsing the response: ```python resp = self.session.get(url, params=params, timeout=10) resp.raise_for_status() data = resp.json() ``` 4. Validate the response schema and enforce that titles are strings of a reasonable maximum length. 5. Remove or replace unexpected control characters and Unicode directionality controls. 6. Do not rely on truncation as a sanitization mechanism. 7. Clearly mark remote content as untrusted data if reports may be processed by another AI Agent. 8. Configure the destination channel to use a safe parse mode and disable unsupported or unnecessary HTML elements. 9. If links are later included, validate their scheme and host against an explicit allowlist before rendering them. ]]>
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 (10)

Natural-Language Policy Violations

Medium
Confidence
95% confidence
Finding
The README content and the prescribed invocation text are entirely in Chinese, with no indication that users may choose another language. Under the policy, forcing a specific language without user opt-in is a natural-language policy violation.

Lp3

Medium
Category
MCP Least Privilege
Confidence
70% confidence
Finding
Without declared permissions the skill's intent is opaque and cannot be validated.

Natural-Language Policy Violations

Medium
Confidence
95% confidence
Finding
The description explicitly states the skill conforms to Chinese investor conventions, including a specific red/green price-color meaning. This imposes a locale-specific presentation standard by default, with no indication that users can choose another locale or display convention.

Natural-Language Policy Violations

Medium
Confidence
93% confidence
Finding
The manifest description is entirely in Chinese ("股票监控预警技能 - 智能监控 + 深度分析"), which indicates the skill is presented in a specific language without any accompanying opt-in, alternative locale, or justification that it is region-specific. Under the policy, language constraints should either be optional for the user or clearly documented as region-specific.

Missing User Warnings

Medium
Confidence
92% confidence
Finding
The skill sends user-configured watchlist identifiers and stock names to third-party services (Sina and Eastmoney) without explicit user notice or consent. While the data is not highly sensitive like credentials, a watchlist can reveal investment interests or positions, and disclosure to external providers creates a privacy leak and unexpected data-sharing surface.

Description-Behavior Mismatch

Medium
Confidence
91% confidence
Finding
The manifest description focuses on stock monitoring and 11 alert rules such as cost thresholds, moving averages, RSI, volume anomalies, gaps, and trailing stops. This file additionally fetches stock news from Eastmoney and generates sentiment-driven 'deep analysis' reports and trading suggestions, which is materially broader than the stated alerting functionality.

Missing User Warnings

Low
Confidence
87% confidence
Finding
This markdown file instructs the user to set up a recurring task that will analyze stock data and push results through a channel, which implies periodic networked activity and outbound notifications. The description does not include any user warning about repeated pushes, data usage, or external communication behavior.

Description-Behavior Mismatch

Low
Confidence
76% confidence
Finding
The manifest context describes a stock monitoring and alert skill with 11 major alert rules, but the config advertises additional capabilities such as sentiment_analysis, fund_flow, and data_cache alongside multiple technical-analysis modules. This makes the declared behavior broader than the stated purpose and rule set, even though no executable code is shown here.

Intent-Code Divergence

Low
Confidence
75% confidence
Finding
The module docstring describes the skill as real-time monitoring, intelligent alerts, and deep analysis using Sina and Eastmoney market-data APIs. Later code uses a different Eastmoney search/news endpoint to collect article-like items and derive sentiment-based recommendations, which is not reflected in the documentation for the module's actual analysis inputs.

Natural-Language Policy Violations

Low
Confidence
97% confidence
Finding
Natural-language strings, comments, docstrings, alerts, and final report text are all hard-coded in Chinese throughout the file, with no opt-in or alternative locale handling. This can violate language/locale policy when a skill forces a specific language on users without offering choice or documenting a justified regional constraint.

Static analysis

No suspicious patterns detected.