Back to skill

Security audit

Kalshi Econ Nowcast Trader

Security checks for vulnerabilities and agentic risk

Overview

This real-money trading skill is not clearly malicious, but it needs careful review because it requests high-value trading credentials and its live-trading safeguards are not consistently fail-closed.

Review this before installing, especially for live mode. Use only a dedicated low-balance trading account or wallet, rotate credentials if testing, verify the simmer-sdk version yourself, and avoid enabling --live until market selection, nowcast freshness, and fail-closed safeguards are fixed or otherwise controlled.

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 (2)

T09 · Insecure Skill Coding Practices

Error
Location
trader.py:278
Finding

Market safety checks fail open when context retrieval fails

Content
View full analysis

Vulnerability Details

File Location: trader.py, lines 278-294; unsafe results are consumed at lines 405-406 and 545-546
Vulnerability Type: Fail-open risk validation
Risk Level: High

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


def check_safeguards(context: dict) -> tuple[bool, list[str]]:
    """Check context for safeguards. Returns (should_trade, reasons)."""
    if not context:
        return True, []

The unsafe result is used before both exit and entry orders:

python
ctx = get_market_context(market_id)
ok, reasons = check_safeguards(ctx)
python
ctx = get_market_context(
    market_id,
    my_probability=model_p if side == "yes" else 1 - model_p
)
ok, reasons = check_safeguards(ctx)

Technical Analysis

get_market_context() catches every exception without recording its cause and returns None. check_safeguards() then treats a missing or empty context as authorization to trade by returning (True, []).

Consequently, failures involving authentication, network connectivity, rate limits, malformed responses, service outages, or unexpected SDK behavior bypass all context-dependent controls. These controls include:

  • Resolved-market detection
  • Discipline and flip-flop warnings
  • Minimum time-to-resolution enforcement
  • Minimum liquidity enforcement
  • Maximum slippage enforcement

A safety mechanism controlling financial transactions should fail closed. The current implementation instead interprets an inability to v ...[truncated 1367 chars]

Remediation
View remediation

Remediation Suggestions

  1. Fail closed whenever context retrieval fails:

    python
    def get_market_context(market_id: str, my_probability: float = None) -> dict:
        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 as exc:
            safe_print(f"Context retrieval failed for {market_id}: {exc}")
            return None
    
    def check_safeguards(context: dict) -> tuple[bool, list[str]]:
        if not context:
            return False, ["Market context unavailable; refusing to trade"]
    
  2. Require successfully validated context before every live buy or sell order.

  3. Distinguish retryable failures, such as timeouts and rate limits, from permanent failures. Apply bounded retries with exponential backoff, but never trade after retries are exhausted.

  4. Validate the context response schema before reading safety fields. Missing required fields should block live execution.

  5. Preserve and report exception details without exposing credentials.

  6. Add tests confirming that timeouts, HTTP errors, malformed responses, empty dictionaries, and SDK exceptions all prevent live order submission.

T08 · Insecure Dependencies

Warning
Location
clawhub.json:3
Finding

Unpinned third-party trading dependency receives high-value credentials

Content
View full analysis

Vulnerability Details

File Location: clawhub.json, lines 3-10; also declared in SKILL.md, line 13 and instantiated in trader.py, lines 196-207
Vulnerability Type: Unpinned security-sensitive dependency
Risk Level: Medium

clawhub.json declares the dependency without a version constraint or integrity hash:

json
"requires": {
  "env": [
    "SIMMER_API_KEY",
    "SOLANA_PRIVATE_KEY"
  ],
  "pip": [
    "simmer-sdk"
  ]
},

SKILL.md similarly declares an unconstrained package:

yaml
requires_pip: "simmer-sdk"

The dependency is imported and given the API credential:

python
def get_client(live=True):
    """Lazy-init SimmerClient singleton."""
    global _client
    if _client is None:
        try:
            from simmer_sdk import SimmerClient
        except ImportError:
            print("Error: simmer-sdk not installed. Run: pip install simmer-sdk")
            sys.exit(1)
        api_key = os.environ.get("SIMMER_API_KEY")
        if not api_key:
            print("Error: SIMMER_API_KEY environment variable not set")
            print("Get your API key from: simmer.markets/dashboard -> SDK tab")
            sys.exit(1)
        venue = os.environ.get("TRADING_VENUE", "kalshi")
        _client = SimmerClient(api_key=api_key, venue=venue, live=live)
    return _client

