Back to skill

Security audit

Simmer Market Maker

Security checks for vulnerabilities and agentic risk

Overview

This is a disclosed trading bot, but live use can cancel all open orders and place real trades with a broad API key, so it should be reviewed carefully before installation.

Install only if you understand that --live can place real orders and cancel existing open orders. Use paper mode first, use a dedicated trading account or scoped API key, do not run it in an account with unrelated active orders, pin/review simmer-sdk before installing, and avoid disabling safeguards unless you have independently reviewed the market and code behavior.

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:2
Finding
Unpinned Security-Sensitive Trading Dependency<![CDATA[ ## Vulnerability Details **File Location**: `clawhub.json:2-6` **Additional Location**: `SKILL.md:37-45` **Vulnerability Type**: Unpinned third-party dependency **Risk Level**: Medium ### Vulnerable Code ```json "requires": { "env": ["SIMMER_API_KEY"], "pip": ["simmer-sdk"] } ``` The installation documentation also instructs users to install the package without a version constraint or integrity verification: ```bash pip install simmer-sdk ``` ### Technical Analysis The Skill relies on `simmer-sdk` without pinning an exact reviewed version or providing package hashes. The dependency is security-sensitive because `market_maker.py` passes the user's `SIMMER_API_KEY` to `SimmerClient`, and the SDK is responsible for authenticated portfolio access and live trading operations. An unconstrained installation resolves whichever package version the configured Python package index currently considers latest. Consequently, the code ultimately executed can change after this Skill has been audited. A compromised publisher account, malicious future release, package-index compromise, or dependency-resolution error could introduce code with access to the API credential and the process's other ambient privileges. No evidence in the audited repository establishes that `simmer-sdk` is currently malicious. The vulnerability is the absence of dependency pinning and integrity controls around a package entrusted with credentials and financial operations. ### Attack Path 1. An attacker compromises the package publisher, distribution channel, or a future `simmer-sdk` release. 2. The attacker publishes a malicious version under the same package name. 3. A user follows the documented `pip install simmer-sdk` instruction or the Skill platform installs the unconstrained manifest dependency. 4. Package installation hooks or imported package code execute on the user's system. 5. When `get_client()` initializes the SDK, the malicious code can access the supplied API ...[truncated 874 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Pin `simmer-sdk` to an exact version that has been reviewed, for example: ```json "pip": ["simmer-sdk==<reviewed-version>"] ``` 2. Replace the installation instruction with the same exact version constraint. 3. Use a lockfile or requirements file containing cryptographic hashes, and install with hash verification: ```bash pip install --require-hashes -r requirements.txt ``` 4. Retrieve packages only from an explicitly configured, trusted package index. 5. Review SDK release changes before upgrading rather than accepting automatic latest-version resolution. 6. Use a narrowly scoped API key with only the permissions necessary for this strategy. 7. Run the Skill in an isolated environment without unrelated secrets, sensitive files, or unnecessary operating-system privileges. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
market_maker.py:204
Finding
Live Runs Cancel All Open Orders Without Strategy or Market Scoping<![CDATA[ ## Vulnerability Details **File Location**: `market_maker.py:204-215` **Invocation Location**: `market_maker.py:335-336` **Vulnerability Type**: Overbroad destructive trading operation **Risk Level**: Medium ### Vulnerable Code ```python def cancel_open_orders(dry_run=True): """Cancel all existing open orders via DELETE /api/sdk/orders.""" if dry_run: print(" [DRY RUN] Would cancel open orders") return True try: get_client()._request("DELETE", "/api/sdk/orders") print(" ✓ Cancelled existing open orders") return True except Exception as e: print(f" Warning: Could not cancel orders: {e}") return False ``` The operation is invoked before this run places replacement orders: ```python # Cancel existing open orders before placing new ones cancel_open_orders(dry_run=dry_run) ``` ### Technical Analysis The live cancellation request uses an unqualified `DELETE /api/sdk/orders` operation. It does not identify orders by trade source, strategy instance, selected market, token, or order ID. The function and documentation explicitly describe this behavior as cancelling all existing open orders. Refreshing this Skill's own quotes does not require cancellation of unrelated account orders. Therefore, the cancellation scope exceeds the minimum privileges and object scope required for the declared strategy. This is especially hazardous when the same account is used by a human trader, another automation process, or another strategy. The return value is also ignored by the caller. If cancellation fails partially or entirely, the strategy continues and may place new orders while old orders remain active, potentially producing duplicate or excess exposure. ### Attack Path 1. A trading account contains active orders created manually or by another strategy. 2. The operator invokes this Skill with `--live`. 3. After market selection, the Skill calls `cancel_open_orders(dry_run=False)`. 4. T ...[truncated 1025 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Retrieve open orders and cancel only orders created by this Skill, using the existing source identifier: ```python TRADE_SOURCE = "sdk:marketmaker" ``` 2. Further restrict cancellation to the selected market IDs and expected sides for the current strategy run. 3. Store and cancel explicit order IDs returned from prior runs rather than issuing an account-wide deletion. 4. If the API cannot filter server-side, list orders and validate each order's source and market locally before deleting it individually. 5. Treat cancellation failure as fail-closed: do not place replacement orders when prior quotes cannot be reliably removed. 6. Log the exact order IDs and markets that will be cancelled and require explicit confirmation for any account-wide cancellation mode. 7. Document whether the API key should be dedicated to this strategy and enforce server-side least-privilege permissions where available. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
market_maker.py:399
Finding
Pre-Trade Safeguards Are Not Consistently Applied to NO Orders<![CDATA[ ## Vulnerability Details **File Location**: `market_maker.py:399-455` **Vulnerability Type**: Inconsistent authorization and safety-check control flow **Risk Level**: Medium ### Vulnerable Code ```python if yes_bid > MAX_ORDER_USD / MIN_SHARES_PER_ORDER: reason = f"YES bid too high for min order at ${MAX_ORDER_USD}" print(f" ⚠ Skip YES: {reason}") skip_reasons.append(reason) else: # Safeguards if use_safeguards: context = get_market_context(market_id) should_trade, reasons = check_context_safeguards(context) if not should_trade: reason = "; ".join(reasons) print(f" 🛡 Safeguard blocked YES: {reason}") skip_reasons.append(reason) print() continue if reasons: print(f" ⚠ Safeguard warning: {'; '.join(reasons)}") reasoning = ( f"Market making YES bid: mid={mid:.3f}, quoting {yes_bid:.2f} " f"({SPREAD_OFFSET:.2f} below mid), vol_24h=${vol:,.0f}" ) if dry_run: print(f" [DRY RUN] Would place YES GTC limit @ {yes_bid:.2f} for ${MAX_ORDER_USD}") trades_attempted += 1 trades_executed += 1 else: trades_attempted += 1 result = execute_limit_order(market_id, "yes", MAX_ORDER_USD, yes_bid, reasoning) if result.get("success"): trades_executed += 1 print(f" ✓ YES limit placed @ {yes_bid:.2f} id={result.get('trade_id')}") elif result.get("skip_reason"): skip_reasons.append(result["skip_reason"]) print(f" ⚠ YES skipped: {result['skip_reason']}") else: err = result.get("error", "unknown error") execution_errors.append(err) print(f" ✗ YES failed: {err}") # NO side if no_bid > MAX_ORDER_USD / MIN_SHARES_PER_ORDER: reason = f"NO bid too high for min order at ${MAX_ORDER_USD}" print(f" ⚠ Skip NO: {reason}") skip_reaso ...[truncated 3149 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Evaluate market-level safeguards once per candidate before processing either side: ```python if use_safeguards: context = get_market_context(market_id) should_trade, reasons = check_context_safeguards(context) if not should_trade: # Skip the entire market. continue ``` 2. Place this check before both the YES and NO minimum-share branches. 3. Fail closed when market context cannot be retrieved if the safeguards are intended to be mandatory. The current `check_context_safeguards(None)` behavior permits trading. 4. Separate market-level controls from side-specific checks so that order eligibility cannot bypass resolution or discipline validation. 5. Add tests covering: - YES ineligible and NO eligible; - NO ineligible and YES eligible; - resolved-market context; - severe warning context; - excessive-slippage context; - context-fetch failure. 6. Require an explicit, clearly logged operator option before allowing live trading with safeguards disabled. ]]>
Vulnerability Patterns
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (6)

Tool Parameter Abuse

High
Category
Tool Misuse
Content
def cancel_open_orders(dry_run=True):
    """Cancel all existing open orders via DELETE /api/sdk/orders."""
    if dry_run:
        print("  [DRY RUN] Would cancel open orders")
        return True
Confidence
97% confidence
Finding
The function issues a broad DELETE to /api/sdk/orders, which appears to cancel all open orders without constraining the operation to orders associated with this strategy. In a trading context this is dangerous because it can silently interfere with unrelated active positions, destroy user intent, and trigger substantial financial loss through strategy interruption or missed fills.

Lp3

Medium
Category
MCP Least Privilege
Confidence
92% confidence
Finding
The skill describes use of environment variables and live network-backed trading actions, but it does not declare any explicit tool scope or permissions boundary. In an agent environment, this weakens reviewability and least-privilege controls, making it easier for the skill to access secrets and place or cancel orders without clear upfront authorization.

Missing User Warnings

Medium
Confidence
96% confidence
Finding
The usage section advertises a '--live' mode for real-money trading and the strategy also cancels existing open orders before placing new ones, but it does not provide a prominent warning about financial loss, unintended order placement, cancellation churn, or market/execution risk. In this context, omission is dangerous because users may switch from dry run to live mode without understanding that the skill can immediately affect real positions and capital.

Intent-Code Divergence

Medium
Confidence
87% confidence
Finding
The code claims Kalshi is unsupported for real trading, but the actual venue is taken from the TRADING_VENUE environment variable without enforcement. This mismatch can cause the strategy to operate against an unintended trading venue with different semantics, safeguards, or risk assumptions than the Polymarket-specific logic was designed for.

Description-Behavior Mismatch

Medium
Confidence
94% confidence
Finding
The skill unconditionally cancels all existing open orders before placing new ones, which exceeds the stated purpose of merely placing bid/ask limit orders for this strategy. In a shared trading account, this can disrupt unrelated user strategies or manually placed orders and create unintended financial exposure or lost opportunity.

Context-Inappropriate Capability

Low
Confidence
89% confidence
Finding
The skill's stated purpose is to find markets and place passive quotes, but the CLI also supports persisting configuration changes via --set and update_config(). Modifying local skill configuration is an auxiliary management capability that is not part of the declared trading function.

Static analysis

No suspicious patterns detected.