Back to skill

Security audit

Polymarket 24h Price Curve Arb Trader

Security checks for vulnerabilities and agentic risk

Overview

This trading skill is not malicious, but it should be reviewed because it can place real trades with an API key and several documented safety limits are missing or inconsistent.

Install only if you are comfortable giving this skill a scoped Simmer/Polymarket trading key. Keep it in paper mode until the liquidity filter, position-limit semantics, and threshold default are corrected or explicitly accepted, and prefer a pinned reviewed simmer-sdk version before using live trading.

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)

T08 · Insecure Dependencies

Warning
Location
clawhub.json:6
Finding
Unpinned Third-Party Trading Dependency Creates a Supply-Chain Risk<![CDATA[ ## Vulnerability Details **File Location**: `clawhub.json:6-9` **Additional Location**: `SKILL.md:119-122` **Vulnerability Type**: Unpinned executable dependency **Risk Level**: Medium ### Vulnerable Code ```json "requires": { "env": [ "SIMMER_API_KEY" ], "pip": [ "simmer-sdk" ] } ``` The documentation identifies the dependency but does not constrain its version: ```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 an exact version constraint or package-integrity hash. Consequently, installations performed at different times may resolve to different and mutable package releases. This dependency operates in a security-sensitive context: it receives `SIMMER_API_KEY`, performs remote market discovery, and submits simulated or real financial trades. A compromised upstream release, malicious maintainer update, or package-index compromise could therefore introduce arbitrary installation-time or runtime behavior without any modification to the audited project. The external dependency source was not included in the project and was outside the static audit scope. No claim is made that its current release is malicious; the issue is the absence of controls that ensure the reviewed dependency remains the dependency that is installed. ### Attack Path 1. An attacker compromises the upstream package publisher, release process, or package-index account. 2. The attacker publishes a malicious or backdoored version under the existing `simmer-sdk` package name. 3. A user installs or deploys this Skill without a lock file or exact version constraint. 4. The package resolver selects the attacker-controlled release. 5. Dependency code executes during installation or when `SimmerClient` is imported and initialized. 6. The malicious dependency can access the p ...[truncated 601 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Pin `simmer-sdk` to a reviewed exact version rather than an unconstrained package name. 2. Use a lock file containing cryptographic hashes, or install with `pip --require-hashes`. 3. Verify package provenance and compare the package artifact against the reviewed upstream source. 4. Run dependency vulnerability and integrity checks in CI. 5. Review dependency updates before changing the pin; do not automatically accept new releases. 6. Restrict the API key to the minimum trading permissions and financial limits supported by the service. 7. Run the Skill in an isolated environment with limited filesystem and network privileges. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
trader.py:303
Finding
Configured Minimum Market Volume Safeguard Is Not Enforced<![CDATA[ ## Vulnerability Details **File Location**: `trader.py:303-323` **Related Locations**: `trader.py:42`, `SKILL.md:100` **Vulnerability Type**: Missing enforcement of a declared financial risk control **Risk Level**: Medium ### Vulnerable Code The minimum-volume setting is loaded: ```python MIN_VOLUME = float(os.environ.get("SIMMER_MIN_VOLUME", "5000")) ``` However, the complete market-validation function never reads market volume or compares it with `MIN_VOLUME`: ```python def valid_market(market) -> tuple[bool, str]: 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" ``` The documented control is: ```markdown | `SIMMER_MIN_VOLUME` | `5000` | Min market volume filter (USD) | ``` ### Technical Analysis `SIMMER_MIN_VOLUME` is presented as a risk-control tunable and is loaded into application state, but it is never used in market discovery, curve construction, signal computation, or final validation. A market can therefore pass `valid_market()` regardless of whether it has zero, missing, or extremely low trading volume. Low-volume markets are easier to move with small orders and can display stale or manipulable probabilities. Since the strategy treats probability inconsistencies as trading signals, failure to enforce liquidity requirements makes it possible for ...[truncated 1151 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Read the SDK's authoritative market-volume field in `valid_market()`. 2. Validate that volume is numeric, finite, non-negative, denominated consistently, and at least `MIN_VOLUME`. 3. Fail closed when volume is missing or malformed rather than allowing the market through. 4. Prefer recent executable volume or order-book depth over lifetime volume where the SDK supports it. 5. Revalidate liquidity immediately before submitting a live order. 6. Add tests covering volume below the threshold, exactly at the threshold, above the threshold, missing volume, and malformed volume. 7. Ensure the documented field name and unit match the SDK's actual market model. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
trader.py:431
Finding
Maximum Position Count Only Limits Orders in the Current Run<![CDATA[ ## Vulnerability Details **File Location**: `trader.py:431-461` **Related Locations**: `trader.py:45`, `SKILL.md:103` **Vulnerability Type**: Ineffective portfolio exposure limit **Risk Level**: Medium ### Vulnerable Code ```python # Execute trades on best violations placed = 0 for market_id, opp in sorted(all_opps.items(), key=lambda x: -x[1][2]): if placed >= MAX_POSITIONS: break market = opp[0] side, size, reasoning = compute_signal(market, opp) 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=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}" print(f" [trade] {side.upper()} ${size} {tag} {status} — {reasoning[:110]}") if r.success: placed += 1 except Exception as e: print(f" [error] {market_id}: {e}") ``` The setting is documented as: ```markdown | `SIMMER_MAX_POSITIONS` | `8` | Max concurrent open positions | ``` ### Technical Analysis The `placed` counter starts at zero every time `run()` executes and counts only successful orders submitted during that invocation. The implementation does not query the account's existing open positions, pending orders, or current exposure. As a result, `MAX_POSITIONS` is an order-per-run limit rather than the documented concurrent-position limit. Repeated manual executions or later automation can add up to `MAX_POSITIONS` successful orders on every run while prior positions remain open. The code also does not reserve capacity atomically. Concurrent instances could each observe their own local counter as zero and submit orders in ...[truncated 1029 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Query current open positions and pending orders before processing opportunities. 2. Compute remaining capacity as the configured maximum minus existing qualifying exposure. 3. Deduplicate opportunities against markets already held or having pending orders. 4. Recheck capacity immediately before each order because portfolio state may change during execution. 5. Use an account-level atomic limit or server-side risk control where available. 6. Prevent overlapping executions with a lock, or design the limit to remain safe under concurrency. 7. Fail closed when portfolio state cannot be retrieved in live mode. 8. Rename the setting if the intended behavior is only “maximum orders per run”; otherwise implement the documented concurrent-position semantics. ]]>

T09 · Insecure Skill Coding Practices

Note
Location
trader.py:50
Finding
Documented Violation Threshold Does Not Match the Executable Default<![CDATA[ ## Vulnerability Details **File Location**: `trader.py:50` **Related Locations**: `clawhub.json:105-116`, `SKILL.md:106` **Vulnerability Type**: Security-relevant configuration inconsistency **Risk Level**: Low ### Vulnerable Code The executable default is 2%: ```python # Minimum curve violation magnitude to trade (prevents noise trades) MIN_VIOLATION = float(os.environ.get("SIMMER_MIN_VIOLATION", "0.02")) ``` The package configuration also sets 2%: ```json { "env": "SIMMER_MIN_VIOLATION", "type": "number", "default": 0.02, "range": [ 0.01, 0.2 ], "step": 0.01, "label": "Min curve violation to trade" } ``` The user-facing documentation instead states 4%: ```markdown | `SIMMER_MIN_VIOLATION` | `0.04` | Min curve violation magnitude to trigger a trade | ``` ### Technical Analysis The documentation promises a default minimum violation of 4%, while both the executable code and package metadata use 2%. The lower executable threshold broadens the set of eligible signals and allows trades on smaller apparent inconsistencies. Because the threshold is specifically described as preventing noise trades, this discrepancy affects the expected financial risk profile. A user who reviews the documentation but does not override the environment variable may unknowingly operate with a less conservative setting. ### Attack Path 1. A user reviews `SKILL.md` and expects trades to require a violation greater than 4%. 2. The user enables live trading without explicitly setting `SIMMER_MIN_VIOLATION`. 3. The runtime obtains the 2% default from code or package configuration. 4. A market displays an apparent violation between 2% and 4%, potentially due to noise, stale data, or manipulation. 5. `find_violations()` accepts the signal because it exceeds the actual 2% threshold. 6. If the remaining gates pass, the Skill submits a live order that the documented configuration implied would be rejected. ### Impact Assessment The inconsistency may ...[truncated 293 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Select one reviewed default and use it consistently in `SKILL.md`, `clawhub.json`, and `trader.py`. 2. If the documented safety posture is intended, change both executable defaults to `0.04`. 3. Maintain tunable defaults in a single source of truth and generate documentation from it where practical. 4. Add a CI test that compares documented defaults, package metadata, and runtime fallbacks. 5. Log the effective risk parameters after remote configuration is applied, especially before live trading. 6. Require explicit confirmation when live-mode parameters are less conservative than documented baseline values. ]]>
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
81% confidence
Finding
The skill references a high-value environment credential (`SIMMER_API_KEY`) but does not declare any explicit tool scope or permissions boundary for environment access. In agent platforms, undeclared access to environment variables weakens least-privilege controls and can allow the skill to read sensitive secrets it was not narrowly scoped to use, increasing the chance of credential exposure or misuse if the skill is modified or composed with other components.

Missing User Warnings

Medium
Confidence
83% confidence
Finding
This manifest requires the sensitive environment variable `SIMMER_API_KEY`, but the file provides no warning or disclosure about credential use, storage, or the fact that the skill will operate with authenticated access. Because this is a trading-related skill, omission of any warning about use of an API key could leave users unaware of the sensitivity of the permission they are granting.

Static analysis

No suspicious patterns detected.