Back to skill

Security audit

Stock-Decision

Security checks for vulnerabilities and agentic risk

Overview

This stock-analysis skill is purpose-aligned overall, but normal stock inputs can reach unsafe shell execution and the financial-analysis scope is broader than clearly controlled.

Review before installing. The main issue is not hidden malware; it is that a stock name or stock code containing shell syntax could execute unintended local commands. Install only after the scripts are changed to use argument-list subprocess calls with strict stock-symbol validation, and confirm you are comfortable with external Bing searches for macro analysis.

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/analyze.py:34
Finding
OS Command Injection Through Stock Search Input## Vulnerability Details **File Location**: `scripts/analyze.py`, lines 34–35 **Vulnerability Type**: OS command injection through shell command construction **Risk Level**: High ### Vulnerable Code ```python cmd = f"node ~/.workbuddy/skills/westock-data/scripts/index.js search '{self.stock_input}'" result = subprocess.run(cmd, shell=True, capture_output=True, text=True) ``` The value used here originates from a command-line argument: ```python stock_input = sys.argv[1] analyzer = StockDecisionAnalyzer(stock_input) ``` ### Technical Analysis `self.stock_input` is derived directly from `sys.argv[1]` and interpolated into a command string executed with `shell=True`. Although the value is enclosed in single quotes, this is not safe shell escaping. An attacker can include a single quote to terminate the quoted argument and then append shell operators and commands. The shell interprets the resulting string rather than passing the stock input as an opaque argument to Node.js. No stock-name validation or robust shell escaping prevents this behavior. ### Attack Path 1. An attacker supplies a malicious stock-name argument to `analyze.py`. 2. The argument is stored in `self.stock_input`. 3. `search_stock()` inserts it into the shell command without safe argument separation. 4. A quote in the input terminates the intended shell argument. 5. Shell metacharacters append an attacker-selected command. 6. `subprocess.run(..., shell=True)` executes that command with the privileges of the Python process. For example, an argument structurally resembling the following can escape the quoted value: ```text ' ; ATTACKER_COMMAND ; # ``` ### Impact Assessment Successful exploitation permits arbitrary command execution under the account running the skill. The attacker could read or modify files accessible to that account, invoke local tools, alter skill data, access environment-provided secrets, or execute additional programs. The flaw does not independently elevate privil ...[truncated 151 chars]
Remediation
## Remediation Suggestions Do not invoke a shell. Resolve the script path in Python and pass each argument separately: ```python from pathlib import Path import subprocess script_path = Path( "~/.workbuddy/skills/westock-data/scripts/index.js" ).expanduser() result = subprocess.run( ["node", str(script_path), "search", self.stock_input], shell=False, capture_output=True, text=True, timeout=30, check=False, ) ``` Additionally: 1. Validate the input against documented stock-name and stock-code constraints. 2. Apply a reasonable maximum input length. 3. Reject control characters and unexpected line breaks. 4. Add tests containing quotes, semicolons, command substitutions, pipes, and newlines. 5. Run the skill with minimum filesystem and environment access so that any future command-execution flaw has reduced impact.

T09 · Insecure Skill Coding Practices

