Back to skill

Security audit

Toc Trading

Security checks for vulnerabilities and agentic risk

Overview

This is a disclosed Chinese A-share analysis and simulated-trading skill that stores local records and uses market-data APIs, with hardening gaps but no evidence of real trading, data theft, deception, or destructive behavior.

Install only if you are comfortable with a Chinese-language stock simulation tool that contacts AKShare/Tushare for market data and keeps local JSON records of watchlists, simulated holdings, trades, recommendations, and challenge state. Treat recommendations as informational, not investment advice, and review local file permissions or clear stored records if the machine is shared.

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
src/data/storage.py:20
Finding
Unrestricted Storage Paths Permit Directory Traversal## Vulnerability Details **File Location**: `src/data/storage.py:20-43` **Vulnerability Type**: Path traversal and arbitrary file access **Risk Level**: Medium ### Vulnerable Code ```python def _get_path(self, filename: str) -> Path: return self.data_dir / filename def load(self, filename: str, default: Any = None) -> Any: """Load a JSON file.""" path = self._get_path(filename) if not path.exists(): return default try: with open(path, 'r', encoding='utf-8') as f: return json.load(f) except (json.JSONDecodeError, IOError): return default def save(self, filename: str, data: Any) -> bool: """Save a JSON file.""" path = self._get_path(filename) try: with open(path, 'w', encoding='utf-8') as f: json.dump(data, f, ensure_ascii=False, indent=2) return True except IOError as e: print(f"Save failed {filename}: {e}") return False ``` ### Technical Analysis `_get_path()` directly joins an unrestricted `filename` value to the configured data directory. It does not reject absolute paths, parent-directory components, path separators, or symbolic-link escapes. It also does not resolve the resulting path and verify that it remains under `data_dir`. A filename such as `../../target.json` can therefore escape the intended storage directory. `load()` may read any accessible file containing valid JSON, while `save()` may overwrite any existing or creatable file writable by the application process with attacker-supplied JSON data. The currently reviewed user-facing command handlers call the storage layer with fixed filenames, so direct exploitation through the documented chat commands was not identified. Exploitation becomes possible if an integration, plugin, test harness, or future command passes attacker-controlled filenames to this public storage API. ### Attack Path 1. An attacker ga ...[truncated 1255 chars]
Remediation
## Remediation Suggestions 1. Allowlist the exact storage filenames required by the application, such as `stock_pool.json`, `positions.json`, `trades.json`, `challenge.json`, `recommendations.json`, and `config.json`. 2. Reject absolute paths, parent-directory components, and values containing directory separators. 3. Resolve both the base directory and destination, then verify that the destination remains inside the base directory. 4. Reject symbolic-link destinations where practical. 5. Keep filename selection internal to the storage layer rather than accepting arbitrary strings from callers. Example hardening: ```python ALLOWED_FILES = { "stock_pool.json", "positions.json", "trades.json", "challenge.json", "recommendations.json", "config.json", } def _get_path(self, filename: str) -> Path: if filename not in ALLOWED_FILES: raise ValueError("Unsupported storage filename") base = self.data_dir.resolve() path = (base / filename).resolve() if path.parent != base: raise ValueError("Storage path escapes the data directory") return path ``` Add tests covering `../`, absolute paths, nested traversal, alternate separators, and symbolic-link escapes.

T09 · Insecure Skill Coding Practices

Note
Location
src/data/storage.py:13
Finding
Financial Records Are Stored Without Enforced Restrictive Permissions## Vulnerability Details **File Location**: `src/data/storage.py:13-43` **Vulnerability Type**: Insecure local file permissions **Risk Level**: Low ### Vulnerable Code ```python def __init__(self, data_dir: str = None): if data_dir is None: data_dir = os.path.join(os.path.dirname(__file__), '..', 'data') self.data_dir = Path(data_dir) self.data_dir.mkdir(parents=True, exist_ok=True) def _get_path(self, filename: str) -> Path: return self.data_dir / filename def load(self, filename: str, default: Any = None) -> Any: """Load a JSON file.""" path = self._get_path(filename) if not path.exists(): return default try: with open(path, 'r', encoding='utf-8') as f: return json.load(f) except (json.JSONDecodeError, IOError): return default def save(self, filename: str, data: Any) -> bool: """Save a JSON file.""" path = self._get_path(filename) try: with open(path, 'w', encoding='utf-8') as f: json.dump(data, f, ensure_ascii=False, indent=2) return True except IOError as e: print(f"Save failed {filename}: {e}") return False ``` The design document at `docs/product-design-full.md:581` states that files should use mode `0600`, but the implementation does not enforce that requirement. ### Technical Analysis The data directory is created without an explicit restrictive mode, and files are created through the regular `open()` function without enforcing owner-only permissions. Effective permissions therefore depend on the host process umask. Existing files also retain any previously permissive mode. These files can contain portfolio positions, trade history, recommendations, challenge state, and user-supplied remarks. On a shared system, permissive directory or file modes may allow another local account to read or alter this data. ### Attack Path 1. The S ...[truncated 993 chars]
Remediation
## Remediation Suggestions 1. Create the data directory with mode `0700` and explicitly correct its permissions after creation. 2. Create data files atomically with mode `0600` instead of relying on the process umask. 3. Correct permissions on existing files during initialization or migration. 4. Write to a temporary file in the same protected directory, flush and synchronize it, then atomically replace the destination. 5. Verify that the destination is a regular file owned by the expected account before replacing it. Example directory hardening: ```python self.data_dir.mkdir(parents=True, exist_ok=True, mode=0o700) os.chmod(self.data_dir, 0o700) ``` A secure save implementation should use `os.open()` with flags such as `O_WRONLY | O_CREAT | O_TRUNC` and mode `0o600`, followed by `os.fdopen()`. For stronger integrity and crash safety, use a protected temporary file, call `flush()` and `os.fsync()`, set mode `0600`, and complete the operation with `os.replace()`.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • 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 (31)

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding
The code behavior is much narrower than the declared description. It acts as a read-only market data wrapper around AKShare, providing quote retrieval, sector/concept ranking, simple keyword-based industry filtering, historical daily data, and text formatting for summaries. While this supports parts of the declared sector analysis and market hotspot features, it does not implement several core advertised capabilities: the AI股神 challenge, stock recommendation/decision logic, watchlist management, holdings simulation, trade execution parsing, or profit/loss computations. Additionally, the skill declares no permissions/resources, but the code clearly depends on external financial data sources via AKShare/Eastmoney. This is a material description-to-behavior mismatch.

