Back to skill

Security audit

A股选股器

Security checks for vulnerabilities and agentic risk

Overview

This stock-screening skill is purpose-aligned, but it saves raw third-party results and CSV output in ways users should review before installing.

Install only if you are comfortable sending screening queries and an MX API key to Eastmoney. Treat generated CSV and raw JSON files as sensitive local records of your screening activity, and be cautious opening CSV files in spreadsheet applications because the script does not neutralize formula-like values from the API response.

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

T09 · Insecure Skill Coding Practices

Warning
Location
mx_xuangu.py:222
Finding

Unneutralized Spreadsheet Formulas in Generated CSV Output

Content
View full analysis

Vulnerability Details

File Location: mx_xuangu.py, lines 222–228
Vulnerability Type: CSV formula injection
Risk Level: Medium

Vulnerable Code

python
fieldnames = list(rows[0].keys())
safe_name = safe_filename(query)
csv_path = output_dir / f"mx_xuangu_{safe_name}.csv"
desc_path = output_dir / f"mx_xuangu_{safe_name}_description.txt"

with open(csv_path, "w", newline="", encoding="utf-8-sig") as f:
    writer = csv.DictWriter(f, fieldnames=fieldnames, extrasaction="ignore")
    writer.writeheader()
    for row in rows:
        writer.writerow(row)

Technical Analysis

The script writes API-provided column names and row values directly to a CSV file. Python's csv module correctly escapes CSV delimiters and quotes, but it does not neutralize spreadsheet formulas.

When a cell begins with a formula marker such as =, +, -, or @—potentially following whitespace—spreadsheet applications may interpret the value as an executable formula rather than plain text. Because the rows and field names are derived from a remote API response, a compromised or attacker-influenced upstream service could place formula payloads in generated output.

Attack Path

  1. An attacker compromises or otherwise gains influence over the stock-screening API response.
  2. The attacker inserts a formula-prefixed string into a returned column name or data value.
  3. datalist_to_rows or the partial-results parser retains that string without spreadsheet-specific neutralization.
  4. The vulnerable code serializes the value unchanged into the generated CSV file.
  5. A user opens the CSV with a spreadsheet application.
  6. The application evaluates the cell as a formula.
  7. Depending on the application, version, security configuration, and payload, the formula may initiate external requests, disclose spreadsheet data, or invoke dangerous application-specific functionality.

Impact Assessment

Successful exploitation requires attacker control or influence ...[truncated 532 chars]

Remediation
View remediation

Remediation Suggestions

Neutralize all spreadsheet-sensitive values before writing headers or rows to CSV:

  1. Convert each value to text before validation.
  2. Detect optional leading whitespace followed by =, +, -, or @.
  3. Prefix matching values with an apostrophe or use another neutralization strategy verified against all supported spreadsheet applications.
  4. Apply the protection to both field names and row values.
  5. Preserve an unmodified representation only in formats that do not execute formulas, such as JSON.
  6. Add regression tests for values such as =1+1, +SUM(A1:A2), -1+2, @SUM(A1:A2), and whitespace-prefixed variants.
  7. Document that generated files contain remotely sourced data and should be treated as untrusted.

For example:

python
FORMULA_PREFIX = re.compile(r"^\s*[=+\-@]")

def neutralize_csv_cell(value: Any) -> str:
    text = "" if value is None else str(value)
    if FORMULA_PREFIX.match(text):
        return "'" + text
    return text

Use the function to sanitize every header and cell immediately before CSV serialization.

Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (6)

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
85% confidence
Finding

The skill invokes a local Python script and explicitly depends on an environment secret (MX_APIKEY), produces output files, and likely performs network access to Eastmoney MX, but it does not declare any tool or permission scope. This creates an authorization gap: a host agent may allow broader execution, file writes, or secret exposure than users or reviewers expect, making misuse or over-privileged execution harder to detect and constrain.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
89% confidence
Finding

The skill sends the user's natural-language query and an API key to an external Eastmoney endpoint. External transmission is inherent to this skill's purpose, but it still creates a real confidentiality boundary: user queries may contain sensitive investment intentions or proprietary screening criteria, and they are disclosed to a third-party service.

Content

Scanner excerpt · mx_xuangu.py (reported line 151)May include surrounding context.

python
"keyword": query
        }
        
        response = requests.post(self.BASE_URL, headers=headers, json=data, timeout=30)
        response.raise_for_status()
        return response.json()

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The skill persists not only derived stock-screening results but also the full raw API response to local disk by default. That expands data exposure beyond what is necessary for the advertised screening function and can leak sensitive query history, account-scoped metadata, or provider-returned content to other local users, backups, or downstream tools.

Content

No source excerpt is available for this finding.

Tainted flow: 'csv_path' from os.getenv (line 222, 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_xuangu.py (reported line 225)May include surrounding context.

python
csv_path = output_dir / f"mx_xuangu_{safe_name}.csv"
        desc_path = output_dir / f"mx_xuangu_{safe_name}_description.txt"
        
        with open(csv_path, "w", newline="", encoding="utf-8-sig") as f:
            writer = csv.DictWriter(f, fieldnames=fieldnames, extrasaction="ignore")
            writer.writeheader()
            for row in rows:

Tainted flow: 'json_path' from os.getenv (line 250, 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_xuangu.py (reported line 251)May include surrounding context.

python
# 保存原始 JSON
        json_path = output_dir / f"mx_xuangu_{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"📄 原始JSON: {json_path}")

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
85% confidence
Finding

The file’s user-facing description and comments present the skill as Chinese-only, and the CLI help/example text also assumes Chinese input. Under the language/locale policy, forcing a specific language without explicit user opt-in can be a natural-language policy issue unless a locale restriction is clearly documented and justified.

Content

No source excerpt is available for this finding.