Back to skill

Security audit

A股模拟持仓分析

Security checks for vulnerabilities and agentic risk

Overview

The skill is for simulated trading, but it can directly change account state, publish posts, and mishandle cancellation and API credentials in ways users should review before installing.

Install only if you trust the MX endpoint configuration and are comfortable granting this skill authority over a simulated trading account. Avoid setting MX_API_URL unless it is a trusted HTTPS endpoint, treat saved output files as private financial-simulation records, and require explicit confirmation before any buy, sell, cancel-all, or post action.

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

T09 · Insecure Skill Coding Practices

Error
Location
mx_moni.py:16
Finding
API Credential Disclosure Through an Unvalidated Configurable Endpoint## Vulnerability Details **File Location**: `mx_moni.py`, lines 16 and 24-35 **Vulnerability Type**: Unvalidated credential destination **Risk Level**: High ### Vulnerable Code ```python MX_API_URL = os.environ.get('MX_API_URL', 'https://mkapi2.dfcfs.com/finskillshub') def api_request(endpoint, payload): """Send an API request to the MX server.""" url = f"{MX_API_URL}{endpoint}" cmd = [ 'curl', '-s', '-X', 'POST', url, '-H', f'apikey: {MX_APIKEY}', '-H', 'Content-Type: application/json; charset=UTF-8', '-d', json.dumps(payload) ] try: result = subprocess.run(cmd, capture_output=True, text=True, timeout=30) ``` ### Technical Analysis The destination of authenticated API requests is controlled entirely by the `MX_API_URL` environment variable. The code does not parse or validate its scheme, hostname, port, user-information component, or expected path before attaching the `MX_APIKEY` header. Consequently, a process that can influence the script's environment can redirect requests to an attacker-controlled server. The script will then disclose both the API key and the request payload. It also permits an `http://` URL, under which the credential and portfolio operation data would be transmitted without transport encryption. Passing the arguments to `subprocess.run` as a list prevents shell command injection, but it does not prevent credential exfiltration because `curl` legitimately sends the sensitive header to the configured destination. ### Attack Path 1. An attacker or compromised launcher modifies the environment used to invoke the skill. 2. The attacker sets `MX_API_URL` to an endpoint they control, such as `https://attacker.example/collect`. 3. The user invokes any supported query or simulated-account mutation. 4. `api_request` appends the API endpoint path to the attacker-controlled base URL. 5. `curl` sends the `apikey` header and JSON ...[truncated 859 chars]
Remediation
## Remediation Suggestions - Prefer a fixed production API origin rather than allowing arbitrary runtime overrides. - If configurability is required, parse the URL and enforce an explicit allowlist of approved HTTPS hostnames and ports. - Reject non-HTTPS schemes, embedded user information, fragments, unexpected ports, IP-literal substitutions, and malformed URLs. - Construct request paths relative to a validated origin rather than concatenating untrusted strings. - Ensure credentials are attached only after the final destination has passed validation. - Continue avoiding shell invocation, and configure `curl` not to follow redirects to unapproved origins. - Document development endpoint overrides separately and use distinct, least-privileged development credentials. - Rotate the API key if there is evidence that the script has run with an untrusted endpoint.

T09 · Insecure Skill Coding Practices

