Back to skill

Security audit

Polymarket 48h Geopolitics Cluster Trader

Security checks for vulnerabilities and agentic risk

Overview

This skill is a clearly disclosed automated trading bot, but its live-trading safeguards and dependency controls are not strong enough for unsupervised real-money use.

Review this carefully before installing for anything beyond simulation. Treat SIMMER_API_KEY as trading authority, keep the key and funded balance tightly limited, and do not run with --live until the volume filter, true open-position limits, and dependency pinning or lockfile are addressed.

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

Warning
Location
trader.py:347
Finding
Documented live-trading risk controls are not fully enforced<![CDATA[ ## Vulnerability Details **File Location**: `trader.py:35-38`, `trader.py:347-369`, `trader.py:551-580`; related claims in `SKILL.md:101-104` **Vulnerability Type**: Missing enforcement of financial risk controls **Risk Level**: Medium ### Vulnerable Code ```python MIN_VOLUME = float(os.environ.get("SIMMER_MIN_VOLUME", "5000")) MAX_SPREAD = float(os.environ.get("SIMMER_MAX_SPREAD", "0.08")) MIN_DAYS = int(os.environ.get( "SIMMER_MIN_DAYS", "0")) MAX_POSITIONS = int(os.environ.get( "SIMMER_MAX_POSITIONS", "8")) ``` ```python def valid_market(market) -> tuple[bool, str]: """Check spread and days-to-resolution gates.""" p = getattr(market, "current_probability", None) if not isinstance(p, (int, float)): return False, "missing probability" spread_cents = getattr(market, "spread_cents", None) if isinstance(spread_cents, (int, float)) and spread_cents / 100 > MAX_SPREAD: return False, f"Spread {spread_cents/100:.1%} > {MAX_SPREAD:.1%}" resolves_at = getattr(market, "resolves_at", None) if resolves_at: try: resolves = datetime.fromisoformat(resolves_at.replace("Z", "+00:00")) days = (resolves - datetime.now(timezone.utc)).days if days < MIN_DAYS: return False, f"Only {days} days to resolve" except Exception: pass return True, "ok" ``` ```python placed = 0 for market_id, opp in sorted(all_opps.items(), key=lambda x: -x[1][2]): if placed >= MAX_POSITIONS: break market, side_hint, violation, reason = opp side, size, reasoning = compute_signal(market, side_hint, violation, reason) if not side: print(f" [skip] {reasoning}") continue ok, why = context_ok(client, market_id) if not ok: print(f" [skip] {why}") continue try: r = client.trade( market_id=market_id, side=side, amount=s ...[truncated 2733 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Enforce the volume threshold before signal generation: ```python volume = getattr(market, "volume", None) if not isinstance(volume, (int, float)): return False, "missing market volume" if float(volume) < MIN_VOLUME: return False, f"Volume ${volume:,.2f} < ${MIN_VOLUME:,.2f}" ``` 2. Confirm the exact volume field and units exposed by `simmer-sdk`. Do not silently accept missing or malformed volume data in live mode. 3. Query the account's existing open positions before placing orders: ```python open_positions = client.get_positions(status="open") remaining_slots = max(0, MAX_POSITIONS - len(open_positions)) ``` 4. Stop trading when `remaining_slots` reaches zero, and decrement it only after a confirmed successful order. 5. Deduplicate exposure by market so repeated executions cannot unintentionally stack the same position unless explicitly permitted. 6. Distinguish between: - Maximum orders per run. - Maximum concurrent open positions. - Maximum aggregate portfolio exposure. 7. Fail closed in live mode if portfolio state, volume, spread, or resolution data cannot be retrieved reliably. 8. Add automated tests covering low-volume rejection, missing-volume rejection, existing-position accounting, repeated invocations, and concurrent execution. ]]>

T08 · Insecure Dependencies

Warning
Location
clawhub.json:3
Finding
Security-sensitive trading dependency is not version-pinned<![CDATA[ ## Vulnerability Details **File Location**: `clawhub.json:3-9`; dependency use in `trader.py:23` **Vulnerability Type**: Unpinned third-party executable dependency **Risk Level**: Medium ### Vulnerable Code ```json { "requires": { "env": [ "SIMMER_API_KEY" ], "pip": [ "simmer-sdk" ] } } ``` ```python from simmer_sdk import SimmerClient ``` The imported dependency is subsequently initialized with the sensitive credential: ```python _client = SimmerClient( api_key=os.environ["SIMMER_API_KEY"], venue=venue, ) ``` ### Technical Analysis The package requirement specifies only `simmer-sdk`, without an exact reviewed version or an integrity hash. Dependency resolution can therefore install a different release over time. This dependency occupies a security-sensitive position: its code executes inside the skill process, receives `SIMMER_API_KEY`, performs network requests, retrieves market data, and submits trades. A compromised upstream release, account takeover, malicious dependency substitution, or unexpectedly incompatible update would execute with the same process permissions as the skill. No evidence was found that the currently referenced package is malicious. The confirmed weakness is the absence of reproducible, integrity-controlled dependency resolution. ### Attack Path 1. The skill is installed or its dependencies are refreshed. 2. The package manager resolves the unversioned `simmer-sdk` requirement to the currently available release rather than a specifically reviewed build. 3. A compromised or behaviorally incompatible release is downloaded and installed. 4. Python imports the package when `trader.py` starts. 5. The package code executes in the skill process and receives `SIMMER_API_KEY` through the `SimmerClient` constructor. 6. A malicious release could transmit the credential, falsify market information, redirect API calls, or modify live trade parameters before submission. ### Impact Assessme ...[truncated 666 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Pin `simmer-sdk` to an exact version that has been reviewed and tested: ```json "pip": [ "simmer-sdk==<reviewed-version>" ] ``` 2. Use a lockfile or hash-verified requirements file so installation verifies the expected distribution artifact. 3. Record the approved package version and cryptographic hash in release metadata. 4. Review upstream release notes and source changes before upgrading the dependency. 5. Use an isolated virtual environment and install only required dependencies. 6. Restrict the API key to the minimum permissions and account balance necessary for the strategy. 7. Rotate the credential if dependency compromise is suspected. 8. Add dependency scanning and provenance verification to the release process, including checks for known vulnerabilities, unexpected ownership changes, and package-name substitution. ]]>
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
84% confidence
Finding
The skill declares a required environment credential (`SIMMER_API_KEY`) and implicitly relies on environment access, but it does not declare any explicit tool scope such as `permissions` or `allowed-tools`. That mismatch weakens least-privilege controls and can cause the runtime to grant broader access than reviewers expect, increasing the chance that a credential-handling trading skill can access or misuse sensitive data.

Missing User Warnings

Medium
Confidence
96% confidence
Finding
The manifest declares a required API key and an automated trader entrypoint, but it does not disclose to users that the skill will access credentials and may place trades automatically. In a trading context, that missing warning materially increases the risk of users enabling the skill without understanding account access, capital exposure, or the consequences of autotrading behavior.

Static analysis

No suspicious patterns detected.