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 +28 -14
- package/package.json +1 -1
- package/src/p2p/server.ts +30 -16
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 =
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
2230
|
-
this.log(chalk6.green(`Peer authenticated: ${peer.
|
|
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.
|
|
2241
|
+
this.emitPeerEvent("auth", peer.ip);
|
|
2233
2242
|
this.startDataStream(peer);
|
|
2234
2243
|
this.startPing(peer);
|
|
2235
2244
|
} catch {
|
|
2236
|
-
|
|
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.
|
|
2270
|
-
this.emitPeerEvent("disconnect", peer.
|
|
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: ${
|
|
2287
|
+
this.log(chalk6.yellow(`Peer connection reset: ${addr}`));
|
|
2277
2288
|
this.audit(peer.ip, "ERROR", "Connection reset");
|
|
2278
|
-
this.emitPeerEvent("error", `connection reset: ${
|
|
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
|
-
|
|
2304
|
+
try {
|
|
2305
|
+
peer.socket.end();
|
|
2306
|
+
} catch {
|
|
2307
|
+
}
|
|
2294
2308
|
}
|
|
2295
2309
|
cleanupPeer(peer) {
|
|
2296
2310
|
if (peer.intervalHandle) {
|
package/package.json
CHANGED
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.
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
341
|
-
this.log(chalk.green(`Peer authenticated: ${peer.
|
|
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.
|
|
351
|
+
this.emitPeerEvent('auth', peer.ip);
|
|
344
352
|
this.startDataStream(peer);
|
|
345
353
|
this.startPing(peer);
|
|
346
354
|
} catch {
|
|
347
|
-
|
|
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.
|
|
388
|
-
this.emitPeerEvent('disconnect', peer.
|
|
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: ${
|
|
405
|
+
this.log(chalk.yellow(`Peer connection reset: ${addr}`));
|
|
396
406
|
this.audit(peer.ip, 'ERROR', 'Connection reset');
|
|
397
|
-
this.emitPeerEvent('error', `connection reset: ${
|
|
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
|
-
|
|
424
|
+
try {
|
|
425
|
+
peer.socket.end();
|
|
426
|
+
} catch {
|
|
427
|
+
// ignore end errors
|
|
428
|
+
}
|
|
415
429
|
}
|
|
416
430
|
|
|
417
431
|
private cleanupPeer(peer: Peer): void {
|