Back to skill

Security audit

Polymarket Candle Doji Breakout Trader

Security checks for vulnerabilities and agentic risk

Overview

This automated Polymarket trading skill is clear about its purpose, but it needs Review because important financial safeguards are under-enforced and the trading SDK is unpinned.

Install only if you understand it can place live Polymarket trades when run with --live. Use paper mode first, use a least-privilege or low-balance API key, pin and review simmer-sdk, and do not rely on the documented volume and max-open-position safeguards until they are fixed or independently enforced.

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

T08 · Insecure Dependencies

Warning
Location
clawhub.json:3
Finding

Unpinned Privileged Trading Dependency Creates Supply-Chain Risk

Content
View full analysis
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
trader.py:215
Finding

Configured Minimum-Volume Safeguard Is Never Enforced

Content
View full analysis
MAX_SPREAD: return None, 0, f"Spread {market.spread_cents/100:.1%} > {MAX_SPREAD:.1%}" # Days-to-resolution gate if market.resolves_at: try: resolves = datetime.fromisoformat(market.resolves_at.replace("Z", "+00:00")) days = (resolves - datetime.now(timezone.utc)).days if days < MIN_DAYS: return None, 0, f"Only {days} days to resolve" except Exception: pass ``` Market discovery at `trader.py:281-303` also accepts markets without applying the configured volume threshold: ```python # 1. Keyword search for kw in KEYWORDS: try: for m in client.find_markets(query=kw): if m.id not in seen: seen.add(m.id) unique.append(m) except Exception as e: safe_print(f"[search] {kw!r}: {e}") # 2. Fallback: scan broad market list for interval matches try: for m in client.get_markets(limit=200): mid = getattr(m, "id", None) q = getattr(m, "question", "") if mid and mid not in seen and _INTERVAL_RE.match(q.strip()): seen.add(mid) unique.append(m) except Exception as e: safe_print(f"[fallback] get_markets: {e}") ``` ### Technical Analysis `SIMMER_MIN_VOLUME` is exposed as a configurable risk parameter and documented as a minimum market-volume filter, but `MIN_VOL ...[truncated 1478 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
trader.py:325
Finding

Maximum Open-Position Limit Resets on Every Execution

Content
View full analysis
= MAX_POSITIONS: break side, size, reasoning = compute_signal(m, trend_dir) if not side: safe_print(f" [skip] {reasoning}") continue ok, why = context_ok(client, m.id) if not ok: safe_print(f" [skip] {why}") continue try: r = client.trade( market_id=m.id, side=side, amount=size, source=TRADE_SOURCE, skill_slug=SKILL_SLUG, reasoning=reasoning, ) tag = "(sim)" if r.simulated else "(live)" status = "OK" if r.success else f"FAIL:{r.error}" safe_print(f" [trade] {side.upper()} ${size} {tag} {status} -- {reasoning[:70]}") if r.success: placed += 1 except Exception as e: safe_print(f" [error] {m.id}: {e}") ``` ### Technical Analysis The implementation treats `MAX_POSITIONS` as a per-process order counter rather than a maximum number of concurrently open positions. `placed` always starts at zero and only counts successful orders submitted during the current invocation. The application does not query existing open positions or pending orders before trading. It also does not reserve capacity atomically, so concurrent instances can independently observe full local capacity and each submit up to `MAX_POSITIONS` orders. This behavior contradicts the documented purpose of `SIMMER_MAX_POSITIONS` as a maximum concurrent-open-position safeguard. ### Attack Path 1. The account already has one or more open positions from an earlier execution. 2. The application is executed again while those positions remain open. 3. The local `placed` counter initializes to zero. 4. Existing positions and pending order ...[truncated 889 chars]
Remediation
View remediation
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
Findings (2)

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
90% confidence
Finding

The skill explicitly requires a high-value credential (SIMMER_API_KEY) and describes live trading capability, yet it does not declare any permissions or allowed-tools scope. That mismatch weakens least-privilege controls and can allow an agent runtime to grant broader environment access than is necessary, increasing the risk of secret exposure or unintended trade execution if the skill is misused or composed with other components.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
84% confidence
Finding

The manifest requires an API key for an automated trading skill but provides no user-facing disclosure about how credentials will be used or that the skill can place trades autonomously. In a trading context, this increases the risk of users supplying sensitive credentials without understanding the financial consequences, account access scope, or operational behavior of the bot.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.