Error
Location
mx_moni.py:253
Finding
Malformed Single-Order Cancellation Silently Triggers Bulk Cancellation## Vulnerability Details **File Location**: `mx_moni.py`, lines 253-260 **Vulnerability Type**: Fail-open handling of a destructive operation **Risk Level**: High ### Vulnerable Code ```python elif intent == 'cancel': info = extract_trade_info(query_text, intent) if info['is_all'] or not info['order_id']: # One-click cancellation payload = {'type': 'all'} else: payload = {'type': 'order', 'orderId': info['order_id'], 'stockCode': info['stock_code']} result = api_request('/api/claw/mockTrading/cancel', payload) ``` The relevant order-ID parser only recognizes numeric identifiers containing at least 16 digits: ```python order_id_matches = re.findall(r'(\d{16,})', query_text) order_id = order_id_matches[0] if order_id_matches else None ``` ### Technical Analysis The bulk-cancellation branch combines two materially different conditions: - The user explicitly requested cancellation of all orders. - The parser failed to identify a valid individual order ID. Treating missing or malformed input as authorization for the broader operation violates fail-safe defaults and least-surprise principles. A parse failure should stop the operation, but the code instead escalates it from an incomplete single-order cancellation to cancellation of every open simulated order. This behavior also conflicts with the documented workflow, which requires the script to ask for an exact order ID rather than guess missing parameters. No confirmation is requested before the bulk mutation is submitted. ### Attack Path 1. A user intends to cancel one simulated order. 2. The user omits the order ID, mistypes it, supplies an ID shorter than 16 digits, or uses an unsupported identifier format. 3. `extract_trade_info` returns `order_id = None`. 4. The condition `not info['order_id']` evaluates to true. 5. The script creates `{"type": "all"}` without asking for clarification or confirmation. ...[truncated 688 chars]
Remediation
## Remediation Suggestions - Permit bulk cancellation only when the user explicitly supplies a recognized phrase such as “cancel all.” - Replace the current condition with separate fail-closed handling: ```python if info['is_all']: # Require confirmation before submitting a bulk cancellation. payload = {'type': 'all'} elif not info['order_id']: print("[ERROR] A valid order ID is required for single-order cancellation") sys.exit(1) else: payload = { 'type': 'order', 'orderId': info['order_id'], 'stockCode': info['stock_code'] } ``` - Require an explicit confirmation step for bulk cancellation. - Validate the order ID against the documented API format and reject malformed identifiers. - Validate whether `stockCode` is required for single-order cancellation and stop if it is absent. - Add regression tests for missing, malformed, and short order IDs, ensuring none produce a bulk-cancellation payload. - Add tests proving that only explicit “cancel all” wording can generate `{"type": "all"}`.

T09 · Insecure Skill Coding Practices

Warning
Location
mx_moni.py:19
Finding
Sensitive Portfolio Results Are Written Without Restrictive File Permissions## Vulnerability Details **File Location**: `mx_moni.py`, lines 19-20 and 266-276 **Vulnerability Type**: Insecure local storage of sensitive account data **Risk Level**: Medium ### Vulnerable Code ```python # Default output directory OUTPUT_DIR = MX_OUTPUT_DIR or os.path.join(os.path.expanduser('~'), '.codex', 'skills-output', 'mx_data', 'output') os.makedirs(OUTPUT_DIR, exist_ok=True) ``` ```python # Save raw result if result: output_json = os.path.join(OUTPUT_DIR, f"mx_moni_{safe_query}_{timestamp}.json") with open(output_json, 'w', encoding='utf-8') as f: json.dump(result, f, ensure_ascii=False, indent=2) # Save formatted result output_txt = os.path.join(OUTPUT_DIR, f"mx_moni_{safe_query}_{timestamp}.txt") formatted_text = format_result(intent, result) with open(output_txt, 'w', encoding='utf-8') as f: f.write(formatted_text) ``` ### Technical Analysis API responses are persisted as JSON and text files using ordinary `open(..., 'w')` calls. Their effective permissions depend on the process umask; the code does not enforce owner-only access. On systems with permissive defaults or a shared `MX_OUTPUT_DIR`, other local users or processes may be able to read the generated files. The stored responses can include account identifiers, balances, holdings, profit and loss, order identifiers, order history, and operation results. The output directory itself is also created without an explicit restrictive mode and may be redirected through the environment to a shared location. This issue is a confidentiality weakness rather than direct code execution. Exploitation requires local read access or influence over the selected output location. ### Attack Path 1. The script runs with a permissive umask, or `MX_OUTPUT_DIR` points to a shared or insufficiently protected directory. 2. A user queries holdings, balances, or orders, or submits a simulated-account operation. 3 ...[truncated 1011 chars]
Remediation
## Remediation Suggestions - Create the output directory with owner-only permissions (`0700`) and verify its ownership and mode before use. - Create result files atomically with mode `0600`, for example with `os.open` using `O_CREAT | O_EXCL | O_WRONLY` and an explicit mode. - Reject output directories that are world-writable, unexpectedly owned, or symbolic links when the deployment threat model includes local attackers. - Resolve and validate `MX_OUTPUT_DIR` against an approved private base directory. - Avoid storing complete raw API responses unless they are required; redact account IDs and order identifiers from persisted output where feasible. - Define and enforce a retention policy so historical portfolio data is removed when no longer needed. - Handle pre-existing files safely to prevent unintended overwrite or link-following behavior.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
Findings (11)

