Back to skill

Security audit

Polymarket Ladder Social Posts Trader

Security checks for vulnerabilities and agentic risk

Overview

This is a disclosed trading bot, but its live mode can place real trades while some documented risk controls are incomplete or unenforced.

Install only if you intend to run an automated trading strategy and can limit the SIMMER_API_KEY and funded account to money you are prepared to risk. Keep it in paper mode unless you have independently verified the market grouping logic, volume/liquidity checks, position exposure limits, and the exact SDK version being installed.

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

T09 · Insecure Skill Coding Practices

Error
Location
trader.py:238
Finding

Incomplete Market Sets Are Treated as Complete Probability Distributions

Content
View full analysis
MIN_VIOLATION: if deviation > 0: # Sum too high -- sell NO on the highest-priced bins target = max(bins_sorted, key=lambda pb: pb.price) lo = target.bin_info["low"] hi = target.bin_info.get("high", "?") opportunities.append(( target.market, "no", abs(deviation), f"Sum={total:.1%} (>105%): overpriced bin {lo}-{hi}=" f"{target.price:.1%} person={target.person} " f"deviation={deviation:+.1%} -- {target.market.question[:55]}" )) else: # Sum too low -- buy YES on the lowest-priced bins target = min(bins_sorted, key=lambda pb: pb.price) ``` ### Technical Analysis The code assumes that any group containing at least two post-count bins is a complete and mutually exclusive probability distribution. It sums all discovered prices and compares the result directly with `1.0`, but it does not establish that: - Every expected market bin was discovered. - The ranges are contiguous and do not contain gaps. - The ranges do not overlap. - Both lower and upper tail outcomes are represented. - Duplicate or semantically incompatible markets were not grouped together. - All bins use identical resolution rules and date boundaries. Market discovery relies on keyword searches and a fallback request limited to 200 markets. A missing market therefore appears as a negative sum deviation even when the complete distribution is correctly priced. ...[truncated 1730 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
trader.py:329
Finding

Configured Minimum-Volume Safeguard Is Never Enforced

Content
View full analysis
tuple[bool, str]: p = getattr(market, "current_probability", None) if not isinstance(p, (int, float)): return False, "missing probability" spread_cents = getattr(market, "spread_cents", None) if isinstance(spread_cents, (int, float)) and spread_cents / 100 > MAX_SPREAD: return False, f"Spread {spread_cents/100:.1%} > {MAX_SPREAD:.1%}" resolves_at = getattr(market, "resolves_at", None) if resolves_at: try: resolves = datetime.fromisoformat(resolves_at.replace("Z", "+00:00")) days = (resolves - datetime.now(timezone.utc)).days if days < MIN_DAYS: return False, f"Only {days} days to resolve" except Exception: pass return True, "ok" ``` ### Technical Analysis `SIMMER_MIN_VOLUME` is declared as a tunable and documented as a minimum market-volume filter, but `valid_market()` only validates probability, spread, and time until resolution. No market volume field is read or compared with `MIN_VOLUME`. Consequently, setting the tunable has no effect. An illiquid market can pass validation as long as its reported probability, spread, and resolution date satisfy the other checks. Low-volume markets are more susceptible to transient prices, manipulation, inadequate order depth, and expensive exits. The validation is also permissive when spread data is absent: only a numeric spread exceeding the limit is rejected. This increases the importance of enforcing the separately documented liquidity control. ### Attack Path 1. An illiquid post-count market is ...[truncated 1101 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
trader.py:475
Finding

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

Content
View full analysis
= MAX_POSITIONS: break market = opp[0] side, size, reasoning = compute_signal(market, opp) if not side: safe_print(f" [skip] {reasoning}") continue ok, why = context_ok(client, market_id) if not ok: safe_print(f" [skip] {why}") continue try: r = client.trade( market_id=market_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} " f"-- {reasoning[:110]}") if r.success: placed += 1 ``` ### Technical Analysis The documentation describes `SIMMER_MAX_POSITIONS` as the maximum number of concurrent open positions. The implementation instead initializes `placed` to zero on every invocation and increments it for successful orders placed during only that process. The code does not: - Query positions already open in the account. - Determine whether an order adds to an existing position. - Include outstanding or partially filled orders. - Coordinate simultaneous skill invocations. - Persist an account-wide position count between runs. Therefore, the parameter limits successful orders per invocation rather than concurrent account positions. Repeated executions can exceed the stated risk boundary. Concurrent executions can also each observe their own zero-valued counter and submit orders independently. ### Attack Path 1. The ac ...[truncated 1049 chars]
Remediation
View remediation

T08 · Insecure Dependencies

Note
Location
clawhub.json:3
Finding

Trading SDK Dependency Is Installed Without Version or Integrity Pinning

Content
View full analysis
Remediation
View remediation
Vulnerability Patterns
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • 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
Findings (2)

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
94% confidence
Finding

The skill explicitly references access to a high-value environment credential (SIMMER_API_KEY) and describes live trading behavior, but it does not declare any permissions or allowed-tools scope to constrain environment access. In an agent ecosystem, missing tool scoping weakens least-privilege controls and can allow the skill broader access than required, increasing the risk of credential misuse or unintended data exposure.

Content

No source excerpt is available for this finding.

Autonomous Decision Making

Medium
Category
Excessive Agency
Confidence
75% confidence
Finding

Skill enables autonomous high-impact decisions without human-in-the-loop verification. Critical operations (destructive commands, financial transactions, data deletion) should require explicit user confirmation.

Content

Scanner excerpt · SKILL.md (reported line 30)May include surrounding context.

md
## The Edge: Distribution Arbitrage for Post-Count Markets

In traditional markets, discrete outcome probabilities must sum to 1.0 -- this is a fundamental axiom. On Polymarket, each post-count range bin trades independently with its own order book and liquidity. Retail treats each bin as an isolated bet without checking the full distribution.

### Violation Type 1: Sum Deviation

Static analysis

No suspicious patterns detected.