pyre-cli 3.9.0 → 3.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -2136,6 +2136,11 @@ var P2PServer = class {
2136
2136
  }
2137
2137
  onConnection(socket) {
2138
2138
  const ip = this.getPeerIP(socket);
2139
+ if (!socket.remoteAddress) {
2140
+ this.audit(ip, "CONNECT", "No remote address");
2141
+ socket.destroy();
2142
+ return;
2143
+ }
2139
2144
  const allowedIPs = this.options.allowedIPs ?? [];
2140
2145
  const deniedIPs = this.options.deniedIPs ?? [];
2141
2146
  if (!isIPAllowed(ip, allowedIPs, deniedIPs)) {
@@ -2164,7 +2169,7 @@ var P2PServer = class {
2164
2169
  pendingNonce: null
2165
2170
  };
2166
2171
  this.peers.set(socket, peer);
2167
- const remoteAddr = socket.remoteAddress ? `${socket.remoteAddress}:${socket.remotePort}` : "unknown";
2172
+ const remoteAddr = `${ip}:${socket.remotePort ?? "?"}`;
2168
2173
  this.log(chalk6.cyan(`Peer connected: ${remoteAddr}`));
2169
2174
  this.audit(ip, "CONNECT", remoteAddr);
2170
2175
  this.emitPeerEvent("connect", remoteAddr);
@@ -2196,45 +2201,50 @@ var P2PServer = class {
2196
2201
  const raw = body.toString("utf-8");
2197
2202
  const msg = decodeMessage(body);
2198
2203
  if (msg.kind !== "auth") {
2199
- peer.socket.write(signMessage("auth-fail", { ok: false, reason: "Expected auth message first" }, this.hmacKey));
2204
+ this.sendSigned(peer, signMessage("auth-fail", { ok: false, reason: "Expected auth message first" }, this.hmacKey));
2200
2205
  this.disconnectPeer(peer, "Expected auth message");
2201
2206
  this.emitPeerEvent("error", `Expected auth message from ${peer.ip}`);
2207
+ this.log(chalk6.red(`Auth failed from ${peer.ip}: expected auth message first`));
2202
2208
  return;
2203
2209
  }
2204
2210
  const authPayload = msg.payload;
2205
2211
  if (!authPayload.passwordHash || !authPayload.nonce) {
2206
- peer.socket.write(signMessage("auth-fail", { ok: false, reason: "Invalid auth payload" }, this.hmacKey));
2212
+ this.sendSigned(peer, signMessage("auth-fail", { ok: false, reason: "Invalid auth payload" }, this.hmacKey));
2207
2213
  this.disconnectPeer(peer, "Invalid auth payload");
2208
2214
  this.emitPeerEvent("error", `Invalid auth payload from ${peer.ip}`);
2215
+ this.log(chalk6.red(`Auth failed from ${peer.ip}: invalid auth payload`));
2209
2216
  return;
2210
2217
  }
2211
2218
  if (peer.pendingNonce && authPayload.nonce !== peer.pendingNonce) {
2212
2219
  this.audit(peer.ip, "AUTH_FAIL", "Nonce mismatch");
2213
- peer.socket.write(signMessage("auth-fail", { ok: false, reason: "Invalid nonce" }, this.hmacKey));
2220
+ this.sendSigned(peer, signMessage("auth-fail", { ok: false, reason: "Invalid nonce" }, this.hmacKey));
2214
2221
  this.disconnectPeer(peer, "Invalid nonce");
2215
2222
  this.emitPeerEvent("error", `Invalid nonce from ${peer.ip}`);
2223
+ this.log(chalk6.red(`Auth failed from ${peer.ip}: invalid nonce`));
2216
2224
  return;
2217
2225
  }
2218
2226
  const expectedHash = hashPassword(this.options.password, authPayload.nonce);
2219
2227
  if (authPayload.passwordHash !== expectedHash) {
2220
2228
  this.audit(peer.ip, "AUTH_FAIL", "Invalid password hash");
2221
- peer.socket.write(signMessage("auth-fail", { ok: false, reason: "Invalid password" }, this.hmacKey));
2229
+ this.sendSigned(peer, signMessage("auth-fail", { ok: false, reason: "Invalid password" }, this.hmacKey));
2222
2230
  this.disconnectPeer(peer, "Authentication failed");
2223
2231
  this.emitPeerEvent("error", `Invalid password from ${peer.ip}`);
2232
+ this.log(chalk6.red(`Auth failed from ${peer.ip}: invalid password`));
2224
2233
  return;
2225
2234
  }
2226
2235
  peer.authenticated = true;
2227
2236
  this.cleanupRateLimit(peer.ip);
2228
2237
  peer.pendingNonce = null;
2229
- peer.socket.write(signMessage("auth-ok", { ok: true }, this.hmacKey));
2230
- this.log(chalk6.green(`Peer authenticated: ${peer.socket.remoteAddress}`));
2238
+ this.sendSigned(peer, signMessage("auth-ok", { ok: true }, this.hmacKey));
2239
+ this.log(chalk6.green(`Peer authenticated: ${peer.ip}`));
2231
2240
  this.audit(peer.ip, "AUTH_OK", "Challenge-response successful");
2232
- this.emitPeerEvent("auth", peer.socket.remoteAddress ?? peer.ip);
2241
+ this.emitPeerEvent("auth", peer.ip);
2233
2242
  this.startDataStream(peer);
2234
2243
  this.startPing(peer);
2235
2244
  } catch {
2236
- peer.socket.write(signMessage("auth-fail", { ok: false, reason: "Invalid message format" }, this.hmacKey));
2245
+ this.sendSigned(peer, signMessage("auth-fail", { ok: false, reason: "Invalid message format" }, this.hmacKey));
2237
2246
  this.disconnectPeer(peer, "Invalid message format");
2247
+ this.log(chalk6.red(`Auth failed from ${peer.ip}: invalid message format`));
2238
2248
  }
2239
2249
  }
2240
2250
  sendSigned(peer, message) {
@@ -2266,16 +2276,17 @@ var P2PServer = class {
2266
2276
  }
2267
2277
  onPeerClose(peer) {
2268
2278
  this.cleanupPeer(peer);
2269
- this.log(chalk6.yellow(`Peer disconnected: ${peer.socket.remoteAddress}`));
2270
- this.emitPeerEvent("disconnect", peer.socket.remoteAddress ?? peer.ip);
2279
+ this.log(chalk6.yellow(`Peer disconnected: ${peer.ip}`));
2280
+ this.emitPeerEvent("disconnect", peer.ip);
2271
2281
  this.audit(peer.ip, "DISCONNECT", "Peer closed connection");
2272
2282
  }
2273
2283
  onPeerError(peer, err) {
2284
+ const addr = peer.socket.remoteAddress ?? peer.ip;
2274
2285
  const code = err.code;
2275
2286
  if (code === "ECONNRESET") {
2276
- this.log(chalk6.yellow(`Peer connection reset: ${peer.socket.remoteAddress}`));
2287
+ this.log(chalk6.yellow(`Peer connection reset: ${addr}`));
2277
2288
  this.audit(peer.ip, "ERROR", "Connection reset");
2278
- this.emitPeerEvent("error", `connection reset: ${peer.socket.remoteAddress}`);
2289
+ this.emitPeerEvent("error", `connection reset: ${addr}`);
2279
2290
  } else if (code === "EPIPE") {
2280
2291
  this.audit(peer.ip, "ERROR", "Broken pipe");
2281
2292
  } else {
@@ -2290,7 +2301,10 @@ var P2PServer = class {
2290
2301
  this.sendSigned(peer, signMessage("disconnect", { reason }, this.hmacKey));
2291
2302
  } catch {
2292
2303
  }
2293
- peer.socket.destroy();
2304
+ try {
2305
+ peer.socket.end();
2306
+ } catch {
2307
+ }
2294
2308
  }
2295
2309
  cleanupPeer(peer) {
2296
2310
  if (peer.intervalHandle) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pyre-cli",
3
- "version": "3.9.0",
3
+ "version": "3.11.0",
4
4
  "description": "CLI Mac monitoring tool: temps, system stats, live dashboard, packet monitor, battery predictor, export",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",
package/src/p2p/server.ts CHANGED
@@ -231,6 +231,12 @@ export class P2PServer {
231
231
  private onConnection(socket: net.Socket): void {
232
232
  const ip = this.getPeerIP(socket);
233
233
 
234
+ if (!socket.remoteAddress) {
235
+ this.audit(ip, 'CONNECT', 'No remote address');
236
+ socket.destroy();
237
+ return;
238
+ }
239
+
234
240
  const allowedIPs = this.options.allowedIPs ?? [];
235
241
  const deniedIPs = this.options.deniedIPs ?? [];
236
242
 
@@ -263,9 +269,7 @@ export class P2PServer {
263
269
  };
264
270
  this.peers.set(socket, peer);
265
271
 
266
- const remoteAddr = socket.remoteAddress
267
- ? `${socket.remoteAddress}:${socket.remotePort}`
268
- : 'unknown';
272
+ const remoteAddr = `${ip}:${socket.remotePort ?? '?'}`;
269
273
  this.log(chalk.cyan(`Peer connected: ${remoteAddr}`));
270
274
  this.audit(ip, 'CONNECT', remoteAddr);
271
275
  this.emitPeerEvent('connect', remoteAddr);
@@ -303,49 +307,54 @@ export class P2PServer {
303
307
  const raw = body.toString('utf-8');
304
308
  const msg = decodeMessage(body);
305
309
  if (msg.kind !== 'auth') {
306
- peer.socket.write(signMessage('auth-fail', { ok: false, reason: 'Expected auth message first' }, this.hmacKey));
310
+ this.sendSigned(peer, signMessage('auth-fail', { ok: false, reason: 'Expected auth message first' }, this.hmacKey));
307
311
  this.disconnectPeer(peer, 'Expected auth message');
308
312
  this.emitPeerEvent('error', `Expected auth message from ${peer.ip}`);
313
+ this.log(chalk.red(`Auth failed from ${peer.ip}: expected auth message first`));
309
314
  return;
310
315
  }
311
316
 
312
317
  const authPayload = msg.payload as { passwordHash?: string; nonce?: string };
313
318
  if (!authPayload.passwordHash || !authPayload.nonce) {
314
- peer.socket.write(signMessage('auth-fail', { ok: false, reason: 'Invalid auth payload' }, this.hmacKey));
319
+ this.sendSigned(peer, signMessage('auth-fail', { ok: false, reason: 'Invalid auth payload' }, this.hmacKey));
315
320
  this.disconnectPeer(peer, 'Invalid auth payload');
316
321
  this.emitPeerEvent('error', `Invalid auth payload from ${peer.ip}`);
322
+ this.log(chalk.red(`Auth failed from ${peer.ip}: invalid auth payload`));
317
323
  return;
318
324
  }
319
325
 
320
326
  if (peer.pendingNonce && authPayload.nonce !== peer.pendingNonce) {
321
327
  this.audit(peer.ip, 'AUTH_FAIL', 'Nonce mismatch');
322
- peer.socket.write(signMessage('auth-fail', { ok: false, reason: 'Invalid nonce' }, this.hmacKey));
328
+ this.sendSigned(peer, signMessage('auth-fail', { ok: false, reason: 'Invalid nonce' }, this.hmacKey));
323
329
  this.disconnectPeer(peer, 'Invalid nonce');
324
330
  this.emitPeerEvent('error', `Invalid nonce from ${peer.ip}`);
331
+ this.log(chalk.red(`Auth failed from ${peer.ip}: invalid nonce`));
325
332
  return;
326
333
  }
327
334
 
328
335
  const expectedHash = hashPassword(this.options.password, authPayload.nonce);
329
336
  if (authPayload.passwordHash !== expectedHash) {
330
337
  this.audit(peer.ip, 'AUTH_FAIL', 'Invalid password hash');
331
- peer.socket.write(signMessage('auth-fail', { ok: false, reason: 'Invalid password' }, this.hmacKey));
338
+ this.sendSigned(peer, signMessage('auth-fail', { ok: false, reason: 'Invalid password' }, this.hmacKey));
332
339
  this.disconnectPeer(peer, 'Authentication failed');
333
340
  this.emitPeerEvent('error', `Invalid password from ${peer.ip}`);
341
+ this.log(chalk.red(`Auth failed from ${peer.ip}: invalid password`));
334
342
  return;
335
343
  }
336
344
 
337
345
  peer.authenticated = true;
338
346
  this.cleanupRateLimit(peer.ip);
339
347
  peer.pendingNonce = null;
340
- peer.socket.write(signMessage('auth-ok', { ok: true }, this.hmacKey));
341
- this.log(chalk.green(`Peer authenticated: ${peer.socket.remoteAddress}`));
348
+ this.sendSigned(peer, signMessage('auth-ok', { ok: true }, this.hmacKey));
349
+ this.log(chalk.green(`Peer authenticated: ${peer.ip}`));
342
350
  this.audit(peer.ip, 'AUTH_OK', 'Challenge-response successful');
343
- this.emitPeerEvent('auth', peer.socket.remoteAddress ?? peer.ip);
351
+ this.emitPeerEvent('auth', peer.ip);
344
352
  this.startDataStream(peer);
345
353
  this.startPing(peer);
346
354
  } catch {
347
- peer.socket.write(signMessage('auth-fail', { ok: false, reason: 'Invalid message format' }, this.hmacKey));
355
+ this.sendSigned(peer, signMessage('auth-fail', { ok: false, reason: 'Invalid message format' }, this.hmacKey));
348
356
  this.disconnectPeer(peer, 'Invalid message format');
357
+ this.log(chalk.red(`Auth failed from ${peer.ip}: invalid message format`));
349
358
  }
350
359
  }
351
360
 
@@ -384,17 +393,18 @@ export class P2PServer {
384
393
 
385
394
  private onPeerClose(peer: Peer): void {
386
395
  this.cleanupPeer(peer);
387
- this.log(chalk.yellow(`Peer disconnected: ${peer.socket.remoteAddress}`));
388
- this.emitPeerEvent('disconnect', peer.socket.remoteAddress ?? peer.ip);
396
+ this.log(chalk.yellow(`Peer disconnected: ${peer.ip}`));
397
+ this.emitPeerEvent('disconnect', peer.ip);
389
398
  this.audit(peer.ip, 'DISCONNECT', 'Peer closed connection');
390
399
  }
391
400
 
392
401
  private onPeerError(peer: Peer, err: Error): void {
402
+ const addr = peer.socket.remoteAddress ?? peer.ip;
393
403
  const code = (err as any).code;
394
404
  if (code === 'ECONNRESET') {
395
- this.log(chalk.yellow(`Peer connection reset: ${peer.socket.remoteAddress}`));
405
+ this.log(chalk.yellow(`Peer connection reset: ${addr}`));
396
406
  this.audit(peer.ip, 'ERROR', 'Connection reset');
397
- this.emitPeerEvent('error', `connection reset: ${peer.socket.remoteAddress}`);
407
+ this.emitPeerEvent('error', `connection reset: ${addr}`);
398
408
  } else if (code === 'EPIPE') {
399
409
  this.audit(peer.ip, 'ERROR', 'Broken pipe');
400
410
  } else {
@@ -411,7 +421,11 @@ export class P2PServer {
411
421
  } catch {
412
422
  // socket may already be closed
413
423
  }
414
- peer.socket.destroy();
424
+ try {
425
+ peer.socket.end();
426
+ } catch {
427
+ // ignore end errors
428
+ }
415
429
  }
416
430
 
417
431
  private cleanupPeer(peer: Peer): void {