Skip to content

Log info leakage: dispatch exc_info=True dumps trade data; KalshiError str() includes URLs #84

Description

@TexasCoding

From Wave 5 security audit, findings F-O-05 and F-O-09. Severity: medium.

F-O-05 — Pydantic ValidationError dumped via `exc_info=True` includes user trade data

`kalshi/ws/dispatch.py:88-95` does:
```python
logger.warning("Failed to parse %s message", msg_type, exc_info=True)
```

Pydantic v2's `ValidationError` repr includes the input dict that failed validation. For private channels — `fill`, `user_order`, `market_positions` — that dict contains user-identifiable trade data: order IDs, sizes in USD, side, ticker, fill prices, the user's `client_order_id`. Operators running at `WARNING` log level (default) or shipping warnings into a SIEM/Splunk/Datadog write financial PII to log infrastructure that's typically lower-trust than the trading environment.

Fix: Drop `exc_info=True` from the parse-failure log, or replace with a sanitized message like `logger.warning("Failed to parse %s message (validation error)", msg_type)`. Gate the full traceback behind an opt-in `KALSHI_DEBUG_WS=1` env flag.

Also: `dispatch.py:73` does `logger.warning("Received non-JSON frame: %s", raw[:100])` — log only the length and first/last 16 bytes, not 100 chars of arbitrary payload.

F-O-09 — `KalshiError` interpolates httpx exception strings (full URLs)

`kalshi/_base_client.py:136, 144, 249, 257` and `ws/connection.py:117-120`:
```python
KalshiError(f"Request timed out: {e}")
KalshiError(f"HTTP error: {e}")
KalshiConnectionError(f"WebSocket connection failed: {e}")
```
`str(e)` on an httpx exception typically contains the full URL including query string. For private endpoints (`/portfolio/positions?ticker=...`) the query isn't sensitive on Kalshi's surface — but anyone constructing a URL with a token-like value in a query param leaks it into uncaught-exception sinks (Sentry, stderr).

Fix: Strip the URL from `e` before interpolation, or use a fixed message string and rely on `cause` (`raise ... from e`) for the detail. Most error trackers serialize `cause` anyway.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingwsWebSocket-related

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions