Skip to content

Commit 9fcbc86

Browse files
committed
kyzn(deep): improve security [run:20260318-192122-b1635917]
Health: 83 → 83 (→0) Cost: $1.2768795
1 parent 588e60f commit 9fcbc86

File tree

5 files changed

+38
-8
lines changed

5 files changed

+38
-8
lines changed
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
src
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
1773861838:/home/bokiko/Projects/btc-liquidations/src/hooks/useMultiExchangeWebSocket.ts:src
2+
1773861854:/home/bokiko/Projects/btc-liquidations/src/hooks/useMultiExchangeWebSocket.ts:src
3+
1773861869:/home/bokiko/Projects/btc-liquidations/src/hooks/useMultiExchangeWebSocket.ts:src
4+
1773861884:/home/bokiko/Projects/btc-liquidations/src/hooks/useMultiExchangeWebSocket.ts:src
5+
1773861899:/home/bokiko/Projects/btc-liquidations/src/hooks/useMultiExchangeWebSocket.ts:src
6+
1773861916:/home/bokiko/Projects/btc-liquidations/src/hooks/useMultiExchangeWebSocket.ts:src
7+
1773861933:/home/bokiko/Projects/btc-liquidations/src/hooks/useMultiExchangeWebSocket.ts:src
8+
1773861948:/home/bokiko/Projects/btc-liquidations/src/hooks/useMultiExchangeWebSocket.ts:src

.kyzn/.improve.lock

Whitespace-only changes.

.kyzn/config.yaml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,13 @@
55
project:
66
name: btc-liquidations
77
type: node
8-
98
preferences:
109
mode: deep
1110
budget: 2.50
1211
max_turns: 30
1312
diff_limit: 2000
1413
trust: guardian
1514
on_build_fail: report
16-
15+
model: opus
1716
focus:
1817
priorities: []

src/hooks/useMultiExchangeWebSocket.ts

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,11 @@ import { useState, useEffect, useCallback, useRef } from 'react';
44
import { Liquidation, Exchange, ExchangeConnection } from '@/types';
55

66
const MAX_LIQUIDATIONS = 200;
7-
const RECONNECT_DELAY = 3000;
7+
const RECONNECT_BASE_DELAY = 3000;
8+
const RECONNECT_MAX_DELAY = 60000;
9+
const MAX_MESSAGE_SIZE = 1024 * 1024; // 1MB guard against oversized payloads
10+
// Close codes that indicate we should not reconnect
11+
const NO_RECONNECT_CODES = new Set([1008, 1011, 4000, 4001, 4003]);
812

913
interface WebSocketConfig {
1014
exchange: Exchange;
@@ -113,6 +117,9 @@ const EXCHANGES: WebSocketConfig[] = [
113117

114118
if (valueUsd < threshold) continue;
115119

120+
const ts = parseInt(liq.ts);
121+
if (!isFinite(ts)) continue;
122+
116123
results.push({
117124
id: `okx-${liq.ts}-${liq.side}`,
118125
exchange: 'OKX',
@@ -121,7 +128,7 @@ const EXCHANGES: WebSocketConfig[] = [
121128
quantity,
122129
price,
123130
valueUsd,
124-
timestamp: new Date(parseInt(liq.ts)),
131+
timestamp: new Date(ts),
125132
});
126133
}
127134
return results;
@@ -239,6 +246,7 @@ export function useMultiExchangeWebSocket(threshold: number = 10000) {
239246

240247
const wsRefs = useRef<Map<Exchange, WebSocket>>(new Map());
241248
const reconnectTimeouts = useRef<Map<Exchange, NodeJS.Timeout>>(new Map());
249+
const reconnectAttempts = useRef<Map<Exchange, number>>(new Map());
242250
const thresholdRef = useRef(threshold);
243251

244252
// Keep threshold ref updated
@@ -255,13 +263,14 @@ export function useMultiExchangeWebSocket(threshold: number = 10000) {
255263
const connectExchange = useCallback(
256264
(config: WebSocketConfig) => {
257265
const existing = wsRefs.current.get(config.exchange);
258-
if (existing?.readyState === WebSocket.OPEN) return;
266+
if (existing?.readyState === WebSocket.OPEN || existing?.readyState === WebSocket.CONNECTING) return;
259267

260268
try {
261269
const ws = new WebSocket(config.url);
262270

263271
ws.onopen = () => {
264272
console.log(`Connected to ${config.exchange}`);
273+
reconnectAttempts.current.set(config.exchange, 0);
265274
updateConnection(config.exchange, { isConnected: true, error: null });
266275

267276
if (config.subscribe) {
@@ -280,6 +289,7 @@ export function useMultiExchangeWebSocket(threshold: number = 10000) {
280289

281290
ws.onmessage = (event) => {
282291
try {
292+
if (typeof event.data === 'string' && event.data.length > MAX_MESSAGE_SIZE) return;
283293
const data = JSON.parse(event.data);
284294
const incoming = config.parse(data, thresholdRef.current);
285295

@@ -300,16 +310,27 @@ export function useMultiExchangeWebSocket(threshold: number = 10000) {
300310
updateConnection(config.exchange, { error: 'Connection error' });
301311
};
302312

303-
ws.onclose = () => {
313+
ws.onclose = (event) => {
304314
updateConnection(config.exchange, { isConnected: false });
305-
console.log(`Disconnected from ${config.exchange}, reconnecting...`);
315+
316+
if (NO_RECONNECT_CODES.has(event.code)) {
317+
console.log(`${config.exchange} closed with code ${event.code}, not reconnecting`);
318+
updateConnection(config.exchange, { error: `Rejected (code ${event.code})` });
319+
return;
320+
}
321+
322+
const attempts = reconnectAttempts.current.get(config.exchange) || 0;
323+
const delay = Math.min(RECONNECT_BASE_DELAY * Math.pow(2, attempts), RECONNECT_MAX_DELAY);
324+
reconnectAttempts.current.set(config.exchange, attempts + 1);
325+
326+
console.log(`Disconnected from ${config.exchange}, reconnecting in ${delay}ms...`);
306327

307328
const existingTimeout = reconnectTimeouts.current.get(config.exchange);
308329
if (existingTimeout) clearTimeout(existingTimeout);
309330

310331
const timeout = setTimeout(() => {
311332
connectExchange(config);
312-
}, RECONNECT_DELAY);
333+
}, delay);
313334

314335
reconnectTimeouts.current.set(config.exchange, timeout);
315336
};
@@ -326,6 +347,7 @@ export function useMultiExchangeWebSocket(threshold: number = 10000) {
326347
const disconnectAll = useCallback(() => {
327348
reconnectTimeouts.current.forEach((timeout) => clearTimeout(timeout));
328349
reconnectTimeouts.current.clear();
350+
reconnectAttempts.current.clear();
329351

330352
wsRefs.current.forEach((ws) => ws.close());
331353
wsRefs.current.clear();

0 commit comments

Comments
 (0)