@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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@miraj181/ipingyou",
3
- "version": "2.1.18",
3
+ "version": "2.1.19",
4
4
  "description": "SecureLink-CLI — Secure peer-to-peer remote access via SSH & Cloudflare Tunnels",
5
5
  "main": "src/cli.js",
6
6
  "bin": {
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
- ramGB: Math.round(os.totalmem() / 1024 / 1024 / 1024),
267
+ ram: Math.round(os.totalmem() / 1024 / 1024 / 1024) + ' GB',
268
268
  action,
269
269
  time: new Date().toISOString()
270
270
  };
@@ -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 fetchApprovalRequests(BROKER_URL, uid, sessionState.hostToken);
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
- fetchApprovalRequests(BROKER_URL, uid, sessionState.hostToken).catch(() => ({ approvals: [] })),
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
- console.log(` ║ ${chalk.cyan('Tunnel:')} ${chalk.dim(sessionState.tunnelUrl.substring(0, 40))} ║`);
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
- // Strip encrypted payloads host dashboard only needs id/status/timestamps
439
- const sanitized = entry.approvals.map(a => ({ id: a.id, status: a.status, createdAt: a.createdAt, decidedAt: a.decidedAt }));
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