Back to skill

Security audit

Polymarket Fast Loop

Security checks for vulnerabilities and agentic risk

Overview

This is a disclosed automated Polymarket trading skill, but it needs Review because live mode can spend real funds, use wallet credentials, cancel orders, and one advertised trade-rate safety control is not enforced.

Install only if you intentionally want automated Polymarket fast-market trading. Use dry-run first, keep live runs isolated in a dedicated wallet with limited USDC, avoid sharing broad environment credentials with unrelated tools, pin/review simmer-sdk before live use, and do not rely on the advertised minimum-time-between-trades setting until it is implemented.

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)

T08 · Insecure Dependencies

Error
Location
clawhub.json:4
Finding
Unpinned privileged trading dependency creates supply-chain exposure<![CDATA[ ## Vulnerability Details **File Location**: `clawhub.json:4-9`; related imports and initialization at `fastloop_trader.py:101` and `fastloop_trader.py:182-194` **Vulnerability Type**: Unpinned third-party dependency **Risk Level**: High ### Vulnerable Code ```json "requires": { "env": [ "SIMMER_API_KEY" ], "pip": [ "simmer-sdk" ] } ``` Related runtime usage: ```python from simmer_sdk.skill import load_config, update_config, get_config_path ``` ```python def get_client(live=True): """Lazy-init SimmerClient singleton.""" global _client if _client is None: try: from simmer_sdk import SimmerClient except ImportError: print("Error: simmer-sdk not installed. Run: pip install simmer-sdk") sys.exit(1) api_key = os.environ.get("SIMMER_API_KEY") if not api_key: print("Error: SIMMER_API_KEY environment variable not set") print("Get your API key from: simmer.markets/dashboard → SDK tab") sys.exit(1) venue = os.environ.get("TRADING_VENUE", "polymarket") _client = SimmerClient(api_key=api_key, venue=venue, live=live) return _client ``` ### Technical Analysis The package manifest requests `simmer-sdk` without an exact version or integrity hash. Package resolution can therefore select a future release that was not part of this audit. The dependency is imported into the same Python process that holds the Simmer API key and, according to the documented live-trading setup, may also hold `WALLET_PRIVATE_KEY`. Python packages can execute arbitrary code during installation and import. Consequently, a compromised package release or package-source substitution would run with all privileges granted to the trader process. This finding concerns dependency trust and pinning. The audited project does not itself contain evidence that the current `simmer-sdk` package is malicious. ### Attack Path 1. An attacker compromises ...[truncated 1106 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Pin `simmer-sdk` to an exact, reviewed version rather than allowing unconstrained resolution. 2. Maintain a lock file containing transitive dependency versions. 3. Require package hashes, such as with `pip install --require-hashes`, and verify artifacts through a trusted package index. 4. Review the package publisher, release history, source repository, and build provenance before deployment. 5. Install dependencies in an isolated virtual environment or container under an unprivileged account. 6. Restrict outbound network access to the documented Simmer, Polymarket, Gamma, and Binance endpoints. 7. Isolate wallet signing from the strategy process where possible, using a constrained signer that authorizes only expected venues, amounts, and operations. 8. Rotate trading credentials immediately if dependency compromise is suspected. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
clawhub.json:62
Finding
Advertised minimum trade interval is not enforced<![CDATA[ ## Vulnerability Details **File Location**: `clawhub.json:62-72`; recurring live execution is documented at `SKILL.md:81-101` **Vulnerability Type**: Missing enforcement of a declared trading safety control **Risk Level**: Medium ### Vulnerable Configuration ```json { "env": "SIMMER_FASTLOOP_MIN_TIME_BETWEEN_TRADES_SEC", "type": "number", "default": 60, "range": [ 10, 600 ], "step": 10, "label": "Min time between trades (seconds)" } ``` The documentation recommends frequent live execution: ```text # Every 5 minutes (one per fast market window) */5 * * * * cd /path/to/skill && python fastloop_trader.py --live --quiet # Every 1 minute (more aggressive, catches mid-window opportunities) * * * * * cd /path/to/skill && python fastloop_trader.py --live --quiet ``` However, `SIMMER_FASTLOOP_MIN_TIME_BETWEEN_TRADES_SEC` has no corresponding entry in `CONFIG_SCHEMA` and no runtime enforcement in `fastloop_trader.py`. ### Technical Analysis The metadata presents a minimum-time-between-trades setting as a configurable control. The implementation neither loads the setting nor stores and checks a last-trade timestamp. An operator can therefore configure the advertised interval and reasonably believe it limits trading frequency, while the application silently ignores it. Daily-budget checks and existing-position deduplication partially limit exposure, but they are not equivalent to global rate enforcement. The budget is updated only after a reported successful non-simulated trade, and deduplication is market-specific. ### Attack Path 1. An operator configures `SIMMER_FASTLOOP_MIN_TIME_BETWEEN_TRADES_SEC`, expecting it to constrain trading frequency. 2. The operator follows the documentation and schedules live execution every minute. 3. Each invocation starts without checking the configured minimum interval or a persistent last-trade timestamp. 4. When different qualifying markets are selected, the program may attempt trades at a freq ...[truncated 700 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Add `min_time_between_trades_sec` to `CONFIG_SCHEMA` using the declared environment variable. 2. Persist the timestamp of the last successful live trade in a local state file using atomic writes. 3. Before submitting another trade, compare the current UTC time with the persisted timestamp and reject attempts inside the configured interval. 4. Use a file lock or equivalent synchronization mechanism so overlapping cron executions cannot bypass the interval check. 5. Validate that the interval is within a safe range and fail closed if the state file is malformed. 6. Update the timestamp only after confirmed execution, while separately rate-limiting repeated failed submissions if necessary. 7. Add automated tests covering sequential invocations, concurrent processes, malformed state, dry-run mode, and UTC boundary conditions. 8. If the feature will not be implemented, remove the tunable from `clawhub.json` so operators are not given a false security expectation. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
fastloop_trader.py:792
Finding
Existing-position deduplication path references an uninitialized variable<![CDATA[ ## Vulnerability Details **File Location**: `fastloop_trader.py:792-806`; delayed initialization at `fastloop_trader.py:850` **Vulnerability Type**: Use of a local variable before initialization **Risk Level**: Medium ### Vulnerable Code ```python # Dedup: skip if we already hold a position on this market _mid = best.get("market_id") or "" _q = best.get("question", "").lower() existing = get_positions() for pos in existing: held = (pos.get("shares_yes") or 0) + (pos.get("shares_no") or 0) if held <= 0: continue if (_mid and pos.get("market_id") == _mid) or (_q and pos.get("question", "").lower() == _q): log(f" ⏸️ Already holding position on this market — skip (dedup)") if not quiet: print(f"📊 Summary: No trade (already holding this market)") skip_reasons.append("already holding") _emit_skip_report() return ``` The required initialization occurs later in the function: ```python momentum_pct = abs(momentum["momentum_pct"]) direction = momentum["direction"] skip_reasons = [] def _emit_skip_report(signals=1, attempted=0): """Emit automaton JSON with skip_reason before early return.""" ``` ### Technical Analysis Python treats `skip_reasons` as a local variable because it is assigned later in `run_fast_market_strategy`. If an existing position matches the selected market, execution reaches `skip_reasons.append(...)` before the local variable has been initialized. This raises `UnboundLocalError`. The same branch also calls `_emit_skip_report()` before that nested function is defined, which would produce a second local-name failure if the list initialization problem were fixed without moving the helper definition. The branch is intended to provide a safety control by preventing duplicate market exposure. Instead, it terminates the scheduled run with an uncaught exception when that control is exercised. ### Attack Path 1. The account already holds YES or NO shares in the f ...[truncated 1260 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Move `skip_reasons = []` to the beginning of `run_fast_market_strategy`, before any branch can reference it. 2. Define `_emit_skip_report()` before the existing-position deduplication branch. 3. Keep all early-return reporting helpers and their state initialized together near the start of the function. 4. Add a regression test in which `get_positions()` returns a matching position with positive shares. 5. Assert that the tested path exits normally, places no order, and emits the expected deduplication report. 6. Add a top-level exception boundary that logs structured errors without exposing credentials, while retaining fail-closed trading behavior. 7. Run static analysis capable of detecting potentially unbound local variables and nested functions referenced before definition. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (12)

Lp3

Medium
Category
MCP Least Privilege
Confidence
94% confidence
Finding
The skill instructs the user/agent to set sensitive environment variables, execute Python, access external APIs, and potentially write config files, but it declares no explicit tool or permission scope. In an agent environment, this missing scope increases the chance the skill is invoked with broader-than-necessary access, enabling unintended network use, secret handling, or file modification during live trading workflows.

Vague Triggers

Medium
Confidence
90% confidence
Finding
The top-level description is broad enough to match many generic crypto trading or automation requests, which can cause an agent to select this skill in situations the user did not specifically intend. Because this skill can lead to live financial trades with real funds, over-broad routing materially raises the risk of inappropriate activation and unsafe execution context.

Vague Triggers

Medium
Confidence
88% confidence
Finding
The 'When to Use This Skill' section lists expansive triggers like short-term crypto prediction trading and custom signals without strong guardrails, making auto-invocation more likely across loosely related user intents. In the context of a skill that handles real-money trading and requests wallet private keys, loose activation criteria make accidental or premature use more dangerous than in a read-only or informational skill.

Missing User Warnings

Medium
Confidence
93% confidence
Finding
This manifest clearly configures an automated trading skill with position size, trade frequency, and daily budget controls, yet it contains no user-facing disclosure that the skill can place real trades or expose the user to financial loss. In a trading context, omission of explicit risk and execution warnings can mislead users into enabling automation without understanding that funds may be committed automatically, increasing the chance of unintended or excessive losses.

Ae4

Medium
Category
analysis-evasion
Confidence
80% confidence
Finding
Suspicious Unicode normalization or mixed-script content

Description-Behavior Mismatch

Medium
Confidence
95% confidence
Finding
The manifest says the skill is for Polymarket BTC 5-minute and 15-minute fast markets, but the code exposes broader trading scope via ETH, SOL, and even a 1h window in internal duration handling. Capability drift matters in trading agents because users and orchestrators may grant trust or permissions based on the narrower declared scope, causing unintended market exposure.

External Transmission

Medium
Category
Data Exfiltration
Content
Returns: {momentum_pct, direction, price_now, price_then, avg_volume, candles}
    """
    url = (
        f"https://api.binance.com/api/v3/klines"
        f"?symbol={symbol}&interval=1m&limit={lookback_minutes}"
    )
    result = _api_request(url)
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
Returns: {momentum_pct, direction, price_now, price_then, avg_volume, candles}
    """
    url = (
        f"https://api.binance.com/api/v3/klines"
        f"?symbol={symbol}&interval=1m&limit={lookback_minutes}"
    )
    result = _api_request(url)
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
When invoked with --live, the skill can place real orders immediately based on fetched market data and configuration, without an additional interactive confirmation or policy gate right before execution. In an agent setting, this increases the risk of accidental or prompt-induced financial actions, especially if the skill is triggered automatically or with inherited credentials.

Natural-Language Policy Violations

Low
Confidence
97% confidence
Finding
The comment says 'Force line-buffered stdout for non-TTY environments,' which describes output buffering behavior, not a language or locale restriction. This does not indicate any natural-language policy violation such as forcing a user language or locale.

Context-Inappropriate Capability

Low
Confidence
78% confidence
Finding
The manifest describes using momentum signals to trade Polymarket fast markets, but does not mention local persistence or external trade journaling. While related to risk controls and logging, these are additional capabilities beyond the stated purpose and are not obviously declared in the manifest text.

Context-Inappropriate Capability

Low
Confidence
82% confidence
Finding
The manifest focuses on trading fast markets from momentum signals, but the implementation also queries portfolio balances, all current positions, and open orders to size trades and manage duplicates/stale orders. These are plausible trading-related capabilities, but they are broader account-access functions not stated in the manifest.

Static analysis

No suspicious patterns detected.