Back to skill

Security audit

Polymarket Candle Timeframe Mismatch Trader

Security checks for vulnerabilities and agentic risk

Overview

This skill is disclosed as a trading bot, but its live-trading risk controls do not fully match the safeguards it documents.

Review carefully before installing or enabling live mode. Use only a restricted API key, keep live balances and venue-side limits low, do not rely on the documented volume and position controls as complete, and avoid live use until the dependency is pinned and the risk-control bugs are fixed.

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)

T08 · Insecure Dependencies

Warning
Location
clawhub.json:6
Finding
Unpinned Third-Party Trading SDK Creates a Supply-Chain Risk<![CDATA[ ## Vulnerability Details **File Location**: `clawhub.json:6-10` **Vulnerability Type**: Unpinned executable 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 cryptographic integrity constraint. Package resolution can therefore install a newer and potentially unreviewed release whenever the skill is deployed. This dependency is security-sensitive because `trader.py` gives it access to the `SIMMER_API_KEY`, selects the trading venue, queries market data, and uses it to submit orders. Dependency code executes inside the same Python process and consequently inherits the process's credential access and operating-system privileges. No evidence indicates that the current package is malicious. The vulnerability is that future installations are not reproducible and implicitly trust whichever compatible package release the package index returns. ### Attack Path 1. An attacker compromises the upstream package account, publishing infrastructure, or a future `simmer-sdk` release. 2. The skill is installed or rebuilt after the compromised release becomes available. 3. Because no version or hash is specified, the installer resolves the compromised release. 4. Malicious package code executes during installation, import, or SDK initialization. 5. The dependency reads `SIMMER_API_KEY` from the process environment or intercepts calls made through `SimmerClient`. 6. The dependency can disclose the credential, modify market information, redirect trading activity, or submit unauthorized orders within the credential's permissions. ### Impact Assessment A compromised dependency would execute with the privileges of the skill process. It could access the high-value trading API credential, manipulate market queries and order parameters, or initiate unauthorized financial activity. The ...[truncated 103 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Pin `simmer-sdk` to an exact, reviewed version rather than accepting the latest available release. 2. Use a lock file and hash-verified installation, such as `pip --require-hashes`, to make dependency resolution reproducible. 3. Review new SDK versions before updating the pin. 4. Obtain packages only from an explicitly configured trusted package index. 5. Restrict `SIMMER_API_KEY` to the minimum required trading permissions and enforce account-level spending and position limits. 6. Run the skill in an isolated environment with minimal filesystem and network privileges. 7. Add automated dependency integrity and vulnerability scanning to the release process. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
trader.py:363
Finding
Declared Minimum-Volume Safeguard Is Not Enforced Before Trading<![CDATA[ ## Vulnerability Details **File Location**: `trader.py:363-434` **Additional Location**: `trader.py:32`, `trader.py:77`; `SKILL.md:81` **Vulnerability Type**: Missing financial risk-control enforcement **Risk Level**: Medium ### Vulnerable Code The volume threshold is loaded: ```python MIN_VOLUME = float(os.environ.get("SIMMER_MIN_VOLUME", "3000")) ``` It is reloaded after applying the skill configuration: ```python MIN_VOLUME = float(os.environ.get("SIMMER_MIN_VOLUME", str(MIN_VOLUME))) ``` However, the order-processing path reaches `client.trade()` without inspecting the hourly market's volume: ```python placed = 0 for key, hourly_m in hourly_markets.items(): if placed >= MAX_POSITIONS: break coin, date_str, hour_start = key sub_intervals = fivemin_by_hour.get(key, []) if len(sub_intervals) < MIN_CONSENSUS: safe_print( f" [{coin} {date_str} {hour_start//60}h] " f"only {len(sub_intervals)} sub-intervals, need {MIN_CONSENSUS}" ) continue sorted_subs = sorted(sub_intervals, key=lambda x: x[0]) up_count = 0 down_count = 0 for _, sub_m in sorted_subs: p = float(sub_m.current_probability) if p > UP_BIAS_THRESHOLD: up_count += 1 elif p < DOWN_BIAS_THRESHOLD: down_count += 1 consensus_dir = None consensus_count = 0 if up_count >= MIN_CONSENSUS: consensus_dir = "up" consensus_count = up_count elif down_count >= MIN_CONSENSUS: consensus_dir = "down" consensus_count = down_count if not consensus_dir: safe_print( f" [{coin} {date_str} {hour_start//60}h] " f"no consensus (up={up_count}, down={down_count}, need {MIN_CONSENSUS})" ) continue hourly_p = float(hourly_m.current_probability) safe_print( f" [{coin} {date_str} {hour_start//60}h] " f"5min consensus={consensus_dir}({consensus_count} ...[truncated 2262 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Read the authoritative volume field from each target market and compare it against `MIN_VOLUME` before signal generation and immediately before order submission. 2. Fail closed when volume is absent, malformed, stale, or expressed in an unexpected unit. 3. Confirm whether the SDK reports lifetime volume, recent volume, or available order-book depth; use a metric appropriate to execution risk. 4. Add order-book depth checks for the intended order amount rather than relying exclusively on aggregate volume and spread. 5. Recalculate liquidity and expected slippage immediately before live submission. 6. Add tests proving that markets below the threshold and markets with missing volume cannot reach `client.trade()`. 7. Ensure documentation identifies the exact volume field, time window, currency, and fallback behavior. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
trader.py:363
Finding
Maximum Concurrent Position Limit Only Counts Orders Placed During the Current Run<![CDATA[ ## Vulnerability Details **File Location**: `trader.py:363-366, 427-439` **Vulnerability Type**: Incomplete enforcement of the position limit **Risk Level**: Medium ### Vulnerable Code The counter starts from zero on every invocation: ```python placed = 0 for key, hourly_m in hourly_markets.items(): if placed >= MAX_POSITIONS: break ``` It is incremented only after a successful order during that invocation: ```python try: r = client.trade( market_id=hourly_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[:100]}") if r.success: placed += 1 ``` ### Technical Analysis `MAX_POSITIONS` is described as the maximum number of concurrent open positions, but the implementation treats it as a maximum number of successful orders placed by one process invocation. The code does not query existing open positions or pending orders before initializing `placed`. It also does not reserve capacity atomically. Repeated or concurrent runs can therefore each observe a local count of zero and place up to `MAX_POSITIONS` additional orders. A successful order count is not equivalent to an open-position count. Existing exposure from earlier invocations remains unaccounted for, and multiple successful orders can also affect the same position. ### Attack Path 1. The account already has open positions created by a previous run. 2. The skill is invoked again in live mode. 3. `placed` is reset to zero without querying account exposure. 4. The new invocation discovers additional qualifying markets. 5. It submits up to `MAX_POSITIONS` additional successful orders. 6. Repeating the invocation, or running multiple instances concurrently, continues increasing exposure beyon ...[truncated 560 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Query authoritative open positions and pending orders before processing candidate markets. 2. Calculate available capacity as the configured limit minus existing positions and pending position-opening orders. 3. Define whether multiple orders in one market count as one position and enforce that definition consistently. 4. Refresh account state immediately before every live order. 5. Use a venue-side atomic limit, transaction, distributed lock, or idempotency mechanism to prevent concurrent invocations from racing. 6. Reject new position-opening orders once the cap is reached while still allowing explicitly controlled position-reducing orders. 7. Add tests covering pre-existing positions, pending orders, repeated invocations, and concurrent workers. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
trader.py:259
Finding
Minimum Trade Size Can Override the Configured Maximum Position Size<![CDATA[ ## Vulnerability Details **File Location**: `trader.py:259` **Additional Location**: `clawhub.json:16-48` **Vulnerability Type**: Inconsistent order-sizing bounds **Risk Level**: Medium ### Vulnerable Code The order amount is calculated using `max`, allowing the minimum trade amount to exceed the maximum position amount: ```python size = max(MIN_TRADE, round(conviction * MAX_POSITION, 2)) ``` The configuration permits contradictory values. `SIMMER_MAX_POSITION` can be as low as `1`: ```json { "env": "SIMMER_MAX_POSITION", "type": "number", "default": 40, "range": [ 1, 200 ], "step": 1, "label": "Max position size (USD)" } ``` At the same time, `SIMMER_MIN_TRADE` can be as high as `100`: ```json { "env": "SIMMER_MIN_TRADE", "type": "number", "default": 5, "range": [ 1, 100 ], "step": 1, "label": "Min trade size (USD)" } ``` ### Technical Analysis The sizing formula treats `MIN_TRADE` as an unconditional floor but does not subsequently clamp the result to `MAX_POSITION`. The configuration schema independently validates each value but does not enforce the cross-field invariant: ```text MIN_TRADE <= MAX_POSITION ``` For example, if `MAX_POSITION=1` and `MIN_TRADE=100`, every qualifying signal produces a `$100` order even though the declared maximum position size is `$1`. The same sizing pattern also exists in the unused generic `compute_signal()` function at `trader.py:208` and `trader.py:215`, but the confirmed live mismatch path uses line 259. ### Attack Path 1. The operator, management UI, or deployment environment configures `SIMMER_MIN_TRADE` to a value greater than `SIMMER_MAX_POSITION`. 2. The skill starts successfully because no cross-field validation rejects the configuration. 3. A qualifying timeframe-mismatch signal is found. 4. Conviction-based sizing calculates a value at or below `MAX_POSITION`. 5. `max(MIN_TRADE, calculated_size)` selects the larger minimum-trade value. 6. The ove ...[truncated 522 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Validate configuration at startup and abort trading when `MIN_TRADE > MAX_POSITION`. 2. Explicitly clamp every calculated amount to `MAX_POSITION`. 3. If the bounded calculated amount is below the venue's minimum order size, skip the trade rather than violating the maximum. 4. Use a sizing sequence such as: ```python if MIN_TRADE > MAX_POSITION: raise ValueError("SIMMER_MIN_TRADE must not exceed SIMMER_MAX_POSITION") calculated = round(conviction * MAX_POSITION, 2) if calculated < MIN_TRADE: calculated = MIN_TRADE size = min(calculated, MAX_POSITION) ``` 5. Enforce the same invariant in `clawhub.json` or the management UI if cross-field validation is supported. 6. Add a final independent amount check immediately before `client.trade()`. 7. Add boundary tests for equal limits, minimum greater than maximum, zero or negative environment overrides, non-finite numbers, and out-of-range direct environment values. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
Findings (4)

Lp3

Medium
Category
MCP Least Privilege
Confidence
84% confidence
Finding
The skill explicitly requires a high-value credential (`SIMMER_API_KEY`) and describes trading execution, but it does not declare any explicit tool scope such as permissions or allowed-tools. That mismatch weakens least-privilege controls and can let an agent access environment-backed capabilities without clear, reviewable restriction, which is especially risky in a trading skill that can place orders.

Natural-Language Policy Violations

Medium
Confidence
93% confidence
Finding
The phrase 'Convergence guarantee' presents a trading thesis as certain, which can mislead operators or downstream agents into treating speculative financial behavior as deterministic. In context, this is dangerous because the skill can be switched to live trading, so overstated certainty can directly drive unjustified risk-taking with real funds.

Natural-Language Policy Violations

Medium
Confidence
95% confidence
Finding
Saying the outcome is 'not a question of if, but when' implies inevitability of profit or directional resolution, which is an unjustified certainty claim for a live-capable trading strategy. This can bias users or autonomous systems to overtrust the signal, ignore contrary evidence, and accept elevated financial exposure.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
The manifest explicitly requires an API credential and describes an automated trading skill, but it provides no user-facing warning that the skill will perform external networked trading activity using that credential. This creates a transparency and consent risk: users may supply sensitive credentials and enable live trading without clearly understanding the external actions, financial exposure, or data flows involved.

Static analysis

No suspicious patterns detected.