cybersentinel-cli 1.0.1 → 1.0.4
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/bin/index.js +208 -3
- package/package.json +1 -1
package/bin/index.js
CHANGED
|
@@ -72,7 +72,21 @@ $ s*" .u@*"" '""\\dP"
|
|
|
72
72
|
""""* RL d>
|
|
73
73
|
"$u. .$
|
|
74
74
|
^"*bo@" tony
|
|
75
|
-
`) + "\n" +
|
|
75
|
+
`) + "\n" +
|
|
76
|
+
chalk.green.bold('================================================================================\n') +
|
|
77
|
+
chalk.green.bold(' CYBERSENTINEL - Audit Platform CLI\n') +
|
|
78
|
+
chalk.green.bold('================================================================================\n') +
|
|
79
|
+
chalk.green('INTRODUCTION & PURPOSE:\n') +
|
|
80
|
+
chalk.white(' CyberSentinel CLI is a secure, interactive terminal-based auditing gateway\n') +
|
|
81
|
+
chalk.white(' designed for white-hat penetration testers and compliance officers. It executes\n') +
|
|
82
|
+
chalk.white(' automated vulnerability assessments, facilitates manual control checkpoints, and\n') +
|
|
83
|
+
chalk.white(' synchronizes verified ratings and uploads directly to the management database.\n\n') +
|
|
84
|
+
chalk.green('CREATOR & LEAD DEVELOPER:\n') +
|
|
85
|
+
chalk.white(' Engr. Brian Perez\n\n') +
|
|
86
|
+
chalk.green('PROPRIETARY LICENSE & COPYRIGHT:\n') +
|
|
87
|
+
chalk.white(' This software package and its distribution binaries are exclusively owned by\n') +
|
|
88
|
+
chalk.white(' the Inspire Alliance Fund Group Incorporated. All rights reserved.\n') +
|
|
89
|
+
chalk.green.bold('================================================================================\n');
|
|
76
90
|
|
|
77
91
|
const SNAKE_SPINNER = {
|
|
78
92
|
interval: 100,
|
|
@@ -122,6 +136,36 @@ function getMtlsClient(config) {
|
|
|
122
136
|
});
|
|
123
137
|
}
|
|
124
138
|
|
|
139
|
+
// Wait for user to decide to quit by pressing Ctrl + C or /exit command
|
|
140
|
+
async function waitToExit() {
|
|
141
|
+
console.log(chalk.gray('\n=================================================='));
|
|
142
|
+
console.log(chalk.green('Task execution complete. Session remains active.'));
|
|
143
|
+
console.log(chalk.green('Type "/exit" or press Ctrl+C to close the connection and exit.'));
|
|
144
|
+
console.log(chalk.gray('==================================================\n'));
|
|
145
|
+
|
|
146
|
+
const readline = require('readline');
|
|
147
|
+
const rl = readline.createInterface({
|
|
148
|
+
input: process.stdin,
|
|
149
|
+
output: process.stdout
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
return new Promise((resolve) => {
|
|
153
|
+
const ask = () => {
|
|
154
|
+
rl.question(chalk.green('cybersentinel> '), (answer) => {
|
|
155
|
+
if (answer.trim() === '/exit') {
|
|
156
|
+
console.log(chalk.yellow('Closing session...'));
|
|
157
|
+
rl.close();
|
|
158
|
+
process.exit(0);
|
|
159
|
+
} else {
|
|
160
|
+
console.log(chalk.gray(`Unknown command "${answer.trim()}". Type "/exit" to quit.`));
|
|
161
|
+
ask();
|
|
162
|
+
}
|
|
163
|
+
});
|
|
164
|
+
};
|
|
165
|
+
ask();
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
|
|
125
169
|
// Print startup banner
|
|
126
170
|
console.log(VIPER_BANNER);
|
|
127
171
|
|
|
@@ -253,13 +297,25 @@ program
|
|
|
253
297
|
console.log(chalk.green(`Files saved to: ${CONFIG_DIR}`));
|
|
254
298
|
console.log(chalk.green(`PKCS12 Container: client.p12 (encrypted)\n`));
|
|
255
299
|
|
|
300
|
+
await waitToExit();
|
|
256
301
|
} catch (error) {
|
|
257
302
|
spinner.fail(chalk.red('Registration failed!'));
|
|
258
303
|
if (error.response) {
|
|
259
|
-
|
|
304
|
+
let msg = '';
|
|
305
|
+
if (typeof error.response.data === 'string') {
|
|
306
|
+
if (error.response.data.includes('The plain HTTP request was sent to HTTPS port')) {
|
|
307
|
+
msg = 'Plain HTTP request was sent to an HTTPS port. Please use "https://" instead of "http://".';
|
|
308
|
+
} else {
|
|
309
|
+
msg = error.response.data.substring(0, 150);
|
|
310
|
+
}
|
|
311
|
+
} else {
|
|
312
|
+
msg = error.response.data.message || error.response.data.error || JSON.stringify(error.response.data);
|
|
313
|
+
}
|
|
314
|
+
console.error(chalk.red(`Error: ${error.response.status} - ${msg}`));
|
|
260
315
|
} else {
|
|
261
316
|
console.error(chalk.red(`Error: ${error.message}`));
|
|
262
317
|
}
|
|
318
|
+
await waitToExit();
|
|
263
319
|
}
|
|
264
320
|
});
|
|
265
321
|
|
|
@@ -267,7 +323,7 @@ program
|
|
|
267
323
|
program
|
|
268
324
|
.command('init')
|
|
269
325
|
.description('Verify credential certificate files status')
|
|
270
|
-
.action(() => {
|
|
326
|
+
.action(async () => {
|
|
271
327
|
if (hasCertificates()) {
|
|
272
328
|
try {
|
|
273
329
|
const config = loadConfig();
|
|
@@ -282,6 +338,7 @@ program
|
|
|
282
338
|
console.log(chalk.yellow('⚠ No active client certificate found. Please run:'));
|
|
283
339
|
console.log(chalk.cyan(' cybersentinel register'));
|
|
284
340
|
}
|
|
341
|
+
await waitToExit();
|
|
285
342
|
});
|
|
286
343
|
|
|
287
344
|
// Command: audit
|
|
@@ -293,6 +350,7 @@ program
|
|
|
293
350
|
.action(async (options) => {
|
|
294
351
|
if (!hasCertificates()) {
|
|
295
352
|
console.error(chalk.red('Error: Local certificate files not found. Run "cybersentinel register" first.'));
|
|
353
|
+
await waitToExit();
|
|
296
354
|
process.exit(1);
|
|
297
355
|
}
|
|
298
356
|
|
|
@@ -301,6 +359,7 @@ program
|
|
|
301
359
|
config = loadConfig();
|
|
302
360
|
} catch (err) {
|
|
303
361
|
console.error(chalk.red(err.message));
|
|
362
|
+
await waitToExit();
|
|
304
363
|
process.exit(1);
|
|
305
364
|
}
|
|
306
365
|
|
|
@@ -325,6 +384,7 @@ program
|
|
|
325
384
|
} else {
|
|
326
385
|
console.error(chalk.red(`Connection error: ${err.message}`));
|
|
327
386
|
}
|
|
387
|
+
await waitToExit();
|
|
328
388
|
process.exit(1);
|
|
329
389
|
}
|
|
330
390
|
|
|
@@ -473,6 +533,29 @@ program
|
|
|
473
533
|
console.log(chalk.gray(`Current: Rating: ${currentVal.rating} | Notes: ${currentVal.notes}`));
|
|
474
534
|
}
|
|
475
535
|
|
|
536
|
+
const actionPrompt = await inquirer.prompt([
|
|
537
|
+
{
|
|
538
|
+
type: 'list',
|
|
539
|
+
name: 'action',
|
|
540
|
+
message: 'Choose action for this checkpoint:',
|
|
541
|
+
choices: [
|
|
542
|
+
{ name: 'Audit / Rate this item', value: 'audit' },
|
|
543
|
+
{ name: 'Skip this item', value: 'skip' },
|
|
544
|
+
{ name: chalk.yellow('Exit & Save Session'), value: 'exit' }
|
|
545
|
+
]
|
|
546
|
+
}
|
|
547
|
+
]);
|
|
548
|
+
|
|
549
|
+
if (actionPrompt.action === 'exit') {
|
|
550
|
+
console.log(chalk.yellow('\n✔ Session progress saved. Exiting audit session. Goodbye!'));
|
|
551
|
+
process.exit(0);
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
if (actionPrompt.action === 'skip') {
|
|
555
|
+
console.log(chalk.gray(`Skipping item ${item.id}.`));
|
|
556
|
+
continue;
|
|
557
|
+
}
|
|
558
|
+
|
|
476
559
|
const itemAnswers = await inquirer.prompt([
|
|
477
560
|
{
|
|
478
561
|
type: 'list',
|
|
@@ -554,6 +637,128 @@ program
|
|
|
554
637
|
}
|
|
555
638
|
|
|
556
639
|
console.log(chalk.green.bold(`\n✔ Completed Category ${selectedCategory.id} Audit!\n`));
|
|
640
|
+
await waitToExit();
|
|
641
|
+
});
|
|
642
|
+
|
|
643
|
+
function startProxyServer(port, targetFilter) {
|
|
644
|
+
const http = require('http');
|
|
645
|
+
const net = require('net');
|
|
646
|
+
const url = require('url');
|
|
647
|
+
|
|
648
|
+
const server = http.createServer((req, res) => {
|
|
649
|
+
try {
|
|
650
|
+
const parsedUrl = url.parse(req.url);
|
|
651
|
+
const host = req.headers.host || '';
|
|
652
|
+
|
|
653
|
+
const shouldLog = !targetFilter || host.includes(targetFilter);
|
|
654
|
+
if (shouldLog) {
|
|
655
|
+
console.log(chalk.green(`[HTTP Request] ${req.method} ${req.url}`));
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
const options = {
|
|
659
|
+
hostname: parsedUrl.hostname || host.split(':')[0],
|
|
660
|
+
port: parsedUrl.port || 80,
|
|
661
|
+
path: parsedUrl.path,
|
|
662
|
+
method: req.method,
|
|
663
|
+
headers: req.headers
|
|
664
|
+
};
|
|
665
|
+
|
|
666
|
+
const proxyReq = http.request(options, (proxyRes) => {
|
|
667
|
+
if (shouldLog) {
|
|
668
|
+
console.log(chalk.cyan(`[HTTP Response] ${req.method} ${req.url} -> Status: ${proxyRes.statusCode}`));
|
|
669
|
+
}
|
|
670
|
+
res.writeHead(proxyRes.statusCode, proxyRes.headers);
|
|
671
|
+
proxyRes.pipe(res);
|
|
672
|
+
});
|
|
673
|
+
|
|
674
|
+
proxyReq.on('error', (err) => {
|
|
675
|
+
if (shouldLog) {
|
|
676
|
+
console.error(chalk.red(`[HTTP Error] ${req.method} ${req.url} -> Error: ${err.message}`));
|
|
677
|
+
}
|
|
678
|
+
res.writeHead(502);
|
|
679
|
+
res.end(`Proxy Gateway Error: ${err.message}`);
|
|
680
|
+
});
|
|
681
|
+
|
|
682
|
+
req.pipe(proxyReq);
|
|
683
|
+
} catch (err) {
|
|
684
|
+
console.error(chalk.red(`Proxy Request Handler Error: ${err.message}`));
|
|
685
|
+
}
|
|
686
|
+
});
|
|
687
|
+
|
|
688
|
+
server.on('connect', (req, clientSocket, head) => {
|
|
689
|
+
try {
|
|
690
|
+
const parts = req.url.split(':');
|
|
691
|
+
const targetHost = parts[0];
|
|
692
|
+
const targetPort = parseInt(parts[1] || '443', 10);
|
|
693
|
+
|
|
694
|
+
const shouldLog = !targetFilter || targetHost.includes(targetFilter);
|
|
695
|
+
if (shouldLog) {
|
|
696
|
+
console.log(chalk.yellow(`[HTTPS CONNECT Tunnel] ${targetHost}:${targetPort}`));
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
const serverSocket = net.connect(targetPort, targetHost, () => {
|
|
700
|
+
clientSocket.write('HTTP/1.1 200 Connection Established\r\n\r\n');
|
|
701
|
+
serverSocket.write(head);
|
|
702
|
+
serverSocket.pipe(clientSocket);
|
|
703
|
+
clientSocket.pipe(serverSocket);
|
|
704
|
+
});
|
|
705
|
+
|
|
706
|
+
serverSocket.on('error', (err) => {
|
|
707
|
+
if (shouldLog) {
|
|
708
|
+
console.error(chalk.red(`[HTTPS Tunnel Error] Tunnel to ${targetHost}:${targetPort} failed: ${err.message}`));
|
|
709
|
+
}
|
|
710
|
+
clientSocket.end('HTTP/1.1 502 Bad Gateway\r\n\r\n');
|
|
711
|
+
});
|
|
712
|
+
|
|
713
|
+
clientSocket.on('error', () => {
|
|
714
|
+
serverSocket.end();
|
|
715
|
+
});
|
|
716
|
+
} catch (err) {
|
|
717
|
+
console.error(chalk.red(`Proxy CONNECT Handler Error: ${err.message}`));
|
|
718
|
+
}
|
|
719
|
+
});
|
|
720
|
+
|
|
721
|
+
server.listen(port, () => {
|
|
722
|
+
console.log(chalk.green.bold(`\n⚡ CyberSentinel Interception Proxy Active!`));
|
|
723
|
+
console.log(chalk.green(`Listening on local port: ${port}`));
|
|
724
|
+
if (targetFilter) {
|
|
725
|
+
console.log(chalk.green(`Filtering target host: ${targetFilter}`));
|
|
726
|
+
}
|
|
727
|
+
console.log(chalk.gray(`Configure your browser (e.g. Chrome) to use HTTP/HTTPS proxy 127.0.0.1:${port}`));
|
|
728
|
+
console.log(chalk.gray(`Type "/exit" or press Ctrl+C to stop the proxy server.\n`));
|
|
729
|
+
});
|
|
730
|
+
|
|
731
|
+
const readline = require('readline');
|
|
732
|
+
const rl = readline.createInterface({
|
|
733
|
+
input: process.stdin,
|
|
734
|
+
output: process.stdout
|
|
735
|
+
});
|
|
736
|
+
|
|
737
|
+
const ask = () => {
|
|
738
|
+
rl.question('', (answer) => {
|
|
739
|
+
if (answer.trim() === '/exit') {
|
|
740
|
+
console.log(chalk.yellow('Stopping proxy server and closing session...'));
|
|
741
|
+
server.close();
|
|
742
|
+
rl.close();
|
|
743
|
+
process.exit(0);
|
|
744
|
+
} else {
|
|
745
|
+
ask();
|
|
746
|
+
}
|
|
747
|
+
});
|
|
748
|
+
};
|
|
749
|
+
ask();
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
// Command: proxy
|
|
753
|
+
program
|
|
754
|
+
.command('proxy')
|
|
755
|
+
.description('Start a local interception proxy server (like Burp Suite CLI)')
|
|
756
|
+
.option('-p, --port <port>', 'Port to listen on', '8088')
|
|
757
|
+
.option('-t, --target <host>', 'Filter logs by target host domain')
|
|
758
|
+
.action((options) => {
|
|
759
|
+
const port = parseInt(options.port, 10);
|
|
760
|
+
startProxyServer(port, options.target);
|
|
557
761
|
});
|
|
558
762
|
|
|
559
763
|
program.parse(process.argv);
|
|
764
|
+
|