Back to skill

Security audit

Polymarket 48h Weather Distribution Trader

Security checks for vulnerabilities and agentic risk

Overview

This is a disclosed trading skill, but its live/paper trading boundary and safeguards have concrete implementation problems that could lead to unintended real-money trades.

Install only if you are comfortable with an autonomous trading script using a SIMMER_API_KEY. Keep it in paper mode unless you have reviewed and fixed the live/paper client caching issue, fail-closed safeguard behavior, volume enforcement, and dependency pinning; use a limited trading credential and small position limits.

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:50
Finding

Cached Client Can Retain Live-Trading Authority During Paper-Mode Runs

Content
View full analysis
SimmerClient: global _client, MAX_POSITION, MIN_VOLUME, MAX_SPREAD, MIN_DAYS, MAX_POSITIONS global YES_THRESHOLD, NO_THRESHOLD, MIN_TRADE, SUM_TOLERANCE if _client is None: venue = "polymarket" if live else "sim" _client = SimmerClient( api_key=os.environ["SIMMER_API_KEY"], venue=venue, ) if live: _client.live = True try: _client.apply_skill_config(SKILL_SLUG) except AttributeError: pass # apply_skill_config only available in Simmer runtime MAX_POSITION = float(os.environ.get("SIMMER_MAX_POSITION", str(MAX_POSITION))) MIN_VOLUME = float(os.environ.get("SIMMER_MIN_VOLUME", str(MIN_VOLUME))) MAX_SPREAD = float(os.environ.get("SIMMER_MAX_SPREAD", str(MAX_SPREAD))) MIN_DAYS = int(os.environ.get( "SIMMER_MIN_DAYS", str(MIN_DAYS))) MAX_POSITIONS = int(os.environ.get( "SIMMER_MAX_POSITIONS", str(MAX_POSITIONS))) YES_THRESHOLD = float(os.environ.get("SIMMER_YES_THRESHOLD", str(YES_THRESHOLD))) NO_THRESHOLD = float(os.environ.get("SIMMER_NO_THRESHOLD", str(NO_THRESHOLD))) MIN_TRADE = float(os.environ.get("SIMMER_MIN_TRADE", str(MIN_TRADE))) SUM_TOLERANCE = float(os.environ.get("SIMMER_SUM_TOLERANCE", str(SUM_TOLERANCE))) return _client ``` ### Technical Analysis The module stores a single `SimmerClient` in the global `_client` variable. The client venue and live-trading state are selected only when `_client` is `None`. Subsequent calls do not verify that the cached client's venue matches the newly requested `live` argument. Con ...[truncated 1454 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
trader.py:389
Finding

Trading Safeguards Fail Open When Market Context Cannot Be Validated

Content
View full analysis
tuple[bool, str]: """Check flip-flop and slippage safeguards.""" try: ctx = client.get_market_context(market_id) if not ctx: return True, "no context" if ctx.get("discipline", {}).get("is_flip_flop"): reason = ctx["discipline"].get("flip_flop_reason", "recent reversal") return False, f"Flip-flop: {reason}" slip = ctx.get("slippage", {}) if isinstance(slip, dict) and slip.get("slippage_pct", 0) > 0.15: return False, f"Slippage {slip['slippage_pct']:.1%}" for w in ctx.get("warnings", []): print(f" [warn] {w}") except Exception as e: print(f" [ctx] {market_id}: {e}") return True, "ok" ``` ### Technical Analysis The function permits trading when `get_market_context()` returns no context. It also catches every exception and returns `True`. Therefore, the flip-flop and slippage checks are bypassed whenever the context API is unavailable, produces an unexpected schema, encounters an authentication problem, or otherwise raises an error. Security and financial controls should fail closed when their status cannot be established, particularly for live trading. Logging the error does not prevent the subsequent order. ### Attack Path 1. A market passes the probability, spread, and date checks. 2. Context retrieval fails because of a network error, SDK error, malformed response, authentication issue, or unexpected field type. 3. The broad `except Exception` handler records the failure but does not reject the market. 4. `context_ok()` returns `(True, "ok")`. 5. `run()` proceeds to `client.trade()`. 6. A live order executes without a verified slippage or reversal asses ...[truncated 514 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
trader.py:335
Finding

Configured Minimum-Volume Safeguard Is Not 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 risk parameter and documented as a minimum market-volume filter. Although its value is loaded into `MIN_VOLUME`, no execution path compares it with a market volume field. As a result, changing this tunable has no effect. A low-liquidity market can pass `valid_market()` as long as its probability, reported spread, and resolution date satisfy the checks. This creates a false assurance that liquidity risk is bounded. ### Attack Path 1. A low-volume weather market is returned by `find_markets()`. 2. Its pricing contributes to a detected distribution or monotonicity violation. 3. The market satisfies the probability threshold and does not report a prohibited spread. 4. `valid_market()` never reads o ...[truncated 680 chars]
Remediation
View remediation

T08 · Insecure Dependencies

Warning
Location
clawhub.json:7
Finding

Security-Sensitive Trading SDK Dependency Is Unpinned

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
91% confidence
Finding

The skill advertises trading behavior and explicitly requires a high-value credential (SIMMER_API_KEY), yet it does not declare any explicit tool scope or permissions boundary. That mismatch weakens least-privilege controls and can allow broader environment or execution access than reviewers and operators expect, which is risky in a skill capable of placing trades.

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 Temperature Markets

In traditional markets, discrete outcome probabilities must sum to 1.0 — this is a fundamental axiom. On Polymarket, each temperature 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.