Back to skill

Security audit

Polymarket Bundle Btc 5min Streak Trader

Security checks for vulnerabilities and agentic risk

Overview

This skill is a disclosed Polymarket trading bot, but its safety controls have implementation gaps that could expose users to unintended real-money trades or larger-than-expected positions.

Review before installing. Use paper mode first, provide only a restricted or sandbox trading key if available, avoid reusing the same long-lived Python process across live and paper runs, and do not rely on the documented volume or open-position limits as account-wide protections until the implementation is 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)

T09 · Insecure Skill Coding Practices

Error
Location
trader.py:49
Finding
Cached Live Client Can Bypass Requested Paper-Trading Mode<![CDATA[ ## Vulnerability Details **File Location**: `trader.py`, lines 49-64 **Vulnerability Type**: Execution-mode confusion caused by unsafe global client caching **Risk Level**: High ### Vulnerable Code ```python _client: SimmerClient | None = None def safe_print(text): """Print with fallback for non-ASCII characters (Windows cp1252 terminals).""" try: print(text) except UnicodeEncodeError: print(text.encode('ascii', 'replace').decode()) def get_client(live: bool = False) -> SimmerClient: """ live=False -> venue="sim" (paper trades -- safe default). live=True -> venue="polymarket" (real trades, only with --live flag). """ global _client, MAX_POSITION, MIN_VOLUME, MAX_SPREAD, MIN_DAYS, MAX_POSITIONS global YES_THRESHOLD, NO_THRESHOLD, MIN_TRADE, STREAK_LENGTH if _client is None: venue = "polymarket" if live else "sim" _client = SimmerClient( api_key=os.environ["SIMMER_API_KEY"], venue=venue, ) if live: _client.live = True ``` ### Technical Analysis The module stores one `SimmerClient` in the global `_client` variable. The selected venue and live-trading state are established only when `_client` is `None`. Subsequent calls return the existing client without checking whether its venue matches the newly requested `live` argument. Consequently, after the client has been initialized with `live=True`, a later call to `get_client(live=False)` or `run(live=False)` in the same Python process reuses the live Polymarket client. The second execution may print that it is in paper mode while still submitting orders through a client configured for real trading. This is especially relevant in managed runtimes, notebooks, test processes, or embedding applications where the module can remain loaded and `run()` can be invoked more than once. ### Attack Path 1. A process imports `trader.py`. 2. The process invokes `run(live=True)` or `get_cli ...[truncated 837 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Do not share one mutable client across live and simulated modes. - Maintain separate clients keyed by venue, such as `{"sim": ..., "polymarket": ...}`, or instantiate a new client for every run. - If caching is required, store the configured mode alongside the client and recreate the client whenever the requested mode differs. - Before each trade, verify that the client's authoritative venue and live state match the current invocation. - Fail closed if the SDK does not expose a verifiable execution mode. - Add a regression test that initializes live mode first and then invokes paper mode in the same process, confirming that the second trade is simulated. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
trader.py:388
Finding
Configured Open-Position Limit Only Counts Orders in the Current Run<![CDATA[ ## Vulnerability Details **File Location**: `trader.py`, lines 388-425 **Vulnerability Type**: Ineffective account-wide risk limit **Risk Level**: Medium ### Vulnerable Code ```python # Detect streaks across all dates placed = 0 for date_str, ivs in by_date.items(): if placed >= MAX_POSITIONS: break streaks = detect_streaks(ivs) if not streaks: safe_print(f" [{date_str}] no streaks of {STREAK_LENGTH}+") continue for streak_dir, streak_len, target in streaks: if placed >= MAX_POSITIONS: break side, size, reasoning = compute_streak_signal(streak_dir, streak_len, target) if not side: safe_print(f" [skip] {reasoning}") continue ok, why = context_ok(client, target.market.id) if not ok: safe_print(f" [skip] {why}") continue try: r = client.trade( market_id=target.market.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 except Exception as e: safe_print(f" [error] {target.market.id}: {e}") ``` ### Technical Analysis `MAX_POSITIONS` is described as a maximum number of concurrent open positions, but the implementation initializes `placed` to zero on every invocation. It increments the counter only after successful orders during that invocation. The code does not retrieve existing open positions or pending orders from ...[truncated 1063 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Query authoritative account positions and pending orders before generating or submitting trades. - Calculate remaining capacity as `MAX_POSITIONS - existing_open_positions - pending_open_orders`. - Recheck the limit immediately before each order to reduce race conditions. - Prevent duplicate exposure to the same market unless explicitly allowed. - Use an account-wide or transactional limit in the trading backend where available. - Treat failures to retrieve current positions as a fail-closed condition in live mode. - Rename the setting if it is intentionally only a per-run order limit; otherwise, make its implementation match the documented concurrent-position semantics. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
trader.py:31
Finding
Declared Minimum-Volume Safeguard Is Never Enforced<![CDATA[ ## Vulnerability Details **File Location**: `trader.py`, lines 31-31 and 64-68 **Vulnerability Type**: Missing liquidity validation **Risk Level**: Medium ### Vulnerable Code The minimum-volume setting is declared: ```python MIN_VOLUME = float(os.environ.get("SIMMER_MIN_VOLUME", "3000")) ``` It is also reloaded after skill configuration: ```python MAX_POSITION = float(os.environ.get("SIMMER_MAX_POSITION", str(MAX_POSITION))) MIN_VOLUME = float(os.environ.get("SIMMER_MIN_VOLUME", str(MIN_VOLUME))) MAX_SPREAD = float(os.environ.get("SIMMER_MAX_SPREAD", str(MAX_SPREAD))) MIN_DAYS = int(os.environ.get( "SIMMER_MIN_DAYS", str(MIN_DAYS))) MAX_POSITIONS = int(os.environ.get( "SIMMER_MAX_POSITIONS", str(MAX_POSITIONS))) ``` However, no market-selection or trade-execution path compares a market's volume against `MIN_VOLUME`. ### Technical Analysis The configuration and documentation present `SIMMER_MIN_VOLUME` as a live risk control, with a default minimum of USD 3,000. The implementation only reads the value. Neither `find_markets()`, `compute_streak_signal()`, nor the final trading loop validates market volume. As a result, markets with little or no liquidity can pass the implemented keyword, probability, spread, and context checks. A declared security or financial safeguard that is not enforced creates a false sense of protection and makes configured limits ineffective. ### Attack Path 1. A low-volume BTC market is returned by `find_markets()` and passes the textual market checks. 2. Its question is parsed successfully and its probabilities form a qualifying streak. 3. `compute_streak_signal()` checks spread but does not inspect market volume. 4. `context_ok()` does not enforce `MIN_VOLUME`. 5. The trading loop submits the order even if the market's volume is below the configured threshold. 6. In live mode, the order may experience poor execution or become difficult to e ...[truncated 413 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Retrieve the market's authoritative volume value before considering it eligible for trading. - Enforce `market_volume >= MIN_VOLUME` in a common validation function used by all signal paths. - Fail closed in live mode if volume is missing, stale, nonnumeric, or obtained from an untrusted field. - Define whether the threshold applies to total volume, recent volume, or available order-book liquidity and document that interpretation. - Consider supplementing total-volume checks with order-book depth and expected execution-price checks. - Add tests confirming that markets below the configured threshold never reach `client.trade()`. ]]>

T08 · Insecure Dependencies

Warning
Location
clawhub.json:6
Finding
Trading SDK Dependency Is Not Version-Pinned<![CDATA[ ## Vulnerability Details **File Location**: `clawhub.json`, lines 6-8 **Vulnerability Type**: Unrestricted third-party executable dependency **Risk Level**: Medium ### Vulnerable Code ```json "pip": [ "simmer-sdk" ] ``` ### Technical Analysis The project requests `simmer-sdk` without an exact version or integrity hash. Package installation can therefore resolve to a future release that was not included in this audit. This dependency is security-sensitive because `trader.py` imports `SimmerClient`, passes `SIMMER_API_KEY` into it, uses it for remote market discovery, and delegates trade submission to it. A compromised package release, package-account takeover, dependency-confusion condition, or incompatible behavioral change could therefore affect credentials and financial transactions. The audit found no evidence that the currently intended package is itself malicious; the issue is the lack of reproducible and integrity-verified dependency resolution. ### Attack Path 1. The skill is installed in a new environment. 2. The package manager resolves the unrestricted `simmer-sdk` requirement. 3. A changed, compromised, or otherwise unsafe release is selected. 4. `trader.py` imports the installed package. 5. The code instantiates `SimmerClient` with `SIMMER_API_KEY`. 6. Malicious dependency code could access the API key, alter market data, change execution behavior, or submit unauthorized requests under the dependency's process privileges. ### Impact Assessment A compromised dependency would execute with the same operating-system permissions as the skill process and could access environment variables available to that process, including the trading API key. It could potentially exfiltrate credentials, manipulate or submit trades, and access other files or network resources permitted to the process. The practical scope depends on runtime sandboxing and the privileges granted to `SIMMER_API_KEY`. ]]>
Remediation
<![CDATA[ ## Remediation Suggestions - Pin `simmer-sdk` to a reviewed exact version rather than accepting any available release. - Use a lock file and require package hashes, such as pip hash-checking mode, for reproducible installation. - Verify the package publisher, repository provenance, and release signatures where available. - Review dependency changes before upgrading and run security scanning against the complete resolved dependency tree. - Install dependencies from an approved package index and disable untrusted extra indexes to reduce dependency-confusion risk. - Restrict the runtime's filesystem and network permissions and provide the API key only to the process that requires it. ]]>
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
88% confidence
Finding
The skill explicitly requires a high-value credential (`SIMMER_API_KEY`) and is intended to place trades, but it does not declare any tool scope or permissions boundary. That creates an authorization ambiguity where an agent/runtime may expose broader environment access than necessary, increasing the chance of credential misuse or unintended capability exposure.

Missing User Warnings

Medium
Confidence
89% confidence
Finding
The manifest declares a required API credential and an automated trading entrypoint, but provides no user-facing disclosure about how the credential will be used or that the skill performs external networked trading activity. In a trading bot context, this increases the risk of users supplying sensitive credentials without understanding scope, permissions, or potential financial consequences from automated execution.

Static analysis

No suspicious patterns detected.