Back to skill

Security audit

StockBuddy

Security checks for vulnerabilities and agentic risk

Overview

The skill is a coherent stock and portfolio assistant, but it needs review because it can silently erase saved portfolio positions during migration and its installer can modify the Python environment.

Review this skill before installing. It stores portfolio and account facts locally, calls external market/news/filing services, and can produce action-oriented trading guidance. Do not run the dependency installer in a system Python environment unless you accept the package-management risk, and back up any existing ~/.stockbuddy/stockbuddy.db before use because the migration code can erase legacy positions.

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

T08 · Insecure Dependencies

Warning
Location
scripts/install_deps.sh:15
Finding
Unpinned Dependencies Can Modify the System Python Environment## Vulnerability Details **File Location**: `scripts/install_deps.sh`, lines 15-24 **Vulnerability Type**: Unpinned third-party dependencies and unsafe system package modification **Risk Level**: Medium ### Vulnerable Code ```bash # Try installation, including a PEP 668 bypass pip3 install numpy pandas --quiet 2>/dev/null if [ $? -ne 0 ]; then echo "Trying installation with --break-system-packages..." pip3 install --break-system-packages numpy pandas --quiet 2>/dev/null fi if [ $? -ne 0 ]; then echo "Trying a user-level installation..." pip3 install --user numpy pandas --quiet 2>/dev/null fi ``` ### Technical Analysis The installer resolves mutable package names without pinning reviewed versions or validating package hashes. It also relies on the invoking environment's pip configuration, including any configured package index or extra index. The `--break-system-packages` fallback bypasses the protections applied to externally managed Python environments under PEP 668. Consequently, the script may overwrite or introduce incompatible packages in a shared Python installation rather than isolating project dependencies. This is a supply-chain and environment-integrity weakness. It is not evidence that the currently named packages are malicious, but it leaves installation behavior dependent on mutable external artifacts and local pip configuration. ### Attack Path 1. The required modules are absent, causing the documented dependency installer to run. 2. The script invokes pip with unpinned package names. 3. Pip resolves packages through the indexes configured in the execution environment. 4. A compromised index, mirror, DNS path, or maliciously altered pip configuration supplies an unexpected distribution. 5. Package build or installation code executes with the privileges of the user running the installer. 6. If the ordinary installation fails, `--break-system-packages` permits modification of the externally managed Python environment. ### I ...[truncated 620 chars]
Remediation
## Remediation Suggestions 1. Create and use a dedicated virtual environment rather than modifying the system interpreter. 2. Pin reviewed dependency versions in a lock file or requirements file. 3. Require hashes, for example through `pip install --require-hashes -r requirements.txt`. 4. Explicitly use a trusted HTTPS package index and review any configured extra indexes. 5. Remove the `--break-system-packages` fallback. 6. Prefer binary wheels from trusted sources where practical, reducing exposure to arbitrary package build steps. 7. Keep installation failures visible instead of suppressing all diagnostic output with `2>/dev/null`.

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/analyze_stock.py:153
Finding
Primary Market Quotes Are Retrieved over Plaintext HTTP## Vulnerability Details **File Location**: `scripts/analyze_stock.py`, lines 153-164 **Vulnerability Type**: Unauthenticated and unencrypted external market-data retrieval **Risk Level**: Medium ### Vulnerable Code ```python def fetch_tencent_quote(code: str) -> dict: """Retrieve a real-time Tencent Finance quote.""" stock = normalize_stock_code(code) symbol = stock['tencent_symbol'] url = f"http://qt.gtimg.cn/q={symbol}" for attempt in range(MAX_RETRIES): try: req = urllib.request.Request(url, headers={ 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36', 'Referer': 'https://gu.qq.com/', }) with urllib.request.urlopen(req, timeout=10) as response: ``` ### Technical Analysis The primary quote endpoint uses HTTP rather than HTTPS. HTTP provides neither transport confidentiality nor server authentication and does not protect response integrity. The response is parsed as authoritative market data. Its price, valuation, market-capitalization, and related fields flow into portfolio valuation, profit-and-loss calculations, technical analysis, and trading recommendations. There is no cryptographic verification or independent consistency check before those values are used. Although the ticker symbol is constrained by stock-code normalization and this is not a general server-side request forgery primitive, an on-path attacker can still observe requested symbols and alter provider responses. ### Attack Path 1. A user requests analysis of a stock or portfolio. 2. The script sends a plaintext HTTP request containing the normalized ticker. 3. An attacker controlling or observing the network path intercepts the connection. 4. The attacker returns or modifies a syntactically valid Tencent quote response. 5. `_parse_tencent_quote` accepts the manipulated fields. 6. The altered data is persisted in the local watchlist metadata and used to calculate ...[truncated 585 chars]
Remediation
## Remediation Suggestions 1. Replace the endpoint with an authenticated HTTPS equivalent. 2. If that provider cannot offer HTTPS, disable it as the primary source and use an HTTPS provider instead. 3. Cross-check security-sensitive quote fields against an independent HTTPS source before producing actionable recommendations. 4. Reject structurally inconsistent responses and apply sanity checks to price, percentage-change, timestamp, and valuation fields. 5. Record source and validation status in the output, and mark recommendations as non-execution-ready when quote integrity cannot be established. 6. Avoid persisting quote data obtained over an unauthenticated transport unless it has been independently validated.

