Back to skill

Security audit

Wangbo Polymarket Copytrading

Security checks for vulnerabilities and agentic risk

Overview

This skill is not clearly malicious, but its live trading mode can repeatedly place real Polymarket orders without the risk controls it advertises.

Review this skill carefully before installing or using it with an authenticated Polymarket CLI. Dry-run mode is safer, but live '--execute' mode can place repeated real-money buy orders and the advertised risk limits are not reliably enforced by the code. Only use live mode with a tightly limited account, independently verified exposure controls, and manual supervision.

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

Error
Location
scripts/auto_copytrade.py:116
Finding
Unbounded Repeated Financial Order Execution<![CDATA[ ## Vulnerability Details **File Location**: `scripts/auto_copytrade.py:116-166, 178-185` **Vulnerability Type**: Missing cumulative exposure controls, order deduplication, and configuration validation **Risk Level**: High ### Relevant Code ```python def process_once(cfg, execute=False): print(f"\n[{datetime.now().isoformat(timespec='seconds')}] scan start") sig = cfg.get("signatureType", "eoa") max_concurrent = int(cfg.get("maxConcurrent", 2)) placed = 0 for r in cfg.get("rules", []): if placed >= max_concurrent: print("- hit maxConcurrent, stop this cycle") break ``` ```python idx = find_outcome_index(market["outcomes"], r["outcome"]) price = market["prices"][idx] token_id = market["token_ids"][idx] max_entry = float(r["maxEntryPrice"]) amount = float(r["amount"]) print(f" market={r['marketSlug']} outcome={r['outcome']} price={price:.3f} threshold<={max_entry:.3f}") if price > max_entry: print(" skip: price above threshold") continue res = place_order(token_id, amount, sig, execute) placed += 1 print(" order:", json.dumps(res, ensure_ascii=False)) ``` ```python while True: try: process_once(cfg, execute=args.execute) except Exception as e: print("scan error:", e) time.sleep(args.interval) ``` ### Technical Analysis The `maxConcurrent` setting does not limit concurrent open positions. It only limits the number of order attempts made during a single invocation of `process_once()`. The local `placed` counter is reset to zero at the beginning of every monitoring cycle. When the script is run with both `--execute` and a positive `--interval`, each qualifying rule can therefore submit another authenticated market order during every cycle. The implementation does not: - Query existing positions or outstanding orders. - Check whether the sam ...[truncated 2541 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. **Enforce actual position limits** - Query open positions and outstanding orders before submitting any order. - Calculate current exposure by market, outcome, and account. - Treat failure to retrieve account state as a blocking error in live mode. 2. **Prevent duplicate execution** - Assign a stable identifier to each rule and trading signal. - Persist submitted order IDs, market IDs, timestamps, and signal state. - Do not submit another order for the same signal unless an explicit re-entry policy permits it. - Use idempotency support from the trading API where available. 3. **Implement hard financial limits** - Enforce a non-configurable or separately trusted maximum order amount. - Add maximum daily spend, maximum per-market exposure, maximum total exposure, and maximum outstanding-order limits. - Compare limits against actual account state rather than an in-memory per-cycle counter. 4. **Validate configuration strictly** - Require `amount` and `maxEntryPrice` to be finite numeric values. - Require `amount > 0`. - Constrain `maxEntryPrice` to the valid market price range. - Require `maxConcurrent` to be a positive integer within a conservative upper bound. - Reject unknown signature types and malformed rules before entering the monitoring loop. 5. **Implement documented loss controls** - Track settled outcomes and consecutive losses. - Stop live trading after the configured daily loss limit or consecutive-loss threshold. - Persist this state so restarting the process cannot bypass the limit. 6. **Strengthen live-mode confirmation** - Display the account, maximum order size, daily budget, and total exposure limit before live execution. - Require explicit confirmation or a dedicated production configuration flag. - Consider requiring a separate confirmation for configurations exceeding conservative defaults. 7. **Improve naming and documentation** - Renam ...[truncated 222 chars]
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (10)

Tp4

High
Category
MCP Tool Poisoning
Confidence
93% confidence
Finding
The skill advertises full copy-trading orchestration, risk controls, and execution handoff, but the described behavior does not clearly demonstrate those safeguards and may only support ranking/scanning. In a trading context, this mismatch is dangerous because users or agents may rely on nonexistent protections and escalate from analysis to live execution under false assumptions.

Missing User Warnings

High
Confidence
97% confidence
Finding
The markdown includes an '--execute' live trading loop but does not prominently warn that it can place live trades or that financial loss may result. In this context, missing warnings are especially dangerous because automated repeated execution every 120 seconds can quickly amplify mistakes, strategy drift, or unauthorized trading behavior.

Lp3

Medium
Category
MCP Least Privilege
Confidence
92% confidence
Finding
The skill invokes local Python scripts and therefore implies shell and file-read capabilities, but it does not declare an explicit tool scope or permissions boundary. That makes the operational surface ambiguous and increases the chance an agent can run code or access files without a clear least-privilege contract or user awareness.

Vague Triggers

Medium
Confidence
94% confidence
Finding
The trigger phrases are broad enough that the skill may activate for loosely related requests about traders, leaderboards, or following strategies. Over-broad activation is risky here because the skill includes pathways toward trading decisions and possible execution, so accidental invocation could produce unsafe financial guidance or initiate higher-risk workflows unnecessarily.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
def run(cmd):
    return subprocess.check_output(cmd, text=True).strip()


def fetch_board(category: str, period: str, limit: int = 100):
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Natural-Language Policy Violations

Medium
Confidence
95% confidence
Finding
The phase names and threshold values are encoded only in Chinese strings (e.g. "巅峰", "强势", "观察", "普通") and are used directly in program logic and printed output. This forces a specific language/locale on all users without opt-in or an alternative, which matches the language/locale policy violation category.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
def fetch_board(category: str, period: str, limit: int = 100):
    url = f"{BASE}?category={category}&timePeriod={period}&orderBy=PNL&limit={limit}&offset=0"
    out = subprocess.check_output(
        ["curl", "-sS", "-L", "--retry", "3", "--retry-delay", "1", "--max-time", "20", url],
        text=True,
    )
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Natural-Language Policy Violations

Medium
Confidence
95% confidence
Finding
The natural-language outputs returned by status_label are fixed to Chinese terms like '巅峰', '强势', '观察', and '普通'. This forces a specific language in user-visible output without opt-in or justification, which matches the language/locale policy-violation category.

Natural-Language Policy Violations

Low
Confidence
79% confidence
Finding
The `minPhase` value uses a Chinese-language label (`巅峰`) in a configuration example, which may impose a language-specific convention on users without any indication of locale choice or opt-in. This can violate language/locale policy expectations when the skill is not clearly documented as region-specific.

Missing User Warnings

Low
Confidence
87% confidence
Finding
This code invokes curl to fetch remote leaderboard data from an external API, which is a network operation covered by the missing-user-warnings rule for code files. There is no confirmation prompt, user-facing log/print, or explanatory comment/docstring near the call to disclose that external data will be requested.

Static analysis

No suspicious patterns detected.