Back to skill

Security audit

Stock Portfolio

Security checks for vulnerabilities and agentic risk

Overview

This is a coherent stock portfolio helper, but users should be careful with local financial data, optional scheduled messages, and unauthenticated market-data APIs.

Install only if you are comfortable storing portfolio positions and alert thresholds in local JSON files and sending stock symbols to third-party finance APIs. Do not use the free HTTP quote data or daily picks as the sole basis for trading decisions, and enable the cron/message examples only after reviewing exactly what they will send and where.

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
scripts/data_sources.py:123
Finding
Market data retrieved over unauthenticated plaintext HTTP<![CDATA[ ## Vulnerability Details **File Locations**: - `scripts/data_sources.py:123-132` - `scripts/data_sources.py:183-192` - `scripts/data_sources.py:250-274` **Vulnerability Type**: Cleartext transport with no server authentication or response-integrity protection **Risk Level**: Medium ### Vulnerable Code ```python # TencentSource.fetch norm_symbol = normalize_symbol(symbol, 'tencent') url = f"http://qt.gtimg.cn/q={norm_symbol}" req = urllib.request.Request(url) req.add_header('User-Agent', 'Mozilla/5.0') req.add_header('Referer', 'https://stockapp.finance.qq.com/') with urllib.request.urlopen(req, timeout=10) as response: content = response.read().decode('gbk', errors='ignore') ``` ```python # SinaSource.fetch norm_symbol = normalize_symbol(symbol, 'sina') url = f"http://hq.sinajs.cn/s={norm_symbol}" req = urllib.request.Request(url) req.add_header('User-Agent', 'Mozilla/5.0') req.add_header('Referer', 'https://finance.sina.com.cn/') with urllib.request.urlopen(req, timeout=10) as response: content = response.read().decode('gbk', errors='ignore') ``` ```python # EastMoneySource.fetch fields = [ 'f43', 'f44', 'f45', 'f46', 'f47', 'f48', 'f50', 'f51', 'f52', 'f58', 'f60', ] url = f"http://push2.eastmoney.com/api/qt/stock/get?secid={code}&fields={','.join(fields)}" req = urllib.request.Request(url) req.add_header('User-Agent', 'Mozilla/5.0') req.add_header('Referer', 'https://quote.eastmoney.com/') with urllib.request.urlopen(req, timeout=10) as response: result = json.loads(response.read().decode('utf-8')) ``` ### Technical Analysis All three active providers are contacted over plaintext HTTP. HTTP provides neither server authentication nor transport integrity. The HTTPS values in the `Referer` headers do not secure the actual requests. An attacker able to observe or alter the user's network traffic—such as a malicious Wi-Fi operator, compromised router, proxy, ISP-level intermediary, or ...[truncated 1854 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Replace every `http://` provider URL with a provider-supported `https://` endpoint. 2. Retain Python's default TLS certificate and hostname verification; do not introduce an unverified SSL context. 3. Explicitly reject redirects from HTTPS to HTTP. 4. If a provider does not support authenticated HTTPS, remove it from the default source list rather than silently downgrading transport security. 5. Validate response structure, timestamps, numeric ranges, and symbol identity before using returned data. 6. For alerting and portfolio calculations, consider corroborating unusually large price movements against a second HTTPS source. 7. Log provider failures without silently treating manipulated or stale information as trustworthy. 8. Update `SKILL.md` and `references/api_docs.md` so examples do not encourage plaintext HTTP use. ]]>

T09 · Insecure Skill Coding Practices

Note
Location
scripts/portfolio_manager.py:17
Finding
Portfolio and alert records are created without explicit restrictive permissions<![CDATA[ ## Vulnerability Details **File Locations**: - `scripts/portfolio_manager.py:17-20` - `scripts/portfolio_manager.py:36-39` **Vulnerability Type**: Insufficient protection of locally stored financial data **Risk Level**: Low ### Vulnerable Code ```python # Data directory DATA_DIR = Path(__file__).parent.parent / 'data' HOLDINGS_FILE = DATA_DIR / 'holdings.json' ALERTS_FILE = DATA_DIR / 'alerts.json' # Ensure the data directory exists DATA_DIR.mkdir(parents=True, exist_ok=True) ``` ```python def save_json(filepath, data): """Save a JSON file.""" with open(filepath, 'w', encoding='utf-8') as f: json.dump(data, f, ensure_ascii=False, indent=2) ``` ### Technical Analysis The data directory and JSON files are created without explicit permission modes. Their effective permissions therefore depend on the account's current process umask and any pre-existing directory permissions. The files contain holdings, share counts, average purchase prices, timestamps, and alert thresholds. In an environment with a permissive umask or a shared Skill directory, another local account may be able to read this financial information. Existing files are also overwritten without checking whether they are regular files owned by the expected user. The direct overwrite is not atomic. A process interruption can leave truncated or invalid JSON. Additionally, where an untrusted local user can modify entries in the data directory, insufficient ownership and file-type checks could expose the save operation to local filesystem manipulation. ### Attack Path 1. The Skill runs under a user account with a permissive umask, or its `data` directory is located in a shared or group-readable installation path. 2. `DATA_DIR.mkdir()` creates or reuses the directory without enforcing owner-only permissions. 3. `open(filepath, 'w')` creates holdings and alert files using umask-derived permissions. 4. Another local account enumerates or reads `data/holdings.json` and `data/ale ...[truncated 958 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Create the data directory with owner-only permissions: ```python DATA_DIR.mkdir(parents=True, exist_ok=True, mode=0o700) os.chmod(DATA_DIR, 0o700) ``` 2. Create new data files with mode `0600`, for example by using `os.open()` with `O_CREAT`, `O_WRONLY`, and an explicit mode. 3. Correct permissions on existing `holdings.json` and `alerts.json` files before reading or writing them. 4. Verify that the directory and destination are owned by the expected user and are not symbolic links. 5. Use atomic writes: write to a securely created temporary file in the same directory, flush and optionally `fsync()` it, set mode `0600`, and replace the destination with `os.replace()`. 6. Handle permission and ownership failures explicitly rather than silently falling back to an insecure location. 7. Document that the data directory contains sensitive financial information and should not be placed in a shared installation path. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Rogue AgentSelf-Modification, Session Persistence
  • 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 (21)

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding
描述声称这是一个完整的股票组合管理与预警技能,包含组合/持仓管理、收益计算、预警、推荐,并说明使用腾讯财经免费 API 且数据本地存储。但实际代码只是一个行情数据抓取模块,核心功能是把股票代码转换为不同供应商格式,并通过 urllib 从腾讯、新浪、东方财富获取报价,再由管理器做轮询容错。代码没有任何本地持仓、收益、预警、推荐或持久化存储实现。最明显的未声明能力是额外访问新浪财经和东方财富接口,这与“使用免费 API(腾讯财经)”不一致。因此描述与代码行为存在实质性不匹配。

Tp4

High
Category
MCP Tool Poisoning
Confidence
92% confidence
Finding
The code substantially matches the core portfolio-management description: it manages holdings, persists data locally, calculates profit/loss summaries, and sets/checks alerts. However, the declared description includes additional capabilities not present in this code chunk, most notably '每日推荐' (daily recommendations). It also claims support for A-shares/HK/US quotes via Tencent Finance, but this file only imports `fetch_stock_price` from another module and does not itself demonstrate those specific data-source or market-coverage claims. Because the declared purpose overstates implemented capabilities in a material way, this should be flagged as a mismatch.

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding
声明描述的是一个较完整的股票组合管理与预警技能,但提供的代码块实际只是一个股票行情查询 CLI 脚本。其主要行为是根据股票代码获取单只股票行情并输出结果,这只覆盖了声明中的“行情查询”一小部分。更重要的是,代码顶部注释明确写有“多数据源轮询负载均衡”,并通过 data_sources.fetch_stock_price/get_manager 等接口取数,这与声明中“使用免费 API(腾讯财经)”不一致。代码中也没有看到任何与持仓、组合、收益、预警、推荐、本地数据存储相关的实现。因此该代码块与声明用途存在明显行为不匹配。

Lp3

Medium
Category
MCP Least Privilege
Confidence
84% confidence
Finding
The skill advertises file read/write and network-driven behavior in its documentation, but it does not declare an explicit tool scope such as permissions or allowed-tools. That creates an unnecessary trust gap: an agent or reviewer cannot easily determine what capabilities are intended, and undeclared file/network access increases the chance of over-broad execution in a host environment.

Missing User Warnings

Medium
Confidence
88% confidence
Finding
The skill stores holdings, alerts, and possibly query history on local disk, but the description does not clearly warn users up front about this persistence. Portfolio data is financially sensitive, and silent local retention can expose private investment positions or behavioral history to other local users, backups, or unrelated tools.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The documentation directs users to fetch market data over plain HTTP from third-party endpoints, which exposes requests and responses to interception or modification by any network attacker on the path. Because this skill concerns financial data and may drive trading or alerting decisions, tampered quotes could mislead users or downstream automation even if no credentials are sent.

Session Persistence

Medium
Category
Rogue Agent
Content
```bash
# 编辑 crontab
crontab -e

# 添加以下行(每天 9:30 执行)
30 9 * * * cd /root/.openclaw/workspace/skills/stock-portfolio/scripts && python3 daily_picks.py | openclaw message send --target your-chat-id
Confidence
85% confidence
Finding
Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Missing User Warnings

Medium
Confidence
92% confidence
Finding
The documentation instructs users to schedule an automated pipeline that sends script output through `openclaw message send` to a chat target, but it does not clearly warn that this creates outbound transmission of generated content on a recurring basis. In a portfolio-management skill, that content could include holdings, recommendations, or other sensitive financial information, so the omission increases the risk of unintended data disclosure or notification abuse.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
This example automatically sends messages whenever script output matches a warning indicator, again without an explicit privacy or safety warning about recurring outbound notifications. Because the trigger is based on script output and the command forwards message content automatically, users may unknowingly exfiltrate portfolio or alert data and create noisy or spoofable notifications if output is manipulated.

Intent-Code Divergence

Medium
Confidence
96% confidence
Finding
The script claims recommendations are based on both technical indicators and fundamental screening, but the implemented scoring only uses price change, amplitude, and volume. In a stock-picking skill, this can mislead users about the rigor and nature of the recommendation process, causing them to place undue trust in outputs that do not match the stated methodology.

Natural-Language Policy Violations

Medium
Confidence
95% confidence
Finding
The file’s natural-language description and all user-facing CLI/help/output strings are written in Chinese, indicating the skill is designed to operate in a single language by default. Under the policy, forcing a specific language without user opt-in or a documented region-specific justification is a locale-policy issue.

Natural-Language Policy Violations

Medium
Confidence
97% confidence
Finding
This code file contains natural-language descriptions and CLI help/messages entirely in Chinese, starting with the module docstring and continuing throughout the command interface. Under the policy rule, forcing a specific language without opt-in is a natural-language policy violation unless the locale restriction is explicitly documented and justified, which is not present here.

Natural-Language Policy Violations

Medium
Confidence
95% confidence
Finding
The file’s description and all user-facing strings are in Chinese, including usage/help output, which effectively forces a specific language for interaction. The policy allows locale constraints only when users are given a choice or the limitation is clearly justified as region-specific, neither of which is present here.

Natural-Language Policy Violations

Low
Confidence
84% confidence
Finding
SQP-3 covers language or locale policy violations, including forcing a specific language without user opt-in. This file presents all instructions and examples in Chinese, with no indication that the skill is region-specific or that another language option is available.

Missing User Warnings

Low
Confidence
79% confidence
Finding
The sample code and command invoke external services, but the surrounding documentation does not explicitly disclose that using them sends query data to third-party providers. For markdown under SQP-2, network behavior affecting privacy should be called out when describing how to use the skill or resource.

Natural-Language Policy Violations

Low
Confidence
90% confidence
Finding
The entire skill documentation is written only in Chinese, with no indication that users may choose another language or that the locale restriction is intentional for a region-specific audience. Under the policy, forcing a specific language without opt-in can be a natural-language policy violation.

Intent-Code Divergence

Low
Confidence
91% confidence
Finding
The output states the picks are updated in real time during trading hours, but the daily random seed makes selection deterministic for a given date and can prevent intraday changes from being reflected in final recommendations. In a financial context, stale or falsely 'real-time' recommendations may cause users to act on outdated assumptions about market responsiveness.

Natural-Language Policy Violations

Low
Confidence
78% confidence
Finding
The natural-language content in this file is entirely in Chinese, including the module docstring and inline documentation, with no indication that the skill is region-specific or that users may choose another language. Under the locale policy rule, forcing a specific language without opt-in can be a policy concern.

Missing User Warnings

Low
Confidence
89% confidence
Finding
This code file makes outbound network requests using a user-supplied symbol, transmitting that input to a third-party service. Although the module has internal docstrings describing data retrieval, there is no user-facing warning, confirmation, or visible log indicating that requests will be sent to external providers.

Missing User Warnings

Low
Confidence
89% confidence
Finding
This code file performs an outbound HTTP request to Sina using the normalized user-provided symbol. The file lacks any visible confirmation prompt, user-facing log, or warning that user input is transmitted to an external service.

Missing User Warnings

Low
Confidence
89% confidence
Finding
This code constructs an EastMoney API request containing the user-supplied symbol and sends it over the network. There is no user-facing disclosure in the file that external services are contacted or that query data leaves the local environment.

Static analysis

No suspicious patterns detected.