T09 · Insecure Skill Coding Practices

Error
Location
scripts/db.py:49
Finding
Legacy Schema Migration Silently Deletes All Portfolio Positions## Vulnerability Details **File Location**: `scripts/db.py`, lines 49-53 **Vulnerability Type**: Destructive and non-recoverable database migration **Risk Level**: High ### Vulnerable Code ```python def _migrate_schema(conn: sqlite3.Connection) -> None: positions_cols = _table_columns(conn, "positions") if positions_cols and "watchlist_id" not in positions_cols: conn.execute("DROP TABLE positions") positions_cols = [] if positions_cols: ``` ### Technical Analysis When an existing `positions` table does not contain `watchlist_id`, the migration drops the entire table. It does not back up the database, rename and preserve the legacy table, transform old rows, verify migration results, or request user confirmation. Normal commands repeatedly call `init_db()`, which invokes this migration. Therefore, merely running the application against a legacy database can trigger irreversible loss of durable portfolio state. This is a destructive migration flaw rather than an external privilege-escalation vulnerability. Exploitation requires a database with the legacy schema or the ability to alter that local database's schema. ### Attack Path 1. A user has an existing `~/.stockbuddy/stockbuddy.db` whose `positions` table predates the `watchlist_id` column. 2. The user runs an ordinary portfolio or analysis command. 3. The command calls `init_db()`. 4. `_migrate_schema` discovers that the table exists without `watchlist_id`. 5. The code executes `DROP TABLE positions`. 6. The current initialization transaction creates or retains an empty replacement schema. 7. Existing position records are lost without an automatic recovery path. If another local process already has permission to modify the database, it could also recreate the triggering legacy schema so that the next normal invocation deletes the positions table. That does not extend the attacker's operating-system privileges, but it can make destructive modification occur through the app ...[truncated 525 chars]
Remediation
## Remediation Suggestions 1. Back up the database before applying any schema migration. 2. Run the complete migration inside an explicit transaction. 3. Rename the legacy table rather than dropping it immediately. 4. Create the new schema and transform each legacy record into the new `watchlist` and `positions` representation. 5. Verify row counts, required fields, and foreign-key integrity before committing. 6. Roll back on any transformation or verification failure. 7. Keep the renamed legacy table or backup until migration success is confirmed. 8. Store and check an explicit schema version so migrations are deterministic and sequential. 9. Add automated migration tests using representative databases from every supported prior release. 10. Require explicit user confirmation when a safe automatic migration is impossible.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Rogue AgentSelf-Modification, Session Persistence
  • 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
Findings (21)

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding
The skill presents itself as a stock-analysis assistant, but also includes environment-modifying package installation behavior via pip. Hidden or under-disclosed package-management actions are risky because they alter the runtime, may execute arbitrary install-time code from dependencies, and expand the attack surface beyond the user’s expected task.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
The skill presents itself as a stock-analysis assistant, but also includes environment-modifying package installation behavior via pip. Hidden or under-disclosed package-management actions are risky because they alter the runtime, may execute arbitrary install-time code from dependencies, and expand the attack surface beyond the user’s expected task.

Ae1

High
Category
analysis-evasion
Content
| `scripts/analyze_stock.py` | Core analysis script for market data retrieval, technical indicators, and valuation scoring |
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Description-Behavior Mismatch

High
Confidence
98% confidence
Finding
The migration logic unconditionally drops the entire positions table when it detects a missing watchlist_id column, which destroys all stored portfolio positions. In a portfolio/trading assistant, positions are core user data, so this can erase holdings history and account associations during startup or upgrade without user consent or recovery.

Missing User Warnings

High
Confidence
97% confidence
Finding
This code performs a destructive schema change silently, with no disclosure, confirmation, backup, or rollback path. Because the skill manages positions, cash, and trading context, silent deletion of the positions table can directly undermine the integrity of portfolio analysis and execution-aware recommendations.

Lp3

Medium
Category
MCP Least Privilege
Confidence
90% confidence
Finding
The skill describes behaviors that require network access and persistent file/database writes, but it does not declare any explicit tool scope such as allowed-tools or permissions. That creates an overbroad execution surface where a host agent may grant more capability than users or reviewers expect, reducing least-privilege protections and making misuse of network/file actions harder to govern.

Vague Triggers

Medium
Confidence
89% confidence
Finding
The description says to use the skill for broad requests like 'stock analysis', 'portfolio analysis', 'buy/sell advice', and 'practical trading actions' for a stock code or company name. These are common conversational intents and the file does not provide negative examples or clearer scope constraints, which increases the chance of unintended invocation.

Session Persistence

