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

Content
View full analysis

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

Content
View full analysis

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

Content
View full analysis

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

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

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.

Content

No source excerpt is available for this finding.

Missing User Warnings

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

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

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

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 · mx_moni.py (reported line 33)May include surrounding context.

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

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

Medium
Category
Data Flow
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.

Content

Scanner excerpt · mx_moni.py (reported line 33)May include surrounding context.

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

Description-Behavior Mismatch

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

Content

No source excerpt is available for this finding.

Missing User Warnings

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

Content

No source excerpt is available for this finding.

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

Medium
Category
Data Flow
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.

Content

Scanner excerpt · mx_moni.py (reported line 268)May include surrounding context.

python
# 保存原始结果
    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)
        
        # 输出格式化结果

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

Medium
Category
Data Flow
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.

Content

Scanner excerpt · mx_moni.py (reported line 274)May include surrounding context.

python
# 输出格式化结果
        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)

Natural-Language Policy Violations

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

Content

No source excerpt is available for this finding.