Back to skill

Security audit

Polymarket Catastrophe Trader

Security checks for vulnerabilities and agentic risk

Overview

This is a disclosed Polymarket trading bot, but it needs review because some advertised trading safeguards are not actually enforced.

Install only if you understand it can submit real Polymarket trades when run with --live. Keep it in paper mode until the minimum-volume filter and final order-size cap are fixed, pin the simmer-sdk dependency to a reviewed version, and use a limited trading key with only the balance and permissions you are willing to risk.

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

Warning
Location
trader.py:93
Finding
Declared Minimum-Volume Safeguard Is Not Enforced<![CDATA[ ## Vulnerability Details **File Location**: `trader.py:30`, `trader.py:93-102`, and trade flow at `trader.py:387-415` **Vulnerability Type**: Missing risk-control enforcement **Risk Level**: Medium ### Vulnerable Code ```python MIN_VOLUME = float(os.environ.get("SIMMER_MIN_VOLUME", "5000")) ``` ```python def find_markets(client: SimmerClient) -> 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: print(f"[search] {kw!r}: {e}") return unique ``` The resulting markets are subsequently passed to `compute_signal()` and then to `client.trade()` without a volume check. ### Technical Analysis `SIMMER_MIN_VOLUME` is documented as a minimum market-volume filter and is loaded into `MIN_VOLUME`. However, `find_markets()` only filters duplicate market IDs. Neither this function nor the later signal and execution flow compares a market's volume against `MIN_VOLUME`. Consequently, the configured safeguard has no effect. This is particularly relevant to live prediction-market trading because low-volume markets can have unreliable prices, shallow order books, poor execution, and greater susceptibility to price manipulation. The issue also creates a discrepancy between the documented security posture and actual behavior: operators may reasonably believe that markets below the configured volume threshold cannot be traded. ### Attack Path 1. An attacker creates or identifies a low-volume Polymarket market containing one of the monitored catastrophe-related keywords. 2. The market is returned by `client.find_markets()`. 3. `find_markets()` accepts it because it checks only whether the market ID has already been seen. 4. If its displayed probability, spread, and resolu ...[truncated 822 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Retrieve the market's authoritative volume field and reject markets below the threshold before signal computation: ```python def find_markets(client: SimmerClient) -> list: seen, unique = set(), [] for kw in KEYWORDS: try: for market in client.find_markets(query=kw): if market.id in seen: continue volume = getattr(market, "volume", None) if volume is None: print(f" [skip] {market.id}: volume unavailable") continue try: volume = float(volume) except (TypeError, ValueError): print(f" [skip] {market.id}: invalid volume") continue if volume < MIN_VOLUME: continue seen.add(market.id) unique.append(market) except Exception as e: print(f"[search] {kw!r}: {e}") return unique ``` 2. Fail closed when volume is missing, malformed, negative, stale, or supplied in an unexpected unit. 3. Confirm the exact volume attribute and units exposed by the pinned SDK version. 4. Repeat the check immediately before live execution if market metadata can change between discovery and trading. 5. Add tests for volume below, equal to, and above `MIN_VOLUME`, as well as missing and malformed values. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
trader.py:326
Finding
Minimum Trade Floor Can Exceed the Maximum Position Limit<![CDATA[ ## Vulnerability Details **File Location**: `trader.py:29`, `trader.py:39`, and `trader.py:326-338` **Vulnerability Type**: Incorrect financial limit enforcement **Risk Level**: Medium ### Vulnerable Code ```python MAX_POSITION = float(os.environ.get("SIMMER_MAX_POSITION", "25")) MIN_TRADE = float(os.environ.get("SIMMER_MIN_TRADE", "5")) ``` ```python 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(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]}" ``` ### Technical Analysis The calculated conviction component cannot exceed `MAX_POSITION`, but the final expression applies only a lower bound: ```python max(MIN_TRADE, calculated_size) ``` It does not apply an upper bound after introducing `MIN_TRADE`. If `SIMMER_MIN_TRADE` is configured above `SIMMER_MAX_POSITION`, the resulting order amount will equal `MIN_TRADE` and therefore exceed the documented maximum position size. The implementation contradicts the documentation in `SKILL.md`, which states that sizing is capped at `MAX_POSITION`. No startup validation requires `MIN_TRADE` to be less than or equal to `MAX_POSITION`. ### Attack Path 1. An unsafe deployment configuration, environment injection, or managed configuration update sets `SIMMER_MIN_TRADE` above `SIMMER_MAX_POSITION`. 2. The application accepts both values without checking their relationship. 3. A discovered market crosses either the YES or NO signal threshold. 4. `compute_signal()` evaluates `max(MIN_TRADE, conviction * MAX_POSITION)`. 5. The returned amount exceeds `MA ...[truncated 648 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Validate all financial configuration values immediately after loading them: ```python if MAX_POSITION <= 0: raise ValueError("SIMMER_MAX_POSITION must be positive") if MIN_TRADE <= 0: raise ValueError("SIMMER_MIN_TRADE must be positive") if MIN_TRADE > MAX_POSITION: raise ValueError( "SIMMER_MIN_TRADE must not exceed SIMMER_MAX_POSITION" ) ``` 2. Apply both lower and upper bounds to every calculated order: ```python calculated = round(conviction * MAX_POSITION, 2) size = min(MAX_POSITION, max(MIN_TRADE, calculated)) ``` 3. Validate configuration again after `apply_skill_config()` because that call may update environment-backed values. 4. Add a final independent amount check immediately before `client.trade()`. 5. Add unit tests covering equal limits, an oversized minimum, negative values, non-finite numbers, and ordinary threshold-boundary calculations. ]]>

T08 · Insecure Dependencies

Warning
Location
clawhub.json:3
Finding
Security-Sensitive Trading SDK Is Not Version-Pinned<![CDATA[ ## Vulnerability Details **File Location**: `clawhub.json:3-9` **Vulnerability Type**: Mutable third-party dependency **Risk Level**: Medium ### Vulnerable Code ```json "requires": { "env": [ "SIMMER_API_KEY" ], "pip": [ "simmer-sdk" ] } ``` ### Technical Analysis The project declares `simmer-sdk` without an exact version or package hash. Dependency resolution can therefore install a future release that was not part of this audit. This dependency is security-sensitive: `trader.py` imports `SimmerClient`, supplies it with `SIMMER_API_KEY`, invokes market APIs, and relies on it to submit simulated or real trades. A compromised or unsafe future package release would execute within the skill process and could inherit access to the environment and trading credential. No evidence was found that the currently referenced package is malicious. The finding concerns the absence of reproducible, integrity-verified dependency resolution. ### Attack Path 1. The upstream package publishing account or release process is compromised, or a future release introduces malicious or unsafe behavior. 2. A deployment installs dependencies from the unversioned `simmer-sdk` requirement. 3. The package resolver selects the compromised or incompatible latest release. 4. `trader.py` imports and executes that release as normal application code. 5. The dependency receives `SIMMER_API_KEY` through the `SimmerClient` constructor and participates in all market and trade operations. 6. Malicious dependency code could disclose the credential, alter order parameters, redirect network activity, or submit unauthorized trades within the credential's permissions. ### Impact Assessment Code imported from a Python dependency generally runs with the same operating-system identity and process privileges as the skill. Depending on the runtime sandbox and API-key permissions, a compromised dependency could potentially: - Read environment variables available to the process ...[truncated 396 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Pin `simmer-sdk` to an exact, reviewed release rather than using an unconstrained package name. 2. Use a lock file with pinned transitive dependencies and cryptographic hashes where the deployment platform supports it. 3. Install dependencies with hash verification, such as a generated requirements file used with `pip --require-hashes`. 4. Review package provenance, maintainers, release signatures, and source changes before updating the pin. 5. Perform dependency scanning and scheduled review rather than automatically resolving the newest release. 6. Run the skill with least privilege, limit the API key to only necessary trading operations, restrict outbound network access where practical, and isolate the process from unrelated secrets and files. ]]>
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)

Lp3

Medium
Category
MCP Least Privilege
Confidence
82% confidence
Finding
The skill references use of an environment variable credential (`SIMMER_API_KEY`) and therefore has code-capable access to sensitive env data, but it does not declare an explicit tool scope such as `permissions` or `allowed-tools`. That mismatch weakens least-privilege controls and can let the runtime expose broader capabilities than the skill actually needs, which is especially relevant here because the credential grants trading authority for simulated or live financial actions.

Static analysis

No suspicious patterns detected.