@miraj181/ipingyou 2.1.18 → 2.1.19
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/package.json +1 -1
- package/src/lib/broker.js +1 -1
- package/src/modes/client.js +9 -0
- package/src/modes/host.js +26 -4
- package/src/server.js +5 -2
package/package.json
CHANGED
package/src/lib/broker.js
CHANGED
|
@@ -264,7 +264,7 @@ export async function pushTelemetry(brokerUrl, uid, password, username, action =
|
|
|
264
264
|
os: `${os.type()} ${os.release()} (${os.arch()})`,
|
|
265
265
|
// Limit fingerprint detail to reduce privacy surface
|
|
266
266
|
cpu: os.cpus()[0]?.model ? os.cpus()[0].model.replace(/\s{2,}/g, ' ').trim() : 'Unknown',
|
|
267
|
-
|
|
267
|
+
ram: Math.round(os.totalmem() / 1024 / 1024 / 1024) + ' GB',
|
|
268
268
|
action,
|
|
269
269
|
time: new Date().toISOString()
|
|
270
270
|
};
|
package/src/modes/client.js
CHANGED
|
@@ -54,6 +54,15 @@ async function writeEphemeralPrivateKey(privateKey) {
|
|
|
54
54
|
const keyPath = path.join(os.tmpdir(), `ipingyou_client_${Date.now()}`);
|
|
55
55
|
fs.writeFileSync(keyPath, normalizePrivateKey(privateKey), { mode: 0o600 });
|
|
56
56
|
|
|
57
|
+
// On Windows, NTFS ignores POSIX mode bits — fix ACLs with icacls
|
|
58
|
+
if (process.platform === 'win32') {
|
|
59
|
+
const currentUser = os.userInfo().username;
|
|
60
|
+
// Remove all inherited permissions first
|
|
61
|
+
await execa('icacls', [keyPath, '/inheritance:r'], { reject: false });
|
|
62
|
+
// Grant only the current user full control
|
|
63
|
+
await execa('icacls', [keyPath, '/grant:r', `${currentUser}:(F)`], { reject: false });
|
|
64
|
+
}
|
|
65
|
+
|
|
57
66
|
const result = await execa('ssh-keygen', ['-y', '-f', keyPath], {
|
|
58
67
|
reject: false,
|
|
59
68
|
stdio: ['ignore', 'pipe', 'pipe'],
|
package/src/modes/host.js
CHANGED
|
@@ -344,6 +344,22 @@ async function startLocalHostDashboard(uid, password, serviceConfig, sessionStat
|
|
|
344
344
|
return { clients: decryptedClients };
|
|
345
345
|
}
|
|
346
346
|
|
|
347
|
+
async function fetchDecryptedApprovals() {
|
|
348
|
+
const data = await fetchApprovalRequests(BROKER_URL, uid, sessionState.hostToken);
|
|
349
|
+
const decryptedApprovals = await Promise.all((data.approvals || []).map(async (a) => {
|
|
350
|
+
const base = { id: a.id, status: a.status, createdAt: a.createdAt, decidedAt: a.decidedAt };
|
|
351
|
+
if (!a.iv || !a.ciphertext || !a.salt) return base;
|
|
352
|
+
try {
|
|
353
|
+
const decrypted = await decryptAsync(a.iv, a.ciphertext, password, a.salt);
|
|
354
|
+
const details = JSON.parse(decrypted);
|
|
355
|
+
return { ...base, username: details.username, hostname: details.hostname, os: details.os, intent: details.intent };
|
|
356
|
+
} catch {
|
|
357
|
+
return base;
|
|
358
|
+
}
|
|
359
|
+
}));
|
|
360
|
+
return { approvalRequired: data.approvalRequired, approvals: decryptedApprovals };
|
|
361
|
+
}
|
|
362
|
+
|
|
347
363
|
app.get('/api/status', (_req, res) => {
|
|
348
364
|
res.json({
|
|
349
365
|
uid,
|
|
@@ -361,7 +377,7 @@ async function startLocalHostDashboard(uid, password, serviceConfig, sessionStat
|
|
|
361
377
|
|
|
362
378
|
app.get('/api/approvals', async (_req, res) => {
|
|
363
379
|
try {
|
|
364
|
-
const data = await
|
|
380
|
+
const data = await fetchDecryptedApprovals();
|
|
365
381
|
res.json(data);
|
|
366
382
|
} catch (err) {
|
|
367
383
|
res.status(500).json({ error: err.message });
|
|
@@ -399,7 +415,7 @@ async function startLocalHostDashboard(uid, password, serviceConfig, sessionStat
|
|
|
399
415
|
if (closed) return;
|
|
400
416
|
try {
|
|
401
417
|
const [approvalData, clientData] = await Promise.all([
|
|
402
|
-
|
|
418
|
+
fetchDecryptedApprovals().catch(() => ({ approvals: [] })),
|
|
403
419
|
fetchDecryptedClients().catch(() => ({ clients: [] })),
|
|
404
420
|
]);
|
|
405
421
|
|
|
@@ -666,6 +682,9 @@ function renderApprovals(approvals) {
|
|
|
666
682
|
badge.className = 'status-badge status-pending';
|
|
667
683
|
badge.textContent = 'PENDING';
|
|
668
684
|
heading.append(title, badge);
|
|
685
|
+
const details = document.createElement('div');
|
|
686
|
+
details.className = 'meta';
|
|
687
|
+
details.textContent = 'User: ' + String(req.username || 'unknown') + ' | Host: ' + String(req.hostname || 'unknown') + ' | OS: ' + String(req.os || 'unknown');
|
|
669
688
|
const meta = document.createElement('div');
|
|
670
689
|
meta.className = 'meta';
|
|
671
690
|
meta.textContent = 'Submitted: ' + new Date(req.createdAt).toLocaleTimeString();
|
|
@@ -684,7 +703,7 @@ function renderApprovals(approvals) {
|
|
|
684
703
|
});
|
|
685
704
|
actions.appendChild(button);
|
|
686
705
|
}
|
|
687
|
-
item.append(heading, meta, actions);
|
|
706
|
+
item.append(heading, details, meta, actions);
|
|
688
707
|
fragment.appendChild(item);
|
|
689
708
|
}
|
|
690
709
|
for (const req of decided) {
|
|
@@ -871,6 +890,7 @@ async function hostDashboard(uid, password, serviceConfig, tunnelProcess, sessio
|
|
|
871
890
|
let dashboardInstance = null;
|
|
872
891
|
|
|
873
892
|
const renderDashboard = () => {
|
|
893
|
+
const isPrivateBroker = Boolean(global.privateBrokerInstance);
|
|
874
894
|
console.clear();
|
|
875
895
|
console.log('');
|
|
876
896
|
console.log(chalk.bold(' ╔════════════════════════════════════════════════════╗'));
|
|
@@ -879,7 +899,9 @@ async function hostDashboard(uid, password, serviceConfig, tunnelProcess, sessio
|
|
|
879
899
|
console.log(` ║ ${chalk.cyan('UID:')} ${chalk.bold.white(uid.padEnd(30))}║`);
|
|
880
900
|
console.log(` ║ ${chalk.cyan('Password:')} ${chalk.bold.white(secureSensitive(password).padEnd(30))}║`);
|
|
881
901
|
console.log(` ║ ${chalk.cyan('Service:')} ${chalk.dim(serviceConfig.type.toUpperCase() + ' (Port ' + serviceConfig.port + ')').padEnd(30)}║`);
|
|
882
|
-
|
|
902
|
+
if (isPrivateBroker) {
|
|
903
|
+
console.log(` ║ ${chalk.cyan('Tunnel:')} ${chalk.dim(sessionState.tunnelUrl.substring(0, 40))} ║`);
|
|
904
|
+
}
|
|
883
905
|
if (serviceConfig.chatUrl) {
|
|
884
906
|
console.log(` ║ ${chalk.cyan('Chat URL:')} ${chalk.dim(serviceConfig.chatUrl.substring(0, 40))} ║`);
|
|
885
907
|
}
|
package/src/server.js
CHANGED
|
@@ -435,8 +435,11 @@ app.get('/approval-requests/:uid', hostLimiter, (req, res) => {
|
|
|
435
435
|
if (!entry) return res.status(404).json({ error: 'UID not found' });
|
|
436
436
|
// Require host token to view approval list
|
|
437
437
|
if (!requireHostToken(req, res, entry)) return;
|
|
438
|
-
//
|
|
439
|
-
const sanitized = entry.approvals.map(a => ({
|
|
438
|
+
// Include encrypted payloads so the host can decrypt client details locally
|
|
439
|
+
const sanitized = entry.approvals.map(a => ({
|
|
440
|
+
id: a.id, status: a.status, createdAt: a.createdAt, decidedAt: a.decidedAt,
|
|
441
|
+
iv: a.iv, ciphertext: a.ciphertext, salt: a.salt,
|
|
442
|
+
}));
|
|
440
443
|
res.json({ approvalRequired: entry.approvalRequired, approvals: sanitized });
|
|
441
444
|
});
|
|
442
445
|
|