Back to skill

Security audit

Polymarket Macro Crypto Geopolitics Trader

Security checks for vulnerabilities and agentic risk

Overview

This skill is transparently a trading bot, but several documented financial safeguards are not actually enforced strongly enough for live trading.

Review carefully before live use. Keep it in paper mode until the liquidity, position-count, and sizing controls are fixed; if enabling --live, use a limited, revocable API key and conservative account funding.

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

Warning
Location
trader.py:32
Finding
Declared Minimum-Volume Safeguard Is Not Enforced<![CDATA[ ## Vulnerability Details **File Location**: `trader.py:32`, `trader.py:75-77`, and `trader.py:350-356` **Vulnerability Type**: Missing enforcement of a documented financial-risk control **Risk Level**: Medium ### Vulnerable Code ```python MIN_VOLUME = float(os.environ.get("SIMMER_MIN_VOLUME", "5000")) ``` The setting is refreshed after applying the skill configuration: ```python # Re-read params in case apply_skill_config updated os.environ. MAX_POSITION = float(os.environ.get("SIMMER_MAX_POSITION", str(MAX_POSITION))) MIN_VOLUME = float(os.environ.get("SIMMER_MIN_VOLUME", str(MIN_VOLUME))) ``` However, the order is submitted without checking the market's volume against `MIN_VOLUME`: ```python r = client.trade( market_id=m.id, side=side, amount=size, source=TRADE_SOURCE, skill_slug=SKILL_SLUG, reasoning=reasoning, ) ``` ### Technical Analysis `SIMMER_MIN_VOLUME` is documented as a minimum market-volume filter and is exposed as a configurable risk parameter. The code loads this value but never uses it when discovering markets, computing signals, or submitting orders. Consequently, any market that matches the broad crypto-classification rules may become tradeable regardless of liquidity. The spread check does not provide an equivalent control: a market can temporarily report an acceptable spread while still having insufficient depth or volume for reliable execution. This is a fail-open implementation of a financial safeguard. Missing or unavailable volume information also does not prevent trading. ### Attack Path 1. An attacker creates or influences a low-volume prediction market whose question matches a crypto term such as `crypto`, `Bitcoin price`, or `BTC above`. 2. The market is returned by `get_markets()` or `find_markets()`. 3. A geopolitical/crypto divergence causes `compute_signal()` to produce a trade. 4. The spread and resolution-date checks pass. 5. Because no volume check exists, live mode submits an order ...[truncated 598 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Obtain a trusted market-volume field and enforce the threshold before signal evaluation and again immediately before order submission: ```python volume = getattr(market, "volume", None) if volume is None: return None, 0, "Market volume unavailable" if float(volume) < MIN_VOLUME: return None, 0, f"Volume ${volume:,.2f} below minimum ${MIN_VOLUME:,.2f}" ``` 2. Fail closed when volume is absent, malformed, stale, or cannot be verified. 3. Prefer executable depth or recent-volume metrics over lifetime volume where supported. 4. Revalidate liquidity immediately before live submission to reduce time-of-check/time-of-use risk. 5. Add automated tests proving that markets below the configured threshold cannot reach `client.trade()`. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
trader.py:334
Finding
Maximum Concurrent Position Limit Ignores Existing Open Positions<![CDATA[ ## Vulnerability Details **File Location**: `trader.py:334-338` **Vulnerability Type**: Incomplete position-limit enforcement **Risk Level**: Medium ### Vulnerable Code ```python placed = 0 for m in crypto_markets: if placed >= MAX_POSITIONS: break side, size, reasoning = compute_signal(m, divergence_type) ``` Successful orders only increment the current run's local counter: ```python if r.success: placed += 1 ``` ### Technical Analysis The documented `SIMMER_MAX_POSITIONS` setting is described as the maximum number of concurrent open positions. The implementation instead limits only the number of successful orders placed during one invocation. The skill does not query the account for existing open positions, pending orders, or prior exposure to the same market. Because `placed` is initialized to zero on every run, repeated manual, automated, or scheduled executions can each submit up to `MAX_POSITIONS` additional orders while earlier positions remain open. This also permits repeated exposure to the same market unless the external SDK independently rejects it. No such guarantee is established by the audited code. ### Attack Path 1. The skill is run in live mode and submits up to `MAX_POSITIONS` successful orders. 2. Those positions remain open. 3. The skill is invoked again while the same or another qualifying divergence exists. 4. The local `placed` counter resets to zero. 5. The second invocation can submit up to another `MAX_POSITIONS` orders. 6. Repetition causes total concurrent exposure to exceed the documented limit substantially. An attacker able to trigger repeated execution, or an operator relying on the documented limit, can therefore cause uncontrolled position accumulation. ### Impact Assessment The issue does not grant system-level privileges. It affects the live trading authority available through `SIMMER_API_KEY`. Potential effects include: - Concurrent positions exceeding the configured maximum. - Du ...[truncated 194 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Query current open positions and pending orders before scanning candidate markets. 2. Calculate the remaining allowance rather than resetting exposure to zero: ```python open_positions = client.get_positions(status="open") pending_orders = client.get_orders(status="pending") occupied = count_distinct_exposures(open_positions, pending_orders) remaining = max(0, MAX_POSITIONS - occupied) ``` 3. Stop without trading when `remaining == 0`. 4. Track market IDs already represented by an open position or pending order and reject duplicate exposure unless explicitly permitted. 5. Use an account-wide atomic risk control where the SDK supports one, so concurrent skill processes cannot race past the limit. 6. Recheck the position count immediately before every live order. 7. Add tests covering repeated runs, pre-existing positions, pending orders, duplicate markets, and concurrent invocations. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
trader.py:260
Finding
Minimum Trade Size Can Override the Configured Maximum Position Size<![CDATA[ ## Vulnerability Details **File Location**: `trader.py:260` and `trader.py:270` **Vulnerability Type**: Conflicting risk limits and unsafe trade-size calculation **Risk Level**: Medium ### Vulnerable Code For downside trades: ```python conviction = (p - NO_THRESHOLD) / (1 - NO_THRESHOLD) size = max(MIN_TRADE, round(conviction * MAX_POSITION, 2)) edge = p - NO_THRESHOLD ``` For upside trades: ```python conviction = (YES_THRESHOLD - p) / YES_THRESHOLD size = max(MIN_TRADE, round(conviction * MAX_POSITION, 2)) edge = YES_THRESHOLD - p ``` The configuration permits a maximum position as low as `1` and a minimum trade as high as `100`: ```json { "env": "SIMMER_MAX_POSITION", "type": "number", "default": 40, "range": [1, 200] }, { "env": "SIMMER_MIN_TRADE", "type": "number", "default": 5, "range": [1, 100] } ``` ### Technical Analysis The sizing formula applies `max(MIN_TRADE, calculated_size)` without subsequently capping the result at `MAX_POSITION`. Thus, whenever `MIN_TRADE` is greater than `MAX_POSITION`, every eligible trade can exceed the configured maximum position size. The published configuration ranges explicitly allow this invalid state. For example, `MAX_POSITION=1` and `MIN_TRADE=100` produces a `$100` order even though the declared maximum is `$1`. Configuration values may come from the environment or be changed by `apply_skill_config()`, but the code performs no cross-field validation after loading them. ### Attack Path 1. The environment or managed skill configuration sets `SIMMER_MIN_TRADE` above `SIMMER_MAX_POSITION`. 2. `get_client()` loads both values without validating their relationship. 3. A market meets the divergence and probability requirements. 4. `compute_signal()` calculates a conviction-based value at or below `MAX_POSITION`. 5. `max(MIN_TRADE, calculated_size)` selects the larger minimum trade amount. 6. Live mode submits an order exceeding the configured maximum position. ### Impact Assessment N ...[truncated 435 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Validate all risk parameters after environment and managed configuration loading: ```python if MAX_POSITION <= 0: raise ValueError("MAX_POSITION must be positive") if MIN_TRADE <= 0 or MIN_TRADE > MAX_POSITION: raise ValueError("MIN_TRADE must be positive and no greater than MAX_POSITION") ``` 2. Apply an explicit hard cap to the final amount: ```python calculated = round(conviction * MAX_POSITION, 2) size = min(MAX_POSITION, max(MIN_TRADE, calculated)) ``` 3. Change metadata ranges or add cross-field validation in the configuration interface so invalid combinations cannot be saved. 4. Fail closed rather than silently correcting dangerous live-mode configuration. 5. Add tests for boundary values, including `MIN_TRADE == MAX_POSITION`, `MIN_TRADE > MAX_POSITION`, zero or negative environment values, and non-finite floating-point values. ]]>

T08 · Insecure Dependencies

Warning
Location
clawhub.json:6
Finding
Privileged Trading SDK Dependency Is Not Version-Pinned<![CDATA[ ## Vulnerability Details **File Location**: `clawhub.json:6-9` **Vulnerability Type**: Unpinned third-party dependency **Risk Level**: Medium ### Vulnerable Code ```json "pip": [ "simmer-sdk" ] ``` The package is imported into the trading process: ```python from simmer_sdk import SimmerClient ``` It is then given the high-value API credential: ```python _client = SimmerClient( api_key=os.environ["SIMMER_API_KEY"], venue=venue, ) ``` ### Technical Analysis The package requirement specifies only the project name and imposes no exact version, integrity hash, or audited lockfile. A fresh installation can therefore resolve to a different future release than the one originally reviewed. This dependency has a particularly sensitive trust position: imported Python package code executes with the process's privileges and can access `SIMMER_API_KEY`, network connectivity, market data, and live order operations. A compromised upstream account, malicious package release, or package-index substitution could therefore change the effective behavior without modifying this repository. The audit found no evidence that the currently intended `simmer-sdk` project is malicious. The vulnerability is the uncontrolled dependency resolution and resulting supply-chain exposure. ### Attack Path 1. An attacker compromises the upstream package publication account, distribution infrastructure, or dependency-resolution path. 2. The attacker publishes or serves a malicious release under the accepted package name. 3. A user installs the skill in a fresh environment. 4. Because no version or hash is pinned, the installer accepts the malicious release. 5. Python imports `simmer_sdk`, executing attacker-controlled package initialization code. 6. The package can read `SIMMER_API_KEY`, transmit it externally, alter market data, or submit unauthorized trades using the process's authority. ### Impact Assessment A successful supply-chain compromise would execute code wi ...[truncated 387 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Pin the dependency to an exact audited version, for example: ```json "pip": [ "simmer-sdk==<audited-version>" ] ``` 2. Use a lockfile with cryptographic hashes and require hash verification during installation. 3. Install only from a trusted, controlled package index. 4. Review the pinned release's source, transitive dependencies, and release provenance. 5. Use automated dependency monitoring, but update only through a reviewed and tested process. 6. Restrict the runtime with least-privilege filesystem and network controls. 7. Keep live-trading credentials scoped, revocable, and separate from broader account credentials. ]]>
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 (2)

Lp3

Medium
Category
MCP Least Privilege
Confidence
87% confidence
Finding
The skill references a high-value credential (`SIMMER_API_KEY`) and describes trading behavior, but it does not declare an explicit tool/permission scope such as allowed environment access. In an agent ecosystem, undeclared env capability weakens least-privilege controls and can allow broader secret access than operators expect, especially for a trading skill that could misuse credentials if execution content later expands.

Missing User Warnings

Medium
Confidence
91% confidence
Finding
This manifest defines an automated trading skill with a managed entrypoint and configurable position-sizing controls, but it does not provide any explicit user-facing warning that the skill may place real-money trades using configured funds. In a trading context, omission of this disclosure increases the risk of users enabling or deploying the skill without understanding that it can autonomously open positions and incur financial loss.

Static analysis

No suspicious patterns detected.