T09 · Insecure Skill Coding Practices
Error
- Location
- scripts/realtime-agent.js:428
- Finding
- Unauthenticated WebSocket Signals Can Trigger Real Financial Transactions<![CDATA[ ## Vulnerability Details **File Location**: `scripts/realtime-agent.js:428-460`, with unsafe execution defaults at `scripts/realtime-agent.js:556-562` **Vulnerability Type**: Unauthenticated remote trade instruction handling **Risk Level**: High ### Vulnerable Code ```js function connectWebsocket(config, state, userMargin) { if (state.stopRequested) { return; } const endpoint = wsUrl(); const socket = new WebSocket(endpoint); state.socket = socket; let openedAt = 0; socket.on("open", () => { openedAt = Date.now(); state.reconnectAttempts = 0; console.log(`[info] websocket connected: ${endpoint}`); socket.send( JSON.stringify({ type: "subscribe", channel: config.channel }) ); }); socket.on("message", (raw) => { let envelope; try { envelope = JSON.parse(String(raw)); } catch (_err) { return; } const signals = signalItemsFromEnvelope(envelope); for (const signal of signals) { const signalAgent = String(signal.agent_name || "").trim(); const side = normalizeSignalSide(signal.side); const confidence = toNumber(signal.confidence, 0); const ts = Math.floor(toNumber(signal.ts, Math.floor(Date.now() / 1000))); if (!side) { continue; } if (config.agentNameFilter && signalAgent !== config.agentNameFilter) { continue; } if (confidence < config.minConfidence) { continue; } enqueueSignalExecution(signal, side, confidence, ts, config, state, userMargin); } }); ``` The execution branch places a real order unless dry-run mode was explicitly enabled: ```js if (config.dryRun) { console.log(`[dry-run] node scripts/order-execute.js ${orderArgs.join(" ")}`); } else { const result = await executeOrder(orderArgs); if (result.stdout.trim()) { process.stdout.write(result.stdout); } if (result.stderr.trim()) { process.stderr.write(result.stderr); } } ...[truncated 4071 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Require authenticated WebSocket sessions before permitting trading signals. 2. Require every signal to have a cryptographic signature from an explicitly trusted signer. 3. Bind the signature to all security-relevant fields, including: - Channel - Signal ID - Agent ID - Market ID - Side - Confidence - Timestamp and expiration time - Intended wallet or trading session 4. Reject signals with missing, stale, or excessively future-dated timestamps. 5. Persist or maintain bounded replay protection using a unique signed signal ID rather than attacker-controlled display fields. 6. Verify that the envelope channel exactly matches the configured subscription. 7. Use immutable agent identifiers rather than self-asserted agent names. 8. Reject `ws://` for all non-loopback destinations. 9. Change direct autotrade to default to dry-run mode. Require an explicit `--live` option for real orders. 10. Set a finite default maximum order count and enforce cumulative notional, deposit, position, and loss limits locally. 11. Require explicit confirmation of market, margin, endpoint, trusted signer, and risk limits before starting a live autonomous session. 12. Consider requiring per-order confirmation unless an independently authenticated and time-bounded live-trading session has been established. ]]>