Medium
Category
Rogue Agent
Content
**Core rule: separate durable facts from derived values.**
- **Persist durable facts**: share count, cost basis, account, available cash, market/currency, lot size, odd-lot support, and other user-confirmed trading constraints
- **Compute in real time**: latest price, market value, position weight, unrealized P&L, executable buy/sell size, and whether partial selling is actually possible
- Do **not** write latest price, position weight, or unrealized P&L back into durable storage

Five core scenarios:
1. **Single-stock analysis** — analyze one stock and produce an action recommendation
Confidence
88% confidence
Finding
The skill explicitly persists durable financial facts such as positions, account context, cash balances, and trading constraints in a local SQLite database under the user home directory. Persistent storage of sensitive portfolio/account data increases privacy and data-retention risk, especially if retention limits, access controls, encryption, or user consent/visibility are not clearly defined.

Natural-Language Policy Violations

Medium
Confidence
94% confidence
Finding
The file mandates a default Chinese response format without checking the user's language preference. This can cause responses in an unexpected language, increasing the risk of user misunderstanding—especially for financial recommendations, order prices, and risk disclosures where clarity is important.

Vague Triggers

Medium
Confidence
94% confidence
Finding
The template explicitly treats vague phrases like “分析一下”, “给我建议”, and “今天怎么操作” as triggers for stock analysis. In a broadly routed assistant, these generic utterances overlap with everyday speech and can cause unintended invocation of a trading skill, leading to unsolicited financial analysis or action-oriented output in the wrong context.

Natural-Language Policy Violations

Medium
Confidence
89% confidence
Finding
The entire reference is written in Chinese and does not indicate that the user can choose another language or that the skill is intentionally limited to Chinese-speaking users. The policy calls for flagging language or locale constraints when they are imposed without user opt-in or clear justification.

Description-Behavior Mismatch

Medium
Confidence
95% confidence
Finding
The module docstring states this is a Hong Kong stock analysis script using Tencent Finance for technical and fundamental analysis. In practice, the code supports CN/HK/US markets and fetches data from multiple additional external services, including Eastmoney fallback for A-shares, Yahoo for US K-line data, Google News RSS, and SEC filings. This is a substantive expansion beyond the documented behavior.

Natural-Language Policy Violations

Medium
Confidence
90% confidence
Finding
The top-level docstring, usage examples, and most user-facing strings in this code are written only in Chinese, which effectively fixes the skill's interface to a specific language. The file does not offer any user opt-in, locale selection, or explanation that this is intentionally limited to a Chinese-speaking or region-specific audience.

External Transmission

Medium
Category
Data Exfiltration
Content
if not cik:
            return {'source': 'sec', 'items': []}
        req = urllib.request.Request(
            f'https://data.sec.gov/submissions/CIK{cik}.json',
            headers={'User-Agent': 'Mozilla/5.0 stockbuddy@example.com'}
        )
        with urllib.request.urlopen(req, timeout=20) as response:
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Missing User Warnings

Medium
Confidence
92% confidence
Finding
The script automatically modifies the Python environment and escalates to `pip3 install --break-system-packages` if normal installation fails, which can overwrite or conflict with OS-managed Python packages. This is dangerous because it makes system-impacting changes without explicit informed consent, and in an agent skill context such a script may be invoked as part of setup by users who do not understand the risks to their workstation or server environment.

Natural-Language Policy Violations

Medium
Confidence
93% confidence
Finding
The module docstring, usage instructions, and command descriptions are presented only in Chinese, with no indication that language can be selected by the user. This creates a locale/language constraint in the skill's natural-language interface without an explicit opt-in or documented justification.

Missing User Warnings

Medium
Confidence
79% confidence
Finding
The analyze command can persist detailed portfolio analysis, including positions, account balances, and profit/loss data, to an arbitrary file path. Although the CLI exposes an --output option, the code provides no cautionary comment, confirmation, or user-facing warning that sensitive financial data will be written to disk and may overwrite existing content.

Vague Triggers

Low
Confidence
83% confidence
Finding
Examples like 'can I buy this stock' and 'look at BYD' are broad natural phrases that could occur in casual conversation. The section provides examples but no boundaries for when the skill should not activate, so trigger scope remains underspecified.

Vague Triggers

Low
Confidence
78% confidence
Finding
Phrases like 'look at my holdings' and similar examples in later scenario sections are ordinary portfolio conversation and may overlap with general chat. Because the document does not state exclusion conditions or a stricter invocation context, activation remains potentially overbroad.

Intent-Code Divergence

Low
Confidence
83% confidence
Finding
The comment labels the following section as 'technical indicator calculation (unchanged)'. However, the subsequent codebase includes not only indicator functions but also expanded composite scoring, market regime detection, confidence estimation, backtesting, and buy/sell recommendation generation, which goes beyond a 'unchanged indicators' section. This creates misleading documentation about what the section actually contains.

Natural-Language Policy Violations

Low
Confidence
95% confidence
Finding
All user-facing comments and console output are in Chinese, with no indication that the language is configurable or chosen by the user. Under the policy, forcing a specific language without opt-in is a natural-language policy concern unless the locale restriction is explicitly justified.

Static analysis

No suspicious patterns detected.