Back to skill

Security audit

Kalshi ETH Bin Distribution Trader

Security checks for vulnerabilities and agentic risk

Overview

This is a real-money trading skill with disclosed dry-run defaults, but it asks for a raw Solana private key too broadly and can trade live when safety context is unavailable.

Review this skill carefully before installing. Use only paper mode unless you are comfortable giving it trading authority, and avoid providing a wallet private key unless the package is changed to request it only for explicit live execution. The fail-open safeguard behavior should be fixed before live use.

Vulnerability Patterns
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • 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
Findings (5)

T05 · Unauthorized Access and Privilege Escalation

Error
Location
clawhub.json:3
Finding

Wallet Private Key Is Requested Outside Live-Trading Operations

Content
View full analysis
Remediation
View remediation

T08 · Insecure Dependencies

Warning
Location
clawhub.json:8
Finding

Security-Critical Trading Dependency Is Not Version or Hash Pinned

Content
View full analysis
SDK tab") sys.exit(1) venue = os.environ.get("TRADING_VENUE", "kalshi") _client = SimmerClient(api_key=api_key, venue=venue, live=live) ``` ### Technical Analysis The package specifies `simmer-sdk` without a fixed version, artifact hash, or lock file. A future installation can therefore resolve to a different package release than the release reviewed during this audit. Python package initialization code executes at import time. The dependency also receives `SIMMER_API_KEY` directly and runs in a process that may contain `SOLANA_PRIVATE_KEY`. A compromised publisher account, malicious future release, or compromised transitive dependency could therefore access credentials or alter order behavior. No evidence establishes that the current `simmer-sdk` release is malicious. The vulnerability is the absence of controls that bind installation to an audited artifact. ### Attack Path 1. An attacker compromises the package publisher, build pipeline, distribution account, or an unpinned transitive depe ...[truncated 901 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
trader.py:348
Finding

Trading Safeguards Fail Open When Market Context Is Unavailable

Content
View full analysis
tuple: """Check context for safeguards. Returns (should_trade, reasons).""" if not context: return True, [] ``` Context retrieval suppresses errors and returns no context: ```python def get_market_context(market_id: str, my_probability: float = None) -> dict: """Get market context with safeguards and edge analysis.""" try: if my_probability is not None: return get_client()._request( "GET", f"/api/sdk/context/{market_id}", params={"my_probability": my_probability} ) return get_client().get_market_context(market_id) except Exception: return None ``` ### Technical Analysis The context response provides information used to enforce resolved-market checks, flip-flop discipline, time-to-resolution limits, liquidity limits, and slippage limits. Any exception in `get_market_context` is converted to `None`, and `check_safeguards(None)` explicitly approves the trade. This reverses the safe default for a financial control. An inability to verify safety conditions should prevent a live order rather than bypass every safeguard. The failure can occur because of transient network errors, authentication problems, malformed API responses, service outages, or an attacker who can interfere with the context endpoint. ### Attack Path 1. The strategy identifies a market signal that qualifies for a trade. 2. Context retrieval fails because of an API outage, malformed response, timeout, or network interference. 3. `get_market_context` suppresses the exception and returns `None`. 4. `check_safeguards` returns `(True, [])`. 5. The strategy proceeds to `execute_trade`. 6. A live order is submitted without validating resolution state, time to ...[truncated 560 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
trader.py:254
Finding

Trade Sizing Can Exceed the Configured Maximum Position Limit

Content
View full analysis
0: conviction = min(abs(edge) / ENTRY_EDGE, 2.0) / 2.0 size = max(MIN_TRADE, round(conviction * MAX_POSITION_USD, 2)) return "yes", size, f"YES bin={p:.0%} fair={fair_price:.0%} edge={edge:+.0%} size=${size} -- {q[:70]}" else: conviction = min(abs(edge) / ENTRY_EDGE, 2.0) / 2.0 size = max(MIN_TRADE, round(conviction * MAX_POSITION_USD, 2)) return "no", size, f"NO bin={p:.0%} fair={fair_price:.0%} edge={edge:+.0%} size=${size} -- {q[:70]}" ``` The maximum position setting permits values below the default minimum trade: ```json { "env": "SIMMER_ETH_BINDIST_MAX_POSITION_USD", "type": "number", "default": 5.00, "range": [1, 200], "step": 1, "label": "Max position size (USD)" } ``` ### Technical Analysis `MIN_TRADE` defaults to `$5`, while `MAX_POSITION_USD` can be configured as low as `$1`. The calculation uses `max(MIN_TRADE, calculated_size)`, so the lower bound overrides the configured upper bound. For example, with `MAX_POSITION_USD=1` and `MIN_TRADE=5`, every qualifying signal is assigned at least `$5`. The behavior contradicts the meaning of a maximum position setting and defeats a user-defined financial control. The same flaw affects both YES and NO entry orders. ### Attack Path 1. A user configures `MAX_POSITION_USD` below `$5`, reasonably expecting it to be a hard cap. 2. A market signal exceeds the entry-edge threshold. 3. Conviction-based sizing produces a value at or below the configured maximum. 4. `max(MIN_TRADE, calculated_size)` raises the order to `$5`. 5. In live mode, the Skill submits an order larger than the user's configured maximum. 6. Additional qualifying signals can repeat the over-limit behavior up to the per- ...[truncated 509 chars]
Remediation
View remediation
MAX_POSITION_USD: return None, 0, "Configured maximum is below the minimum executable trade" size = min( MAX_POSITION_USD, max(MIN_TRADE, round(conviction * MAX_POSITION_USD, 2)), ) ``` 4. Validate all environment-derived numbers for finite values, positivity, and supported ranges. 5. Add tests for maximum values of `$1`, `$4`, `$5`, and larger amounts. 6. Apply a separate aggregate exposure limit across all markets and all trades in a run. 7. Confirm the final amount immediately before calling the SDK, rather than relying only on the signal calculation. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
trader.py:119
Finding

Heuristic Market Grouping Can Treat Incomplete or Unrelated Markets as Exhaustive Bins

Content
View full analysis
dict: """Group markets into bin sets that should sum to 100%. Returns dict of group_key -> list of (market, price) tuples. Markets are grouped by shared resolution date and event type. """ groups = {} for m in markets: question = (m.get("question") or "").lower() price = m.get("external_price_yes") or m.get("price_yes") or 0 resolve_date = m.get("resolves_at", "")[:10] # YYYY-MM-DD # Build group key from resolution date and base question # Strip specific price numbers to group related bins base_q = question for char in "0123456789$,.": base_q = base_q.replace(char, "") base_q = " ".join(base_q.split()[:6]) # First 6 words as group key group_key = f"{resolve_date}|{base_q}" if group_key not in groups: groups[group_key] = [] groups[group_key].append({"market": m, "price": price, "question": question}) # Filter to groups with 3+ bins (likely a complete bin set) return {k: v for k, v in groups.items() if len(v) >= 3} ``` The resulting group is normalized as though it were exhaustive: ```python total = sum(b["price"] for b in bin_group) if total <= 0: return [] adjustments = [] for b in bin_group: current = b["price"] fair = current / total # Normalize so sum = 1.0 ``` ### Technical Analysis The strategy's core assumption is valid only when markets are mutually exclusive, collectively exhaustive, and governed by the same event and resolution rules. The implementation does not establish those properties. Instead, it: - Removes all numeric and currency characters from market questions. - Uses only the first six remaining words. - Groups by a date substring rathe ...[truncated 1706 chars]
Remediation
View remediation
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (9)

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding

