@kakuzu_aon/apkz 1.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.
@@ -0,0 +1,169 @@
1
+ // ────────────[ KAKUZU ]────────────────────────────
2
+ // | Discord : kakuzu_aon
3
+ // | Telegram : kakuzu_aon
4
+ // | Github : kakuzu-aon
5
+ // | File : sign.js
6
+ // | License : MIT License © 2026 Kakuzu
7
+ // | Brief : APK signing command implementation
8
+ // ────────────────★─────────────────────────────────
9
+
10
+ const { Command } = require('commander');
11
+ const chalk = require('chalk').default;
12
+ const { default: ora } = require('ora');
13
+ const fs = require('fs-extra');
14
+ const path = require('path');
15
+ const { exec } = require('child_process');
16
+ const util = require('util');
17
+ const execPromise = util.promisify(exec);
18
+
19
+ const signCommand = new Command('sign')
20
+ .description('Sign APK with keystore')
21
+ .argument('<apk-file>', 'APK file to sign')
22
+ .option('-k, --keystore <path>', 'Path to keystore file')
23
+ .option('-a, --alias <alias>', 'Keystore alias')
24
+ .option('-p, --password <password>', 'Keystore password')
25
+ .option('-o, --output <file>', 'Output signed APK file')
26
+ .option('--debug', 'Create debug keystore and sign')
27
+ .action(async (apkFile, options) => {
28
+ let spinner;
29
+ try {
30
+ if (!fs.existsSync(apkFile)) {
31
+ console.error(chalk.red(`🔴 Error: APK file not found: ${apkFile}`));
32
+ process.exit(1);
33
+ }
34
+
35
+ spinner = ora('🔐 Signing APK...').start();
36
+
37
+ await signAPK(apkFile, options);
38
+
39
+ spinner.succeed('APK signed successfully!');
40
+
41
+ } catch (error) {
42
+ if (spinner) spinner.fail('Signing failed');
43
+ console.error(chalk.red('🔴 Error:'), error.message);
44
+ process.exit(1);
45
+ }
46
+ });
47
+
48
+ async function signAPK(apkPath, options) {
49
+ const resolvedApkPath = path.resolve(apkPath);
50
+ let signedApkPath;
51
+
52
+ if (options.output) {
53
+ signedApkPath = path.resolve(options.output);
54
+ } else {
55
+ const parsed = path.parse(apkPath);
56
+ signedApkPath = path.join(parsed.dir, `${parsed.name}-signed${parsed.ext}`);
57
+ }
58
+
59
+ if (options.debug) {
60
+ // Create debug keystore and sign
61
+ await signWithDebugKeystore(resolvedApkPath, signedApkPath);
62
+ } else {
63
+ // Sign with provided keystore
64
+ await signWithKeystore(resolvedApkPath, signedApkPath, options);
65
+ }
66
+
67
+ console.log(chalk.green(`✅ Signed APK saved to: ${signedApkPath}`));
68
+
69
+ // Verify signature
70
+ await verifySignature(signedApkPath);
71
+ }
72
+
73
+ async function signWithDebugKeystore(apkPath, outputPath) {
74
+ console.log(chalk.blue('🔧 Creating debug keystore...'));
75
+
76
+ const debugKeystore = path.join(path.dirname(apkPath), 'debug.keystore');
77
+
78
+ // Create debug keystore if it doesn't exist
79
+ if (!fs.existsSync(debugKeystore)) {
80
+ try {
81
+ await execPromise(`keytool -genkey -v -keystore "${debugKeystore}" -storepass android -alias androiddebugkey -keypass android -keyalg RSA -keysize 2048 -validity 10000 -dname "CN=Android Debug,O=Android,C=US"`);
82
+ console.log(chalk.green('✅ Debug keystore created'));
83
+ } catch (error) {
84
+ throw new Error(`Failed to create debug keystore: ${error.message}`);
85
+ }
86
+ }
87
+
88
+ // Sign with debug keystore
89
+ try {
90
+ await execPromise(`jarsigner -verbose -sigalg SHA1withRSA -digestalg SHA1 -keystore "${debugKeystore}" -storepass android -keypass android "${apkPath}" androiddebugkey`);
91
+
92
+ // Copy to output path if different
93
+ if (outputPath !== apkPath) {
94
+ await fs.copy(apkPath, outputPath);
95
+ }
96
+
97
+ console.log(chalk.green('✅ APK signed with debug keystore'));
98
+ } catch (error) {
99
+ throw new Error(`Failed to sign APK: ${error.message}`);
100
+ }
101
+ }
102
+
103
+ async function signWithKeystore(apkPath, outputPath, options) {
104
+ if (!options.keystore) {
105
+ throw new Error('Keystore path is required for signing');
106
+ }
107
+
108
+ if (!fs.existsSync(options.keystore)) {
109
+ throw new Error(`Keystore not found: ${options.keystore}`);
110
+ }
111
+
112
+ if (!options.alias) {
113
+ throw new Error('Keystore alias is required');
114
+ }
115
+
116
+ const keystorePath = path.resolve(options.keystore);
117
+
118
+ try {
119
+ let signCommand = `jarsigner -verbose -sigalg SHA1withRSA -digestalg SHA1 -keystore "${keystorePath}"`;
120
+
121
+ if (options.password) {
122
+ signCommand += ` -storepass "${options.password}"`;
123
+ }
124
+
125
+ signCommand += ` "${apkPath}" "${options.alias}"`;
126
+
127
+ await execPromise(signCommand);
128
+
129
+ // Copy to output path if different
130
+ if (outputPath !== apkPath) {
131
+ await fs.copy(apkPath, outputPath);
132
+ }
133
+
134
+ console.log(chalk.green(`✅ APK signed with keystore: ${options.keystore}`));
135
+ } catch (error) {
136
+ throw new Error(`Failed to sign APK: ${error.message}`);
137
+ }
138
+ }
139
+
140
+ async function verifySignature(apkPath) {
141
+ try {
142
+ console.log(chalk.blue('🔍 Verifying signature...'));
143
+
144
+ const { stdout } = await execPromise(`jarsigner -verify -verbose "${apkPath}"`);
145
+
146
+ if (stdout.includes('jar verified')) {
147
+ console.log(chalk.green('✅ APK signature verified successfully'));
148
+ } else {
149
+ console.log(chalk.yellow('⚠️ APK signature verification returned warnings'));
150
+ console.log(chalk.gray(stdout));
151
+ }
152
+ } catch (error) {
153
+ console.log(chalk.yellow('⚠️ Could not verify signature:'));
154
+ console.log(chalk.gray(error.message));
155
+ }
156
+ }
157
+
158
+ // Check if required tools are available
159
+ async function checkSigningTools() {
160
+ try {
161
+ await execPromise('jarsigner -help');
162
+ await execPromise('keytool -help');
163
+ return true;
164
+ } catch (error) {
165
+ return false;
166
+ }
167
+ }
168
+
169
+ module.exports = signCommand;
@@ -0,0 +1,404 @@
1
+ // ────────────[ KAKUZU ]────────────────────────────
2
+ // | Discord : kakuzu_aon
3
+ // | Telegram : kakuzu_aon
4
+ // | Github : kakuzu-aon
5
+ // | File : vulnerability-scan.js
6
+ // | License : MIT License © 2026 Kakuzu
7
+ // | Brief : APK vulnerability scanning command
8
+ // ────────────────★─────────────────────────────────
9
+
10
+ const { Command } = require('commander');
11
+ const chalk = require('chalk').default;
12
+ const { default: ora } = require('ora');
13
+ const fs = require('fs-extra');
14
+ const path = require('path');
15
+ const VulnerabilityScanner = require('../utils/vulnerability-scanner');
16
+ const ObfuscationDetector = require('../utils/obfuscation-detector');
17
+
18
+ const vulnScanCommand = new Command('vuln-scan')
19
+ .description('Comprehensive vulnerability and security scan')
20
+ .argument('<apk-file>', 'APK file to scan')
21
+ .option('-d, --decode-dir <dir>', 'Use existing decoded directory')
22
+ .option('-o, --output <file>', 'Save scan report to file')
23
+ .option('-f, --format <format>', 'Report format (json, html, csv)', 'json')
24
+ .option('--obfuscation', 'Include obfuscation analysis')
25
+ .option('--deep', 'Perform deep analysis (slower)')
26
+ .option('--severity <level>', 'Minimum severity level (low, medium, high, critical)', 'low')
27
+ .action(async (apkFile, options) => {
28
+ let spinner;
29
+ try {
30
+ if (!fs.existsSync(apkFile)) {
31
+ console.error(chalk.red(`🔴 Error: APK file not found: ${apkFile}`));
32
+ process.exit(1);
33
+ }
34
+
35
+ spinner = ora('🔍 Starting comprehensive security scan...').start();
36
+
37
+ await performVulnerabilityScan(apkFile, options);
38
+
39
+ spinner.succeed('Security scan completed!');
40
+
41
+ } catch (error) {
42
+ if (spinner) spinner.fail('Security scan failed');
43
+ console.error(chalk.red('🔴 Error:'), error.message);
44
+ process.exit(1);
45
+ }
46
+ });
47
+
48
+ async function performVulnerabilityScan(apkPath, options) {
49
+ let decodedPath;
50
+ const tempDir = path.join(path.dirname(apkPath), 'apkz_vuln_' + Date.now());
51
+
52
+ try {
53
+ // Use existing decoded directory or decode APK
54
+ if (options.decodeDir && fs.existsSync(options.decodeDir)) {
55
+ decodedPath = path.resolve(options.decodeDir);
56
+ console.log(chalk.blue(`📁 Using existing decoded directory: ${decodedPath}`));
57
+ } else {
58
+ decodedPath = path.join(tempDir, 'decoded');
59
+ await decodeAPK(apkPath, decodedPath);
60
+ }
61
+
62
+ // Initialize scanners
63
+ const vulnScanner = new VulnerabilityScanner();
64
+ const obfDetector = new ObfuscationDetector();
65
+
66
+ // Perform vulnerability scan
67
+ const vulnSpinner = ora('🔍 Scanning for vulnerabilities...').start();
68
+ const vulnResults = await vulnScanner.scanAPK(decodedPath, options);
69
+ vulnSpinner.succeed('Vulnerability scan completed!');
70
+
71
+ // Perform obfuscation analysis if requested
72
+ let obfResults = null;
73
+ if (options.obfuscation) {
74
+ const obfSpinner = ora('🔍 Analyzing code obfuscation...').start();
75
+ obfResults = await obfDetector.analyzeObfuscation(decodedPath);
76
+ obfSpinner.succeed('Obfuscation analysis completed!');
77
+ }
78
+
79
+ // Display results
80
+ displayScanResults(vulnResults, obfResults, options);
81
+
82
+ // Save report if requested
83
+ if (options.output) {
84
+ const reportData = obfResults ?
85
+ { vulnerabilities: vulnResults, obfuscation: obfResults } :
86
+ vulnResults;
87
+
88
+ await saveReport(reportData, options.output, options.format);
89
+ console.log(chalk.green(`💾 Report saved to: ${options.output}`));
90
+ }
91
+
92
+ // Show risk assessment
93
+ showRiskAssessment(vulnResults, obfResults);
94
+
95
+ } finally {
96
+ // Cleanup temp directory if we created one
97
+ if (!options.decodeDir) {
98
+ await fs.remove(tempDir);
99
+ }
100
+ }
101
+ }
102
+
103
+ function displayScanResults(vulnResults, obfResults, options) {
104
+ console.log(chalk.bold('\n🔒 Security Scan Results'));
105
+ console.log(chalk.gray('─'.repeat(50)));
106
+
107
+ // Vulnerability Summary
108
+ console.log(chalk.bold('\n📊 Vulnerability Summary:'));
109
+ const vulnSummary = vulnResults.summary;
110
+
111
+ // Risk score visualization
112
+ const riskScore = vulnSummary.riskScore;
113
+ let riskColor = chalk.green;
114
+ if (riskScore >= 70) riskColor = chalk.red;
115
+ else if (riskScore >= 40) riskColor = chalk.yellow;
116
+
117
+ console.log(`Risk Score: ${riskColor(`${riskScore}/100`)}`);
118
+
119
+ // Severity breakdown
120
+ const severities = ['critical', 'high', 'medium', 'low'];
121
+ severities.forEach(severity => {
122
+ const count = vulnSummary[severity];
123
+ if (count > 0) {
124
+ const color = getSeverityColor(severity);
125
+ console.log(`${color(`${severity.toUpperCase()}: ${count}`)}`);
126
+ }
127
+ });
128
+
129
+ // Top vulnerabilities
130
+ if (vulnSummary.total > 0) {
131
+ console.log(chalk.bold('\n🚨 Top Vulnerabilities:'));
132
+ const allVulns = [
133
+ ...vulnResults.vulnerabilities.critical,
134
+ ...vulnResults.vulnerabilities.high,
135
+ ...vulnResults.vulnerabilities.medium
136
+ ].slice(0, 5);
137
+
138
+ allVulns.forEach((vuln, index) => {
139
+ const severity = vuln.severity.toUpperCase();
140
+ const color = getSeverityColor(vuln.severity);
141
+ console.log(`${index + 1}. ${color(`[${severity}]`)} ${vuln.name}`);
142
+ console.log(chalk.gray(` File: ${vuln.file}`));
143
+ console.log(chalk.gray(` CVSS: ${vuln.cvss}`));
144
+ console.log(chalk.gray(` ${vuln.description.substring(0, 100)}${vuln.description.length > 100 ? '...' : ''}`));
145
+ console.log('');
146
+ });
147
+ }
148
+
149
+ // Obfuscation results
150
+ if (obfResults) {
151
+ console.log(chalk.bold('\n🔍 Obfuscation Analysis:'));
152
+ const obfLevel = obfResults.obfuscationLevel;
153
+ const obfScore = obfResults.overallScore;
154
+
155
+ let obfColor = chalk.green;
156
+ if (obfLevel === 'none') obfColor = chalk.red;
157
+ else if (obfLevel === 'light') obfColor = chalk.yellow;
158
+ else if (obfLevel === 'heavy') obfColor = chalk.green;
159
+
160
+ console.log(`Obfuscation Level: ${obfColor(obfLevel.toUpperCase())}`);
161
+ console.log(`Obfuscation Score: ${obfScore}%`);
162
+ console.log(`Confidence: ${obfResults.confidence}%`);
163
+
164
+ if (obfResults.antiTampering.techniques.length > 0) {
165
+ console.log(chalk.blue('\n🛡️ Anti-Tampering Techniques:'));
166
+ obfResults.antiTampering.techniques.forEach(technique => {
167
+ console.log(chalk.gray(` • ${technique}`));
168
+ });
169
+ }
170
+ }
171
+
172
+ // Compliance mapping
173
+ if (vulnResults.compliance.owaspTop10.length > 0) {
174
+ console.log(chalk.bold('\n📋 OWASP Top 10 Mapping:'));
175
+ vulnResults.compliance.owaspTop10.forEach(item => {
176
+ console.log(chalk.gray(` • ${item}`));
177
+ });
178
+ }
179
+ }
180
+
181
+ function showRiskAssessment(vulnResults, obfResults) {
182
+ console.log(chalk.bold('\n🎯 Risk Assessment:'));
183
+
184
+ const vulnScore = vulnResults.summary.riskScore;
185
+ const obfScore = obfResults ? obfResults.overallScore : 0;
186
+
187
+ // Overall risk assessment
188
+ let overallRisk = 'low';
189
+ let riskColor = chalk.green;
190
+
191
+ if (vulnScore >= 70 || (vulnScore >= 50 && obfScore < 50)) {
192
+ overallRisk = 'critical';
193
+ riskColor = chalk.red;
194
+ } else if (vulnScore >= 40 || (vulnScore >= 20 && obfScore < 70)) {
195
+ overallRisk = 'high';
196
+ riskColor = chalk.red;
197
+ } else if (vulnScore >= 20 || obfScore < 50) {
198
+ overallRisk = 'medium';
199
+ riskColor = chalk.yellow;
200
+ }
201
+
202
+ console.log(`Overall Risk: ${riskColor(overallRisk.toUpperCase())}`);
203
+
204
+ // Recommendations summary
205
+ if (vulnResults.recommendations.length > 0) {
206
+ console.log(chalk.bold('\n💡 Key Recommendations:'));
207
+ vulnResults.recommendations.slice(0, 3).forEach(rec => {
208
+ console.log(chalk.blue(`• ${rec.title}`));
209
+ });
210
+ }
211
+
212
+ // Action items
213
+ console.log(chalk.bold('\n⚡ Immediate Actions Required:'));
214
+ const criticalVulns = vulnResults.vulnerabilities.critical.length + vulnResults.vulnerabilities.high.length;
215
+
216
+ if (criticalVulns > 0) {
217
+ console.log(chalk.red(`🚨 Address ${criticalVulns} critical/high vulnerabilities immediately`));
218
+ } else {
219
+ console.log(chalk.green('✅ No critical vulnerabilities found'));
220
+ }
221
+
222
+ if (obfResults && obfResults.obfuscationLevel === 'none') {
223
+ console.log(chalk.yellow('⚠️ Implement code obfuscation to protect against reverse engineering'));
224
+ }
225
+
226
+ if (obfResults && obfResults.antiTampering.techniques.length === 0) {
227
+ console.log(chalk.yellow('⚠️ Add anti-tampering protection for production builds'));
228
+ }
229
+ }
230
+
231
+ function getSeverityColor(severity) {
232
+ switch (severity) {
233
+ case 'critical': return chalk.red;
234
+ case 'high': return chalk.red;
235
+ case 'medium': return chalk.yellow;
236
+ case 'low': return chalk.blue;
237
+ default: return chalk.gray;
238
+ }
239
+ }
240
+
241
+ async function saveReport(data, outputPath, format) {
242
+ await fs.ensureDir(path.dirname(outputPath));
243
+
244
+ let content;
245
+ switch (format.toLowerCase()) {
246
+ case 'html':
247
+ content = generateHTMLReport(data);
248
+ break;
249
+ case 'csv':
250
+ content = generateCSVReport(data);
251
+ break;
252
+ default:
253
+ content = JSON.stringify(data, null, 2);
254
+ }
255
+
256
+ await fs.writeFile(outputPath, content);
257
+ }
258
+
259
+ function generateHTMLReport(data) {
260
+ const vulnResults = data.vulnerabilities || data;
261
+ const obfResults = data.obfuscation;
262
+
263
+ return `
264
+ <!DOCTYPE html>
265
+ <html>
266
+ <head>
267
+ <title>APKZ Security Report</title>
268
+ <style>
269
+ body { font-family: Arial, sans-serif; margin: 20px; background: #f5f5f5; }
270
+ .container { max-width: 1200px; margin: 0 auto; background: white; padding: 30px; border-radius: 10px; box-shadow: 0 4px 6px rgba(0,0,0,0.1); }
271
+ .header { text-align: center; border-bottom: 2px solid #007bff; padding-bottom: 20px; margin-bottom: 30px; }
272
+ .risk-score { font-size: 48px; font-weight: bold; text-align: center; margin: 20px 0; }
273
+ .critical { color: #dc3545; }
274
+ .high { color: #fd7e14; }
275
+ .medium { color: #ffc107; }
276
+ .low { color: #28a745; }
277
+ .summary { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 20px; margin: 30px 0; }
278
+ .metric { background: #f8f9fa; padding: 20px; border-radius: 8px; text-align: center; }
279
+ .vulnerability { background: white; margin: 15px 0; padding: 20px; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); border-left: 5px solid #007bff; }
280
+ .critical { border-left-color: #dc3545; }
281
+ .high { border-left-color: #fd7e14; }
282
+ .medium { border-left-color: #ffc107; }
283
+ .low { border-left-color: #28a745; }
284
+ .recommendation { background: #e7f3ff; padding: 15px; border-radius: 5px; margin: 10px 0; }
285
+ </style>
286
+ </head>
287
+ <body>
288
+ <div class="container">
289
+ <div class="header">
290
+ <h1>🔒 APKZ Security Report</h1>
291
+ <p>Generated: ${new Date().toISOString()}</p>
292
+ </div>
293
+
294
+ <div class="risk-score ${getRiskLevel(vulnResults.summary.riskScore)}">
295
+ Risk Score: ${vulnResults.summary.riskScore}/100
296
+ </div>
297
+
298
+ <div class="summary">
299
+ <div class="metric">
300
+ <h3>Critical</h3>
301
+ <p>${vulnResults.summary.critical}</p>
302
+ </div>
303
+ <div class="metric">
304
+ <h3>High</h3>
305
+ <p>${vulnResults.summary.high}</p>
306
+ </div>
307
+ <div class="metric">
308
+ <h3>Medium</h3>
309
+ <p>${vulnResults.summary.medium}</p>
310
+ </div>
311
+ <div class="metric">
312
+ <h3>Low</h3>
313
+ <p>${vulnResults.summary.low}</p>
314
+ </div>
315
+ </div>
316
+
317
+ <h2>🚨 Vulnerabilities</h2>
318
+ ${generateVulnerabilitiesHTML(vulnResults.vulnerabilities)}
319
+
320
+ ${obfResults ? `
321
+ <h2>🔍 Obfuscation Analysis</h2>
322
+ <div class="metric">
323
+ <h3>Obfuscation Level</h3>
324
+ <p>${obfResults.obfuscationLevel.toUpperCase()}</p>
325
+ </div>
326
+ <div class="metric">
327
+ <h3>Score</h3>
328
+ <p>${obfResults.overallScore}%</p>
329
+ </div>
330
+ ` : ''}
331
+
332
+ <h2>💡 Recommendations</h2>
333
+ ${generateRecommendationsHTML(vulnResults.recommendations)}
334
+ </div>
335
+ </body>
336
+ </html>`;
337
+ }
338
+
339
+ function getRiskLevel(score) {
340
+ if (score >= 70) return 'critical';
341
+ if (score >= 40) return 'high';
342
+ if (score >= 20) return 'medium';
343
+ return 'low';
344
+ }
345
+
346
+ function generateVulnerabilitiesHTML(vulnerabilities) {
347
+ let html = '';
348
+
349
+ for (const [severity, vulns] of Object.entries(vulnerabilities)) {
350
+ if (vulns.length === 0) continue;
351
+
352
+ for (const vuln of vulns) {
353
+ html += `
354
+ <div class="vulnerability ${severity}">
355
+ <h3>${vuln.name}</h3>
356
+ <p><strong>Severity:</strong> ${severity.toUpperCase()}</p>
357
+ <p><strong>CVSS:</strong> ${vuln.cvss}</p>
358
+ <p><strong>File:</strong> ${vuln.file}</p>
359
+ <p><strong>Description:</strong> ${vuln.description}</p>
360
+ ${vuln.evidence ? `<p><strong>Evidence:</strong> <code>${vuln.evidence.join(', ')}</code></p>` : ''}
361
+ </div>`;
362
+ }
363
+ }
364
+
365
+ return html;
366
+ }
367
+
368
+ function generateRecommendationsHTML(recommendations) {
369
+ let html = '';
370
+
371
+ for (const rec of recommendations) {
372
+ html += `
373
+ <div class="recommendation">
374
+ <h3>${rec.title}</h3>
375
+ <p>${rec.description}</p>
376
+ <ul>
377
+ ${rec.actions.map(action => `<li>${action}</li>`).join('')}
378
+ </ul>
379
+ </div>`;
380
+ }
381
+
382
+ return html;
383
+ }
384
+
385
+ function generateCSVReport(data) {
386
+ const vulnResults = data.vulnerabilities || data;
387
+ let csv = 'Severity,ID,Name,File,CVSS,Description\n';
388
+
389
+ for (const [severity, vulns] of Object.entries(vulnResults.vulnerabilities)) {
390
+ for (const vuln of vulns) {
391
+ csv += `${severity},${vuln.id},"${vuln.name}","${vuln.file}",${vuln.cvss},"${vuln.description}"\n`;
392
+ }
393
+ }
394
+
395
+ return csv;
396
+ }
397
+
398
+ async function decodeAPK(apkPath, outputDir) {
399
+ const AdmZip = require('adm-zip');
400
+ const zip = new AdmZip(apkPath);
401
+ zip.extractAllTo(outputDir, true);
402
+ }
403
+
404
+ module.exports = vulnScanCommand;
@@ -0,0 +1,97 @@
1
+ // ────────────[ KAKUZU ]────────────────────────────
2
+ // | Discord : kakuzu_aon
3
+ // | Telegram : kakuzu_aon
4
+ // | Github : kakuzu-aon
5
+ // | File : web.js
6
+ // | License : MIT License © 2026 Kakuzu
7
+ // | Brief : Web interface command
8
+ // ────────────────★─────────────────────────────────
9
+
10
+ const { Command } = require('commander');
11
+ const chalk = require('chalk').default;
12
+ const { default: ora } = require('ora');
13
+ const WebServer = require('../web/web-server');
14
+
15
+ const webCommand = new Command('web')
16
+ .description('Start web interface for APK analysis')
17
+ .option('-p, --port <number>', 'Port for web server', '3000')
18
+ .option('--host <address>', 'Host address', 'localhost')
19
+ .option('--no-open', 'Do not open browser automatically')
20
+ .action(async (options) => {
21
+ try {
22
+ await startWebInterface(options);
23
+ } catch (error) {
24
+ console.error(chalk.red('🔴 Error:'), error.message);
25
+ process.exit(1);
26
+ }
27
+ });
28
+
29
+ async function startWebInterface(options) {
30
+ const port = parseInt(options.port) || 3000;
31
+ const host = options.host || 'localhost';
32
+
33
+ console.log(chalk.blue('🌐 Starting APKZ Web Interface...'));
34
+
35
+ const webServer = new WebServer(port);
36
+
37
+ try {
38
+ await webServer.start();
39
+
40
+ // Display startup information
41
+ console.log(chalk.green('\n✅ Web server started successfully!'));
42
+ console.log(chalk.blue(`🌐 Local: http://${host}:${port}`));
43
+ console.log(chalk.blue(`🌐 Network: http://0.0.0.0:${port}`));
44
+
45
+ console.log(chalk.bold('\n📋 Available Features:'));
46
+ console.log(chalk.gray(' • Upload and analyze APK files'));
47
+ console.log(chalk.gray(' • Vulnerability scanning'));
48
+ console.log(chalk.gray(' • Obfuscation analysis'));
49
+ console.log(chalk.gray(' • Network analysis'));
50
+ console.log(chalk.gray(' • Real-time progress tracking'));
51
+ console.log(chalk.gray(' • Download reports (JSON, HTML, CSV)'));
52
+ console.log(chalk.gray(' • Batch processing support'));
53
+
54
+ console.log(chalk.bold('\n🎮 API Endpoints:'));
55
+ console.log(chalk.gray(' • POST /api/upload - Upload APK'));
56
+ console.log(chalk.gray(' • POST /api/analyze - Start analysis'));
57
+ console.log(chalk.gray(' • GET /api/results/:jobId - Get results'));
58
+ console.log(chalk.gray(' • GET /api/jobs - List all jobs'));
59
+ console.log(chalk.gray(' • GET /api/download/:jobId/:format - Download report'));
60
+
61
+ // Open browser if not disabled
62
+ if (options.open !== false) {
63
+ const open = require('open');
64
+ try {
65
+ await open(`http://${host}:${port}`);
66
+ console.log(chalk.green('\n🌍 Browser opened automatically'));
67
+ } catch (error) {
68
+ console.log(chalk.yellow('\n⚠️ Could not open browser automatically'));
69
+ console.log(chalk.blue(` Please open: http://${host}:${port}`));
70
+ }
71
+ }
72
+
73
+ console.log(chalk.bold('\n🛑 Press Ctrl+C to stop the server'));
74
+
75
+ // Handle graceful shutdown
76
+ process.on('SIGINT', async () => {
77
+ console.log(chalk.yellow('\n🛑 Shutting down web server...'));
78
+ await webServer.stop();
79
+ console.log(chalk.green('✅ Web server stopped'));
80
+ process.exit(0);
81
+ });
82
+
83
+ // Keep process running
84
+ await new Promise(() => {});
85
+
86
+ } catch (error) {
87
+ if (error.code === 'EADDRINUSE') {
88
+ console.error(chalk.red(`🔴 Port ${port} is already in use`));
89
+ console.error(chalk.yellow(`💡 Try a different port: apkz web -p ${port + 1}`));
90
+ } else {
91
+ console.error(chalk.red('🔴 Failed to start web server:'), error.message);
92
+ }
93
+ throw error;
94
+ }
95
+ }
96
+
97
+ module.exports = webCommand;