Back to skill

Security audit

Polymarket Sports Live Trader

Security checks for vulnerabilities and agentic risk

Overview

This trading skill is paper-by-default, but its live mode can place real-money orders while relying on documented risk controls that are mismatched or not enforced.

Review this before installing or enabling live mode. It is not showing hidden exfiltration or automatic startup, but it can trade with real funds and the documented safeguards do not fully match the code. Use paper mode unless the volume filter, default values, final position-size cap, date parsing behavior, open-position accounting, and dependency pinning are corrected 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 (3)

T09 · Insecure Skill Coding Practices

Error
Location
trader.py:27
Finding
Declared market and portfolio risk controls are missing or weaker at runtime<![CDATA[ ## Vulnerability Details **File Location**: `trader.py:27-35, 183-228, 251-280`; related documentation in `SKILL.md:89-95` **Vulnerability Type**: Missing and inconsistently implemented financial risk controls **Risk Level**: High ### Complete Code Snippet ```python MAX_POSITION = float(os.environ.get("SIMMER_MAX_POSITION", "25")) MIN_VOLUME = float(os.environ.get("SIMMER_MIN_VOLUME", "1000")) MAX_SPREAD = float(os.environ.get("SIMMER_MAX_SPREAD", "0.08")) MIN_DAYS = int(os.environ.get( "SIMMER_MIN_DAYS", "0")) MAX_POSITIONS = int(os.environ.get( "SIMMER_MAX_POSITIONS", "8")) YES_THRESHOLD = float(os.environ.get("SIMMER_YES_THRESHOLD", "0.42")) NO_THRESHOLD = float(os.environ.get("SIMMER_NO_THRESHOLD", "0.58")) MIN_TRADE = float(os.environ.get("SIMMER_MIN_TRADE", "5")) ``` ```python def compute_signal(market) -> tuple[str | None, float, str]: p = market.current_probability q = market.question # Spread gate 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%}" # 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 bias = sport_bias(q) if p <= YES_THRESHOLD: conviction = min(1.0, (YES_THRESHOLD - p) / YES_THRESHOLD * bias) size = max(MIN_TRADE, round(conviction * MAX_POSITION, 2)) edge = YES_THRESHOLD - p return "yes", size, f"YES {p:.0%} edge={edge:.0%} bias={bias:.2f}x size=${size} — {q[:65]}" if p >= NO_THRESHOLD: conviction = min(1.0, (p - NO_THRESHOLD) / (1 - NO_THRESHOLD) * bias) size = max(MIN_TRADE, round(convicti ...[truncated 3424 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Enforce volume before signal generation: ```python volume = getattr(market, "volume", None) if volume is None: return None, 0, "Missing market volume" if volume < MIN_VOLUME: return None, 0, f"Volume ${volume:,.2f} below ${MIN_VOLUME:,.2f}" ``` 2. Align the defaults in `trader.py`, `clawhub.json`, and `SKILL.md` so the documented and effective policies are identical. 3. Query existing open positions before trading and calculate remaining capacity from the actual portfolio: ```python open_positions = client.get_positions(status="open") remaining = MAX_POSITIONS - len(open_positions) ``` 4. Count unique open positions rather than merely successful orders in the current run. 5. Fail closed when required market fields or timestamps cannot be parsed. Log the rejected market and reason rather than silently ignoring parsing errors. 6. Validate all SDK market values for type, range, and presence before making a live-trading decision. 7. Add automated tests covering low volume, missing volume, malformed dates, immediate resolution, existing positions, and repeated invocations. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
trader.py:214
Finding
Minimum trade setting can override the maximum position limit<![CDATA[ ## Vulnerability Details **File Location**: `trader.py:214-224`; related tunable ranges in `clawhub.json:15-29, 91-101` **Vulnerability Type**: Financial limit bypass through inconsistent configuration **Risk Level**: High ### Complete Code Snippet ```python if p <= YES_THRESHOLD: # conviction=0 at threshold boundary, conviction=1 at p=0 — scaled by sport bias conviction = min(1.0, (YES_THRESHOLD - p) / YES_THRESHOLD * bias) size = max(MIN_TRADE, round(conviction * MAX_POSITION, 2)) edge = YES_THRESHOLD - p return "yes", size, f"YES {p:.0%} edge={edge:.0%} bias={bias:.2f}x size=${size} — {q[:65]}" if p >= NO_THRESHOLD: conviction = min(1.0, (p - NO_THRESHOLD) / (1 - NO_THRESHOLD) * bias) size = max(MIN_TRADE, round(conviction * MAX_POSITION, 2)) edge = p - NO_THRESHOLD return "no", size, f"NO YES={p:.0%} edge={edge:.0%} bias={bias:.2f}x size=${size} — {q[:65]}" ``` The exposed configuration ranges permit an inconsistent state: ```json { "env": "SIMMER_MAX_POSITION", "default": 25, "range": [ 1, 200 ], "step": 1, "label": "Max position size (USD)" } ``` ```json { "env": "SIMMER_MIN_TRADE", "type": "number", "default": 5, "range": [ 1, 100 ], "step": 1, "label": "Min trade size (USD)" } ``` ### Technical Analysis The order size is calculated with `max(MIN_TRADE, calculated_size)`, but it is never capped again at `MAX_POSITION`. If `MIN_TRADE` is configured above `MAX_POSITION`, the minimum-trade floor becomes the effective order size and directly bypasses the stated maximum. The configuration schema permits `SIMMER_MAX_POSITION=1` and `SIMMER_MIN_TRADE=100`. Under that configuration, any qualifying signal can generate a $100 order despite a nominal $1 maximum. The same issue affects both YES and NO orders. ### Attack Path 1. Through the Simmer UI or environment variables, set `SIMMER_MAX_POSITION` to a value lower than `SIMMER_MIN_TRADE`. 2. Start the trader with `- ...[truncated 849 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Reject inconsistent settings during startup: ```python if MIN_TRADE > MAX_POSITION: raise ValueError("SIMMER_MIN_TRADE must not exceed SIMMER_MAX_POSITION") ``` 2. Apply an explicit final cap to both branches: ```python calculated = round(conviction * MAX_POSITION, 2) size = min(MAX_POSITION, max(MIN_TRADE, calculated)) ``` 3. Constrain the configuration UI so the minimum trade cannot be set above the current maximum position. 4. Revalidate configuration after `apply_skill_config()` changes environment variables. 5. Add unit tests for equal limits, inverted limits, boundary probabilities, zero conviction, and the maximum values allowed by the schema. 6. Consider aborting rather than silently correcting invalid financial settings so operators cannot unknowingly run with a policy different from the one they configured. ]]>

T08 · Insecure Dependencies

Warning
Location
clawhub.json:3
Finding
Security-sensitive trading dependency is not version-pinned<![CDATA[ ## Vulnerability Details **File Location**: `clawhub.json:3-9` **Vulnerability Type**: Unpinned third-party dependency with credential and trade access **Risk Level**: Medium ### Complete Code Snippet ```json "requires": { "env": [ "SIMMER_API_KEY" ], "pip": [ "simmer-sdk" ] } ``` ### Technical Analysis The package requirement specifies only `simmer-sdk`, without an exact version or an integrity hash. Each installation may therefore resolve to a different, unreviewed release. This dependency is security-sensitive because `trader.py` imports `SimmerClient`, passes it `SIMMER_API_KEY`, relies on it for market data, and uses it to submit simulated or live trades. A compromised upstream release, dependency-account takeover, or incompatible behavioral change would execute with the same process privileges and access to the trading credential. The audit found no evidence that the currently intended package is malicious. The issue is the mutable and unauthenticated dependency resolution policy, not confirmed malicious behavior by the dependency. ### Attack Path 1. An upstream package release is compromised, maliciously modified, or introduces an unsafe behavior. 2. A new deployment installs `simmer-sdk` without a version constraint. 3. The package manager resolves the changed release rather than the version originally reviewed. 4. Python imports the package when `trader.py` starts. 5. The package executes within the trader process and receives or can access `SIMMER_API_KEY`. 6. It could misuse the credential, alter market data, change order parameters, or submit unauthorized requests within the authority granted to that key. ### Impact Assessment A compromised dependency would run with the privileges of the skill process. It could read environment variables available to that process, including the trading API key, and influence all SDK-mediated network and trading operations. In live mode, the financial scope may include the account' ...[truncated 175 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Pin the dependency to an exact version that has been reviewed: ```json "pip": [ "simmer-sdk==<audited-version>" ] ``` 2. Use a lockfile or deployment mechanism that records cryptographic hashes for all direct and transitive packages. 3. Install from the official package index over authenticated TLS and disallow unexpected alternate indexes. 4. Review release notes and source changes before upgrading the pinned version. 5. Run dependency vulnerability and provenance checks in CI. 6. Restrict `SIMMER_API_KEY` to the minimum venue permissions and financial limits required. 7. Isolate the process and avoid exposing unrelated credentials in its environment. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
Findings (5)

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding
The description promises league table, injury, and Elo-driven trading, but the actual documented behavior is a simple threshold-and-bias heuristic with no corresponding data ingestion. This mismatch can mislead operators into granting trust, capital, or permissions under false assumptions, which is especially dangerous in a live trading skill where strategy quality directly affects financial loss.

Lp3

Medium
Category
MCP Least Privilege
Confidence
89% confidence
Finding
The skill documents access to a high-value credential (`SIMMER_API_KEY`) and describes trade execution, but it does not declare any explicit tool scope or allowed-tools boundary. In an agent environment, missing permission scoping can let the skill access environment secrets more broadly than users expect, increasing the chance of credential misuse or unintended execution paths.

Intent-Code Divergence

Medium
Confidence
84% confidence
Finding
The skill claims it exploits edges 'without any external API' while elsewhere advertising remix paths that depend on external sports feeds and data providers. This inconsistency can cause users to misunderstand the trust boundary, reproducibility, and operational dependencies of the strategy, leading to unsafe deployment decisions or overconfidence in how signals are derived.

Missing User Warnings

Medium
Confidence
87% confidence
Finding
Although the mode table notes paper versus live trading, the skill lacks a strong, prominent warning that enabling `--live` can execute real-money trades using a privileged API key. In a financial automation context, insufficient warning and friction can increase the risk of accidental live execution, operator misunderstanding, and direct monetary loss.

Missing User Warnings

Medium
Confidence
87% confidence
Finding
This manifest declares a required environment variable named SIMMER_API_KEY, which indicates the skill depends on a sensitive credential. In this file there is no accompanying warning or disclosure about credential handling, privacy implications, or the fact that the skill will access authenticated external services.

Static analysis

No suspicious patterns detected.