Tp4

High
Category
MCP Tool Poisoning
Confidence
93% confidence
Finding
The code is clearly related to the stock-trading domain described by the skill, so it is not wholly unrelated. It uses challenge status, positions, simulated profit/loss, and market summaries, which overlap with the declared stock assistant functionality. However, this specific chunk’s primary behavior is a monitoring and scheduled push subsystem: it runs heartbeat checks, determines trading-session timing, emits morning/noon/closing reports, and raises automatic alerts for large moves and stop-loss conditions. Those are materially distinct operational capabilities from the declared interactive assistant features such as opening challenges, analyzing four sectors, managing watchlists, and handling simulated trade commands. The declared triggers are conversational/user-invoked, while this code is time-based and autonomous. Therefore the description does not accurately represent this code chunk’s actual purpose. There is also an internal inconsistency where get_market_summary references self.recommendation, which is not initialized on Monitor; that suggests either a bug or missing context, but the main mismatch is the undeclared monitoring/push behavior.

Tp4

High
Category
MCP Tool Poisoning
Confidence
94% confidence
Finding
The description presents a comprehensive stock assistant with AI stock recommendation/decision features, sector analysis, market hot topics, self-selected stock pool management, and trading simulation. The supplied code chunk only covers the 'challenge' service and performs challenge state persistence and statistics tracking. It does not fetch market data, analyze sectors, manage a stock pool, execute simulated buy/sell logic, or perform any AI stock-picking. The code’s behavior is therefore a materially narrower subset of the declared purpose. There is no obvious harmful undeclared capability, but the declared description does not accurately represent what this specific code chunk actually does.

Lp3

Medium
Category
MCP Least Privilege
Confidence
89% confidence
Finding
The skill declares no explicit tool scope or permissions even though its described implementation indicates access to environment variables and local file read/write via JSON storage. Missing scope declarations weaken least-privilege controls and make it harder for users or the host platform to understand that the skill may persist portfolio data or access secrets such as TUSHARE_TOKEN.

Vague Triggers

Medium
Confidence
94% confidence
Finding
Broad trigger phrases like '有什么消息' or similarly generic market prompts can cause accidental invocation during normal conversation. That increases the chance the skill runs unexpectedly, potentially making external requests or writing simulated trading data when the user did not intend to activate it.

Missing User Warnings

Medium
Confidence
91% confidence
Finding
The skill describes watchlists, holdings, and trade history persistence but does not clearly warn users that portfolio and transaction data may be written to storage and retained. In a finance-related context, this is more sensitive than ordinary app state because holdings and trading behavior can reveal private financial interests and habits.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
The design explicitly includes stock recommendation and timed push behavior but does not pair these features with user-facing warnings, consent controls, or suitability disclaimers. In a finance context, this increases the chance that users interpret outputs as actionable advice and receive unsolicited prompts that may influence trading decisions.

Vague Triggers

Medium
Confidence
94% confidence
Finding
The recommendation trigger phrases are broad natural-language commands such as '有什么消息吗' and '推荐一只股票', which can overlap with ordinary conversation and cause the skill to activate unintentionally. In a financial skill, unintended activation can produce unsolicited stock picks or market guidance, creating meaningful user-harm and compliance risk even without direct code execution.

Natural-Language Policy Violations

Medium
Confidence
81% confidence
Finding
SQP-3 applies to all file types. The entire skill design, commands, labels, and examples are Chinese-only, and there is no indication that users may choose another language or that the skill is intentionally limited to a Chinese-speaking/regional context. This can violate a language/locale policy when a specific language is effectively forced without opt-in.

