Back to skill

Security audit

Polymarket Food Agriculture Trader

Security checks for vulnerabilities and agentic risk

Overview

This automated trading skill is transparent about real-money use, but its risk controls do not fully match what it promises.

Review this carefully before installing. It defaults to paper trading and requires --live for real Polymarket orders, but do not rely on the documented minimum-volume or maximum-position safeguards until they are fixed and tested. Use a tightly scoped API key, keep live limits low, and pin/review the trading SDK dependency.

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)

T09 · Insecure Skill Coding Practices

Warning
Location
trader.py:303
Finding
Minimum Market Volume Safeguard Is Not Enforced<![CDATA[ ## Vulnerability Details **File Location**: `trader.py:32`, `trader.py:303-330` **Vulnerability Type**: Missing enforcement of a declared financial-risk control **Risk Level**: Medium ### Vulnerable Code ```python MIN_VOLUME = float(os.environ.get("SIMMER_MIN_VOLUME", "1000")) ``` ```python def run(live: bool = False) -> None: mode = "LIVE" if live else "PAPER (sim)" print(f"[polymarket-food-agriculture-trader] mode={mode} max_pos=${MAX_POSITION} min_vol=${MIN_VOLUME} max_spread={MAX_SPREAD:.0%} min_days={MIN_DAYS}") client = get_client(live=live) markets = find_markets(client) print(f"[polymarket-food-agriculture-trader] {len(markets)} candidate markets") placed = 0 for m in markets: if placed >= MAX_POSITIONS: break side, size, reasoning = compute_signal(m) if not side: print(f" [skip] {reasoning}") continue ok, why = context_ok(client, m.id) if not ok: print(f" [skip] {why}") continue try: r = client.trade( market_id=m.id, side=side, amount=size, source=TRADE_SOURCE, skill_slug=SKILL_SLUG, reasoning=reasoning, ) ``` ### Technical Analysis The application reads `SIMMER_MIN_VOLUME` and displays its value, but it never compares this threshold against a market's trading volume. Markets returned by `find_markets()` proceed directly through signal, context, and order-submission logic. This contradicts the documented purpose of `SIMMER_MIN_VOLUME` as a minimum-volume market filter. A configured safeguard that is not enforced creates a false sense of protection, particularly in live mode. Low-volume prediction markets are more susceptible to: - High price impact and poor execution. - Market-price manipulation. - Unreliable probability signals. - Difficulty exiting a position. - Slippage no ...[truncated 1105 chars]
Remediation
<![CDATA[ ## Remediation Suggestions Enforce the liquidity threshold before signal calculation and before any order can reach `client.trade()`: ```python def market_has_sufficient_volume(market) -> bool: try: volume = float(market.volume) except (AttributeError, TypeError, ValueError): return False return volume >= MIN_VOLUME ``` Use the check in the trading loop: ```python for m in markets: if not market_has_sufficient_volume(m): print(f" [skip] Insufficient or unavailable market volume") continue ``` Additional hardening should include: 1. Treat missing, malformed, stale, or negative volume as a failure rather than allowing the trade. 2. Confirm which SDK field represents the relevant volume and whether it is expressed in USD, USDC, cents, or another unit. 3. Consider requiring both minimum total volume and minimum order-book depth. 4. Revalidate liquidity immediately before live order submission. 5. Add tests proving that markets below the threshold, with missing volume, and with malformed volume never reach `client.trade()`. 6. Reconcile the documented default in `SKILL.md` with the effective default in `clawhub.json` and `trader.py`. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
trader.py:269
Finding
Minimum Trade Floor Can Bypass the Maximum Position Limit<![CDATA[ ## Vulnerability Details **File Location**: `trader.py:269-275`; related configuration ranges at `clawhub.json:19-27` and `clawhub.json:91-99` **Vulnerability Type**: Incorrect risk-limit calculation and missing cross-field validation **Risk Level**: Medium ### Vulnerable Code ```python if p <= YES_THRESHOLD: # conviction=0 at threshold boundary, conviction=1 at p=0 — scaled by harvest cycle 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)) ``` The allowed configuration ranges are: ```json { "env": "SIMMER_MAX_POSITION", "default": 20, "range": [ 1, 200 ], "step": 1, "label": "Max position size (USD)" } ``` ```json { "env": "SIMMER_MIN_TRADE", "type": "number", "default": 5, "range": [ 1, 100 ], "step": 1, "label": "Min trade size (USD)" } ``` ### Technical Analysis The order size is calculated with: ```python max(MIN_TRADE, conviction * MAX_POSITION) ``` This applies a lower bound but does not subsequently apply a hard upper bound. If `MIN_TRADE` is greater than `MAX_POSITION`, the selected size is `MIN_TRADE`, directly violating the maximum-position limit. The configuration schema independently permits `MAX_POSITION` as low as `1` and `MIN_TRADE` as high as `100`. No validation ensures that: ```text MIN_TRADE <= MAX_POSITION ``` Environment variables can also supply values without range or relationship validation. For example: ```text SIMMER_MAX_POSITION=1 SIMMER_MIN_TRADE=100 ``` Any qualifying signal produces an order of at least $100, despite the stated $1 maximum. This contradicts the documentation's claim that sizing i ...[truncated 1214 chars]
Remediation
<![CDATA[ ## Remediation Suggestions Validate all trading parameters after configuration is loaded: ```python def validate_config() -> None: if MAX_POSITION <= 0: raise ValueError("MAX_POSITION must be positive") if MIN_TRADE <= 0: raise ValueError("MIN_TRADE must be positive") if MIN_TRADE > MAX_POSITION: raise ValueError("MIN_TRADE must not exceed MAX_POSITION") ``` Apply an explicit hard cap when calculating the amount: ```python calculated = round(conviction * MAX_POSITION, 2) size = min(MAX_POSITION, max(MIN_TRADE, calculated)) ``` Further hardening should include: 1. Add schema-level cross-field validation where the configuration platform supports it. 2. Adjust individual ranges so obviously incompatible combinations cannot be selected. 3. Validate environment-derived values for finiteness, sign, range, and relationships. 4. Fail closed before client creation or market discovery if risk parameters are invalid. 5. Revalidate the final amount immediately before `client.trade()`. 6. Add unit tests for `MIN_TRADE < MAX_POSITION`, equality, `MIN_TRADE > MAX_POSITION`, zero, negative, NaN, infinity, and out-of-range inputs. 7. Ensure cumulative exposure is also constrained separately from the per-order maximum. ]]>

T08 · Insecure Dependencies

Note
Location
clawhub.json:5
Finding
Security-Critical Trading SDK Dependency Is Unpinned<![CDATA[ ## Vulnerability Details **File Location**: `clawhub.json:5-10` **Vulnerability Type**: Unpinned third-party dependency **Risk Level**: Low ### Vulnerable Code ```json "requires": { "env": [ "SIMMER_API_KEY" ], "pip": [ "simmer-sdk" ] } ``` ### Technical Analysis The project requests `simmer-sdk` without an exact version or integrity hash. Package resolution can therefore install a future release different from the release that was reviewed. This dependency is security-sensitive because it is imported directly by `trader.py`, receives `SIMMER_API_KEY`, performs network requests, loads skill configuration, retrieves market data, and submits simulated or live trades. Python package code can execute at import time, so a compromised dependency does not need an explicit application call to begin malicious activity. The audit found no evidence that the currently intended package is itself malicious. The issue is that deployment is not reproducible and does not constrain which future package version may execute. ### Attack Path 1. The upstream package publisher account, release process, or package repository is compromised, or a future package release introduces malicious behavior. 2. A deployment installs dependencies using the unversioned `simmer-sdk` requirement. 3. The resolver selects the compromised or unexpectedly changed release. 4. Package code executes when this statement runs: ```python from simmer_sdk import SimmerClient ``` 5. The dependency can access process environment variables, including `SIMMER_API_KEY`, and can alter client behavior. 6. It may exfiltrate credentials, falsify market information, redirect API requests, or modify live-order parameters. ### Impact Assessment A malicious dependency would execute with the same operating-system permissions as the skill process. Within that scope, it could potentially: - Read the `SIMMER_API_KEY` and other accessible environment variables. - Read or modify fi ...[truncated 448 chars]
Remediation
<![CDATA[ ## Remediation Suggestions Pin the dependency to an exact reviewed release: ```json "pip": [ "simmer-sdk==<reviewed-version>" ] ``` Where supported, also: 1. Generate and enforce a lockfile containing transitive dependency versions. 2. Require package hashes so installation verifies artifact integrity. 3. Install only from an explicitly trusted package index. 4. Review package ownership, release history, source repository, and published artifacts. 5. Run dependency vulnerability and provenance scanning in CI. 6. Test upgrades in an isolated environment before changing the pinned version. 7. Restrict the skill process using least-privilege filesystem access and outbound-network allowlists. 8. Scope and rotate `SIMMER_API_KEY`, and use separate credentials for simulated and live trading if supported. 9. Alert on dependency changes and require manual approval for updates to the trading SDK. ]]>
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
70% confidence
Finding
Without declared permissions the skill's intent is opaque and cannot be validated.

Missing User Warnings

Medium
Confidence
93% confidence
Finding
The manifest explicitly requires an API credential and points to an automated trading entrypoint, but it does not surface any user-facing disclosure that the skill will place networked trades using provided credentials. In a trading context, this omission is dangerous because users may enable the skill without understanding that it can autonomously access an external service and execute financially meaningful actions.

Static analysis

No suspicious patterns detected.