Error
Location
scripts/backtest.py:29
Finding
OS Command Injection Through Backtest Stock Code## Vulnerability Details **File Location**: `scripts/backtest.py`, lines 29–30 and 56–57 **Vulnerability Type**: OS command injection through unquoted shell interpolation **Risk Level**: High ### Vulnerable Code The stock code is inserted into two shell commands: ```python cmd = f"node ~/.workbuddy/skills/westock-data/scripts/index.js kline {self.stock_code} daily {days} hfq" result = subprocess.run(cmd, shell=True, capture_output=True, text=True) ``` ```python cmd = f"node ~/.workbuddy/skills/westock-data/scripts/index.js technical {self.stock_code} ma,macd,kdj,rsi,dmi,vol,boll {start_date} {end_date}" result = subprocess.run(cmd, shell=True, capture_output=True, text=True) ``` The value originates from a command-line argument: ```python stock_code = sys.argv[1] engine = BacktestEngine(stock_code) ``` ### Technical Analysis `self.stock_code` is taken from `sys.argv[1]` without validation, quoting, or safe argument separation. It is interpolated directly into command strings executed using `shell=True`. Because the value is not even enclosed in shell quotes, shell operators such as command separators, pipelines, substitutions, and redirections can be interpreted immediately. Either `get_historical_data()` or `get_historical_technical()` can reach the vulnerable execution path. ### Attack Path 1. An attacker supplies a crafted value as the stock-code argument to `backtest.py`. 2. The value is assigned to `stock_code` and then `self.stock_code`. 3. The backtest engine constructs a Node.js command by directly interpolating the value. 4. Shell syntax embedded in the stock code changes the command structure. 5. `subprocess.run(..., shell=True)` executes the injected command. 6. The injected command runs with the same operating-system privileges as the skill process. A malicious argument can structurally use a command separator: ```text hk00700; ATTACKER_COMMAND ``` Both vulnerable calls must be corrected because the second remains exploitable even if o ...[truncated 605 chars]
Remediation
## Remediation Suggestions Replace both shell command strings with argument-array invocations: ```python from pathlib import Path import subprocess script_path = Path( "~/.workbuddy/skills/westock-data/scripts/index.js" ).expanduser() result = subprocess.run( [ "node", str(script_path), "kline", self.stock_code, "daily", str(days), "hfq", ], shell=False, capture_output=True, text=True, timeout=30, check=False, ) ``` Use the same pattern for the technical-indicator request: ```python result = subprocess.run( [ "node", str(script_path), "technical", self.stock_code, "ma,macd,kdj,rsi,dmi,vol,boll", start_date, end_date, ], shell=False, capture_output=True, text=True, timeout=30, check=False, ) ``` Also enforce an allowlisted stock-code format before either call, for example: ```python import re if not re.fullmatch(r"(?:hk\d{5}|sz\d{6}|sh\d{6})", self.stock_code): raise ValueError("Unsupported stock-code format") ``` Validate `days` against a bounded positive range, add command-injection regression tests, and execute the skill under least privilege.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
Findings (37)

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding
The code substantially matches the technical-analysis portion of the description: it gathers stock data, calculates and evaluates several indicators, and produces recommendations with stop-loss/take-profit suggestions. However, two major declared capabilities are absent: macro environment assessment and historical backtesting. The script contains no logic for industry cycle, corporate governance, macroeconomic analysis, or any backtest/simulation over historical data. Therefore the declared description overstates the implemented functionality in materially important ways, making this a mismatch.

Tp4

High
Category
MCP Tool Poisoning
Confidence
94% confidence
Finding
The description overstates the scope. This code does cover part of the declared purpose: it uses several technical indicators and performs historical backtesting. However, the declared 'comprehensive' analysis including macro environment factors is not present anywhere in the code. The code strictly performs technical-signal-based backtesting using historical data and generates a report of simulated trades and metrics. It also contains sell rules resembling stop-loss/take-profit logic, but these are internal backtest conditions rather than explicit recommendation outputs with stop-loss/take-profit levels. Therefore, the actual behavior is materially narrower than the declared description.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding
The supplied code chunk is narrowly focused on macro and governance analysis via external web search. It queries Bing for industry outlook, governance/fraud/regulatory signals, and US macroeconomic outlook, then counts keywords to produce coefficients and risk warnings. This only partially overlaps with the declared description's macro-environment component. Major advertised capabilities—technical indicator computation, historical backtesting, and concrete trading recommendations including stop-loss/take-profit—are absent from the code. Additionally, the code uses network access to Bing, which is not reflected in the declared permissions. Therefore the description materially overstates and misrepresents the code's actual behavior.

Tool Parameter Abuse

High
Category
Tool Misuse
Content
"""搜索股票"""
        print(f"\n🔍 搜索股票: {self.stock_input}")
        cmd = f"node ~/.workbuddy/skills/westock-data/scripts/index.js search '{self.stock_input}'"
        result = subprocess.run(cmd, shell=True, capture_output=True, text=True)

        if result.returncode == 0:
            output = result.stdout
Confidence
99% confidence
Finding
This is a classic tool-parameter abuse case: attacker-controlled input is embedded into a shell command, allowing arbitrary command execution instead of a benign stock lookup. Because the skill accepts free-form stock queries, the exploitation surface is broad and directly exposed to untrusted users.

