Back to skill

Security audit

Polymarket Candle Three Soldiers Trader

Security checks for vulnerabilities and agentic risk

Overview

This is a disclosed Polymarket trading bot, but it needs review because live mode can place real-money trades while some documented risk limits are not actually enforced.

Treat this as a high-risk automated trading skill. It appears purpose-aligned and not malicious, but do not enable live mode unless you are comfortable with real USDC exposure, have externally limited the API key/account funds, and understand that some advertised risk tunables may not reliably cap liquidity, resolution horizon, or total open positions.

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

T09 · Insecure Skill Coding Practices

Error
Location
trader.py:362
Finding
Declared Live-Trading Risk Controls Are Not Fully Enforced<![CDATA[ ## Vulnerability Details **File Location**: `trader.py:32-35`, `trader.py:240-264`, and `trader.py:362-391`; related declarations in `SKILL.md:81-84` **Vulnerability Type**: Missing enforcement of financial risk controls **Risk Level**: High The documentation describes `SIMMER_MIN_VOLUME` as a minimum market-volume filter and `SIMMER_MAX_POSITIONS` as the maximum number of concurrent open positions. The implementation loads these settings, but `MIN_VOLUME` is never applied before an order, while `MAX_POSITIONS` only counts successful orders during the current process invocation. The pattern-trading path also does not enforce the configured `MIN_DAYS` resolution constraint. ### Vulnerable Code ```python MIN_VOLUME = float(os.environ.get("SIMMER_MIN_VOLUME", "3000")) MIN_DAYS = int(os.environ.get( "SIMMER_MIN_DAYS", "0")) MAX_POSITIONS = int(os.environ.get( "SIMMER_MAX_POSITIONS", "8")) ``` The pattern signal checks spread but not market volume or days until resolution: ```python def compute_pattern_signal(pattern: str, target: IntervalMarket) -> tuple[str | None, float, str]: """ Continuation signal after Three White Soldiers or Three Black Crows. Soldiers (UP trend) + next interval < 0.55 -> buy YES (hasn't caught up). Crows (DOWN trend) + next interval > 0.45 -> buy NO (hasn't caught down). """ m = target.market p = target.p q = m.question # Spread gate if m.spread_cents is not None and m.spread_cents / 100 > MAX_SPREAD: return None, 0, f"Spread {m.spread_cents/100:.1%} > {MAX_SPREAD:.1%}" if pattern == "soldiers": lag = 0.55 - p conviction = min(1.0, lag / 0.55) if lag > 0 else 0.05 size = max(MIN_TRADE, round(conviction * MAX_POSITION, 2)) return "yes", size, ( f"3-SOLDIERS -> YES {p:.0%} lag={lag:.0%} size=${size} -- {q[:60]}" ) elif pattern == "crows": lag = p - 0.45 conviction = min(1.0, lag / ...[truncated 3886 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Enforce minimum volume immediately before order placement using a reliable market-volume field: ```python volume = float(getattr(m, "volume", 0) or 0) if volume < MIN_VOLUME: return None, 0, f"Volume ${volume:,.2f} below ${MIN_VOLUME:,.2f}" ``` 2. Apply the `MIN_DAYS` check inside a shared validation function used by every signal path. Fail closed when a live market has a missing or malformed resolution timestamp. 3. Query current open positions from the venue before processing opportunities: ```python open_positions = client.get_positions(status="open") remaining = max(0, MAX_POSITIONS - len(open_positions)) ``` Stop trading when `remaining` reaches zero. 4. Count unique portfolio positions rather than orders. If several orders for one market can exist, define whether the control limits markets, positions, or orders and enforce that definition consistently. 5. Revalidate volume, spread, resolution time, and portfolio capacity immediately before each `client.trade()` call to reduce time-of-check/time-of-use issues. 6. Add automated tests proving that low-volume markets, near-resolution markets, and portfolios already at the position limit cannot place orders in either simulated or live mode. ]]>

T08 · Insecure Dependencies

Warning
Location
clawhub.json:3
Finding
Security-Critical Trading SDK Dependency Is Not Version-Pinned<![CDATA[ ## Vulnerability Details **File Location**: `clawhub.json:3-9` **Vulnerability Type**: Unpinned privileged third-party dependency **Risk Level**: Medium ### Vulnerable Code ```json "requires": { "env": [ "SIMMER_API_KEY" ], "pip": [ "simmer-sdk" ] } ``` ### Technical Analysis The package requirement specifies only `simmer-sdk`, without an exact version, lock file, package hash, or integrity constraint. Each new installation may therefore resolve to a different release than the one reviewed during this audit. This dependency executes in the same Python process as the Skill and receives the high-value `SIMMER_API_KEY` directly: ```python _client = SimmerClient( api_key=os.environ["SIMMER_API_KEY"], venue=venue, ) ``` It also implements market discovery and trade submission. A compromised, malicious, or unexpectedly incompatible future release would consequently execute with access to the credential and the Skill's trading authority. The reviewed project does not establish that the current upstream package is malicious. The issue is the absence of reproducible and integrity-verified dependency resolution. ### Attack Path 1. An attacker compromises the upstream package account, release process, or distribution artifact for `simmer-sdk`. 2. The attacker publishes a malicious release under the same legitimate package name. 3. A subsequent Skill installation resolves the unpinned requirement to that release. 4. Python imports the altered package when `trader.py` starts. 5. Package initialization or `SimmerClient` code executes in the Skill process. 6. The malicious dependency can read `SIMMER_API_KEY`, alter market data, change order parameters, submit unauthorized trades, or transmit the credential externally. This path requires compromise or malicious modification of the dependency distribution channel; it is not directly exploitable solely through the checked-in project files. ### Impact Assessment A compromised depend ...[truncated 599 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Pin `simmer-sdk` to an exact version that has been reviewed and tested: ```json "pip": [ "simmer-sdk==<audited-version>" ] ``` 2. Use a lock file with cryptographic hashes, or install with a hash-verified requirements file using `--require-hashes`. 3. Record the expected package source and artifact hashes in release documentation. 4. Review dependency updates before changing the pinned version. Include release-note review, static analysis, and live-trading regression tests. 5. Run the Skill under a dedicated, least-privileged operating-system identity and provide only the environment variables required for execution. 6. Restrict the API credential to the minimum trading permissions and financial limits supported by the service, and rotate it if dependency compromise is suspected. ]]>
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)

Lp3

Medium
Category
MCP Least Privilege
Confidence
91% confidence
Finding
The skill references a high-value environment credential (`SIMMER_API_KEY`) but does not declare any explicit tool scope or permissions boundary. In an agent platform, undeclared environment access weakens least-privilege controls and can allow the skill to read sensitive secrets beyond what reviewers or runtime policy expect, which is more dangerous here because the credential grants trading authority for live markets.

Missing User Warnings

Medium
Confidence
93% confidence
Finding
The manifest requires a live trading API key for an automated trading skill, but the metadata shown to users contains no explicit warning that the skill can place real trades or consume privileged credentials. In this context, missing disclosure materially increases the risk of users enabling the skill without understanding financial consequences, especially because the skill description explicitly targets automated Polymarket trading decisions.

Static analysis

No suspicious patterns detected.