Technical Analysis

simmer-sdk is installed without an exact version, lockfile, or cryptographic hash. The package is a security-sensitive dependency because it:

  • Executes inside the Skill process
  • Receives SIMMER_API_KEY directly
  • Mediates market and position queries
  • Submits live financial transactions
  • Runs in a process whose declared environment also contains SOLANA_PRIVATE_KEY

The audited project does not contain the dependency source, so the dependency's network destinations, credential storage, private-key handling, update behavior, and ...[truncated 2036 chars]

Remediation
View remediation

Remediation Suggestions

  1. Pin simmer-sdk to an exact, reviewed version rather than accepting arbitrary future releases.

  2. Use a lockfile with cryptographic hashes, or install with hash verification, so package contents cannot change silently.

  3. Review and retain the source corresponding to the pinned release. Verify package ownership, release provenance, signatures, and build artifacts.

  4. Document and enforce an allowlist of network destinations used by the SDK.

  5. Avoid placing SOLANA_PRIVATE_KEY in the general process environment unless live transaction signing strictly requires it. Prefer an isolated signer, restricted wallet, hardware-backed key, or narrowly scoped signing service.

  6. Apply server-side spending limits, per-order limits, venue restrictions, and credential rotation procedures. Client-side limits alone are insufficient if the dependency is compromised.

  7. Run the Skill under a dedicated low-privilege account or container with minimal filesystem access, restricted outbound networking, and no unrelated credentials.

  8. Continuously monitor dependency advisories and require explicit review before upgrading the pinned version.

Vulnerability Patterns
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • 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
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
Findings (8)

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding

The documented purpose understates the actual operational and credential footprint: the skill is not just analytical but can discover markets, manage positions, mutate config, and use an additional live-trading secret. This mismatch undermines informed consent and review, which is especially dangerous in a trading skill because users may provide sensitive credentials or enable automation without understanding that real position management and persistent changes are possible.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

High
Category
Not specified by scanner
Confidence
98% confidence
Finding

Access to a blockchain wallet credential is unjustified by the stated Kalshi nowcast-trading use case, making the requirement suspicious and dangerous in context. Because private keys are highly sensitive bearer credentials, merely granting the skill access creates a pathway for theft, unauthorized on-chain activity, or covert multi-platform abuse if the agent or one of its dependencies is malicious or compromised.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
84% confidence
Finding

The skill declares access to sensitive environment-backed credentials but does not specify an explicit tool/permission scope. In an agent ecosystem, missing scope declarations can cause overbroad access, make review harder, and increase the chance that high-value secrets such as API or wallet keys are exposed to components that do not strictly need them.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The metadata says only SIMMER_API_KEY is required, while later documentation also requires SOLANA_PRIVATE_KEY for live trading. Contradictory credential requirements are dangerous because they impair security review and can lead users to install or authorize a skill without realizing it may request or depend on a private signing key for real-money execution.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The manifest requests SOLANA_PRIVATE_KEY even though the skill description only justifies access to a Kalshi-related API key for CPI bin trading. Unnecessary access to a blockchain private key materially expands the blast radius: if the skill code, dependencies, or runtime are compromised, the key could be used to transfer wallet funds or sign unauthorized transactions unrelated to the stated purpose.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The skill presents itself as using the Cleveland Fed CPI Nowcast, but the actual estimate and standard deviation are taken from static environment variables with hardcoded defaults rather than being fetched and validated from the source. In a live trading context, stale or manipulated inputs can systematically misprice probabilities and trigger real-money trades on false assumptions.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The market discovery and filtering logic broadly imports and trades any active Kalshi market whose question contains CPI/inflation-related keywords, then attempts to parse ranges from the question text. That behavior exceeds the stated scope of trading only Cleveland Fed nowcast-backed CPI bin markets, creating strategy drift and the possibility of placing trades on unrelated or differently-structured inflation products using an invalid pricing model.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Low
Category
Not specified by scanner
Confidence
86% confidence
Finding

The prominent note says the skill is a template and that it 'handles all the plumbing (market discovery, trade execution, safeguards),' which implies concrete implemented behavior. However, in the provided skill file there is no code or executable content at all, only documentation, so the documentation overstates what is actually present here.

Content

No source excerpt is available for this finding.