Tool Parameter Abuse

High
Category
Tool Misuse
Content
"""获取K线数据"""
        print(f"\n📊 获取近{days}日K线数据...")
        cmd = f"node ~/.workbuddy/skills/westock-data/scripts/index.js kline {self.stock_code} day {days} qfq"
        result = subprocess.run(cmd, shell=True, capture_output=True, text=True)

        if result.returncode == 0:
            try:
Confidence
90% confidence
Finding
Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).

Tool Parameter Abuse

High
Category
Tool Misuse
Content
"""获取K线数据"""
        print(f"\n📊 获取近{days}日K线数据...")
        cmd = f"node ~/.workbuddy/skills/westock-data/scripts/index.js kline {self.stock_code} day {days} qfq"
        result = subprocess.run(cmd, shell=True, capture_output=True, text=True)

        if result.returncode == 0:
            try:
Confidence
90% confidence
Finding
Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).

Tool Parameter Abuse

High
Category
Tool Misuse
Content
# 获取K线数据
        cmd = f"node ~/.workbuddy/skills/westock-data/scripts/index.js kline {self.stock_code} daily {days} hfq"
        result = subprocess.run(cmd, shell=True, capture_output=True, text=True)

        if result.returncode == 0:
            try:
Confidence
99% confidence
Finding
This finding correctly identifies tool parameter abuse: untrusted input is inserted into a shell command and executed, enabling command injection. In an agent skill context this is especially dangerous because user-provided stock symbols are a natural input surface and the agent may run with access to local files, credentials, or other tools.

Tool Parameter Abuse

High
Category
Tool Misuse
Content
end_date = datetime.now().strftime('%Y-%m-%d')
        start_date = (datetime.now() - timedelta(days=days)).strftime('%Y-%m-%d')
        cmd = f"node ~/.workbuddy/skills/westock-data/scripts/index.js technical {self.stock_code} ma,macd,kdj,rsi,dmi,vol,boll {start_date} {end_date}"
        result = subprocess.run(cmd, shell=True, capture_output=True, text=True)

        if result.returncode == 0:
            try:
Confidence
99% confidence
Finding
This second subprocess call repeats the same parameter-abuse flaw for technical indicator retrieval. Because it is reachable during normal backtesting flow, an attacker can exploit routine analysis requests to execute arbitrary shell commands.

Lp1

High
Category
MCP Least Privilege
Confidence
96% confidence
Finding
The script makes outbound HTTP requests to Bing using `requests.get`, so it has real network capability despite that capability not being declared. In an agent-skill context, undeclared network access is dangerous because it can exfiltrate user-provided inputs and fetch untrusted remote content without platform visibility or user consent.

Missing User Warnings

Medium
Confidence
90% confidence
Finding
The changelog states that the skill performs real web searches via Bing/Google and may fall back to curl, but there is no corresponding warning here about sending stock symbols, company names, or query context to third-party services. This creates a privacy and transparency risk because user inputs and analysis targets may be transmitted externally without clear disclosure or consent.

Context-Inappropriate Capability

Medium
Confidence
90% confidence
Finding
The manifest describes a stock decision skill performing technical, macro, and backtesting analysis. This changelog states the implementation uses '命令行curl作为备用' (command-line curl as a fallback), which introduces command execution capability that is not obviously required for providing stock analysis and is broader than ordinary data retrieval.

Vague Triggers

Medium
Confidence
91% confidence
Finding
This markdown file says the skill will auto-trigger 'when the user asks about stock buy suggestions' and gives examples including broad phrasing like '帮我分析一下XX股票的买入点'. It does not define exact trigger phrases, scope boundaries, or exclusion conditions, which could cause unintended invocation during ordinary finance discussion.

Description-Behavior Mismatch

Medium
Confidence
96% confidence
Finding
The skill metadata and documentation present the capability as a comprehensive stock-decision system, but this section admits macro analysis must be manually supplemented and is not actually implemented. In a financial-decision skill, overstating implemented analysis can mislead users or downstream agents into trusting recommendations that lack the promised risk controls and validation layers.

