@miraj181/ipingyou 2.1.19 → 2.1.22
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/README.md +1 -2
- package/package.json +1 -1
- package/src/cli.js +22 -14
- package/src/lib/ai/safety.js +1 -1
- package/src/lib/{broker.js → client/broker.js} +24 -8
- package/src/lib/{path-browser.js → client/path-browser.js} +6 -2
- package/src/lib/{session-log.js → mod/session-log.js} +17 -8
- package/src/lib/{worker-runtime.js → mod/worker-runtime.js} +1 -1
- package/src/lib/{chat.js → services/chat.js} +2 -2
- package/src/lib/services/platform.js +364 -0
- package/src/lib/{tunnel.js → services/tunnel.js} +2 -2
- package/src/modes/ai.js +18 -9
- package/src/modes/client.js +90 -25
- package/src/modes/doctor.js +5 -7
- package/src/modes/host.js +303 -153
- package/src/server.js +50 -4
- package/src/lib/platform.js +0 -90
- /package/src/lib/{allowlist.js → mod/allowlist.js} +0 -0
- /package/src/lib/{animations.js → mod/animations.js} +0 -0
- /package/src/lib/{checksum.js → mod/checksum.js} +0 -0
- /package/src/lib/{cleanup.js → mod/cleanup.js} +0 -0
- /package/src/lib/{config.js → mod/config.js} +0 -0
- /package/src/lib/{crypto.js → mod/crypto.js} +0 -0
- /package/src/lib/{open-url.js → mod/open-url.js} +0 -0
- /package/src/lib/{secure-print.js → mod/secure-print.js} +0 -0
- /package/src/lib/{socket-firewall.js → mod/socket-firewall.js} +0 -0
- /package/src/lib/{tmux.js → mod/tmux.js} +0 -0
- /package/src/lib/{uid.js → mod/uid.js} +0 -0
- /package/src/lib/{ssh.js → services/ssh.js} +0 -0
package/src/server.js
CHANGED
|
@@ -13,7 +13,13 @@ import express from 'express';
|
|
|
13
13
|
import rateLimit from 'express-rate-limit';
|
|
14
14
|
import helmet from 'helmet';
|
|
15
15
|
import crypto from 'node:crypto';
|
|
16
|
-
import
|
|
16
|
+
import fs from 'node:fs';
|
|
17
|
+
import path from 'node:path';
|
|
18
|
+
import { fileURLToPath } from 'node:url';
|
|
19
|
+
import { cleanupSessionLog, initSessionLog, logSessionEvent } from './lib/mod/session-log.js';
|
|
20
|
+
|
|
21
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
22
|
+
const packageJson = JSON.parse(fs.readFileSync(path.join(__dirname, '../package.json'), 'utf8'));
|
|
17
23
|
|
|
18
24
|
const app = express();
|
|
19
25
|
const brokerLogPath = initSessionLog('broker');
|
|
@@ -359,6 +365,30 @@ app.get('/resolve/:uid', generalLimiter, (req, res) => {
|
|
|
359
365
|
if (!approved) {
|
|
360
366
|
return res.status(423).json({ error: 'Host approval required before resolving this session' });
|
|
361
367
|
}
|
|
368
|
+
|
|
369
|
+
// Client-specific E2E gating: verify client IP matches approved request IP
|
|
370
|
+
const clientIp = (req.headers['x-forwarded-for'] || req.socket.remoteAddress || '').split(',')[0].trim();
|
|
371
|
+
if (approved.ip && approved.ip !== clientIp) {
|
|
372
|
+
return res.status(403).json({ error: 'Client IP mismatch — access denied' });
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
if (approved.approvedPayload) {
|
|
376
|
+
entry.resolveCount = (entry.resolveCount || 0) + 1;
|
|
377
|
+
console.log(`🔍 [${new Date().toLocaleTimeString()}] Resolved UID (Client Specific): ${uid} (resolve #${entry.resolveCount})`);
|
|
378
|
+
res.json({
|
|
379
|
+
uid,
|
|
380
|
+
iv: approved.approvedPayload.iv,
|
|
381
|
+
ciphertext: approved.approvedPayload.ciphertext,
|
|
382
|
+
salt: approved.approvedPayload.salt,
|
|
383
|
+
isClientSpecific: true,
|
|
384
|
+
ip: clientIp
|
|
385
|
+
});
|
|
386
|
+
|
|
387
|
+
if (entry.oneTime) {
|
|
388
|
+
deleteStoreEntry(uid);
|
|
389
|
+
}
|
|
390
|
+
return;
|
|
391
|
+
}
|
|
362
392
|
}
|
|
363
393
|
|
|
364
394
|
// Increment resolve counter atomically BEFORE sending response
|
|
@@ -400,6 +430,7 @@ app.post('/approval-request/:uid', generalLimiter, (req, res) => {
|
|
|
400
430
|
|
|
401
431
|
// Generate cryptographically secure request ID
|
|
402
432
|
const id = crypto.randomBytes(12).toString('hex');
|
|
433
|
+
const ip = (req.headers['x-forwarded-for'] || req.socket.remoteAddress || '').split(',')[0].trim();
|
|
403
434
|
const request = {
|
|
404
435
|
id,
|
|
405
436
|
iv: req.body.iv,
|
|
@@ -408,6 +439,7 @@ app.post('/approval-request/:uid', generalLimiter, (req, res) => {
|
|
|
408
439
|
status: entry.approvalRequired ? 'pending' : 'approved',
|
|
409
440
|
createdAt: Date.now(),
|
|
410
441
|
decidedAt: entry.approvalRequired ? null : Date.now(),
|
|
442
|
+
ip,
|
|
411
443
|
};
|
|
412
444
|
|
|
413
445
|
const requestBytes = encryptedPayloadBytes(request);
|
|
@@ -439,6 +471,7 @@ app.get('/approval-requests/:uid', hostLimiter, (req, res) => {
|
|
|
439
471
|
const sanitized = entry.approvals.map(a => ({
|
|
440
472
|
id: a.id, status: a.status, createdAt: a.createdAt, decidedAt: a.decidedAt,
|
|
441
473
|
iv: a.iv, ciphertext: a.ciphertext, salt: a.salt,
|
|
474
|
+
ip: a.ip,
|
|
442
475
|
}));
|
|
443
476
|
res.json({ approvalRequired: entry.approvalRequired, approvals: sanitized });
|
|
444
477
|
});
|
|
@@ -461,6 +494,14 @@ app.post('/approval-requests/:uid/:requestId/:decision', strictLimiter, (req, re
|
|
|
461
494
|
|
|
462
495
|
request.status = req.params.decision;
|
|
463
496
|
request.decidedAt = Date.now();
|
|
497
|
+
|
|
498
|
+
if (req.params.decision === 'approved' && req.body && req.body.ciphertext) {
|
|
499
|
+
request.approvedPayload = {
|
|
500
|
+
iv: req.body.iv,
|
|
501
|
+
ciphertext: req.body.ciphertext,
|
|
502
|
+
salt: req.body.salt
|
|
503
|
+
};
|
|
504
|
+
}
|
|
464
505
|
res.json({ status: request.status });
|
|
465
506
|
});
|
|
466
507
|
|
|
@@ -473,8 +514,13 @@ app.get('/approval-status/:uid/:requestId', generalLimiter, (req, res) => {
|
|
|
473
514
|
if (!entry) return res.status(404).json({ error: 'UID not found' });
|
|
474
515
|
const request = entry.approvals.find(item => item.id === req.params.requestId);
|
|
475
516
|
if (!request) return res.status(404).json({ error: 'Request not found' });
|
|
476
|
-
|
|
477
|
-
|
|
517
|
+
|
|
518
|
+
const clientIp = (req.headers['x-forwarded-for'] || req.socket.remoteAddress || '').split(',')[0].trim();
|
|
519
|
+
res.json({
|
|
520
|
+
status: request.status,
|
|
521
|
+
ip: clientIp,
|
|
522
|
+
approvedPayload: request.status === 'approved' ? request.approvedPayload : undefined
|
|
523
|
+
});
|
|
478
524
|
});
|
|
479
525
|
|
|
480
526
|
/**
|
|
@@ -571,7 +617,7 @@ const HOST = process.env.HOST || '0.0.0.0';
|
|
|
571
617
|
app.listen(PORT, HOST, () => {
|
|
572
618
|
console.log('');
|
|
573
619
|
console.log(' ╔══════════════════════════════════════════╗');
|
|
574
|
-
console.log(
|
|
620
|
+
console.log(` ║ 🔗 SecureLink Broker — Active (v${packageJson.version})`.padEnd(45) + '║');
|
|
575
621
|
console.log(` ║ 📡 Port: ${String(PORT).padEnd(29)}║`);
|
|
576
622
|
console.log(' ║ 🔒 Zero-Knowledge Encrypted Store ║');
|
|
577
623
|
console.log(' ║ ⏱️ TTL: 1 hour per UID ║');
|
package/src/lib/platform.js
DELETED
|
@@ -1,90 +0,0 @@
|
|
|
1
|
-
import { execa } from 'execa';
|
|
2
|
-
import chalk from 'chalk';
|
|
3
|
-
import os from 'node:os';
|
|
4
|
-
import fs from 'node:fs';
|
|
5
|
-
|
|
6
|
-
export function detectOS() {
|
|
7
|
-
const platform = process.platform;
|
|
8
|
-
return {
|
|
9
|
-
platform,
|
|
10
|
-
isLinux: platform === 'linux',
|
|
11
|
-
isMac: platform === 'darwin',
|
|
12
|
-
isWindows: platform === 'win32',
|
|
13
|
-
distro: null,
|
|
14
|
-
arch: os.arch(),
|
|
15
|
-
hostname: os.hostname(),
|
|
16
|
-
};
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export async function detectLinuxDistro() {
|
|
20
|
-
try {
|
|
21
|
-
const data = await fs.promises.readFile('/etc/os-release', 'utf8');
|
|
22
|
-
const lower = data.toLowerCase();
|
|
23
|
-
if (/(ubuntu|debian|kali|mint)/.test(lower)) return 'debian';
|
|
24
|
-
if (/(arch|manjaro)/.test(lower)) return 'arch';
|
|
25
|
-
if (/(fedora|centos|rhel)/.test(lower)) return 'fedora';
|
|
26
|
-
} catch {
|
|
27
|
-
// Manual instructions fall back to the generic Linux guidance.
|
|
28
|
-
}
|
|
29
|
-
return 'unknown';
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
export async function commandExists(command) {
|
|
33
|
-
if (!/^[a-zA-Z0-9._+-]{1,64}$/.test(String(command || ''))) return false;
|
|
34
|
-
try {
|
|
35
|
-
const probe = process.platform === 'win32' ? ['where', command] : ['which', command];
|
|
36
|
-
await execa(probe[0], [probe[1]], {
|
|
37
|
-
reject: true,
|
|
38
|
-
timeout: 5000,
|
|
39
|
-
maxBuffer: 64 * 1024,
|
|
40
|
-
});
|
|
41
|
-
return true;
|
|
42
|
-
} catch {
|
|
43
|
-
return false;
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
function printInstallGuidance(osInfo, missing) {
|
|
48
|
-
console.log(chalk.yellow(' ⚠️ Required system tools are missing.'));
|
|
49
|
-
console.log(chalk.dim(' iPingYou does not download or execute native installers automatically.'));
|
|
50
|
-
console.log(chalk.dim(' Install the tools through your trusted OS package manager, then rerun iPingYou.'));
|
|
51
|
-
console.log('');
|
|
52
|
-
|
|
53
|
-
if (missing.includes('cloudflared')) {
|
|
54
|
-
if (osInfo.isMac) console.log(chalk.cyan(' brew install cloudflared'));
|
|
55
|
-
else if (osInfo.isWindows) console.log(chalk.cyan(' winget install --id Cloudflare.cloudflared -e'));
|
|
56
|
-
else console.log(chalk.cyan(' Follow: https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/downloads/'));
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
if (missing.includes('ssh')) {
|
|
60
|
-
if (osInfo.isMac) console.log(chalk.cyan(' Enable Remote Login in System Settings → General → Sharing'));
|
|
61
|
-
else if (osInfo.isWindows) console.log(chalk.cyan(' Add OpenSSH Client/Server from Windows Optional Features'));
|
|
62
|
-
else console.log(chalk.cyan(' Debian/Ubuntu: sudo apt-get install openssh-client openssh-server'));
|
|
63
|
-
}
|
|
64
|
-
console.log('');
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
export async function checkDependencies() {
|
|
68
|
-
const osInfo = detectOS();
|
|
69
|
-
const results = {
|
|
70
|
-
ssh: await commandExists('ssh'),
|
|
71
|
-
cloudflared: await commandExists('cloudflared'),
|
|
72
|
-
};
|
|
73
|
-
|
|
74
|
-
console.log('');
|
|
75
|
-
console.log(chalk.bold(' 🔍 Dependency Check'));
|
|
76
|
-
console.log(chalk.dim(' ─────────────────────────────────'));
|
|
77
|
-
console.log(` ${results.ssh ? chalk.green('✓') : chalk.red('✗')} ssh ${results.ssh ? chalk.dim('found') : chalk.red('missing')}`);
|
|
78
|
-
console.log(` ${results.cloudflared ? chalk.green('✓') : chalk.red('✗')} cloudflared ${results.cloudflared ? chalk.dim('found') : chalk.red('missing')}`);
|
|
79
|
-
console.log('');
|
|
80
|
-
|
|
81
|
-
const missing = Object.entries(results)
|
|
82
|
-
.filter(([, available]) => !available)
|
|
83
|
-
.map(([name]) => name);
|
|
84
|
-
if (missing.length > 0) {
|
|
85
|
-
printInstallGuidance(osInfo, missing);
|
|
86
|
-
} else {
|
|
87
|
-
console.log(chalk.green(' ✅ All dependencies satisfied!\n'));
|
|
88
|
-
}
|
|
89
|
-
return results;
|
|
90
|
-
}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|