Back to skill

Security audit

庄家异动探测器

Security checks for vulnerabilities and agentic risk

Overview

The skill discloses a small SkillPay charge, but it ships an exposed billing API key and uses under-scoped payment verification that users should review before installing.

Install only if you trust the publisher and billing flow. The embedded SkillPay key should be revoked and moved to a real secret source, SkillPay hosts should be allowlisted, and paid charge IDs should be bound to the correct user, product, amount, currency, and one-time redemption before this is treated as production-safe.

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

T09 · Insecure Skill Coding Practices

Error
Location
main.py:10
Finding

Hard-Coded SkillPay Credential Can Be Exposed or Redirected to an Untrusted Server

Content
View full analysis
Tuple[str, str]: if not SKILLPAY_API_KEY: raise HTTPException(status_code=400, detail="Missing SKILLPAY_API_KEY") url = f"{SKILLPAY_API_BASE.rstrip('/')}/v1/charges" headers = { "Authorization": f"Bearer {SKILLPAY_API_KEY}", "Content-Type": "application/json", } body = { "amount": amount, "currency": currency, "title": "OpenClaw Skill Payment", "description": "Polymarket Movers x3", } r = requests.post(url, json=body, headers=headers, timeout=20) if r.status_code not in (200, 201): raise HTTPException(status_code=502, detail="SkillPay create charge failed") data = r.json() cid = str(data.get("id") or data.get("charge_id") or "") purl = data.get("payment_url") if not purl and cid: purl = f"{SKILLPAY_WEB_BASE.rstrip('/')}/checkout/{cid}" if not cid or not purl: raise HTTPException(status_code=502, detail="Invalid SkillPay response") return cid, purl def get_skillpay_status(charge_id: str) -> str: if not SKILLPAY_API_KEY: raise HTTPException(status_code=400, detail="Missing SKILLPAY_API_KEY") url = f"{SKILLPAY_API_BASE.rstrip('/')}/v1/charges/{charge_id}" headers = {"Authorization": f"Bearer {SKILLPAY_API_KEY}"} r = requests.get(url, headers=headers, timeout=20) ``` ...[truncated 2585 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
main.py:242
Finding

Payment Authorization Can Be Bypassed Through Unbound or Replayed Charge IDs

Content
View full analysis
MoversResponse: if not req.charge_id: cid, purl = create_skillpay_charge(PRICE_AMOUNT, PRICE_CURRENCY) return MoversResponse(requires_payment=True, charge_id=cid, payment_url=purl, status="pending") status = "" for _ in range(10): status = get_skillpay_status(req.charge_id) if status in ["paid", "succeeded", "success", "completed"]: break if status in ["failed", "canceled", "expired"]: return MoversResponse(requires_payment=True, charge_id=req.charge_id, payment_url=None, status=status) time.sleep(3) if status not in ["paid", "succeeded", "success", "completed"]: return MoversResponse(requires_payment=True, charge_id=req.charge_id, payment_url=None, status=status or "pending") markets = fetch_markets() top10 = pick_active_top10(markets) movers = compute_movers(top10) return MoversResponse(requires_payment=False, data=movers, status="ok") ``` The status lookup returns only the status and discards all other charge attributes: ```python def get_skillpay_status(charge_id: str) -> str: if not SKILLPAY_API_KEY: raise HTTPException(status_code=400, detail="Missing SKILLPAY_API_KEY") url = f"{SKILLPAY_API_BASE.rstrip('/')}/v1/charges/{charge_id}" headers = {"Authorization": f"Bearer {SKILLPAY_API_KEY}"} r = requests.get(url, headers=headers, timeout=20) if r.status_code != 200: raise HTTPException(status_code=502, detail="SkillPay status query failed") data = r.json() status = str(data.get("status") or data.get("state") or "").lower() return status ``` ### Technical Analysis The endpoint accepts a caller-supplied `charge ...[truncated 2190 chars]
Remediation
View remediation
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • 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
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (12)

Tainted flow: 'url' from os.getenv (line 233, credential/environment) → requests.get (network output)

Critical
Category
Data Flow
Confidence
90% confidence
Finding

Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Content

Scanner excerpt · main.py (reported line 176)May include surrounding context.

python
]
    for url in candidates:
        try:
            r = requests.get(url, timeout=15)
            if r.status_code != 200:
                continue
            payload = r.json()

Tainted flow: 'url' from os.getenv (line 233, credential/environment) → requests.post (network output)

Critical
Category
Data Flow
Confidence
96% confidence
Finding

The SkillPay API base URL is taken from an environment variable and then used to send authenticated requests containing the bearer token. If that environment variable is changed in deployment, the service can be induced to send its secret API key and payment metadata to an attacker-controlled endpoint, which is effectively SSRF plus credential exfiltration. In a payment-processing context, this is more dangerous because the leaked credential may enable unauthorized billing operations or charge inspection.

