@miraj181/ipingyou 1.0.1 → 2.0.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/README.md +36 -96
- package/package.json +11 -4
- package/src/cli.js +84 -4
- package/src/lib/chat.js +333 -0
- package/src/lib/cleanup.js +78 -0
- package/src/lib/config.js +41 -0
- package/src/lib/crypto.js +23 -14
- package/src/lib/platform.js +415 -53
- package/src/modes/client.js +451 -58
- package/src/modes/host.js +416 -119
- package/src/server.js +0 -143
package/src/lib/cleanup.js
CHANGED
|
@@ -20,6 +20,17 @@ let _revokeUID = null;
|
|
|
20
20
|
/** @type {string|null} — Broker URL for revocation */
|
|
21
21
|
let _brokerUrl = null;
|
|
22
22
|
|
|
23
|
+
/** @type {Array<() => Promise<void>|void>} — Custom cleanup hooks */
|
|
24
|
+
const _cleanupHooks = [];
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Register a custom cleanup hook to run on shutdown.
|
|
28
|
+
* @param {() => Promise<void>|void} hook
|
|
29
|
+
*/
|
|
30
|
+
export function addCleanupHook(hook) {
|
|
31
|
+
_cleanupHooks.push(hook);
|
|
32
|
+
}
|
|
33
|
+
|
|
23
34
|
/**
|
|
24
35
|
* Register a spawned child PID for tracking.
|
|
25
36
|
* @param {number} pid
|
|
@@ -80,6 +91,15 @@ export async function cleanupAll() {
|
|
|
80
91
|
await Promise.allSettled(kills);
|
|
81
92
|
trackedPIDs.clear();
|
|
82
93
|
|
|
94
|
+
// Run custom cleanup hooks
|
|
95
|
+
for (const hook of _cleanupHooks) {
|
|
96
|
+
try {
|
|
97
|
+
await hook();
|
|
98
|
+
} catch (err) {
|
|
99
|
+
console.error(chalk.red(` Cleanup hook failed: ${err.message}`));
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
83
103
|
// Revoke UID from broker
|
|
84
104
|
if (_revokeUID && _brokerUrl) {
|
|
85
105
|
try {
|
|
@@ -129,3 +149,61 @@ export function installShutdownHandlers() {
|
|
|
129
149
|
export function getTrackedCount() {
|
|
130
150
|
return trackedPIDs.size;
|
|
131
151
|
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Execute Panic Mode (Self-Destruct)
|
|
155
|
+
* Wipes all configs, keys, and forcefully kills associated processes.
|
|
156
|
+
*/
|
|
157
|
+
import fs from 'node:fs';
|
|
158
|
+
import os from 'node:os';
|
|
159
|
+
import path from 'node:path';
|
|
160
|
+
import { execaCommand } from 'execa';
|
|
161
|
+
|
|
162
|
+
export async function executePanicMode() {
|
|
163
|
+
console.log(chalk.bold.red('\n 🚨 INITIATING SECURELINK PANIC MODE 🚨\n'));
|
|
164
|
+
|
|
165
|
+
// 1. Force kill all cloudflared & ipingyou processes
|
|
166
|
+
console.log(chalk.dim(' [1/4] Terminating all tunnel and host processes...'));
|
|
167
|
+
try {
|
|
168
|
+
if (process.platform === 'win32') {
|
|
169
|
+
await execaCommand('taskkill /F /IM cloudflared.exe', { reject: false });
|
|
170
|
+
await execaCommand('taskkill /F /IM sshd.exe', { reject: false });
|
|
171
|
+
} else {
|
|
172
|
+
await execaCommand('pkill -9 -f cloudflared', { reject: false });
|
|
173
|
+
await execaCommand('pkill -9 -f "sshd:.*@"', { reject: false });
|
|
174
|
+
}
|
|
175
|
+
} catch {}
|
|
176
|
+
|
|
177
|
+
// 2. Delete configuration and aliases
|
|
178
|
+
console.log(chalk.dim(' [2/4] Wiping configuration files...'));
|
|
179
|
+
const configPath = path.join(os.homedir(), '.ipingyou', 'config.json');
|
|
180
|
+
try {
|
|
181
|
+
if (fs.existsSync(configPath)) {
|
|
182
|
+
fs.unlinkSync(configPath);
|
|
183
|
+
}
|
|
184
|
+
const configDir = path.join(os.homedir(), '.ipingyou');
|
|
185
|
+
if (fs.existsSync(configDir)) {
|
|
186
|
+
fs.rmSync(configDir, { recursive: true, force: true });
|
|
187
|
+
}
|
|
188
|
+
} catch {}
|
|
189
|
+
|
|
190
|
+
// 3. Delete ephemeral keys and temp files
|
|
191
|
+
console.log(chalk.dim(' [3/4] Purging ephemeral keys and temporary files...'));
|
|
192
|
+
try {
|
|
193
|
+
const tmpDir = os.tmpdir();
|
|
194
|
+
const files = fs.readdirSync(tmpDir);
|
|
195
|
+
for (const file of files) {
|
|
196
|
+
if (file.startsWith('ipingyou_')) {
|
|
197
|
+
fs.unlinkSync(path.join(tmpDir, file));
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
} catch {}
|
|
201
|
+
|
|
202
|
+
// 4. Scrub SSH authorized_keys if we know we injected
|
|
203
|
+
// Note: We don't want to wipe the user's whole authorized_keys, but if we have a hook, we could.
|
|
204
|
+
// We'll skip scraping the actual file here unless we know the exact comment.
|
|
205
|
+
console.log(chalk.dim(' [4/4] Finalizing cleanup...'));
|
|
206
|
+
|
|
207
|
+
console.log(chalk.bold.green('\n ✅ Panic Mode Complete. All traces removed.\n'));
|
|
208
|
+
process.exit(0);
|
|
209
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import os from 'node:os';
|
|
4
|
+
|
|
5
|
+
const CONFIG_DIR = path.join(os.homedir(), '.ipingyou');
|
|
6
|
+
const CONFIG_FILE = path.join(CONFIG_DIR, 'config.json');
|
|
7
|
+
|
|
8
|
+
function ensureConfig() {
|
|
9
|
+
if (!fs.existsSync(CONFIG_DIR)) {
|
|
10
|
+
fs.mkdirSync(CONFIG_DIR, { recursive: true });
|
|
11
|
+
}
|
|
12
|
+
if (!fs.existsSync(CONFIG_FILE)) {
|
|
13
|
+
fs.writeFileSync(CONFIG_FILE, JSON.stringify({ aliases: {}, settings: {} }, null, 2));
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function getConfig() {
|
|
18
|
+
ensureConfig();
|
|
19
|
+
try {
|
|
20
|
+
return JSON.parse(fs.readFileSync(CONFIG_FILE, 'utf8'));
|
|
21
|
+
} catch {
|
|
22
|
+
return { aliases: {}, settings: {} };
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function saveConfig(config) {
|
|
27
|
+
ensureConfig();
|
|
28
|
+
fs.writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2));
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function saveAlias(aliasName, data) {
|
|
32
|
+
const config = getConfig();
|
|
33
|
+
if (!config.aliases) config.aliases = {};
|
|
34
|
+
config.aliases[aliasName] = data;
|
|
35
|
+
saveConfig(config);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function getAlias(aliasName) {
|
|
39
|
+
const config = getConfig();
|
|
40
|
+
return config.aliases?.[aliasName] || null;
|
|
41
|
+
}
|
package/src/lib/crypto.js
CHANGED
|
@@ -9,43 +9,52 @@
|
|
|
9
9
|
|
|
10
10
|
import crypto from 'node:crypto';
|
|
11
11
|
|
|
12
|
-
const DEFAULT_HEX_KEY = 'b374a26d71590483815c467a99623e1b7db95f269c2889279a32c4530fc4159f';
|
|
13
|
-
|
|
14
12
|
/**
|
|
15
|
-
*
|
|
16
|
-
*
|
|
13
|
+
* Derive a 256-bit encryption key from a password and salt using PBKDF2.
|
|
14
|
+
* @param {string} password
|
|
15
|
+
* @param {Buffer} salt
|
|
16
|
+
* @returns {Buffer}
|
|
17
17
|
*/
|
|
18
|
-
export function
|
|
19
|
-
|
|
20
|
-
return
|
|
18
|
+
export function deriveKey(password, salt) {
|
|
19
|
+
// Use 100,000 iterations for strong security
|
|
20
|
+
return crypto.pbkdf2Sync(password, salt, 100000, 32, 'sha256');
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
/**
|
|
24
|
-
* Encrypt a plaintext string with AES-256-CBC.
|
|
24
|
+
* Encrypt a plaintext string with AES-256-CBC using a password.
|
|
25
25
|
* @param {string} plaintext
|
|
26
|
-
* @
|
|
26
|
+
* @param {string} password
|
|
27
|
+
* @returns {{ iv: string, ciphertext: string, salt: string }}
|
|
27
28
|
*/
|
|
28
|
-
export function encrypt(plaintext) {
|
|
29
|
-
const
|
|
29
|
+
export function encrypt(plaintext, password) {
|
|
30
|
+
const salt = crypto.randomBytes(16);
|
|
31
|
+
const key = deriveKey(password, salt);
|
|
30
32
|
const iv = crypto.randomBytes(16);
|
|
33
|
+
|
|
31
34
|
const cipher = crypto.createCipheriv('aes-256-cbc', key, iv);
|
|
32
35
|
let enc = cipher.update(plaintext, 'utf8', 'base64');
|
|
33
36
|
enc += cipher.final('base64');
|
|
37
|
+
|
|
34
38
|
return {
|
|
35
39
|
iv: iv.toString('hex'),
|
|
36
40
|
ciphertext: enc,
|
|
41
|
+
salt: salt.toString('hex')
|
|
37
42
|
};
|
|
38
43
|
}
|
|
39
44
|
|
|
40
45
|
/**
|
|
41
|
-
* Decrypt a ciphertext with AES-256-CBC.
|
|
46
|
+
* Decrypt a ciphertext with AES-256-CBC using a password and salt.
|
|
42
47
|
* @param {string} ivHex — 32-char hex IV
|
|
43
48
|
* @param {string} cipherBase64
|
|
49
|
+
* @param {string} password
|
|
50
|
+
* @param {string} saltHex
|
|
44
51
|
* @returns {string}
|
|
45
52
|
*/
|
|
46
|
-
export function decrypt(ivHex, cipherBase64) {
|
|
47
|
-
const
|
|
53
|
+
export function decrypt(ivHex, cipherBase64, password, saltHex) {
|
|
54
|
+
const salt = Buffer.from(saltHex, 'hex');
|
|
55
|
+
const key = deriveKey(password, salt);
|
|
48
56
|
const iv = Buffer.from(ivHex, 'hex');
|
|
57
|
+
|
|
49
58
|
const decipher = crypto.createDecipheriv('aes-256-cbc', key, iv);
|
|
50
59
|
let dec = decipher.update(cipherBase64, 'base64', 'utf8');
|
|
51
60
|
dec += decipher.final('utf8');
|