The declared description understates the skill's effective capabilities by omitting position management, market discovery, config persistence, journaling, and the additional live-trading credential. For a financial trading skill, this mismatch is dangerous because users may authorize or run it without realizing it can manage existing positions, persist settings, or use additional secrets to place live trades.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

High
Category
Not specified by scanner
Confidence
99% confidence
Finding

The manifest requests SOLANA_PRIVATE_KEY even though the skill description and dependency set are for a Kalshi ETH bin trader and provide no Solana-related functionality. Requesting an unrelated blockchain private key is a strong indicator of over-privileged credential collection and could enable theft of the user's Solana assets if the key is later accessed by the entrypoint.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

High
Category
Not specified by scanner
Confidence
99% confidence
Finding

Access to a Solana private key is context-inappropriate for a Kalshi ETH distribution trading skill, making the secret request suspicious on its face. In this context, the mismatch increases danger because users may provide a highly sensitive key under the assumption it is required for legitimate trading, creating a direct path to wallet compromise.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
87% confidence
Finding

The skill requests access to environment-sourced credentials but does not declare an explicit tool/permission scope. In a trading skill, undeclared env access increases the chance that an agent or platform grants broader secret access than users expect, which can expose API keys used for real-money actions.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
89% confidence
Finding

This markdown skill file describes what the skill does, but it does not define when the skill should be invoked, what user requests should trigger it, or any exclusions/negative examples. For manifest/markdown files, that missing trigger scope can cause ambiguous routing or unintended invocation by an agent selecting skills from descriptions alone.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The manifest asks for a private key but provides no warning about the sensitivity of that credential, how it will be used, or the financial risk of automated trading. Even if the key request were legitimate, omitting explicit disclosure increases the chance that users will unknowingly expose high-value secrets and enable unsafe automated actions.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The header documents only SIMMER_API_KEY as a requirement for the skill overall, but live mode also depends on SOLANA_PRIVATE_KEY and triggers real trading when run with --live. Understating credential and execution requirements is dangerous because operators may approve or deploy the skill under an incomplete trust model, leading to unintended exposure of sensitive signing keys and accidental live trading.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

The skill description says it trades ETH bin markets, but the implementation also performs remote market discovery and import operations through list_importable_markets and import_kalshi_market before trading. This expanded behavior increases risk because a user expecting a passive trader may unknowingly authorize asset-discovery and remote state-changing API actions, broadening both operational impact and attack surface.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Low
Category
Not specified by scanner
Confidence
95% confidence
Finding

The documentation inconsistently states credential requirements, first saying only SIMMER_API_KEY is required and later adding SOLANA_PRIVATE_KEY for live trading. In a trading context, conflicting secret requirements can cause users to provide sensitive private keys unnecessarily or misunderstand when real-money signing capability is enabled.

Content

No source excerpt is available for this finding.