Back to skill

Security audit

Futu-Stock

Security checks for vulnerabilities and agentic risk

Overview

Review before installing: this is a Futu stock and account integration, but it can expose sensitive local environment variables to a third-party MCP server and install unpinned code.

Install only after reviewing the MCP server dependency and preferably pinning versions in an isolated environment. Do not run it with unrelated secrets in your shell environment, avoid administrator/root execution, keep FUTU_ENABLE_TRADING disabled unless you intentionally want AI-assisted trading, and treat account, funds, and positions results as sensitive financial data.

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

T08 · Insecure Dependencies

Warning
Location
executor.py:216
Finding
Unpinned Third-Party Dependencies Permit Supply-Chain Payload Substitution## Vulnerability Details **File Location**: `executor.py:216-241`; additional references in `package.json:5-6` and `SKILL.md:56-57, 197-198, 364` **Vulnerability Type**: Unpinned third-party package installation **Risk Level**: Medium ### Vulnerable Code ```python def run_install_deps() -> None: """Install missing dependencies (mcp, futu-stock-mcp-server).""" installed = [] # mcp package if not HAS_MCP: try: subprocess.run([sys.executable, "-m", "pip", "install", "mcp"], check=True, capture_output=True) installed.append("mcp") except subprocess.CalledProcessError as e: print(f"Failed to install mcp: {e}", file=sys.stderr) sys.exit(1) # futu-mcp-server if shutil.which("futu-mcp-server") is None: ok = False if shutil.which("pipx"): try: subprocess.run(["pipx", "install", "futu-stock-mcp-server"], check=True, capture_output=True) installed.append("futu-stock-mcp-server") ok = True except subprocess.CalledProcessError as e: print(f"pipx install failed: {e}", file=sys.stderr) if not ok and shutil.which("pip"): try: subprocess.run([sys.executable, "-m", "pip", "install", "futu-stock-mcp-server"], check=True, capture_output=True) installed.append("futu-stock-mcp-server") ok = True except subprocess.CalledProcessError as e: print(f"pip install failed: {e}", file=sys.stderr) ``` The package setup script contains the same unsafe installation pattern: ```json "scripts": { "setup": "pip install mcp" } ``` ### Technical Analysis Both `mcp` and `futu-stock-mcp-server` are installed without exact version constraints, cryptographic hashes, or a reviewed lockfile. Consequently, the effective code installe ...[truncated 2005 chars]
Remediation
## Remediation Suggestions 1. Pin every dependency to an explicitly reviewed version, for example: ```bash python -m pip install "mcp==REVIEWED_VERSION" pipx install "futu-stock-mcp-server==REVIEWED_VERSION" ``` 2. Maintain a lockfile or requirements file containing cryptographic hashes and install with hash enforcement: ```bash python -m pip install --require-hashes -r requirements.txt ``` 3. Pin build-time and transitive dependencies as well as direct dependencies. 4. Replace `package.json`'s unpinned setup command with a controlled installation script that consumes the reviewed lockfile. 5. Avoid automatic dependency installation during normal Skill execution. Fail closed with clear installation instructions when dependencies are absent. 6. Retrieve packages only from an approved package index over TLS, and consider an internal artifact repository containing reviewed artifacts. 7. Run dependency installation and the MCP server in a dedicated, unprivileged virtual environment or isolated service account. 8. Add automated dependency scanning and require security review before updating pinned versions or hashes.

T09 · Insecure Skill Coding Practices

Warning
Location
executor.py:188
Finding
Unrestricted Environment Inheritance Exposes Unrelated Secrets to the MCP Server## Vulnerability Details **File Location**: `executor.py:144-151, 166-173, 188-195`; related environment merge at `executor.py:27-35` **Vulnerability Type**: Excessive disclosure of process environment variables **Risk Level**: Medium ### Vulnerable Code The tool-execution path merges the complete parent environment into the environment of the externally installed MCP server: ```python async def call_tool_on_server(server_config, tool_name: str, arguments: dict): """Execute a tool call on MCP server.""" env = {**server_config.get("env", {}), **os.environ} _ensure_futu_ready(env) server_params = StdioServerParameters( command=server_config["command"], args=server_config.get("args", []), env=env ) ``` The list and describe paths repeat the same behavior: ```python async def list_tools_from_server(server_config): """Get list of available tools from MCP server.""" env = {**server_config.get("env", {}), **os.environ} _ensure_futu_ready(env) server_params = StdioServerParameters( command=server_config["command"], args=server_config.get("args", []), env=env ) ``` ```python async def describe_tool_from_server(server_config, tool_name: str): """Get detailed schema for a specific tool from MCP server.""" env = {**server_config.get("env", {}), **os.environ} _ensure_futu_ready(env) server_params = StdioServerParameters( command=server_config["command"], args=server_config.get("args", []), env=env ) ``` ### Technical Analysis `os.environ` can contain credentials unrelated to Futu, including cloud access keys, source-control tokens, CI/CD secrets, proxy credentials, database connection strings, and API tokens. The dictionary merge passes all such values to `futu-mcp-server`, although that subprocess only requires a limited set of Futu configuration variab ...[truncated 2006 chars]
Remediation
## Remediation Suggestions 1. Replace unrestricted inheritance with an explicit environment allowlist: ```python ALLOWED_ENV = { "FUTU_HOST", "FUTU_PORT", "FUTU_TRADE_ENV", "FUTU_TRD_MARKET", "FUTU_SECURITY_FIRM", "FUTU_ENABLE_POSITIONS", "FUTU_ENABLE_TRADING", "FUTU_DEBUG_MODE", } env = dict(server_config.get("env", {})) for key in ALLOWED_ENV: if key in os.environ: env[key] = os.environ[key] ``` 2. Add only runtime variables demonstrably required on the target platform, such as a controlled `PATH`, rather than copying the complete environment. 3. Use the same centralized helper to build the environment for list, describe, and call operations so all subprocess paths enforce identical policy. 4. Validate configuration values, including ensuring that `FUTU_PORT` is a valid port and that feature flags use approved values. 5. Run the MCP server under a dedicated unprivileged account or container that does not possess unrelated credentials. 6. Remove sensitive variables from the parent environment before launching third-party processes when practical. 7. Add a regression test that places a sentinel secret in `os.environ`, builds the server parameters, and verifies that the sentinel is absent from the child environment.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Rogue AgentSelf-Modification, Session Persistence
Findings (28)

