Back to skill

Security audit

Polymarket Geopolitics Weekly Trader

Security checks for vulnerabilities and agentic risk

Overview

This is a disclosed prediction-market trading skill, but some advertised live-trading safeguards are not actually enforced reliably.

Install only if you are comfortable with an automated trading template that can place real Polymarket orders when run with --live. Use paper mode first, use a least-privilege API key if Simmer supports one, keep position sizes low, and do not rely on the documented volume, context, resolution-date, or concurrent-position safeguards until they are fixed and tested.

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

T09 · Insecure Skill Coding Practices

Warning
Location
trader.py:244
Finding

Configured Minimum-Volume Safeguard Is Never Enforced

Content
View full analysis
list: """Find active markets matching strategy keywords, deduplicated.""" seen, unique = set(), [] 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}") return unique ``` ### Technical Analysis The skill defines and exposes `MIN_VOLUME` as a configurable risk parameter. The documentation describes it as the minimum market-volume filter, but neither `find_markets()` nor `compute_signal()` inspects a market's volume. Consequently, every discovered market that satisfies the other signal conditions remains eligible for an order, regardless of liquidity. This is an ineffective security and financial-risk control rather than merely an undocumented feature. ### Attack Path 1. An attacker creates, promotes, or influences a low-volume prediction market. 2. The market question contains a keyword accepted by `KEYWORDS` and `_GEOPOLITICS_RE`. 3. `find_markets()` adds the market without evaluating its volume. 4. The probability, spread, and resolution checks produce a trade signal. 5. In live mode, the skill submits a real order into the illiquid market. 6. The attacker may exploit the shallow market through price manipulation or unfavorable liquidity. ### Impact Assessment The issue does not grant operating-system privileges. Its scope is the trading account authorized by `SIMMER_API_KEY`. Potential consequences include: - Orders in illiquid or easily manipulated markets. - Greater realized slippage than expected ...[truncated 144 chars]
Remediation
View remediation
tuple[bool, str]: try: volume = float(market.volume) except (AttributeError, TypeError, ValueError): return False, "Missing or invalid market volume" if volume < MIN_VOLUME: return False, f"Volume ${volume:,.2f} below ${MIN_VOLUME:,.2f}" return True, "ok" ``` Apply this check before `compute_signal()` or as the first gate inside it. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
trader.py:197
Finding

Market-Context Safety Checks Fail Open

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", []): safe_print(f" [warn] {w}") except Exception as e: safe_print(f" [ctx] {market_id}: {e}") return True, "ok" ``` ### Technical Analysis `context_ok()` is intended to prevent flip-flop trading and reject estimated slippage above 15%. However, it authorizes trading when: - `get_market_context()` returns no context. - The context request times out. - Authentication or authorization fails. - The service returns malformed data. - Any other exception occurs. This is a fail-open design. The control becomes ineffective precisely when the information needed to evaluate risk is unavailable. The broad `except Exception` also hides the distinction between temporary network errors, schema changes, programming defects, and authentication failures. ### Attack Path 1. A market passes signal-generation checks. 2. The context service becomes unavailable, times out, or returns malformed data. 3. Alternatively, an attacker capable of interfering with that service causes context retrieval to fail. 4. `context_ok()` logs the error but returns `(True, "ok")`. 5. The caller proceeds to `client.trade()`. 6. A live or ...[truncated 534 chars]
Remediation
View remediation
tuple[bool, str]: try: ctx = client.get_market_context(market_id) except (TimeoutError, ConnectionError) as exc: if live: return False, f"Context unavailable: {exc}" return True, f"Paper-mode context unavailable: {exc}" if not isinstance(ctx, dict): return (False, "Missing or invalid market context") if live else ( True, "Paper-mode context unavailable", ) # Continue with strict discipline and slippage validation. ``` The caller should invoke it as `context_ok(client, m.id, live=live)`. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
trader.py:163
Finding

Malformed Resolution Dates Bypass the Minimum-Days Gate

Content
View full analysis
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
trader.py:267
Finding

Maximum Concurrent Positions Setting Only Limits Orders Per Invocation

Content
View full analysis
= MAX_POSITIONS: break side, size, reasoning = compute_signal(m) 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 ``` ### Technical Analysis `MAX_POSITIONS` is described as the maximum number of concurrent open positions, but the implementation initializes `placed` to zero on every process invocation. It counts successful orders during that invocation only. The code does not retrieve existing open positions, account for positions created by previous runs, detect duplicate exposure to the same market, or reserve capacity atomically. Repeated or overlapping executions can therefore exceed the configured position limit. ### Attack Path 1. The account already has open positions, or the trader completes one run with up to `MAX_POSITIONS` successful orders. 2. The process is invoked again manually or by automation. 3. `placed` resets to zero. 4. The trader submits up to another `MAX_POSITIONS` successful orders. 5. Repeated or concurrent runs continue accumulating exposure beyond the stated cap. ### Impact Assessment The vulnerability does not grant access beyond the existing trading authority. It undermines account- ...[truncated 300 chars]
Remediation
View remediation

T08 · Insecure Dependencies

Warning
Location
clawhub.json:6
Finding

Trading SDK Dependency Is Not Version-Pinned

Content
View full analysis
Remediation
View remediation
" ] ``` Where supported, additionally require an expected artifact hash: ```text simmer-sdk== \ --hash=sha256: ``` ]]>
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
89% confidence
Finding

The skill declares access to a sensitive environment variable (SIMMER_API_KEY) via requires_env but does not declare any explicit tool scope or permissions boundary. In an agent ecosystem, this creates an authorization ambiguity: a host may expose credentials to the skill without a clear least-privilege contract, increasing the chance of secret misuse or unintended propagation into prompts, logs, or downstream code.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

The manifest requests an API credential (SIMMER_API_KEY) and configures an automated trading entrypoint, but it provides no user-facing disclosure that the skill can place trades or access sensitive account capabilities. In a trading context, undisclosed credential use increases the risk of users granting powerful access without understanding financial exposure, especially because the skill is explicitly designed to trade prediction markets automatically.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.