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

Content
View full analysis

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
View 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

Content
View full analysis

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
View 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
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · SKILL.md (reported line 314)May include surrounding context.

复制 .env.example 为 .env 并按需修改:

bash
cp .env.example .env

.env 示例内容:

Env Variable Harvesting

High
Category
Data Exfiltration
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.

Content

Scanner excerpt · executor.py (reported line 144)May include surrounding context.

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(

Env Variable Harvesting

High
Category
Data Exfiltration
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.

Content

Scanner excerpt · executor.py (reported line 168)May include surrounding context.

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(

Env Variable Harvesting

High
Category
Data Exfiltration
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.

Content

Scanner excerpt · executor.py (reported line 194)May include surrounding context.

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(

Context-Inappropriate Capability

High
Category
Not specified by scanner
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.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

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.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
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.

Content

No source excerpt is available for this finding.

Session Persistence

Medium
Category
Rogue Agent
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.

Content

Scanner excerpt · SKILL.md (reported line 74)May include surrounding context.

启动:

bash
# Linux/macOS
nohup ./FutuOpenD > opend.log 2>&1 &

# Windows
FutuOpenD.exe

Missing User Warnings

Medium
Category
Not specified by scanner
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.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
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.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
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.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
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.

Content

No source excerpt is available for this finding.

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · executor.py (reported line 84)May include surrounding context.

python
try:
        import time
        subprocess.Popen(
            [str(exe)],
            cwd=str(Path(opend_path)),
            stdout=subprocess.DEVNULL,

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
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.

Content

No source excerpt is available for this finding.

subprocess module call

Medium
Category
Dangerous Code Execution
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.

Content

Scanner excerpt · executor.py (reported line 216)May include surrounding context.

python
# 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)

subprocess module call

Medium
Category
Dangerous Code Execution
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.

Content

Scanner excerpt · executor.py (reported line 227)May include surrounding context.

python
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:

subprocess module call

Medium
Category
Dangerous Code Execution
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.

Content

Scanner excerpt · executor.py (reported line 234)May include surrounding context.

python
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:

Description-Behavior Mismatch

Low
Category
Not specified by scanner
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.

Content

No source excerpt is available for this finding.

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.

Content

No source excerpt is available for this finding.

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.

Content

No source excerpt is available for this finding.

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.

Content

No source excerpt is available for this finding.

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.

Content

No source excerpt is available for this finding.

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.

Content

No source excerpt is available for this finding.

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.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
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.

Content

No source excerpt is available for this finding.