Back to skill

Security audit

Mx Selfselect

Security checks for vulnerabilities and agentic risk

Overview

The skill appears purpose-aligned, but it can change an authenticated financial watchlist from free-form input and handles account data with limited safeguards.

Review before installing. Use it only if you trust the Eastmoney API key workflow, keep MX_APIKEY out of shared project files, use a private output directory, and be careful with natural-language commands because non-query text may modify your watchlist without a separate confirmation step.

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)

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/mx_self_select.py:144
Finding
Financial account data is written with ambient filesystem permissions<![CDATA[ ## Vulnerability Details **File Location**: `scripts/mx_self_select.py`, lines 144–164 and 187–190 **Vulnerability Type**: Sensitive data stored with non-restrictive default permissions **Risk Level**: Medium ### Vulnerable Code ```python with open(csv_path, "w", newline="", encoding="utf-8-sig") as f: writer = csv.DictWriter(f, fieldnames=fieldnames) writer.writeheader() for row in csv_rows: writer.writerow(row) # Save raw JSON json_path = output_dir / f"mx_self_select_{safe_filename(safe_name)}_raw.json" with open(json_path, "w", encoding="utf-8") as f: json.dump(result, f, ensure_ascii=False, indent=2) print(f"\n✅ CSV 已保存: {csv_path}") print(f"📄 原始JSON: {json_path}") ``` The destination directory is also created without an explicit restrictive mode: ```python default_output = Path("/root/.openclaw/workspace/mx_data/output") output_dir = Path(args.output_dir) if args.output_dir else default_output output_dir.mkdir(parents=True, exist_ok=True) ``` ### Technical Analysis The Skill stores a user's financial watchlist in both CSV and raw JSON files. Neither the output directory nor the files are created with explicit owner-only permissions. Their effective permissions therefore depend on the process umask. Under a common umask of `0022`, newly created directories can be mode `0755` and files can be mode `0644`. Consequently, other local accounts may be able to read the watchlist and any additional fields included in the complete API response. The use of ordinary `open(..., "w")` also follows symbolic links and truncates existing targets. If an untrusted local user can modify the selected output directory, that user could potentially redirect a write to another file writable by the Skill process. ### Attack Path 1. A user runs the Skill and authenticates with `MX_APIKEY`. 2. The Skill retrieves account-specific watchlist data from the Eastmoney API. 3. It creates the output directory and writes CSV and raw JSON files us ...[truncated 1078 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Create the output directory with owner-only permissions and verify its final mode: ```python output_dir.mkdir(parents=True, exist_ok=True, mode=0o700) os.chmod(output_dir, 0o700) ``` - Create output files atomically with mode `0600`, such as through `os.open` using `O_CREAT | O_EXCL | O_WRONLY` and `0o600`. - Reject symbolic links and verify that the resolved destination remains inside the intended output directory. - Use a temporary file in the same protected directory, flush and synchronize it, and then atomically rename it into place. - Consider making raw JSON retention opt-in because it may contain more information than the formatted CSV. - Validate user-supplied `--output-dir` paths and document that the destination must not be shared or writable by untrusted users. ]]>

T08 · Insecure Dependencies

Note
Location
scripts/requirements.txt:1
Finding
Third-party dependency is not reproducibly pinned<![CDATA[ ## Vulnerability Details **File Location**: `scripts/requirements.txt`, line 1 **Vulnerability Type**: Unbounded dependency resolution without integrity verification **Risk Level**: Low ### Vulnerable Code ```text requests>=2.31.0 ``` ### Technical Analysis The requirement accepts version `2.31.0` or any later release of `requests`. It does not impose an upper bound, pin an audited exact version, or provide an artifact hash. As a result, installations performed at different times can resolve to different code. If the configured package index, dependency-resolution environment, or a future accepted release is compromised, unreviewed code can enter the execution environment. This finding does not establish that the current `requests` package is malicious; it identifies the absence of reproducible dependency and integrity controls. ### Attack Path 1. An operator installs dependencies using `pip install -r scripts/requirements.txt`. 2. The package resolver selects the newest compatible release available from the configured index rather than a specifically audited artifact. 3. A compromised index, malicious mirror, or compromised future package release supplies altered code. 4. The altered package is installed and imported by `mx_self_select.py`. 5. Malicious package initialization or runtime behavior executes with the permissions and environment of the user running the Skill. ### Impact Assessment A compromised dependency could execute arbitrary Python code under the Skill process account. That code could access the `MX_APIKEY` environment variable, read files available to that account, alter API requests, or tamper with generated results. The practical likelihood is reduced when installation exclusively uses the official Python Package Index and trusted releases, but the current requirement does not enforce either artifact identity or reproducibility. ]]>
Remediation
<![CDATA[ ## Remediation Suggestions - Pin `requests` and its transitive dependencies to explicitly reviewed versions. - Generate a lock file containing cryptographic hashes for every permitted distribution artifact. - Install with hash enforcement, for example: ```bash python -m pip install --require-hashes -r requirements.lock ``` - Use only a trusted package index and disable unapproved additional indexes or mirrors. - Review and update the lock file through a controlled dependency-update process that includes vulnerability scanning and testing. ]]>
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
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
Findings (13)

Tainted flow: 'headers' from os.environ.get (line 71, credential/environment) → requests.post (network output)

Critical
Category
Data Flow
Content
}
    
    try:
        response = requests.post(QUERY_URL, headers=headers, json={}, timeout=30)
        response.raise_for_status()
        return response.json()
    except Exception as e:
Confidence
90% confidence
Finding
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Tainted flow: 'headers' from os.environ.get (line 71, credential/environment) → requests.post (network output)

Critical
Category
Data Flow
Content
}
    
    try:
        response = requests.post(MANAGE_URL, headers=headers, json=data, timeout=30)
        response.raise_for_status()
        return response.json()
    except Exception as e:
Confidence
90% confidence
Finding
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Credential Access

High
Category
Privilege Escalation
Content
apikey = os.environ.get("MX_APIKEY", "")
    if not apikey:
        # 尝试从.env文件读取
        env_file = os.path.join(os.path.dirname(os.path.dirname(__file__)), ".env")
        if os.path.exists(env_file):
            try:
                with open(env_file, "r") as f:
Confidence
81% confidence
Finding
Falling back to a repo-adjacent .env file for API credentials increases the chance that long-lived secrets are stored insecurely on disk, accidentally committed, or exposed to other local users/processes. In this skill, those credentials authorize account-backed watchlist actions, so compromise could let an attacker query or modify the user's self-selected stocks.

Lp3

Medium
Category
MCP Least Privilege
Confidence
94% confidence
Finding
The skill documents access to environment variables, filesystem output, and external network endpoints, but it does not declare any explicit tool scope or permissions. This creates an authorization gap where the runtime may grant broader capabilities than reviewers or users expect, increasing the chance of unintended data access, credential use, or exfiltration through network/file operations.

Context-Inappropriate Capability

Medium
Confidence
86% confidence
Finding
The manifest describes a stock watchlist management skill that supports querying, adding, and deleting favorites. While calling backend APIs is expected, proactively reading secrets from environment variables and traversing to a local .env file introduces credential-access behavior beyond the user-facing purpose described in the manifest.

External Transmission

Medium
Category
Data Exfiltration
Content
}
    
    try:
        response = requests.post(QUERY_URL, headers=headers, json={}, timeout=30)
        response.raise_for_status()
        return response.json()
    except Exception as e:
Confidence
80% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
}
    
    try:
        response = requests.post(MANAGE_URL, headers=headers, json=data, timeout=30)
        response.raise_for_status()
        return response.json()
    except Exception as e:
Confidence
80% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Missing User Warnings

Medium
Confidence
93% confidence
Finding
Natural-language input is routed directly into state-changing add/delete operations without a confirmation step, dry-run preview, or stricter intent validation. In an agent setting, ambiguous or adversarial prompts could cause unintended modification of a user's watchlist, which is especially risky because the action affects an authenticated account.

Natural-Language Policy Violations

Low
Confidence
83% confidence
Finding
The natural-language content of the skill file is entirely Chinese, and the document does not indicate that users can choose another language or that the skill is restricted to a Chinese-speaking or region-specific audience. Under the policy rule, forcing a specific language without opt-in can be a locale-policy issue.

Intent-Code Divergence

Low
Confidence
95% confidence
Finding
The skill inconsistently refers to the API key as both MX_APIKEY and EASTMONEY_APIKEY. This mismatch can cause operators to place secrets in the wrong variable or break expected secret-handling controls, leading to failed authentication, accidental disclosure during debugging, or insecure ad hoc workarounds.

Natural-Language Policy Violations

Low
Confidence
82% confidence
Finding
The module docstring presents the skill's user-facing description entirely in Chinese, which may impose a language choice without offering localization or opt-in. Under the stated policy, forcing a specific language can be a natural-language policy violation unless the locale constraint is documented and justified.

Unpinned Dependencies

Low
Category
Supply Chain
Content
requests>=2.31.0
Confidence
94% confidence
Finding
The dependency is specified as `requests>=2.31.0`, which allows any future version to be installed and prevents reproducible builds. This creates supply-chain risk because a later vulnerable or breaking release could be pulled in without review, and in a skill that may handle account and market data, dependency behavior matters for confidentiality and integrity.

Unverifiable Dependency: requests has 16 known advisory(ies) (CVE-2014-1830 (Exposure of Sensitive Information to an Unauthorized Actor in Requests); CVE-2024-47081 (Requests vulnerable to .netrc credentials leak via malicious URLs); CVE-2024-35195 (Requests `Session` object does not verify requests after making first request wi) +13 more), but the manifest does not pin a version, so it is unknown whether the installed release is affected

Low
Category
Supply Chain
Confidence
88% confidence
Finding
The manifest references `requests` without pinning an exact version, while known advisories exist for some releases of that package. Because the installed version is not fixed, it is impossible to verify from this file alone whether deployment will use a patched or vulnerable release, leaving a supply-chain exposure window.

Static analysis

No suspicious patterns detected.