Missing User Warnings

High
Confidence
97% confidence
Finding
Buy, sell, and cancel operations are sent directly to the remote API without a confirmation step, dry-run summary, or safety interlock. In an agent setting, natural-language parsing mistakes or prompt confusion can therefore trigger unintended account actions, which is especially risky even in a simulated trading context because it changes portfolio state and can publish misleading performance history.

Lp3

Medium
Category
MCP Least Privilege
Confidence
92% confidence
Finding
The skill invokes a local Python script and relies on environment variables and output files, but it does not declare any explicit tool scope or permission boundaries. That creates an authorization gap: an agent/runtime may allow shell, environment access, and file writes more broadly than intended, increasing the chance of unintended command execution, secret exposure, or filesystem modification if the skill is misrouted or later modified unsafely.

Missing User Warnings

Medium
Confidence
81% confidence
Finding
The code accesses MX_APIKEY from the environment and uses it as an authentication header for outbound requests. There is no nearby warning or documentation in this file explaining that the skill consumes a sensitive credential and sends it to the configured remote endpoint.

Context-Inappropriate Capability

Medium
Confidence
95% confidence
Finding
The skill persistently stores account balances, holdings, orders, and posting results to local files by default. For a trading-related skill, this creates unnecessary at-rest exposure of sensitive financial activity and account metadata, especially on shared hosts or developer workstations.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
'-d', json.dumps(payload)
    ]
    try:
        result = subprocess.run(cmd, capture_output=True, text=True, timeout=30)
        if result.returncode != 0:
            print(f"[ERROR] curl failed: {result.stderr}")
            return None
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Tainted flow: 'cmd' from os.environ.get (line 26, credential/environment) → subprocess.run (code execution)

Medium
Category
Data Flow
Content
'-d', json.dumps(payload)
    ]
    try:
        result = subprocess.run(cmd, capture_output=True, text=True, timeout=30)
        if result.returncode != 0:
            print(f"[ERROR] curl failed: {result.stderr}")
            return None
Confidence
65% confidence
Finding
Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.

Description-Behavior Mismatch

Medium
Confidence
93% confidence
Finding
The skill adds an automatic posting workflow that is outside the manifest's stated scope of acting only when the user explicitly asks for simulated trading operations. This expands behavior into unsolicited remote posting logic and can cause unintended disclosure or actions that the user did not deliberately request in the current interaction.

Missing User Warnings

Medium
Confidence
88% confidence
Finding
The auto-post workflow transmits user-entered text to a remote service after a generic prompt, without a clear disclosure at the point of collection that the content will be published externally. That increases the risk of accidental disclosure of sensitive information, trading details, or private notes.

Tainted flow: 'output_json' from os.environ.get (line 267, credential/environment) → open (file write)

Medium
Category
Data Flow
Content
# 保存原始结果
    if result:
        output_json = os.path.join(OUTPUT_DIR, f"mx_moni_{safe_query}_{timestamp}.json")
        with open(output_json, 'w', encoding='utf-8') as f:
            json.dump(result, f, ensure_ascii=False, indent=2)
        
        # 输出格式化结果
Confidence
65% confidence
Finding
Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.

Tainted flow: 'output_txt' from os.environ.get (line 272, credential/environment) → open (file write)

Medium
Category
Data Flow
Content
# 输出格式化结果
        output_txt = os.path.join(OUTPUT_DIR, f"mx_moni_{safe_query}_{timestamp}.txt")
        formatted_text = format_result(intent, result)
        with open(output_txt, 'w', encoding='utf-8') as f:
            f.write(formatted_text)
        
        print("\n" + formatted_text)
Confidence
65% confidence
Finding
Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.

Natural-Language Policy Violations

Low
Confidence
90% confidence
Finding
The file's docstrings, help text, prompts, examples, and supported trigger phrases are written only in Chinese, which effectively constrains use to a specific language without user opt-in. The policy allows locale constraints when explicitly justified, but no such justification or language-selection option is provided here.

Static analysis

No suspicious patterns detected.