Description-Behavior Mismatch

Medium
Confidence
91% confidence
Finding
The design adds scheduled push notifications and anomaly alerts that are not described in the stated skill surface. This creates a capability-expansion risk: users may receive proactive trading-related messages without clear consent, and the system may process or disclose portfolio/watchlist information more broadly than expected.

Description-Behavior Mismatch

Medium
Confidence
94% confidence
Finding
The document introduces Feishu as an outbound message channel without corresponding disclosure in the visible skill contract. External delivery channels can expose trading interests, positions, or alerts to third-party systems and broaden the skill's data-sharing boundary beyond user expectations.

Natural-Language Policy Violations

Medium
Confidence
94% confidence
Finding
The natural-language strings and descriptions throughout the file are entirely Chinese, including user-visible status and output text, with no indication that users may choose another language. Under the policy, forcing a specific language without user opt-in is a locale/language policy concern.

Natural-Language Policy Violations

Medium
Confidence
95% confidence
Finding
The module title, class docstrings, and all command patterns are written exclusively in Chinese, which effectively forces a single language/locale for interaction. Under the policy, language-specific behavior should either provide user opt-in/choice or clearly document a justified regional constraint.

Natural-Language Policy Violations

Medium
Confidence
88% confidence
Finding
This Python file uses natural-language strings exclusively in Chinese for its top-level description and method docstrings/comments, with no indication that the skill is region-specific or that users can opt into a language preference. Under the policy, forcing a specific language without user choice or justification is a natural-language policy concern.

Natural-Language Policy Violations

Medium
Confidence
91% confidence
Finding
The file’s natural-language strings and descriptions consistently specify Chinese output and Chinese-language reporting conventions, but there is no indication that the user can opt into another language or that the skill is restricted to a Chinese-specific compliance or regional context. Under the locale policy rule, forcing a single language without opt-in is a policy concern.

Natural-Language Policy Violations

Medium
Confidence
95% confidence
Finding
This code defines the service name and docstrings in Chinese and all returned user-facing messages are hard-coded in Chinese, indicating the skill operates in a single language by default. The policy requires flagging language or locale constraints when the user is not given an explicit choice or opt-in.

Missing User Warnings

Medium
Confidence
84% confidence
Finding
This code records a sell trade and overwrites stored positions, potentially removing a holding entirely, but there is no confirmation prompt or explicit warning before the irreversible portfolio state change. The surrounding docstrings describe functionality but do not warn that calling this method mutates persistent records and may delete the position when fully sold.

Missing User Warnings

Medium
Confidence
87% confidence
Finding
This code performs state-changing trade actions through `self.position.buy(...)`, `self.position.sell(...)`, and challenge trade recording, but the surrounding docstrings and returned prompts do not disclose that these commands persist transaction records or alter portfolio/challenge state. Because these are safety-relevant write operations affecting user data, some explicit warning or confirmation would be expected in code or user-visible help text.

Missing User Warnings

Low
Confidence
89% confidence
Finding
Routine and anomaly-triggered push notifications are described without warning users that trading-related information may be proactively messaged. Even if content is only simulated trading data, unsolicited exposure through notifications can leak sensitive financial behavior to lock screens, shared devices, or unintended recipients.

Missing User Warnings

Low
Confidence
88% confidence
Finding
The storage design persists stock pool, positions, and trade history in local JSON files but does not warn users about retention, location, or access implications. Local persistence of trading records can expose financial behavior to other local users, backups, or misconfigured file permissions.

Missing User Warnings

Low
Confidence
84% confidence
Finding
This code fetches live stock data from AKShare's external public data source, which is a network operation. Although the module prints that the data source is loaded, it does not clearly disclose at call time that invoking this method sends requests to an external service.

Natural-Language Policy Violations

Low
Confidence
92% confidence
Finding
The file's docstrings and all user-visible return messages are written in Chinese, which effectively constrains the skill's language behavior without offering a locale choice. Under the stated policy, forcing a specific language is a natural-language policy issue unless the locale restriction is explicitly documented and justified.

Intent-Code Divergence

Low
Confidence
89% confidence
Finding
The docstring says the method records a buy transaction, which implies creation of a trade record comparable to the explicit trade logging performed in `sell` at L103-L118. In practice, `buy` only appends to positions storage at L52-L55, so the documented intent of transaction recording diverges from actual behavior and leads to incomplete trade history.

Natural-Language Policy Violations

Low
Confidence
78% confidence
Finding
Multiple docstrings, comments, and user-facing return strings are exclusively in Chinese, and the file does not indicate that users can choose another language. Under the policy, forcing a specific language without opt-in is a natural-language policy concern.

Missing User Warnings

Low
Confidence
83% confidence
Finding
The method persists stock recommendation data by reading and then saving recommendation records through the storage layer, but there is no confirmation prompt, user-visible log, or warning in this file indicating that data will be stored. Because this is a code file and the operation writes user-relevant data, it should disclose that persistence occurs.

Static analysis

No suspicious patterns detected.