Description-Behavior Mismatch

Medium
Confidence
96% confidence
Finding
This section describes a combined decision score and final recommendation framework as if it is operational, while later sections state the macro/composite scoring is still pending. That inconsistency can cause users or invoking agents to treat output as validated end-to-end analysis when it may only reflect technical indicators, increasing the chance of unsafe financial advice.

Intent-Code Divergence

Medium
Confidence
93% confidence
Finding
The report format and examples imply a finished comprehensive rating pipeline, but the version history says the comprehensive scoring system is not yet implemented. In this context, polished examples can create false assurance and induce users to rely on recommendations with missing analytical components.

Natural-Language Policy Violations

Medium
Confidence
87% confidence
Finding
User-facing descriptions, examples, warnings, and disclaimers are entirely in Chinese, and the document does not mention that the skill is Chinese-only or provide any language/locale opt-in. This can violate language/locale policy when a skill implicitly forces a specific language without user choice or documented justification.

Vague Triggers

Medium
Confidence
94% confidence
Finding
The trigger examples include very broad natural-language phrases like asking whether a stock is suitable to buy, which can cause the skill to activate unintentionally during ordinary financial conversation. In a stock recommendation skill, accidental invocation is more sensitive because it may generate actionable investment guidance without the user explicitly intending to request this specific analysis workflow.

Natural-Language Policy Violations

Medium
Confidence
84% confidence
Finding
Most instructional content, trigger phrases, output examples, and safety notes are presented only in Chinese, and there is no statement that users may choose another language. Under the policy, forcing a specific language without opt-in can be a natural-language policy violation unless the locale restriction is clearly documented and justified.

Vague Triggers

Medium
Confidence
92% confidence
Finding
The trigger conditions are broad enough to activate on generic stock questions, which can cause the agent to invoke trading-analysis behavior in situations where the user did not request a full recommendation workflow. In a finance context, overbroad invocation is risky because it may produce authoritative-seeming investment guidance, external lookups, or script execution on ambiguous prompts.

Natural-Language Policy Violations

Medium
Confidence
95% confidence
Finding
Nearly all user-facing strings, including usage text, status messages, and the final report, are fixed in Chinese. The policy for all file types says to flag language or locale constraints when the skill forces a specific language without user opt-in or an explicitly justified regional scope.

Context-Inappropriate Capability

Medium
Confidence
84% confidence
Finding
A stock decision skill is expected to retrieve market data, but this implementation does so by invoking an external Node.js script through shell=True rather than using a direct in-process API or library. Spawning shell commands adds a broader execution capability than the manifest's analytical purpose suggests.

Missing User Warnings

Medium
Confidence
98% confidence
Finding
The script passes raw user-controlled input into a shell command without any sanitization or warning, enabling tool parameter abuse and command injection. In this skill context, users are expected to type stock names or codes, so the vulnerable path is directly reachable through normal use.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
"""搜索股票"""
        print(f"\n🔍 搜索股票: {self.stock_input}")
        cmd = f"node ~/.workbuddy/skills/westock-data/scripts/index.js search '{self.stock_input}'"
        result = subprocess.run(cmd, shell=True, capture_output=True, text=True)

        if result.returncode == 0:
            output = result.stdout
Confidence
99% confidence
Finding
This command builds a shell string with untrusted user input (`self.stock_input`) and executes it with `shell=True`, creating a direct command injection path. An attacker can break out of the quoted argument and execute arbitrary local commands under the user's account.

Context-Inappropriate Capability

Medium
Confidence
84% confidence
Finding
The code obtains K-line and technical data by launching a separate Node.js program with shell execution. While market-data access is expected for stock analysis, arbitrary subprocess invocation is a more powerful capability than necessary for the stated purpose and is not justified by the manifest text.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
"""获取K线数据"""
        print(f"\n📊 获取近{days}日K线数据...")
        cmd = f"node ~/.workbuddy/skills/westock-data/scripts/index.js kline {self.stock_code} day {days} qfq"
        result = subprocess.run(cmd, shell=True, capture_output=True, text=True)

        if result.returncode == 0:
            try:
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Static analysis

No suspicious patterns detected.