Back to skill

Security audit

Polymarket Energy Transition Trader

Security checks for vulnerabilities and agentic risk

Overview

This skill is disclosed as a Polymarket trading tool, but its live-trading safeguards can fail open and may exceed configured financial limits.

Review before installing, especially if you intend to use --live. Use a narrowly funded and scoped Simmer/Polymarket credential, keep the skill in paper mode until the fail-open checks and trade-size validation are fixed, and pin or review the simmer-sdk dependency before trusting it with live trading authority.

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

Warning
Location
clawhub.json:3
Finding
Unpinned Trading SDK Creates a Supply-Chain Risk<![CDATA[ ## Vulnerability Details **File Location**: `clawhub.json:3-9` **Vulnerability Type**: Unpinned third-party dependency **Risk Level**: Medium ### Vulnerable Code ```json "requires": { "env": [ "SIMMER_API_KEY" ], "pip": [ "simmer-sdk" ] } ``` The dependency is also documented without a version constraint at `SKILL.md:115-117`: ```markdown ## Dependency `simmer-sdk` by Simmer Markets (SpartanLabsXyz) - PyPI: https://pypi.org/project/simmer-sdk/ - GitHub: https://github.com/SpartanLabsXyz/simmer-sdk ``` ### Technical Analysis The project installs `simmer-sdk` without pinning a version or verifying a package hash. This SDK is imported directly by `trader.py` and receives the high-value `SIMMER_API_KEY`. It is also responsible for market discovery, configuration retrieval, and trade submission. Because no audited version is fixed, a future compromised or malicious package release could be installed automatically. Python modules execute top-level code when imported, so a compromised release would not need to exploit any additional defect in the project. The audit found no evidence that the currently referenced package is malicious. The vulnerability is the absence of controls that ensure the installed dependency remains the reviewed dependency. ### Attack Path 1. An attacker compromises the upstream package publishing account, build pipeline, or distribution artifact. 2. The attacker publishes a malicious version of `simmer-sdk`. 3. The Skill is installed or rebuilt, and the unconstrained dependency resolves to the malicious version. 4. `trader.py` imports `SimmerClient` from the package. 5. Malicious import-time or runtime code executes with the same operating-system privileges as the Skill. 6. The package can access `SIMMER_API_KEY`, observe market and order data, and alter or redirect trading operations. ### Impact Assessment A compromised dependency could obtain all privileges available to the Python process. Within the de ...[truncated 276 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Pin `simmer-sdk` to a specifically reviewed version rather than accepting any available release. 2. Use a lockfile containing hashes for all direct and transitive dependencies. 3. Install dependencies with hash verification, such as `pip install --require-hashes`. 4. Verify the package publisher, source repository, release signatures, and build provenance. 5. Run the trading process with a dedicated low-privilege operating-system account. 6. Restrict the process's filesystem and network access to only what is required. 7. Use a narrowly scoped API credential and rotate it after any suspected dependency compromise. 8. Review and retest dependency updates before changing the pinned version. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
trader.py:257
Finding
Live Trading Safeguards Are Incomplete and Fail Open<![CDATA[ ## Vulnerability Details **File Location**: `trader.py:257-269`, `trader.py:285-309`, and `trader.py:311-347` **Vulnerability Type**: Incomplete and fail-open financial safety validation **Risk Level**: High ### Vulnerable Code The spread and resolution checks at `trader.py:257-269` allow missing spread information and ignore resolution parsing errors: ```python # Spread gate if market.spread_cents is not None and market.spread_cents / 100 > MAX_SPREAD: return None, 0, f"Spread {market.spread_cents/100:.1%} > {MAX_SPREAD:.1%}" # Days-to-resolution gate if market.resolves_at: try: resolves = datetime.fromisoformat(market.resolves_at.replace("Z", "+00:00")) days = (resolves - datetime.now(timezone.utc)).days if days < MIN_DAYS: return None, 0, f"Only {days} days to resolve" except Exception: pass ``` The context and slippage check at `trader.py:285-309` returns approval after an SDK or API failure: ```python def context_ok(client: SimmerClient, market_id: str) -> tuple[bool, str]: """Check flip-flop and slippage safeguards.""" try: ctx = client.get_market_context(market_id) if not ctx: return True, "no context" if ctx.get("discipline", {}).get("is_flip_flop"): reason = ctx["discipline"].get("flip_flop_reason", "recent reversal") return False, f"Flip-flop: {reason}" slip = ctx.get("slippage", {}) if isinstance(slip, dict) and slip.get("slippage_pct", 0) > 0.15: return False, f"Slippage {slip['slippage_pct']:.1%}" for w in ctx.get("warnings", []): print(f" [warn] {w}") except Exception as e: print(f" [ctx] {market_id}: {e}") return True, "ok" ``` The execution loop limits orders placed during the current invocation, but it does not retrieve or count existing open positions: ```python placed = 0 for m in markets: if placed >= MAX_POSITIONS: break ...[truncated 2871 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. In live mode, reject markets when spread, volume, resolution, or context data is missing or malformed. 2. Replace broad exception suppression with explicit validation and fail-closed error handling. 3. Enforce `MIN_VOLUME` before computing or submitting any trade. 4. Query the account's current open positions and ensure the total, including the proposed order, remains within `MAX_POSITIONS`. 5. Distinguish between a confirmed safe context and unavailable context; unavailable context must not return approval in live mode. 6. Require valid numeric values with defined ranges for probability, spread, slippage, and volume. 7. Require a valid timezone-aware resolution timestamp when `MIN_DAYS` is enabled. 8. Consider permitting fail-open behavior only in paper mode, with a prominent warning. 9. Add automated tests for missing fields, malformed timestamps, API exceptions, empty context responses, low-volume markets, and pre-existing positions. 10. Align the documented defaults in `SKILL.md` with the actual defaults in `trader.py` and `clawhub.json`. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
trader.py:271
Finding
Minimum Trade Floor Can Exceed the Configured Maximum Position<![CDATA[ ## Vulnerability Details **File Location**: `trader.py:271-283` and `clawhub.json:11-20,91-101` **Vulnerability Type**: Incorrect financial limit enforcement **Risk Level**: High ### Vulnerable Code Both signal branches at `trader.py:271-283` apply a minimum floor without applying a final maximum cap: ```python if p <= YES_THRESHOLD: # conviction=0 at threshold boundary, conviction=1 at p=0 — scaled by transition bias conviction = min(1.0, (YES_THRESHOLD - p) / YES_THRESHOLD * bias) size = max(MIN_TRADE, round(conviction * MAX_POSITION, 2)) edge = YES_THRESHOLD - p return "yes", size, f"YES {p:.0%} edge={edge:.0%} bias={bias:.2f}x size=${size} — {q[:65]}" if p >= NO_THRESHOLD: conviction = min(1.0, (p - NO_THRESHOLD) / (1 - NO_THRESHOLD) * bias) size = max(MIN_TRADE, round(conviction * MAX_POSITION, 2)) edge = p - NO_THRESHOLD return "no", size, f"NO YES={p:.0%} edge={edge:.0%} bias={bias:.2f}x size=${size} — {q[:65]}" ``` The metadata allows the maximum position to be configured as low as 1 USD: ```json { "env": "SIMMER_MAX_POSITION", "default": 25, "range": [ 1, 200 ], "step": 1, "label": "Max position size (USD)" } ``` At the same time, the minimum trade can be configured as high as 100 USD: ```json { "env": "SIMMER_MIN_TRADE", "type": "number", "default": 5, "range": [ 1, 100 ], "step": 1, "label": "Min trade size (USD)" } ``` ### Technical Analysis The formula uses: ```python max(MIN_TRADE, calculated_size) ``` This guarantees a minimum order value but does not guarantee that the resulting value is less than or equal to `MAX_POSITION`. The metadata permits configurations where `MIN_TRADE > MAX_POSITION`. For example: - `SIMMER_MAX_POSITION=1` - `SIMMER_MIN_TRADE=100` For a qualifying signal, the expression returns 100, even though the configured maximum position is 1. This contradicts the source-code documentation stating that the result is capped so the ...[truncated 1398 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Reject startup configuration unless `0 < MIN_TRADE <= MAX_POSITION`. 2. Apply the maximum as the final sizing operation: ```python size = min(MAX_POSITION, max(MIN_TRADE, round(conviction * MAX_POSITION, 2))) ``` 3. Validate every numeric environment variable for type, finiteness, sign, and documented range. 4. Perform validation again after `apply_skill_config()` modifies the environment. 5. Reject contradictory configuration rather than silently altering the operator's limits. 6. Add tests covering equal limits, minimum above maximum, boundary probabilities, zero conviction, negative values, and non-finite values. 7. Treat `MAX_POSITION` as a hard server-side or SDK-side limit as well as an application-side check, if the trading platform supports it. ]]>
Vulnerability Patterns
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
Findings (2)

Lp3

Medium
Category
MCP Least Privilege
Confidence
87% confidence
Finding
The skill references environment-based credentials (`SIMMER_API_KEY`) and describes live trading capability, but it does not declare an explicit tool/permission scope such as allowed environment access. In an agent ecosystem, missing scope boundaries can let the skill access sensitive secrets more broadly than intended and increases the chance of unauthorized credential use or accidental exposure.

Vague Triggers

Medium
Confidence
87% confidence
Finding
The invocation text is broad and matches generic investing intent, which can cause the agent to auto-select this skill in situations where the user did not explicitly ask for speculative trading. Because this skill can ultimately place live trades when run with a flag and uses high-value credentials, over-broad routing increases the risk of inappropriate activation and financially consequential actions.

Static analysis

No suspicious patterns detected.