Content

Scanner excerpt · main.py (reported line 218)May include surrounding context.

python
"title": "OpenClaw Skill Payment",
        "description": "Polymarket Movers x3",
    }
    r = requests.post(url, json=body, headers=headers, timeout=20)
    if r.status_code not in (200, 201):
        raise HTTPException(status_code=502, detail="SkillPay create charge failed")
    data = r.json()

Tainted flow: 'url' from os.getenv (line 233, credential/environment) → requests.get (network output)

Critical
Category
Data Flow
Confidence
96% confidence
Finding

This status-check path also builds a URL from an environment-controlled base and sends the SkillPay bearer token in the Authorization header. A compromised or misconfigured environment can redirect these requests to an attacker server, leaking credentials and charge identifiers and enabling fraudulent or unauthorized payment workflow manipulation. Because this endpoint is repeatedly polled, it increases the number of opportunities for secret disclosure.

Content

Scanner excerpt · main.py (reported line 235)May include surrounding context.

python
raise HTTPException(status_code=400, detail="Missing SKILLPAY_API_KEY")
    url = f"{SKILLPAY_API_BASE.rstrip('/')}/v1/charges/{charge_id}"
    headers = {"Authorization": f"Bearer {SKILLPAY_API_KEY}"}
    r = requests.get(url, headers=headers, timeout=20)
    if r.status_code != 200:
        raise HTTPException(status_code=502, detail="SkillPay status query failed")
    data = r.json()

Possible Typosquatting: 'uvicorn' resembles popular package 'gunicorn'

High
Category
Supply Chain
Confidence
70% confidence
Finding

Package name closely resembles a popular package, suggesting possible typosquatting. Attackers publish malicious packages with similar names to trick developers into installing them.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The file’s natural-language instructions and descriptions are entirely in Chinese, and there is no indication that users may choose another language or that the skill is intentionally restricted to a Chinese-speaking audience. Under the policy, forcing a specific language without user opt-in is a natural-language policy violation.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
89% confidence
Finding

The skill includes payment-gating and third-party billing logic even though the apparent functional goal is simply returning Polymarket mover data. Embedding commerce flow where it is not clearly justified increases user harm risk, creates opportunities for deceptive monetization, and expands the attack surface through charge creation and payment-status handling. In this skill context, that mismatch makes the behavior more suspicious than it would be in an explicitly commercial billing skill.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
84% confidence
Finding

The code initiates and checks payment-related transactions without any visible in-band disclosure, consent flow, or user warning in the implementation. In a skill context, silently introducing billing operations can mislead users and facilitate unauthorized or unexpected charges, especially when paired with opaque third-party payment handling. The lack of disclosure makes the monetization behavior materially more dangerous.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
90% confidence
Finding

This code transmits payment-related data to an external service, which is expected for billing but still represents a real security-relevant data egress point. In this implementation, the risk is amplified because the destination host is configurable and the request includes authorization material; external transmission in a payment path can expose sensitive operational data if misdirected or logged insecurely.

Content

Scanner excerpt · main.py (reported line 218)May include surrounding context.

python
"title": "OpenClaw Skill Payment",
        "description": "Polymarket Movers x3",
    }
    r = requests.post(url, json=body, headers=headers, timeout=20)
    if r.status_code not in (200, 201):
        raise HTTPException(status_code=502, detail="SkillPay create charge failed")
    data = r.json()

Known Vulnerable Dependency: requests==2.31.0 — 6 advisory(ies): 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); CVE-2026-25645 (Requests has Insecure Temp File Reuse in its extract_zipped_paths() utility func) +3 more

Medium
Category
Supply Chain
Confidence
95% confidence
Finding

The dependency pins requests to 2.31.0, which is identified by the scanner as having multiple published advisories, including credential leakage and TLS verification-related issues. In an agent skill that is likely to make outbound HTTP requests, using a known-vulnerable HTTP client can expose secrets, weaken transport security, or introduce unsafe file-handling behavior depending on code paths used.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The manifest description is written only in Chinese and indicates the skill's behavior in a single fixed language, with no indication that users can choose another language or locale. This can violate language/locale policy when the skill is not documented as region-specific or offering opt-in language selection.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
78% confidence
Finding

The skill hard-codes the payment currency to USDT and amount to 0.01, which imposes a specific payment locale/currency behavior without user opt-in or any documented rationale in the file. This is a natural-language/policy concern because the skill does not offer a currency choice or explain why the restriction is necessary.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Low
Category
Not specified by scanner
Confidence
85% confidence
Finding

The /invoke GET handler reports api_connected using API_KEY is not None, but no API_KEY is defined and the service actually depends on Polymarket and SkillPay connectivity instead. This is not merely incomplete documentation-like metadata in the response; it actively misrepresents readiness/connectivity semantics exposed by the endpoint.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.