Credential Access

High
Category
Privilege Escalation
Content
复制 `.env.example` 为 `.env` 并按需修改:

```bash
cp .env.example .env
```

`.env` 示例内容:
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Env Variable Harvesting

High
Category
Data Exfiltration
Content
async def list_tools_from_server(server_config):
    """Get list of available tools from MCP server."""
    env = {**server_config.get("env", {}), **os.environ}
    _ensure_futu_ready(env)

    server_params = StdioServerParameters(
Confidence
98% confidence
Finding
This passes the full process environment to the MCP server, which can expose unrelated secrets such as API keys, tokens, cloud credentials, or proxy settings to a child process. Because the server is an external dependency and may process tool listing requests, even non-sensitive operations could leak high-value host secrets.

Env Variable Harvesting

High
Category
Data Exfiltration
Content
async def describe_tool_from_server(server_config, tool_name: str):
    """Get detailed schema for a specific tool from MCP server."""
    env = {**server_config.get("env", {}), **os.environ}
    _ensure_futu_ready(env)

    server_params = StdioServerParameters(
Confidence
98% confidence
Finding
The same environment-merging pattern is used for tool description requests, unnecessarily exposing all host environment variables to the child MCP server. This broadens the blast radius because a low-risk metadata action still transmits potentially sensitive credentials.

Env Variable Harvesting

High
Category
Data Exfiltration
Content
async def call_tool_on_server(server_config, tool_name: str, arguments: dict):
    """Execute a tool call on MCP server."""
    env = {**server_config.get("env", {}), **os.environ}
    _ensure_futu_ready(env)

    server_params = StdioServerParameters(
Confidence
99% confidence
Finding
This tool-execution path forwards the entire host environment into the MCP server process, enabling harvesting of secrets by that server or any compromised dependency it loads. Given the skill's financial context and external server dependency, this is the most security-relevant issue in the file.

Context-Inappropriate Capability

High
Confidence
98% confidence
Finding
The ability to install software packages on the host is a significant privilege that is unjustified by the skill's described purpose. In practice, this creates both supply-chain exposure and unauthorized system modification, making the skill substantially more dangerous than advertised.

Lp3

Medium
Category
MCP Least Privilege
Confidence
91% confidence
Finding
The skill declares shell, environment-variable, and MCP-driven capabilities but does not define any explicit tool scope such as allowed-tools or permissions. That makes the effective execution surface broader than the manifest communicates, increasing the chance that an agent can run installation commands, inspect local state, or start processes beyond simple stock-data access.

Natural-Language Policy Violations

Medium
Confidence
98% confidence
Finding
The skill instructions and descriptions are written entirely in Chinese, with no indication that the user can choose another language or that the skill is intentionally restricted to a Chinese-speaking context. Under the language/locale policy, a fixed language without opt-in or justification is a natural-language policy concern.

Session Persistence

Medium
Category
Rogue Agent
Content
**启动**:
```bash
# Linux/macOS
nohup ./FutuOpenD > opend.log 2>&1 &

# Windows
FutuOpenD.exe
Confidence
65% 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
90% confidence
Finding
The skill exposes account-list, funds, and positions tools but does not prominently warn that these can reveal highly sensitive financial information. In an agent context, insufficient disclosure can lead to accidental retrieval or oversharing of brokerage data.

Description-Behavior Mismatch

Medium
Confidence
96% confidence
Finding
The manifest frames the skill as stock-market data and account-info access, but the documentation describes an option to enable live trading operations. That scope expansion is security-significant because a user or agent may trust the skill as read-only while it can be configured for order placement in a real brokerage environment.

Description-Behavior Mismatch

Medium
Confidence
95% confidence
Finding
The executor auto-starts a local trading-related service, which exceeds the stated purpose of simply accessing market/account data and expands the skill's operational authority on the host. Hidden service management is dangerous because it changes local system state and could start untrusted binaries if configuration is tampered with.

Context-Inappropriate Capability

Medium
Confidence
91% confidence
Finding
Spawning local processes to start FutuOpenD gives the skill host-execution capability not inherently required for querying stock data. In this context, the skill is connected to financial/account infrastructure, so unexpected process execution increases risk and user surprise.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
try:
        import time
        subprocess.Popen(
            [str(exe)],
            cwd=str(Path(opend_path)),
            stdout=subprocess.DEVNULL,
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Description-Behavior Mismatch

Medium
Confidence
98% confidence
Finding
The executor includes dependency installation behavior that is not implied by a stock-data access skill and results in direct software changes to the host. This is especially risky because it pulls executable code from external repositories at runtime.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
# mcp package
    if not HAS_MCP:
        try:
            subprocess.run([sys.executable, "-m", "pip", "install", "mcp"], check=True, capture_output=True)
            installed.append("mcp")
        except subprocess.CalledProcessError as e:
            print(f"Failed to install mcp: {e}", file=sys.stderr)
Confidence
87% confidence
Finding
This code can install a package from PyPI at runtime on the host system, which introduces supply-chain risk and unauthorized software modification. In a stock-data skill, silently changing the host environment is outside the expected trust boundary and could execute unreviewed code during installation.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
ok = False
        if shutil.which("pipx"):
            try:
                subprocess.run(["pipx", "install", "futu-stock-mcp-server"], check=True, capture_output=True)
                installed.append("futu-stock-mcp-server")
                ok = True
            except subprocess.CalledProcessError as e:
Confidence
89% confidence
Finding
Running pipx install at runtime allows the skill to fetch and install code from an external package source onto the host. That creates a supply-chain and host-modification risk that is not necessary for merely accessing stock data.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
print(f"pipx install failed: {e}", file=sys.stderr)
        if not ok and shutil.which("pip"):
            try:
                subprocess.run([sys.executable, "-m", "pip", "install", "futu-stock-mcp-server"], check=True, capture_output=True)
                installed.append("futu-stock-mcp-server")
                ok = True
            except subprocess.CalledProcessError as e:
Confidence
89% confidence
Finding
This pip-based fallback also installs external code dynamically, exposing the host to package compromise, typo-squatting, or unexpected package updates. Because the skill may run in privileged user contexts, the resulting impact can extend beyond the skill itself.

Description-Behavior Mismatch

Low
Confidence
87% confidence
Finding
The manifest describes access to Futu market data through an MCP server, but the file documents that the executor may install packages (`pipx install`, `pip install`) and auto-start OpenD when unavailable. These host-modifying setup behaviors go beyond a plain data-access description and should be disclosed if they are part of runtime behavior.

Rp1

Low
Category
MCP Rug Pull
Confidence
84% confidence
Finding
The documentation instructs the agent to install the `mcp` package without pinning a version, which introduces supply-chain and reproducibility risk. A future compromised or incompatible release could be fetched implicitly during skill use.

Rp1

Low
Category
MCP Rug Pull
Confidence
87% confidence
Finding
The skill recommends `pip install futu-stock-mcp-server` without version pinning, so execution may pull whatever package version is current at runtime. For an MCP server that can access account and possibly trading functionality, this expands supply-chain exposure.

Rp1

Low
Category
MCP Rug Pull
Confidence
84% confidence
Finding
Again instructing `pip install mcp` without a pinned version leaves the runtime dependent on unreviewed future releases. Repetition of unpinned install instructions increases the likelihood that users follow the unsafe path.

Rp1

Low
Category
MCP Rug Pull
Confidence
86% confidence
Finding
This later installation example repeats unpinned installation of the MCP server, preserving the same supply-chain risk. Because this skill interfaces with financial systems, even low-complexity package substitution could have outsized consequences.

Rp1

Low
Category
MCP Rug Pull
Confidence
84% confidence
Finding
The FAQ again suggests installing `mcp` without a version constraint, creating avoidable dependency drift and package-trust risk. This is especially relevant where an agent may execute the documented commands automatically.

Rp1

Low
Category
MCP Rug Pull
Confidence
81% confidence
Finding
The unpinned recommendation to install mcp allows different versions to be retrieved over time, reducing reproducibility and increasing exposure to malicious or breaking upstream releases. While the string itself is not executable code, it encourages insecure dependency management for software this executor depends on.

Natural-Language Policy Violations

Low
Confidence
92% confidence
Finding
This code file contains natural-language comments and later user-facing output in Chinese such as "缺失", "监听中", and "启动 OpenD 或设置 OPEND_PATH" without offering a language choice. The policy requires flagging language or locale constraints when the skill forces a specific language without opt-in or clear regional justification.

Static analysis

No suspicious patterns detected.