Back to skill

Security audit

Kalshi Econ Revision Drift Trader

Security checks for vulnerabilities and agentic risk

Overview

This is a disclosed dry-run-by-default Kalshi trading skill, but live mode can spend real funds and the artifacts show safety and credential-scope concerns that require careful review.

Review before installing in any funded environment. Keep it in dry-run unless you are comfortable exposing both trading API credentials and a Solana private key to the skill process and its SDK dependency. For live use, pin and review simmer-sdk, confirm the private key is required and isolated, and fix or account for the fail-open safeguard behavior so missing market context cannot authorize new trades.

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)

T08 · Insecure Dependencies

Error
Location
clawhub.json:8
Finding

Unpinned High-Privilege Trading Dependency Creates a Supply-Chain Risk

Content
View full analysis
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
trader.py:339
Finding

Trading Safeguards Fail Open When Market Context Retrieval Fails

Content
View full analysis
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, [] reasons = [] ``` The resulting live-trade path at `trader.py:605-628` is: ```python # Safeguards ctx = get_market_context(market_id, my_probability=revised_p if side == "yes" else 1 - revised_p) ok, reasons = check_safeguards(ctx) if not ok: log(f" Skipped: {'; '.join(reasons)}") skip_reasons.extend(reasons) continue if reasons: log(f" Warnings: {'; '.join(reasons)}") if not dry_run: result = execute_trade( market_id, side, size, reasoning=f"Revision drift: bin={lo_str}-{hi_str} mkt={mkt_price:.1%} revised={revised_p:.1%} edge={edge:+.1%} bias=+{REVISION_BIAS_PP}pp", signal_data={"revised_prob": round(revised_p, 4), "initial_prob": round(b["initial_prob"], 4), "bin_lower": lo if lo > float('-inf') else -99, "bin_upper": hi if hi < float('inf') else 99, "edge": round(edge, 4), "revision_bias": REVISION_BIAS_PP, "cpi_mean_estimate": round(cpi_mean, 4)}, ) ``` ### Technical Analysis `get_market_context()` catches every exception and ...[truncated 2980 chars]
Remediation
View remediation
tuple[bool, list[str]]: if not isinstance(context, dict) or not context: return False, ["Market context unavailable; refusing to open exposure"] ``` 2. Preserve and log a sanitized description of the context-retrieval error rather than silently discarding it. 3. Catch only expected network or SDK exceptions instead of every `Exception`. 4. Validate the schema and types of all required context fields before approving a trade. 5. Require fresh values for market status, time to resolution, slippage, and liquidity in live mode. 6. Add bounded retries with exponential backoff for transient context failures. If retries are exhausted, skip the entry. 7. Separate entry and exit policies: - New exposure should always fail closed. - Risk-reducing exits should follow a documented contingency policy and enforce local price, size, and slippage limits where possible. 8. Add tests proving that timeouts, empty dictionaries, malformed responses, authentication errors, and SDK exceptions cannot lead to a live entry order. 9. Emit an automaton skip reason such as `context_unavailable` so operators can distinguish a blocked safety check from a lack of trading signals. ]]>
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 (8)

Context-Inappropriate Capability

High
Category
Not specified by scanner
Confidence
99% confidence
Finding

Requesting a blockchain wallet private key without a stated need is a serious over-privilege issue. In this context, the skill is supposed to trade Kalshi CPI markets, so access to a Solana private key is unrelated and could enable theft of funds, unauthorized signing, or persistence into the user's broader crypto environment if mishandled.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
91% confidence
Finding

The skill requests high-value credentials via environment variables, including an API key and a live-trading private key, but does not declare an explicit tool/permission scope in the skill metadata. In an agent ecosystem, that weakens least-privilege controls and makes it easier for the skill to access sensitive secrets without transparent review, which is especially concerning because the skill can trigger real financial trades when run with live mode.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
86% confidence
Finding

This JSON manifest defines runtime settings and an entrypoint, but it provides no invocation description, trigger phrases, or constraints explaining when the skill should activate. For manifest files, missing trigger specificity can lead to ambiguous or unintended invocation because there are no positive or negative activation conditions documented.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
98% confidence
Finding

The manifest requests a SOLANA_PRIVATE_KEY even though the skill description and declared Python dependency only justify Kalshi/CPI trading via SIMMER_API_KEY. Unnecessary private-key access materially expands the blast radius: if the skill code, its dependencies, or the runtime are compromised, a sensitive blockchain credential could be exfiltrated or abused for unrelated asset transfers.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Low
Category
Not specified by scanner
Confidence
89% confidence
Finding

The manifest description and metadata say the skill requires SIMMER_API_KEY and simmer-sdk, but the documentation later states that both SIMMER_API_KEY and SOLANA_PRIVATE_KEY are required for setup/live trading. This is an active documentation contradiction about required credentials, which can mislead operators about the skill's true execution requirements.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
93% confidence
Finding

Requiring a locale- or ecosystem-specific blockchain private key without justification is another sign of unjustified credential collection. Even if not immediately malicious, this kind of secret request increases exposure of highly sensitive user assets and is especially risky because private keys are irreversible, high-value credentials.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Low
Category
Not specified by scanner
Confidence
91% confidence
Finding

The skill description says it trades CPI revision-bias opportunities, but the code also scans current positions and can automatically sell them via check_exits(). In an automated trading context, undocumented exit and position-management behavior can surprise operators, cause unintended liquidation, and undermine review or approval controls even if the logic is not overtly malicious.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Low
Category
Not specified by scanner
Confidence
95% confidence
Finding

The CLI supports --set and then calls update_config(), which persistently modifies local strategy configuration on disk, but that side effect is not disclosed in the manifest. Hidden persistence changes are risky because they alter future trading behavior beyond the current run, reducing operator awareness and making review, rollback, and forensic attribution harder.

Content

No source excerpt is available for this finding.