Back to skill

Security audit

Polymarket Twitter Cross Contagion Trader

Security checks for vulnerabilities and agentic risk

Overview

The skill is openly a trading bot and is paper-by-default, but live-trading safeguards are weaker than documented and its trading SDK dependency is unpinned.

Review this skill before installing with any live-capable key. Use only paper mode or a restricted API key with tight account-level limits unless the dependency is pinned and audited and the volume and open-position safeguards 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:7
Finding

Unpinned Third-Party Trading SDK Creates a Supply-Chain Risk

Content
View full analysis
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
trader.py:202
Finding

Configured Minimum Market Volume Safeguard Is Not Enforced

Content
View full analysis
tuple[str | None, float, str]: p = market.current_probability q = market.question if market.spread_cents is not None and market.spread_cents / 100 > MAX_SPREAD: return None, 0, f"Spread {market.spread_cents/100:.1%} > {MAX_SPREAD:.1%}" if market.resolves_at: try: resolves = datetime.fromisoformat(market.resolves_at.replace("Z", "+00:00")) days_left = (resolves - datetime.now(timezone.utc)).days if days_left < MIN_DAYS: return None, 0, f"Only {days_left} days left" except Exception: pass person, bl, bu, period_days = parse_post_market(q) if not person: return None, 0, "Not a post-count bin market" boost = contagion_boosts.get(person['key'], 1.0) if p <= YES_THRESHOLD: conviction = min(1.0, (YES_THRESHOLD - p) / YES_THRESHOLD * boost) size = max(MIN_TRADE, round(conviction * MAX_POSITION, 2)) edge = YES_THRESHOLD - p return "yes", size, ( f"YES {p:.0%} edge={edge:.0%} contagion={boost:.2f}x ${size} " f"-- {person['key']} {bl}-{bu} -- {q[:50]}" ) if p >= NO_THRESHOLD: conviction = min(1.0, (p - NO_THRESHOLD) / (1 - NO_THRESHOLD) * boost) size = max(MIN_TRADE, round(conviction * MAX_POSITION, 2)) edge = p - NO_THRESHOLD return "no", size, ( f" ...[truncated 2100 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
trader.py:267
Finding

Maximum Open Position Limit Only Counts Orders Placed During the Current Run

Content
View full analysis
= MAX_POSITIONS: break side, size, reasoning = compute_signal(m, contagion_boosts) 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}") safe_print(f"[twitter-cross-contagion] done. {placed} orders placed.") ``` ### Technical Analysis The configuration and documentation describe `MAX_POSITIONS` as the maximum number of concurrent open positions. The implementation instead initializes `placed` to zero for every process invocation and increments it only when an order succeeds during that invocation. The code does not query existing open positions, pending orders, or prior exposure. It also does not reserve capacity atomically before placing an order or prevent duplicate exposure to a market. Thus, `MAX_POSITIONS` is only an order-count limit per run, not an open-position limit. If the skill is run repeatedly while earlier positions remain open, each execution can add up to `MAX_POSITIONS` more orders. Concurrent invocations can make the l ...[truncated 1503 chars]
Remediation
View remediation
Vulnerability Patterns
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
Findings (1)

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
91% confidence
Finding

The skill declares access to a sensitive environment credential (SIMMER_API_KEY) but does not define any explicit tool scope such as permissions or allowed-tools. In an agent ecosystem, missing scope boundaries can let the runtime expose more capability than intended and makes it harder to enforce least privilege around credential access and trading-related actions.

Content

No source excerpt is available for this finding.