Back to skill

Security audit

庄家异动探测器

Security checks for vulnerabilities and agentic risk

Overview

The skill is mostly coherent as a paid Polymarket market-mover API, but it ships a hardcoded payment-service bearer key and has weak payment authorization controls.

Review before installing. The skill does what it claims at a high level, but the publisher should remove and rotate the embedded SkillPay key, restrict the payment API host, upgrade vulnerable dependencies, and bind/consume paid charge IDs server-side before this is used with real payments.

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 Bearer Credential Exposed and Transmitted<![CDATA[ ## Vulnerability Details **File Location**: `main.py:10`, with network transmission at `main.py:200-235` **Vulnerability Type**: Hard-coded secret and insecure credential management **Risk Level**: High ### Vulnerable Code ```python SKILLPAY_API_KEY = os.getenv("SKILLPAY_API_KEY", "sk_8b36c2ca9e774eb0243752f907b086e78c8af866a4088d3e3475113ed446b71") ``` The embedded credential is subsequently placed in authorization headers and transmitted to the configured SkillPay API: ```python def create_skillpay_charge(amount: str, currency: str) -> 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) if r.status_code != 200: raise HTTPException(status_code=502, detail="SkillPay status q ...[truncated 2356 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Revoke and rotate the exposed SkillPay credential immediately. 2. Remove the hard-coded fallback and require secret injection: ```python SKILLPAY_API_KEY = os.getenv("SKILLPAY_API_KEY") if not SKILLPAY_API_KEY: raise RuntimeError("SKILLPAY_API_KEY must be configured securely") ``` 3. Store the replacement credential in a deployment secret manager rather than source control, package files, images, logs, or general configuration. 4. Use a dedicated, minimally scoped API credential limited to the required charge-creation and status-query operations. 5. Restrict or validate `SKILLPAY_API_BASE` against an explicit HTTPS host allowlist before attaching authorization headers. 6. Review repository history, build artifacts, deployment images, and SkillPay audit logs for exposure or unauthorized use. 7. Add automated secret scanning to development and release pipelines and block publication when credential-like values are detected. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
main.py:243
Finding
Caller-Supplied Paid Charge IDs Can Be Replayed or Substituted<![CDATA[ ## Vulnerability Details **File Location**: `main.py:243-264` **Vulnerability Type**: Insufficient payment authorization and replay protection **Risk Level**: Medium ### Vulnerable Code ```python @app.post("/invoke", response_model=MoversResponse) def invoke(req: InvokeRequest) -> 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") ``` ### Technical Analysis The application treats any caller-supplied `charge_id` with a successful status as sufficient authorization to access the paid result. It does not maintain a server-side association between the charge and the invocation for which it was created. After retrieving the status, the application does not verify: - That this application created the charge. - That the charge belongs to the expected merchant or requester. - That the amount is exactly `0.01`. - That the currency is `USDT`. - That the charge metadata identifies the intended Skill operation. - That the charge has not already been consumed. - That the charge is bound to a specific user, session, or one-time entitlement. Because successful charge ...[truncated 1620 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Record every created charge in server-side persistent storage with: - Charge ID. - Expected amount and currency. - Skill or product identifier. - Requester or entitlement identity. - Creation and expiration timestamps. - Consumption status. 2. Before releasing paid data, retrieve and validate the full charge object rather than only its status. 3. Require exact matches for merchant identity, amount (`0.01`), currency (`USDT`), product metadata, and requester binding. 4. Mark the charge as consumed using an atomic database transaction before returning the paid result. Reject previously consumed charge IDs. 5. Use unguessable server-generated correlation values in charge metadata and verify them during confirmation. 6. Prefer signed provider webhooks or callbacks with signature verification, while retaining authenticated server-to-server confirmation as appropriate. 7. Define whether payment grants one invocation or a time-limited entitlement. Enforce that policy explicitly rather than inferring authorization solely from payment status. 8. Apply rate limits and bounded asynchronous payment handling to prevent repeated polling from exhausting application workers. ]]>
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
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
Findings (13)

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

Critical
Category
Data Flow
Content
]
    for url in candidates:
        try:
            r = requests.get(url, timeout=15)
            if r.status_code != 200:
                continue
            payload = r.json()
Confidence
90% confidence
Finding
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

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

Critical
Category
Data Flow
Content
"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()
Confidence
90% confidence
Finding
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

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

Critical
Category
Data Flow
Content
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()
Confidence
90% confidence
Finding
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

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.

Missing User Warnings

Medium
Confidence
91% confidence
Finding
The skill description states that it automatically handles cryptocurrency payment callbacks, but it does not provide any user-facing warning or disclosure about payment-triggered effects, charges, or financial risk. In a crypto-focused skill with a paywall, lack of clear notice can mislead users about when payments occur or what actions may have financial consequences, increasing the chance of unintended charges or abuse of payment flows.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The code contains a hardcoded default value for SKILLPAY_API_KEY that appears to be a live secret format, meaning sensitive credentials may be embedded directly in source. Hardcoded API keys can be extracted by anyone with code access and abused to create charges, query payment data, or impersonate the service to the payment provider.

Context-Inappropriate Capability

Medium
Confidence
84% confidence
Finding
The skill embeds payment processing and gates core functionality behind a charge flow, but the file provides little transparency about billing behavior, refund handling, or trust boundaries. In an agent-skill ecosystem, hidden monetization logic can mislead users or platforms and create abuse or unauthorized charging risk if invoked unexpectedly.

Missing User Warnings

Medium
Confidence
89% confidence
Finding
The code performs billing-related network actions using a bearer credential without any in-band disclosure to the caller before creating or checking charges. In a skill context, that makes the payment flow less transparent and increases the risk of confusing or unexpected billable actions.

External Transmission

Medium
Category
Data Exfiltration
Content
"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()
Confidence
80% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

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
98% confidence
Finding
The dependency is pinned to requests==2.31.0, and the finding cites multiple published advisories affecting that version. Using a known-vulnerable HTTP client can expose the application to credential leakage, TLS/session verification flaws, or unsafe file-handling behavior depending on how the library is used. In a FastAPI-based skill that likely makes outbound network requests, this context makes the issue more relevant rather than theoretical.

Natural-Language Policy Violations

Medium
Confidence
91% confidence
Finding
The manifest description is written only in Chinese, which imposes a specific language/locale without indicating user choice or a region-specific justification. The policy requires either user opt-in or clear documentation when a skill is language-constrained.

Natural-Language Policy Violations

Low
Confidence
93% confidence
Finding
SQP-3 applies to all file types and includes language or locale policy violations. The file content is presented entirely in Chinese and does not indicate that users can choose another language or that the locale limitation is intentional and justified.

Context-Inappropriate Capability

Low
Confidence
74% confidence
Finding
The code retrieves payment service configuration and an API key from environment variables even though no manifest describes a purpose requiring privileged billing credentials. In the absence of declared intent, accessing such credentials is an unjustified capability.

Static analysis

No suspicious patterns detected.