Back to skill

Security audit

A股持仓监控助手

Security checks for vulnerabilities and agentic risk

Overview

This A-share portfolio skill is mostly coherent, but it contains unsafe shell execution from portfolio menu input that can run unintended local commands.

Review this skill before installing or running it. Do not use the interactive menu with untrusted input, and prefer direct, validated script calls only after the shell execution is fixed. Be aware it stores holdings locally, contacts Tencent Finance for quotes, uses HTTP for some market data, and does not actually implement automatic alerts as advertised.

Vulnerability Patterns
  • Tool Hijacking and SpoofingModifies or replaces tools so legitimate-looking calls execute attacker logic
  • 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)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/run_monitor.py:34
Finding
Shell Command Injection Through Unsanitized Portfolio Input<![CDATA[ ## Vulnerability Details **File Location**: `scripts/run_monitor.py`, lines 34-40 **Vulnerability Type**: OS command injection **Risk Level**: High ### Vulnerable Code ```python elif choice == '3': code = input("股票代码: ") cost = input("成本价: ") qty = input("数量: ") os.system(f"python portfolio.py add {code} --cost {cost} --qty {qty}") elif choice == '4': code = input("股票代码: ") os.system(f"python portfolio.py remove {code}") ``` ### Technical Analysis Values read from `code`, `cost`, and `qty` are interpolated directly into command strings passed to `os.system`. This function invokes a system shell, so shell metacharacters contained in any of these values are interpreted as command syntax rather than as literal arguments. The program does not restrict stock codes to the expected numeric format and does not ensure that cost and quantity values are valid positive numbers before constructing the command. Consequently, any user or process able to supply interactive input can append an additional shell command. This vulnerability does not itself elevate privileges. Injected commands execute with the same operating-system privileges, environment, filesystem access, and credentials as the user running the monitor. ### Attack Path 1. A victim launches `scripts/run_monitor.py`. 2. The attacker supplies input directly, through a wrapper, or through a manipulated terminal/input stream. 3. The attacker selects the add or remove operation. 4. A stock-code value containing a shell command separator is entered, such as: - POSIX shell: `600000; id` - Windows command shell: `600000 & whoami` 5. The application constructs a command containing the injected separator. 6. `os.system` passes the complete string to the system shell. 7. The shell runs both the intended portfolio operation and the injected command with the victim's privileges. ### Impact Assessment Successful exploitation provides arbitrary command execution in the context ...[truncated 566 chars]
Remediation
<![CDATA[ ## Remediation Suggestions Avoid invoking a shell. Use `subprocess.run` with an argument list, the current Python interpreter, and an absolute script path: ```python import re import subprocess from pathlib import Path SCRIPT_DIR = Path(__file__).resolve().parent PORTFOLIO_SCRIPT = SCRIPT_DIR / "portfolio.py" def validate_code(value: str) -> str: value = value.strip() if not re.fullmatch(r"\d{6}", value): raise ValueError("The stock code must contain exactly six digits.") return value code = validate_code(input("Stock code: ")) cost = float(input("Cost: ")) qty = int(input("Quantity: ")) if cost <= 0 or qty <= 0: raise ValueError("Cost and quantity must be positive.") subprocess.run( [ sys.executable, str(PORTFOLIO_SCRIPT), "add", code, "--cost", str(cost), "--qty", str(qty), ], check=True, ) ``` Apply equivalent validation to remove operations. Do not attempt to fix this solely by escaping shell characters; eliminating the shell is more reliable. Handle conversion and subprocess errors explicitly so invalid input cannot produce a partially executed operation. ]]>

T07 · Tool Hijacking and Spoofing

Warning
Location
scripts/run_monitor.py:29
Finding
Executable and Script Path Hijacking in Menu Actions<![CDATA[ ## Vulnerability Details **File Location**: `scripts/run_monitor.py`, lines 29-40 **Vulnerability Type**: Untrusted search path and relative script execution **Risk Level**: Medium ### Vulnerable Code ```python if choice == '1': print("\n正在生成持仓报告...") os.system("python portfolio.py analyze") elif choice == '2': print("\n正在运行选股...") os.system("python selector.py") elif choice == '3': code = input("股票代码: ") cost = input("成本价: ") qty = input("数量: ") os.system(f"python portfolio.py add {code} --cost {cost} --qty {qty}") elif choice == '4': code = input("股票代码: ") os.system(f"python portfolio.py remove {code}") ``` ### Technical Analysis The application invokes `python` by name and supplies relative paths for `portfolio.py` and `selector.py`. The operating system resolves `python` using the process environment, typically through `PATH`. An attacker who can influence `PATH` may cause an attacker-controlled executable or wrapper named `python` to run. The script arguments are also resolved relative to the current working directory, not relative to the directory containing `run_monitor.py`. This is especially problematic because the documented command is: ```bash python scripts/run_monitor.py ``` When run from the project root, the commands attempt to open `portfolio.py` and `selector.py` from that root, although the legitimate files are located under `scripts/`. If an attacker can place a file with one of those names in the working directory, the menu can execute that file instead of the intended implementation. ### Attack Path #### Relative-script hijacking 1. The victim starts the monitor from a directory other than `scripts/`, including the project root as documented. 2. An attacker with write access to that directory creates a malicious `portfolio.py` or `selector.py`. 3. The victim selects the corresponding menu operation. 4. The shell resolves the relative script argument against the current working dir ...[truncated 1136 chars]
Remediation
<![CDATA[ ## Remediation Suggestions Bind both the interpreter and target scripts to trusted absolute paths, and avoid shell resolution: ```python import subprocess import sys from pathlib import Path SCRIPT_DIR = Path(__file__).resolve().parent PORTFOLIO_SCRIPT = SCRIPT_DIR / "portfolio.py" SELECTOR_SCRIPT = SCRIPT_DIR / "selector.py" subprocess.run( [sys.executable, str(PORTFOLIO_SCRIPT), "analyze"], check=True, ) subprocess.run( [sys.executable, str(SELECTOR_SCRIPT)], check=True, ) ``` For add and remove operations, append separately validated arguments to the list rather than building a string. Additional hardening should include: - Confirming that the resolved script paths are regular files. - Installing project files in a location that is not writable by untrusted users. - Avoiding reliance on the caller's current working directory. - Using a controlled environment if the monitor is launched through automation or a privileged scheduler. - Updating the documented launch instructions and testing execution from arbitrary working directories. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/selector.py:78
Finding
Stock Quote Data Retrieved Over Unencrypted HTTP<![CDATA[ ## Vulnerability Details **File Location**: `scripts/selector.py`, lines 78-82 **Vulnerability Type**: Cleartext transmission without transport integrity **Risk Level**: Medium ### Vulnerable Code ```python prefix = 'sh' if code.startswith('6') else 'sz' url = f"http://qt.gtimg.cn/q={prefix}{code}" req = urllib.request.Request(url, headers={'User-Agent': 'Mozilla/5.0'}) resp = urllib.request.urlopen(req, timeout=5) text = resp.read().decode('gbk') ``` ### Technical Analysis The stock quote endpoint is accessed using plaintext HTTP. HTTP provides neither server authentication nor protection against modification in transit. An attacker positioned on the network path can intercept the request and return a forged quote response. The application parses the response without cryptographic verification and uses fields such as price, percentage change, volume, transaction amount, and valuation metrics to calculate rankings. Although the request does not contain credentials in the audited code, the integrity of the financial data is security-relevant because manipulated data can alter the generated stock-selection results. ### Attack Path 1. The user runs `scripts/selector.py` on an untrusted or compromised network. 2. The application requests the Tencent quote endpoint over HTTP. 3. A network-positioned attacker intercepts or redirects the request through techniques such as a malicious access point, compromised proxy, DNS manipulation, or gateway control. 4. The attacker returns a syntactically valid but manipulated quote response. 5. The application decodes and parses the forged values. 6. The scoring logic processes the manipulated price, volume, amount, and valuation fields. 7. The application presents attacker-influenced stocks and scores to the user as if they originated from the expected market-data provider. ### Impact Assessment A successful attack can compromise the integrity of displayed market information and ranking results. Potential eff ...[truncated 418 chars]
Remediation
<![CDATA[ ## Remediation Suggestions Use an HTTPS endpoint supported by the market-data provider: ```python url = f"https://qt.gtimg.cn/q={prefix}{code}" ``` Retain standard certificate and hostname verification, and do not disable TLS validation. Also consider: - Failing closed when TLS verification fails. - Validating response structure and expected field counts before use. - Rejecting implausible or non-finite numeric values. - Comparing critical values against a second authenticated data source where integrity is important. - Recording the source and retrieval time in generated reports. - Clearly warning users if the provider does not offer authenticated transport and plaintext HTTP must remain in use. A timeout protects availability but does not protect authenticity or integrity, so it is not a substitute for HTTPS. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (24)

Tainted flow: 'code' from input (line 35, user input) → os.system (code execution)

Critical
Category
Data Flow
Content
code = input("股票代码: ")
            cost = input("成本价: ")
            qty = input("数量: ")
            os.system(f"python portfolio.py add {code} --cost {cost} --qty {qty}")
        elif choice == '4':
            code = input("股票代码: ")
            os.system(f"python portfolio.py remove {code}")
Confidence
100% confidence
Finding
The taint finding is valid: user input from code reaches os.system without sanitization, and cost/qty are also unsafely interpolated into the same shell command. This is classic command injection and can lead to arbitrary command execution, data loss, credential exposure, or host compromise.

Tainted flow: 'code' from input (line 40, user input) → os.system (code execution)

Critical
Category
Data Flow
Content
os.system(f"python portfolio.py add {code} --cost {cost} --qty {qty}")
        elif choice == '4':
            code = input("股票代码: ")
            os.system(f"python portfolio.py remove {code}")
        elif choice == '5':
            print("再见!")
            break
Confidence
100% confidence
Finding
The user-controlled code value flows directly into os.system in the remove path, making arbitrary command execution possible. Even though this is a local interactive skill, the portfolio-management context does not justify shell access and makes the behavior more dangerous because users may trust and run it with access to personal financial data.

Tp4

High
Category
MCP Tool Poisoning
Confidence
91% confidence
Finding
声明强调的是监控/提醒型助手,核心卖点包括“每日自动报告”“止损止盈提醒”“实时盈亏跟踪”。但提供的代码没有任何定时任务、后台运行、消息通知、阈值提醒或事件触发逻辑;它只是一个手动调用的 CLI 脚本,负责本地持仓增删改查,并在执行 analyze 时调用外部分析函数输出当前盈亏与分时分析。因此其实际能力更接近“本地持仓管理与手动分析工具”,与声明中的自动监控和提醒能力存在实质不符。

Tp4

High
Category
MCP Tool Poisoning
Confidence
94% confidence
Finding
The description emphasizes automated daily reporting, stop-loss/take-profit reminders, and real-time P/L tracking. However, this code chunk only implements a manual terminal menu that dispatches commands to other scripts. Its observable behavior is primarily a local launcher for reporting, stock selection, and portfolio modification. The included 'run stock selection' feature is undeclared, while key declared capabilities like alerts and real-time tracking are not present in the supplied code. Although 'view position report' partially aligns with the description, the overall behavior shown is materially different from the declared purpose.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
The declared description says the skill monitors a user's existing A-share holdings and provides daily position reports, stop-loss/take-profit reminders, and real-time profit/loss tracking. The supplied code does none of that. Instead, it fetches live quote data for a hardcoded list of stocks, computes a selection score, filters high-scoring names, and prints the top candidates. There is no concept of user holdings, cost basis, portfolio state, alert thresholds, scheduled daily reporting, or realized/unrealized P&L. This is a materially different primary purpose, so the description does not accurately represent the code's behavior.

os.system() or os exec-family call

High
Category
Dangerous Code Execution
Content
if choice == '1':
            print("\n正在生成持仓报告...")
            os.system("python portfolio.py analyze")
        elif choice == '2':
            print("\n正在运行选股...")
            os.system("python selector.py")
Confidence
85% confidence
Finding
os.system() and os exec-family calls run shell commands with the process's full privileges, enabling arbitrary command execution.

Context-Inappropriate Capability

High
Confidence
99% confidence
Finding
This summary finding is accurate because the skill exposes shell execution for core menu actions and includes unsanitized user input in command strings for add/remove operations. The skill context increases risk: a finance-related assistant may be run by non-technical users on systems containing brokerage exports, credentials, or sensitive reports, so arbitrary command execution is especially severe.

os.system() or os exec-family call

High
Category
Dangerous Code Execution
Content
os.system("python portfolio.py analyze")
        elif choice == '2':
            print("\n正在运行选股...")
            os.system("python selector.py")
        elif choice == '3':
            code = input("股票代码: ")
            cost = input("成本价: ")
Confidence
85% confidence
Finding
os.system() and os exec-family calls run shell commands with the process's full privileges, enabling arbitrary command execution.

os.system() or os exec-family call

High
Category
Dangerous Code Execution
Content
code = input("股票代码: ")
            cost = input("成本价: ")
            qty = input("数量: ")
            os.system(f"python portfolio.py add {code} --cost {cost} --qty {qty}")
        elif choice == '4':
            code = input("股票代码: ")
            os.system(f"python portfolio.py remove {code}")
Confidence
99% confidence
Finding
This command builds a shell string with untrusted values from input() and passes it to os.system, allowing shell metacharacters in code, cost, or qty to execute arbitrary commands. In an interactive local tool, a user or any upstream automation feeding stdin could trigger arbitrary OS command execution with the privileges of the script.

os.system() or os exec-family call

High
Category
Dangerous Code Execution
Content
os.system(f"python portfolio.py add {code} --cost {cost} --qty {qty}")
        elif choice == '4':
            code = input("股票代码: ")
            os.system(f"python portfolio.py remove {code}")
        elif choice == '5':
            print("再见!")
            break
Confidence
99% confidence
Finding
This interpolates the user-supplied stock code directly into a shell command, enabling command injection via crafted input containing shell separators or substitutions. Because deletion is exposed as a simple menu action, exploitation is straightforward for anyone who can provide input to the program.

Description-Behavior Mismatch

High
Confidence
98% confidence
Finding
The file banner and stated purpose implement a short-term stock picking/scanning tool, which materially differs from the declared skill purpose of portfolio monitoring, reporting, and alerts. This mismatch is dangerous because users may grant trust, install, or run the skill under false expectations, causing unauthorized behavior scope expansion and undermining review controls.

Description-Behavior Mismatch

High
Confidence
99% confidence
Finding
The core logic iterates over a large predefined stock pool, fetches market data, scores securities, and outputs ranked picks rather than monitoring a user's existing holdings. In the context of an investment assistant, this is a significant scope violation that can mislead users and reviewers about what the skill actually does, potentially resulting in unintended trading guidance or use of external data flows not expected from a monitoring tool.

Natural-Language Policy Violations

Medium
Confidence
92% confidence
Finding
The skill documentation is entirely written in Chinese and presents the skill as a Chinese A-share assistant without any indication that users may choose another language or locale. Under the policy rule, forcing a specific language without user opt-in is a natural-language policy violation unless the locale constraint is explicitly documented and justified.

Lp3

Medium
Category
MCP Least Privilege
Confidence
83% confidence
Finding
The skill advertises and demonstrates capabilities that imply shell execution, local file access, and external network access, but it declares no explicit tool scope or permission boundaries. This creates a least-privilege and transparency problem: an agent or reviewer cannot tell what the skill is expected to access, increasing the risk of unintended file modification, arbitrary command execution, or unreviewed outbound requests.

Description-Behavior Mismatch

Medium
Confidence
92% confidence
Finding
The manifest describes a portfolio monitoring assistant with daily automatic reports, stop-loss/take-profit reminders, and real-time P&L tracking. In this file, the documented and implemented behavior is limited to local commands for showing, adding, updating, removing, and manually analyzing positions, with no scheduling, alerting, or autonomous reporting logic present.

Missing User Warnings

Medium
Confidence
92% confidence
Finding
The script calls `analyze_stock(code, with_minute=True)` for each holding without informing the user that stock codes may be transmitted to an external service or data source. In a portfolio-management context, even stock codes can reveal investment interests and strategy, creating a privacy leak when combined across holdings.

Description-Behavior Mismatch

Medium
Confidence
88% confidence
Finding
The manifest limits the skill to portfolio monitoring tasks such as daily holding reports, stop-loss/stop-profit reminders, and real-time P/L tracking. This file adds a distinct '运行选股' feature that launches selector.py, which goes beyond monitoring existing holdings into stock screening/selection behavior.

Missing User Warnings

Medium
Confidence
93% confidence
Finding
The menu option for deleting a stock immediately runs `portfolio.py remove` after collecting the code, with no confirmation prompt and no explanatory warning beyond the bare menu label. Because this is a user-data modification/removal action, the file should visibly disclose the effect before executing it.

Natural-Language Policy Violations

Medium
Confidence
94% confidence
Finding
Natural-language policy violations apply to all file types, including code docstrings and user-facing output. The top-level description and console text force a specific language/locale without indicating user opt-in or a documented region-specific requirement.

Natural-Language Policy Violations

Low
Confidence
88% confidence
Finding
The file’s natural-language content is entirely in Chinese, including the skill name and usage comments, with no indication that language choice is optional or region-specific. This can violate language/locale policy when a skill implicitly requires a specific language without user opt-in.

Intent-Code Divergence

Low
Confidence
83% confidence
Finding
The script stores portfolio data under a different skill namespace (`a-stock-analysis`) than the advertised skill (`a-stock-portfolio-monitor`). This can cause unintended cross-skill data access, confusion about where sensitive holdings data is stored, and accidental reading or overwriting of another skill's local state.

Missing User Warnings

Low
Confidence
91% confidence
Finding
This code persists user portfolio information to a JSON file under the user's home directory, which affects local user data. While there are success prints after the fact, there is no prior user-facing warning or disclosure in the script header/docstring that add, update, remove, and analyze operations will modify persistent storage.

Natural-Language Policy Violations

Low
Confidence
95% confidence
Finding
All user-facing strings, prompts, and menu text are hard-coded in Chinese, and the script provides no language selection or opt-in. This can violate language/locale policy when a skill is expected to avoid forcing a specific language absent explicit user choice or documented regional scope.

Missing User Warnings

Low
Confidence
89% confidence
Finding
The code makes an external HTTP request to a third-party market data endpoint without user-facing disclosure or consent messaging. While the requested data is public market information and no obvious user secrets are transmitted here, undisclosed network access still reduces transparency and can violate user expectations or platform policy.

Static analysis

No suspicious patterns detected.