@ayurak/aribot-cli 1.1.2 → 1.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +428 -404
- package/dist/index.d.ts +2 -2
- package/dist/index.js +7 -15
- package/dist/sdk.js +13 -58
- package/dist/theme.d.ts +15 -15
- package/dist/theme.js +123 -147
- package/package.json +2 -1
- package/src/cli.ts +44 -10
- package/src/index.ts +2 -2
package/dist/cli.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
"use strict";
|
|
3
2
|
/**
|
|
4
3
|
* Aribot CLI - Economic, Regulatory & Security APIs for Modern Applications
|
|
5
4
|
*
|
|
@@ -19,30 +18,34 @@
|
|
|
19
18
|
* aribot export <diagram-id> # Export report
|
|
20
19
|
* aribot diagrams # List all diagrams
|
|
21
20
|
*/
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
const
|
|
33
|
-
const
|
|
34
|
-
const
|
|
21
|
+
import { Command } from 'commander';
|
|
22
|
+
import chalk from 'chalk';
|
|
23
|
+
import ora from 'ora';
|
|
24
|
+
import Conf from 'conf';
|
|
25
|
+
import inquirer from 'inquirer';
|
|
26
|
+
import FormData from 'form-data';
|
|
27
|
+
import fs from 'fs';
|
|
28
|
+
import path from 'path';
|
|
29
|
+
import { fileURLToPath } from 'url';
|
|
30
|
+
import { createRequire } from 'module';
|
|
31
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
32
|
+
const __dirname = path.dirname(__filename);
|
|
33
|
+
const require = createRequire(import.meta.url);
|
|
34
|
+
// Read version from package.json
|
|
35
|
+
const packageJson = require('../package.json');
|
|
36
|
+
const VERSION = packageJson.version;
|
|
37
|
+
const config = new Conf({ projectName: 'aribot-cli' });
|
|
35
38
|
const API_BASE = 'https://api.aribot.ayurak.com/aribot-api';
|
|
36
|
-
const program = new
|
|
39
|
+
const program = new Command();
|
|
37
40
|
program
|
|
38
41
|
.name('aribot')
|
|
39
42
|
.description('Aribot - Economic, Regulatory & Security APIs for Modern Applications')
|
|
40
|
-
.version(
|
|
43
|
+
.version(VERSION);
|
|
41
44
|
// Helper to get auth headers
|
|
42
45
|
function getHeaders() {
|
|
43
46
|
const apiKey = config.get('apiKey');
|
|
44
47
|
if (!apiKey) {
|
|
45
|
-
console.error(
|
|
48
|
+
console.error(chalk.red('Not authenticated. Run: aribot login'));
|
|
46
49
|
process.exit(1);
|
|
47
50
|
}
|
|
48
51
|
return {
|
|
@@ -85,22 +88,22 @@ program
|
|
|
85
88
|
.action(async (options) => {
|
|
86
89
|
// Welcome banner
|
|
87
90
|
console.log();
|
|
88
|
-
console.log(
|
|
89
|
-
console.log(
|
|
90
|
-
console.log(
|
|
91
|
+
console.log(chalk.cyan('='.repeat(50)));
|
|
92
|
+
console.log(chalk.cyan.bold(' ARIBOT CLI - Secure Authentication'));
|
|
93
|
+
console.log(chalk.cyan('='.repeat(50)));
|
|
91
94
|
console.log();
|
|
92
95
|
// Check existing auth
|
|
93
96
|
const existingKey = config.get('apiKey');
|
|
94
97
|
if (existingKey) {
|
|
95
|
-
console.log(
|
|
98
|
+
console.log(chalk.yellow('You are already logged in.'));
|
|
96
99
|
const email = config.get('userEmail');
|
|
97
100
|
const company = config.get('company');
|
|
98
101
|
if (email) {
|
|
99
|
-
console.log(` ${
|
|
100
|
-
console.log(` ${
|
|
102
|
+
console.log(` ${chalk.dim('Email:')} ${email}`);
|
|
103
|
+
console.log(` ${chalk.dim('Company:')} ${company || 'N/A'}`);
|
|
101
104
|
}
|
|
102
105
|
console.log();
|
|
103
|
-
const { reauth } = await
|
|
106
|
+
const { reauth } = await inquirer.prompt([{
|
|
104
107
|
type: 'confirm',
|
|
105
108
|
name: 'reauth',
|
|
106
109
|
message: 'Would you like to re-authenticate with a different key?',
|
|
@@ -113,28 +116,28 @@ program
|
|
|
113
116
|
if (options.openPortal) {
|
|
114
117
|
const open = (await import('open')).default;
|
|
115
118
|
const portalUrl = 'https://developer.ayurak.com';
|
|
116
|
-
console.log(
|
|
117
|
-
console.log(
|
|
119
|
+
console.log(chalk.cyan('Opening developer portal...'));
|
|
120
|
+
console.log(chalk.dim(portalUrl));
|
|
118
121
|
await open(portalUrl);
|
|
119
122
|
console.log();
|
|
120
|
-
console.log(
|
|
121
|
-
console.log(' ' +
|
|
123
|
+
console.log(chalk.dim('After creating your API key, run:'));
|
|
124
|
+
console.log(' ' + chalk.green('aribot login'));
|
|
122
125
|
return;
|
|
123
126
|
}
|
|
124
127
|
// Show instructions
|
|
125
|
-
console.log(
|
|
128
|
+
console.log(chalk.bold('To authenticate, you need an API key.'));
|
|
126
129
|
console.log();
|
|
127
|
-
console.log(
|
|
128
|
-
console.log(' ' +
|
|
130
|
+
console.log(chalk.dim('Get your API key from the developer portal:'));
|
|
131
|
+
console.log(' ' + chalk.cyan('https://developer.ayurak.com'));
|
|
129
132
|
console.log();
|
|
130
|
-
console.log(
|
|
133
|
+
console.log(chalk.dim('Or run: ' + chalk.green('aribot login --open-portal') + ' to open it'));
|
|
131
134
|
console.log();
|
|
132
135
|
// Get API key
|
|
133
136
|
let apiKey = options.key;
|
|
134
137
|
if (!apiKey) {
|
|
135
|
-
console.log(
|
|
136
|
-
console.log(
|
|
137
|
-
const response = await
|
|
138
|
+
console.log(chalk.bold('Enter your API key below'));
|
|
139
|
+
console.log(chalk.dim('(input is hidden for security)'));
|
|
140
|
+
const response = await inquirer.prompt([{
|
|
138
141
|
type: 'password',
|
|
139
142
|
name: 'apiKey',
|
|
140
143
|
message: 'API Key:',
|
|
@@ -143,19 +146,19 @@ program
|
|
|
143
146
|
apiKey = response.apiKey;
|
|
144
147
|
}
|
|
145
148
|
else {
|
|
146
|
-
console.log(
|
|
149
|
+
console.log(chalk.dim('Using provided API key...'));
|
|
147
150
|
}
|
|
148
151
|
// Validate format
|
|
149
152
|
apiKey = apiKey.trim();
|
|
150
153
|
if (apiKey.length < 20) {
|
|
151
154
|
console.log();
|
|
152
|
-
console.log(
|
|
153
|
-
console.log(
|
|
154
|
-
console.log(
|
|
155
|
+
console.log(chalk.red('Invalid API key format'));
|
|
156
|
+
console.log(chalk.dim('API keys should be at least 20 characters long.'));
|
|
157
|
+
console.log(chalk.dim('Get a valid key from: https://developer.ayurak.com'));
|
|
155
158
|
return;
|
|
156
159
|
}
|
|
157
160
|
console.log();
|
|
158
|
-
const spinner = (
|
|
161
|
+
const spinner = ora('Validating API key...').start();
|
|
159
162
|
try {
|
|
160
163
|
const fetch = (await import('node-fetch')).default;
|
|
161
164
|
const response = await fetch(`${API_BASE}/v1/developer/token/`, {
|
|
@@ -171,52 +174,52 @@ program
|
|
|
171
174
|
config.set('apiKey', apiKey);
|
|
172
175
|
config.set('userEmail', data.user?.email);
|
|
173
176
|
config.set('company', data.user?.company);
|
|
174
|
-
spinner.succeed(
|
|
177
|
+
spinner.succeed(chalk.green('Authentication successful!'));
|
|
175
178
|
console.log();
|
|
176
|
-
console.log(
|
|
177
|
-
console.log(` ${
|
|
178
|
-
console.log(` ${
|
|
179
|
-
console.log(` ${
|
|
179
|
+
console.log(chalk.bold('Account Details:'));
|
|
180
|
+
console.log(` ${chalk.cyan('Email:')} ${data.user?.email || 'N/A'}`);
|
|
181
|
+
console.log(` ${chalk.cyan('Company:')} ${data.user?.company || 'N/A'}`);
|
|
182
|
+
console.log(` ${chalk.cyan('Plan:')} ${data.plan || 'Standard'}`);
|
|
180
183
|
console.log();
|
|
181
|
-
console.log(
|
|
182
|
-
console.log(' ' +
|
|
183
|
-
console.log(' ' +
|
|
184
|
+
console.log(chalk.bold('Security:'));
|
|
185
|
+
console.log(' ' + chalk.green('API key stored in secure config'));
|
|
186
|
+
console.log(' ' + chalk.dim('Your key is encrypted and safe.'));
|
|
184
187
|
console.log();
|
|
185
|
-
console.log(
|
|
186
|
-
console.log(' ' +
|
|
187
|
-
console.log(' ' +
|
|
188
|
-
console.log(' ' +
|
|
189
|
-
console.log(' ' +
|
|
188
|
+
console.log(chalk.bold('Next steps:'));
|
|
189
|
+
console.log(' ' + chalk.cyan('aribot diagrams') + ' - List your diagrams');
|
|
190
|
+
console.log(' ' + chalk.cyan('aribot analyze <file>') + ' - Analyze a new diagram');
|
|
191
|
+
console.log(' ' + chalk.cyan('aribot status') + ' - Check API status');
|
|
192
|
+
console.log(' ' + chalk.cyan('aribot --help') + ' - See all commands');
|
|
190
193
|
}
|
|
191
194
|
else {
|
|
192
|
-
spinner.fail(
|
|
195
|
+
spinner.fail(chalk.red('Authentication failed'));
|
|
193
196
|
console.log();
|
|
194
197
|
try {
|
|
195
198
|
const error = await response.json();
|
|
196
|
-
console.log(
|
|
199
|
+
console.log(chalk.red(error.message || error.detail || 'Invalid API key'));
|
|
197
200
|
}
|
|
198
201
|
catch {
|
|
199
202
|
if (response.status === 401) {
|
|
200
|
-
console.log(
|
|
203
|
+
console.log(chalk.red('Invalid API key. Please check and try again.'));
|
|
201
204
|
}
|
|
202
205
|
else if (response.status === 403) {
|
|
203
|
-
console.log(
|
|
206
|
+
console.log(chalk.red('API key is disabled or expired.'));
|
|
204
207
|
}
|
|
205
208
|
else {
|
|
206
|
-
console.log(
|
|
209
|
+
console.log(chalk.red(`Authentication failed (HTTP ${response.status})`));
|
|
207
210
|
}
|
|
208
211
|
}
|
|
209
212
|
console.log();
|
|
210
|
-
console.log(
|
|
211
|
-
console.log(' ' +
|
|
213
|
+
console.log(chalk.dim('Need a new API key? Visit:'));
|
|
214
|
+
console.log(' ' + chalk.cyan('https://developer.ayurak.com'));
|
|
212
215
|
}
|
|
213
216
|
}
|
|
214
217
|
catch (error) {
|
|
215
|
-
spinner.fail(
|
|
218
|
+
spinner.fail(chalk.red('Authentication failed'));
|
|
216
219
|
console.log();
|
|
217
220
|
if (error.code === 'ETIMEDOUT' || error.code === 'ECONNREFUSED') {
|
|
218
|
-
console.log(
|
|
219
|
-
console.log(
|
|
221
|
+
console.log(chalk.red('Could not connect to Aribot servers.'));
|
|
222
|
+
console.log(chalk.dim('Please check your internet connection.'));
|
|
220
223
|
}
|
|
221
224
|
else {
|
|
222
225
|
console.error(error.message || error);
|
|
@@ -229,7 +232,7 @@ program
|
|
|
229
232
|
.description('Remove stored credentials')
|
|
230
233
|
.action(() => {
|
|
231
234
|
config.delete('apiKey');
|
|
232
|
-
console.log(
|
|
235
|
+
console.log(chalk.green('Logged out successfully.'));
|
|
233
236
|
});
|
|
234
237
|
// List diagrams
|
|
235
238
|
program
|
|
@@ -237,21 +240,21 @@ program
|
|
|
237
240
|
.description('List all your diagrams')
|
|
238
241
|
.option('-l, --limit <number>', 'Number of diagrams to show', '10')
|
|
239
242
|
.action(async (options) => {
|
|
240
|
-
const spinner = (
|
|
243
|
+
const spinner = ora('Fetching diagrams...').start();
|
|
241
244
|
try {
|
|
242
245
|
const data = await apiRequest(`/v2/threat-modeling/diagrams/?limit=${options.limit}`);
|
|
243
246
|
spinner.stop();
|
|
244
|
-
console.log(
|
|
247
|
+
console.log(chalk.bold('\nYour Diagrams:\n'));
|
|
245
248
|
if (!data.results?.length) {
|
|
246
|
-
console.log(
|
|
249
|
+
console.log(chalk.dim('No diagrams found. Create one at https://developer.ayurak.com'));
|
|
247
250
|
return;
|
|
248
251
|
}
|
|
249
252
|
data.results.forEach((d) => {
|
|
250
|
-
const status = d.stage === 'completed' ?
|
|
253
|
+
const status = d.stage === 'completed' ? chalk.green('✓') : chalk.yellow('⋯');
|
|
251
254
|
const name = d.name || d.filename || 'Unnamed';
|
|
252
|
-
console.log(`${status} ${
|
|
255
|
+
console.log(`${status} ${chalk.cyan(d.id.slice(0, 8))} ${name} ${chalk.dim(d.threats_count + ' threats')}`);
|
|
253
256
|
});
|
|
254
|
-
console.log(
|
|
257
|
+
console.log(chalk.dim(`\nShowing ${data.results.length} of ${data.count} diagrams`));
|
|
255
258
|
}
|
|
256
259
|
catch (error) {
|
|
257
260
|
spinner.fail('Failed to fetch diagrams');
|
|
@@ -265,16 +268,16 @@ program
|
|
|
265
268
|
.option('-n, --name <name>', 'Diagram name')
|
|
266
269
|
.option('--auto-threats', 'Automatically generate AI threats', true)
|
|
267
270
|
.action(async (file, options) => {
|
|
268
|
-
if (!
|
|
269
|
-
console.error(
|
|
271
|
+
if (!fs.existsSync(file)) {
|
|
272
|
+
console.error(chalk.red(`File not found: ${file}`));
|
|
270
273
|
process.exit(1);
|
|
271
274
|
}
|
|
272
|
-
const spinner = (
|
|
275
|
+
const spinner = ora('Uploading diagram...').start();
|
|
273
276
|
try {
|
|
274
277
|
const fetch = (await import('node-fetch')).default;
|
|
275
|
-
const form = new
|
|
276
|
-
form.append('file',
|
|
277
|
-
form.append('name', options.name ||
|
|
278
|
+
const form = new FormData();
|
|
279
|
+
form.append('file', fs.createReadStream(file));
|
|
280
|
+
form.append('name', options.name || path.basename(file, path.extname(file)));
|
|
278
281
|
form.append('auto_generate_threats', options.autoThreats ? 'true' : 'false');
|
|
279
282
|
const apiKey = config.get('apiKey');
|
|
280
283
|
const response = await fetch(`${API_BASE}/v2/threat-modeling/diagrams/upload-analyze/`, {
|
|
@@ -287,12 +290,12 @@ program
|
|
|
287
290
|
}
|
|
288
291
|
const data = await response.json();
|
|
289
292
|
spinner.succeed('Diagram uploaded!');
|
|
290
|
-
console.log(
|
|
291
|
-
console.log(` ID: ${
|
|
293
|
+
console.log(chalk.bold('\nDiagram Created:'));
|
|
294
|
+
console.log(` ID: ${chalk.cyan(data.id)}`);
|
|
292
295
|
console.log(` Name: ${data.name}`);
|
|
293
296
|
console.log(` Status: ${data.status}`);
|
|
294
297
|
if (options.autoThreats) {
|
|
295
|
-
const threatSpinner = (
|
|
298
|
+
const threatSpinner = ora('Generating AI threats...').start();
|
|
296
299
|
// Poll for completion
|
|
297
300
|
let attempts = 0;
|
|
298
301
|
while (attempts < 30) {
|
|
@@ -305,7 +308,7 @@ program
|
|
|
305
308
|
attempts++;
|
|
306
309
|
}
|
|
307
310
|
}
|
|
308
|
-
console.log(
|
|
311
|
+
console.log(chalk.dim(`\nView at: https://developer.ayurak.com/diagrams/${data.id}`));
|
|
309
312
|
}
|
|
310
313
|
catch (error) {
|
|
311
314
|
spinner.fail('Analysis failed');
|
|
@@ -318,7 +321,7 @@ program
|
|
|
318
321
|
.description('List threats for a diagram')
|
|
319
322
|
.option('-s, --severity <level>', 'Filter by severity (critical, high, medium, low)')
|
|
320
323
|
.action(async (diagramId, options) => {
|
|
321
|
-
const spinner = (
|
|
324
|
+
const spinner = ora('Fetching threats...').start();
|
|
322
325
|
try {
|
|
323
326
|
// Resolve short UUID to full UUID
|
|
324
327
|
const fullId = await resolveDiagramId(diagramId);
|
|
@@ -328,21 +331,21 @@ program
|
|
|
328
331
|
}
|
|
329
332
|
const data = await apiRequest(url);
|
|
330
333
|
spinner.stop();
|
|
331
|
-
console.log(
|
|
334
|
+
console.log(chalk.bold('\nThreats:\n'));
|
|
332
335
|
const severityColors = {
|
|
333
|
-
critical:
|
|
334
|
-
high:
|
|
335
|
-
medium:
|
|
336
|
-
low:
|
|
336
|
+
critical: chalk.red,
|
|
337
|
+
high: chalk.yellow,
|
|
338
|
+
medium: chalk.blue,
|
|
339
|
+
low: chalk.dim
|
|
337
340
|
};
|
|
338
341
|
const threats = data.threats || data.results || [];
|
|
339
342
|
threats.forEach((t) => {
|
|
340
|
-
const color = severityColors[t.severity] ||
|
|
343
|
+
const color = severityColors[t.severity] || chalk.white;
|
|
341
344
|
console.log(`${color(`[${t.severity?.toUpperCase() || 'UNKNOWN'}]`)} ${t.title || t.name}`);
|
|
342
|
-
console.log(
|
|
345
|
+
console.log(chalk.dim(` Category: ${t.category || t.stride_category || 'N/A'} | ID: ${String(t.id).slice(0, 8)}`));
|
|
343
346
|
console.log();
|
|
344
347
|
});
|
|
345
|
-
console.log(
|
|
348
|
+
console.log(chalk.dim(`Total: ${threats.length} threats`));
|
|
346
349
|
}
|
|
347
350
|
catch (error) {
|
|
348
351
|
spinner.fail('Failed to fetch threats');
|
|
@@ -356,7 +359,7 @@ program
|
|
|
356
359
|
.option('-f, --format <format>', 'Export format (pdf, json, csv)', 'json')
|
|
357
360
|
.option('-o, --output <file>', 'Output file path')
|
|
358
361
|
.action(async (diagramId, options) => {
|
|
359
|
-
const spinner = (
|
|
362
|
+
const spinner = ora(`Exporting ${options.format.toUpperCase()} report...`).start();
|
|
360
363
|
try {
|
|
361
364
|
// Resolve short UUID to full UUID
|
|
362
365
|
const fullId = await resolveDiagramId(diagramId);
|
|
@@ -380,13 +383,13 @@ program
|
|
|
380
383
|
const outputPath = options.output || `aribot-report-${fullId.slice(0, 8)}.${options.format}`;
|
|
381
384
|
if (options.format === 'json') {
|
|
382
385
|
const data = await response.json();
|
|
383
|
-
|
|
386
|
+
fs.writeFileSync(outputPath, JSON.stringify(data, null, 2));
|
|
384
387
|
}
|
|
385
388
|
else {
|
|
386
389
|
const buffer = await response.arrayBuffer();
|
|
387
|
-
|
|
390
|
+
fs.writeFileSync(outputPath, Buffer.from(buffer));
|
|
388
391
|
}
|
|
389
|
-
spinner.succeed(`Report saved to ${
|
|
392
|
+
spinner.succeed(`Report saved to ${chalk.cyan(outputPath)}`);
|
|
390
393
|
}
|
|
391
394
|
catch (error) {
|
|
392
395
|
spinner.fail('Export failed');
|
|
@@ -398,7 +401,7 @@ program
|
|
|
398
401
|
.command('generate-threats <diagram-id>')
|
|
399
402
|
.description('Generate AI threats for an existing diagram')
|
|
400
403
|
.action(async (diagramId) => {
|
|
401
|
-
const spinner = (
|
|
404
|
+
const spinner = ora('Generating AI threats...').start();
|
|
402
405
|
try {
|
|
403
406
|
// Resolve short UUID to full UUID
|
|
404
407
|
const fullId = await resolveDiagramId(diagramId);
|
|
@@ -431,8 +434,8 @@ program
|
|
|
431
434
|
.action(async () => {
|
|
432
435
|
const apiKey = config.get('apiKey');
|
|
433
436
|
if (!apiKey) {
|
|
434
|
-
console.log(
|
|
435
|
-
console.log(
|
|
437
|
+
console.log(chalk.yellow('Not authenticated'));
|
|
438
|
+
console.log(chalk.dim('Run: aribot login'));
|
|
436
439
|
return;
|
|
437
440
|
}
|
|
438
441
|
try {
|
|
@@ -441,7 +444,7 @@ program
|
|
|
441
444
|
apiRequest('/v1/developer/api-keys/')
|
|
442
445
|
]);
|
|
443
446
|
const company = keysData?.[0]?.company_name || userData.company || 'N/A';
|
|
444
|
-
console.log(
|
|
447
|
+
console.log(chalk.green('Authenticated as:'));
|
|
445
448
|
console.log(` Email: ${userData.email}`);
|
|
446
449
|
console.log(` Company: ${company}`);
|
|
447
450
|
if (keysData?.[0]) {
|
|
@@ -449,7 +452,7 @@ program
|
|
|
449
452
|
}
|
|
450
453
|
}
|
|
451
454
|
catch {
|
|
452
|
-
console.log(
|
|
455
|
+
console.log(chalk.yellow('API key stored but validation failed'));
|
|
453
456
|
}
|
|
454
457
|
});
|
|
455
458
|
// Status command - Check API status and rate limits
|
|
@@ -457,29 +460,29 @@ program
|
|
|
457
460
|
.command('status')
|
|
458
461
|
.description('Check API status and rate limits')
|
|
459
462
|
.action(async () => {
|
|
460
|
-
const spinner = (
|
|
463
|
+
const spinner = ora('Checking API status...').start();
|
|
461
464
|
try {
|
|
462
465
|
const fetch = (await import('node-fetch')).default;
|
|
463
466
|
// Check health endpoint (no auth required)
|
|
464
467
|
const healthResponse = await fetch(`${API_BASE}/health/`);
|
|
465
468
|
const health = await healthResponse.json();
|
|
466
469
|
spinner.stop();
|
|
467
|
-
console.log(
|
|
468
|
-
console.log(` Status: ${health.status === 'healthy' ?
|
|
469
|
-
console.log(` Version: ${
|
|
470
|
-
console.log(` Features: ${health.features_enabled ?
|
|
470
|
+
console.log(chalk.bold('\nAPI Status:\n'));
|
|
471
|
+
console.log(` Status: ${health.status === 'healthy' ? chalk.green('✓ Healthy') : chalk.red('✗ Unhealthy')}`);
|
|
472
|
+
console.log(` Version: ${chalk.cyan(health.version || 'N/A')}`);
|
|
473
|
+
console.log(` Features: ${health.features_enabled ? chalk.green('Enabled') : chalk.yellow('Disabled')}`);
|
|
471
474
|
// Check rate limits if authenticated
|
|
472
475
|
const apiKey = config.get('apiKey');
|
|
473
476
|
if (apiKey) {
|
|
474
477
|
try {
|
|
475
478
|
const limits = await apiRequest('/v2/developer-portal/rate-limits/usage/');
|
|
476
|
-
console.log(
|
|
479
|
+
console.log(chalk.bold('\nRate Limits:\n'));
|
|
477
480
|
console.log(` Requests/min: ${limits.requests_per_minute?.used || 0}/${limits.requests_per_minute?.limit || 'unlimited'}`);
|
|
478
481
|
console.log(` Requests/hour: ${limits.requests_per_hour?.used || 0}/${limits.requests_per_hour?.limit || 'unlimited'}`);
|
|
479
482
|
console.log(` Requests/day: ${limits.requests_per_day?.used || 0}/${limits.requests_per_day?.limit || 'unlimited'}`);
|
|
480
483
|
}
|
|
481
484
|
catch {
|
|
482
|
-
console.log(
|
|
485
|
+
console.log(chalk.dim('\nRate limit info requires authentication'));
|
|
483
486
|
}
|
|
484
487
|
}
|
|
485
488
|
}
|
|
@@ -497,7 +500,7 @@ program
|
|
|
497
500
|
.option('--list-standards', 'List all available compliance standards')
|
|
498
501
|
.action(async (diagramId, options) => {
|
|
499
502
|
if (options.listStandards) {
|
|
500
|
-
console.log(
|
|
503
|
+
console.log(chalk.bold('\nSupported Compliance Standards:\n'));
|
|
501
504
|
const standards = [
|
|
502
505
|
'SOC2', 'ISO27001', 'ISO27017', 'ISO27018', 'ISO22301',
|
|
503
506
|
'NIST-CSF', 'NIST-800-53', 'NIST-800-171',
|
|
@@ -509,16 +512,16 @@ program
|
|
|
509
512
|
'SOX', 'GLBA', 'FISMA',
|
|
510
513
|
'CSA-CCM', 'MITRE-ATT&CK', 'OWASP-TOP-10',
|
|
511
514
|
];
|
|
512
|
-
standards.forEach(s => console.log(` ${
|
|
515
|
+
standards.forEach(s => console.log(` ${chalk.cyan('•')} ${s}`));
|
|
513
516
|
return;
|
|
514
517
|
}
|
|
515
518
|
if (!diagramId) {
|
|
516
|
-
console.error(
|
|
517
|
-
console.log(
|
|
518
|
-
console.log(
|
|
519
|
+
console.error(chalk.red('Error: diagram-id is required for compliance assessment'));
|
|
520
|
+
console.log(chalk.dim('Usage: aribot compliance <diagram-id> --standard SOC2'));
|
|
521
|
+
console.log(chalk.dim(' aribot compliance --list-standards'));
|
|
519
522
|
process.exit(1);
|
|
520
523
|
}
|
|
521
|
-
const spinner = (
|
|
524
|
+
const spinner = ora(`Running ${options.standard} compliance assessment...`).start();
|
|
522
525
|
try {
|
|
523
526
|
// Resolve short UUID to full UUID
|
|
524
527
|
const fullId = await resolveDiagramId(diagramId);
|
|
@@ -530,15 +533,15 @@ program
|
|
|
530
533
|
})
|
|
531
534
|
});
|
|
532
535
|
spinner.succeed('Compliance assessment complete!');
|
|
533
|
-
console.log(
|
|
534
|
-
console.log(` Score: ${data.score >= 80 ?
|
|
535
|
-
console.log(` Passed Controls: ${
|
|
536
|
-
console.log(` Failed Controls: ${
|
|
537
|
-
console.log(` Status: ${data.status === 'compliant' ?
|
|
536
|
+
console.log(chalk.bold(`\n${options.standard} Compliance Report:\n`));
|
|
537
|
+
console.log(` Score: ${data.score >= 80 ? chalk.green(data.score + '%') : data.score >= 60 ? chalk.yellow(data.score + '%') : chalk.red(data.score + '%')}`);
|
|
538
|
+
console.log(` Passed Controls: ${chalk.green(data.passed_controls || 0)}`);
|
|
539
|
+
console.log(` Failed Controls: ${chalk.red(data.failed_controls || 0)}`);
|
|
540
|
+
console.log(` Status: ${data.status === 'compliant' ? chalk.green('Compliant') : chalk.yellow('Non-Compliant')}`);
|
|
538
541
|
if (data.findings?.length > 0) {
|
|
539
|
-
console.log(
|
|
542
|
+
console.log(chalk.bold('\nTop Findings:\n'));
|
|
540
543
|
data.findings.slice(0, 5).forEach((f) => {
|
|
541
|
-
const severityColor = f.severity === 'high' ?
|
|
544
|
+
const severityColor = f.severity === 'high' ? chalk.red : f.severity === 'medium' ? chalk.yellow : chalk.dim;
|
|
542
545
|
console.log(` ${severityColor(`[${f.severity?.toUpperCase()}]`)} ${f.title || f.control_id}`);
|
|
543
546
|
});
|
|
544
547
|
}
|
|
@@ -553,12 +556,12 @@ program
|
|
|
553
556
|
.command('economics')
|
|
554
557
|
.description('Economic intelligence and cost analysis')
|
|
555
558
|
.option('--roi <investment>', 'Calculate ROI for security investment (in USD)')
|
|
556
|
-
.option('--tco <
|
|
559
|
+
.option('--tco <diagram-id>', 'Calculate TCO for a diagram using economic intelligence')
|
|
557
560
|
.option('--analyze <diagram-id>', 'Analyze costs for a diagram')
|
|
558
561
|
.option('--cost <diagram-id>', 'AI-powered cost intelligence for diagram')
|
|
559
562
|
.option('--dashboard', 'Show economic intelligence dashboard')
|
|
560
563
|
.action(async (options) => {
|
|
561
|
-
const spinner = (
|
|
564
|
+
const spinner = ora('Calculating...').start();
|
|
562
565
|
try {
|
|
563
566
|
if (options.roi) {
|
|
564
567
|
const investment = parseFloat(options.roi);
|
|
@@ -571,27 +574,48 @@ program
|
|
|
571
574
|
})
|
|
572
575
|
});
|
|
573
576
|
spinner.succeed('ROI Analysis Complete!');
|
|
574
|
-
console.log(
|
|
575
|
-
console.log(` Investment: ${
|
|
576
|
-
console.log(` Expected ROI: ${
|
|
577
|
-
console.log(` NPV: ${
|
|
578
|
-
console.log(` Payback Period: ${
|
|
579
|
-
console.log(` Risk Reduction: ${
|
|
577
|
+
console.log(chalk.bold('\nSecurity ROI Analysis:\n'));
|
|
578
|
+
console.log(` Investment: ${chalk.cyan('$' + investment.toLocaleString())}`);
|
|
579
|
+
console.log(` Expected ROI: ${chalk.green((data.roi_percentage || data.roi || 0) + '%')}`);
|
|
580
|
+
console.log(` NPV: ${chalk.green('$' + (data.npv || 0).toLocaleString())}`);
|
|
581
|
+
console.log(` Payback Period: ${chalk.cyan((data.payback_months || data.payback_period || 0) + ' months')}`);
|
|
582
|
+
console.log(` Risk Reduction: ${chalk.green('50%')}`);
|
|
580
583
|
}
|
|
581
584
|
else if (options.tco) {
|
|
585
|
+
// TCO per diagram using economic intelligence
|
|
586
|
+
const fullId = await resolveDiagramId(options.tco);
|
|
582
587
|
const data = await apiRequest('/v2/economic/tco/', {
|
|
583
588
|
method: 'POST',
|
|
584
589
|
body: JSON.stringify({
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
590
|
+
diagram_id: fullId,
|
|
591
|
+
years: 3,
|
|
592
|
+
include_hidden_costs: true,
|
|
593
|
+
include_risk_costs: true
|
|
588
594
|
})
|
|
589
595
|
});
|
|
590
596
|
spinner.succeed('TCO Analysis Complete!');
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
console.log(
|
|
594
|
-
|
|
597
|
+
const tco = data.tco || data;
|
|
598
|
+
const diagramName = tco.diagram_name || 'Diagram';
|
|
599
|
+
console.log(chalk.bold(`\nTotal Cost of Ownership - ${diagramName}:\n`));
|
|
600
|
+
// Cost summary
|
|
601
|
+
console.log(` Monthly Cost: ${chalk.cyan('$' + (tco.monthly_cost || tco.total_monthly || 0).toLocaleString())}`);
|
|
602
|
+
console.log(` Annual Cost: ${chalk.cyan('$' + (tco.annual_cost || tco.year_1 || 0).toLocaleString())}`);
|
|
603
|
+
console.log(` 3-Year TCO: ${chalk.yellow('$' + (tco.total_3_year || tco.total_cost || 0).toLocaleString())}`);
|
|
604
|
+
// Cost breakdown by component
|
|
605
|
+
if (tco.cost_breakdown) {
|
|
606
|
+
console.log(chalk.bold('\nCost Breakdown:\n'));
|
|
607
|
+
const breakdown = tco.cost_breakdown;
|
|
608
|
+
Object.entries(breakdown).forEach(([key, value]) => {
|
|
609
|
+
if (typeof value === 'number') {
|
|
610
|
+
const label = key.replace(/_/g, ' ').replace(/\b\w/g, c => c.toUpperCase());
|
|
611
|
+
console.log(` ${label}: ${chalk.cyan('$' + value.toLocaleString())}`);
|
|
612
|
+
}
|
|
613
|
+
});
|
|
614
|
+
}
|
|
615
|
+
// Customizable costs info
|
|
616
|
+
if (tco.customizable) {
|
|
617
|
+
console.log(chalk.dim('\nCosts can be customized in the Economic Intelligence panel'));
|
|
618
|
+
}
|
|
595
619
|
}
|
|
596
620
|
else if (options.analyze) {
|
|
597
621
|
const data = await apiRequest('/v2/economic/analyze/', {
|
|
@@ -599,32 +623,32 @@ program
|
|
|
599
623
|
body: JSON.stringify({ diagram_id: options.analyze })
|
|
600
624
|
});
|
|
601
625
|
spinner.succeed('Cost Analysis Complete!');
|
|
602
|
-
console.log(
|
|
603
|
-
console.log(` Estimated Monthly: ${
|
|
604
|
-
console.log(` Security Costs: ${
|
|
605
|
-
console.log(` Breach Risk Cost: ${
|
|
626
|
+
console.log(chalk.bold('\nDiagram Cost Analysis:\n'));
|
|
627
|
+
console.log(` Estimated Monthly: ${chalk.cyan('$' + (data.monthly_estimate || 0).toLocaleString())}`);
|
|
628
|
+
console.log(` Security Costs: ${chalk.yellow('$' + (data.security_cost || 0).toLocaleString())}`);
|
|
629
|
+
console.log(` Breach Risk Cost: ${chalk.red('$' + (data.breach_risk_cost || 0).toLocaleString())}`);
|
|
606
630
|
}
|
|
607
631
|
else if (options.cost) {
|
|
608
632
|
// Diagram-specific cost analysis
|
|
609
633
|
const fullId = await resolveDiagramId(options.cost);
|
|
610
634
|
const data = await apiRequest(`/v2/threat-modeling/diagrams/${fullId}/cost-intelligence/`);
|
|
611
635
|
spinner.succeed('Cost analysis complete!');
|
|
612
|
-
console.log(
|
|
636
|
+
console.log(chalk.bold('\nDiagram Cost Analysis:\n'));
|
|
613
637
|
const summary = data.cost_summary || data;
|
|
614
|
-
console.log(` Monthly Cost: ${
|
|
615
|
-
console.log(` Annual Cost: ${
|
|
616
|
-
console.log(` Component Count: ${
|
|
617
|
-
console.log(` Region: ${
|
|
638
|
+
console.log(` Monthly Cost: ${chalk.cyan('$' + (summary.total_monthly || summary.monthly || 0).toLocaleString())}`);
|
|
639
|
+
console.log(` Annual Cost: ${chalk.yellow('$' + (summary.total_annual || (summary.total_monthly || 0) * 12).toLocaleString())}`);
|
|
640
|
+
console.log(` Component Count: ${chalk.white(summary.component_count || data.components?.length || 0)}`);
|
|
641
|
+
console.log(` Region: ${chalk.white(summary.region || 'us-east-1')}`);
|
|
618
642
|
if (data.cost_breakdown?.length > 0) {
|
|
619
|
-
console.log(
|
|
643
|
+
console.log(chalk.bold('\nCost Breakdown:\n'));
|
|
620
644
|
data.cost_breakdown.slice(0, 5).forEach((c) => {
|
|
621
|
-
console.log(` ${
|
|
645
|
+
console.log(` ${chalk.cyan('•')} ${c.name || c.component}: ${chalk.yellow('$' + (c.monthly || c.cost || 0).toLocaleString())}/mo`);
|
|
622
646
|
});
|
|
623
647
|
}
|
|
624
648
|
if (data.recommendations?.length > 0) {
|
|
625
|
-
console.log(
|
|
649
|
+
console.log(chalk.bold('\nOptimization Recommendations:\n'));
|
|
626
650
|
data.recommendations.slice(0, 3).forEach((r) => {
|
|
627
|
-
console.log(` ${
|
|
651
|
+
console.log(` ${chalk.green('•')} ${r.title || r.description || r}`);
|
|
628
652
|
});
|
|
629
653
|
}
|
|
630
654
|
}
|
|
@@ -632,28 +656,28 @@ program
|
|
|
632
656
|
// Get economic intelligence from threat modeling endpoint
|
|
633
657
|
const data = await apiRequest('/v2/threat-modeling/economic-intelligence/');
|
|
634
658
|
spinner.succeed('Dashboard loaded!');
|
|
635
|
-
console.log(
|
|
659
|
+
console.log(chalk.bold('\nEconomic Intelligence Dashboard:\n'));
|
|
636
660
|
const summary = data.company_summary || data.summary || data;
|
|
637
|
-
console.log(` Total Monthly: ${
|
|
638
|
-
console.log(` Total Annual: ${
|
|
639
|
-
console.log(` Total Diagrams: ${
|
|
640
|
-
console.log(` Region: ${
|
|
661
|
+
console.log(` Total Monthly: ${chalk.cyan('$' + (summary.total_monthly || summary.total_security_spend || 0).toLocaleString())}`);
|
|
662
|
+
console.log(` Total Annual: ${chalk.yellow('$' + (summary.total_annual || 0).toLocaleString())}`);
|
|
663
|
+
console.log(` Total Diagrams: ${chalk.white(summary.total_diagrams || 0)}`);
|
|
664
|
+
console.log(` Region: ${chalk.white(summary.region || 'us-east-1')}`);
|
|
641
665
|
if (data.top_cost_drivers?.length > 0) {
|
|
642
|
-
console.log(
|
|
666
|
+
console.log(chalk.bold('\nTop Cost Drivers:\n'));
|
|
643
667
|
data.top_cost_drivers.slice(0, 5).forEach((d) => {
|
|
644
|
-
console.log(` ${
|
|
668
|
+
console.log(` ${chalk.cyan('•')} ${d.name}: ${chalk.yellow('$' + (d.monthly_cost || 0).toLocaleString())}/mo (${d.component_count || 0} components)`);
|
|
645
669
|
});
|
|
646
670
|
}
|
|
647
671
|
if (data.intelligence?.recommendations?.length > 0) {
|
|
648
|
-
console.log(
|
|
672
|
+
console.log(chalk.bold('\nAI Recommendations:\n'));
|
|
649
673
|
data.intelligence.recommendations.slice(0, 3).forEach((r) => {
|
|
650
|
-
console.log(` ${
|
|
674
|
+
console.log(` ${chalk.green('•')} ${r.title || r.description || r}`);
|
|
651
675
|
});
|
|
652
676
|
}
|
|
653
677
|
}
|
|
654
678
|
else {
|
|
655
679
|
spinner.stop();
|
|
656
|
-
console.log(
|
|
680
|
+
console.log(chalk.yellow('Usage: aribot economics [--roi <amount>] [--tco <diagram-id>] [--analyze <diagram-id>] [--dashboard]'));
|
|
657
681
|
}
|
|
658
682
|
}
|
|
659
683
|
catch (error) {
|
|
@@ -670,28 +694,28 @@ program
|
|
|
670
694
|
.option('--dashboard', 'Show cloud security dashboard')
|
|
671
695
|
.option('-s, --severity <level>', 'Filter findings by severity (critical, high, medium, low)')
|
|
672
696
|
.action(async (options) => {
|
|
673
|
-
const spinner = (
|
|
697
|
+
const spinner = ora('Scanning cloud security...').start();
|
|
674
698
|
try {
|
|
675
699
|
if (options.scan) {
|
|
676
700
|
const provider = typeof options.scan === 'string' ? options.scan : undefined;
|
|
677
701
|
// Use security posture endpoint
|
|
678
702
|
const data = await apiRequest('/v2/compliances/dashboard/cloud-stats/' + (provider ? `?provider=${provider}` : ''));
|
|
679
703
|
spinner.succeed('Cloud security scan complete!');
|
|
680
|
-
console.log(
|
|
704
|
+
console.log(chalk.bold('\nCloud Security Posture:\n'));
|
|
681
705
|
const stats = data.stats || data;
|
|
682
|
-
console.log(` Security Score: ${stats.security_score >= 80 ?
|
|
683
|
-
console.log(` Total Resources: ${
|
|
684
|
-
console.log(` Compliant: ${
|
|
685
|
-
console.log(` Non-Compliant: ${
|
|
686
|
-
console.log(` Critical Issues: ${
|
|
706
|
+
console.log(` Security Score: ${stats.security_score >= 80 ? chalk.green(stats.security_score + '%') : chalk.yellow(stats.security_score + '%' || 'N/A')}`);
|
|
707
|
+
console.log(` Total Resources: ${chalk.cyan(stats.total_resources || stats.resource_count || 0)}`);
|
|
708
|
+
console.log(` Compliant: ${chalk.green(stats.compliant_resources || stats.compliant || 0)}`);
|
|
709
|
+
console.log(` Non-Compliant: ${chalk.red(stats.non_compliant_resources || stats.non_compliant || 0)}`);
|
|
710
|
+
console.log(` Critical Issues: ${chalk.red(stats.critical_findings || stats.critical || 0)}`);
|
|
687
711
|
if (provider) {
|
|
688
|
-
console.log(`\n Provider: ${
|
|
712
|
+
console.log(`\n Provider: ${chalk.cyan(provider.toUpperCase())}`);
|
|
689
713
|
}
|
|
690
714
|
// Show provider breakdown if available
|
|
691
715
|
if (data.by_provider && !provider) {
|
|
692
|
-
console.log(
|
|
716
|
+
console.log(chalk.bold('\nBy Provider:\n'));
|
|
693
717
|
Object.entries(data.by_provider).forEach(([p, s]) => {
|
|
694
|
-
console.log(` ${
|
|
718
|
+
console.log(` ${chalk.cyan(p.toUpperCase().padEnd(8))} Resources: ${s.count || 0} | Score: ${s.score || 'N/A'}%`);
|
|
695
719
|
});
|
|
696
720
|
}
|
|
697
721
|
}
|
|
@@ -703,37 +727,37 @@ program
|
|
|
703
727
|
}
|
|
704
728
|
const data = await apiRequest(url);
|
|
705
729
|
spinner.stop();
|
|
706
|
-
console.log(
|
|
730
|
+
console.log(chalk.bold('\nCloud Security Findings:\n'));
|
|
707
731
|
const findings = data.results || data.findings || [];
|
|
708
732
|
if (findings.length === 0) {
|
|
709
|
-
console.log(
|
|
733
|
+
console.log(chalk.green(' No open findings! Your cloud is secure.'));
|
|
710
734
|
}
|
|
711
735
|
else {
|
|
712
736
|
const severityColors = {
|
|
713
|
-
critical:
|
|
714
|
-
high:
|
|
715
|
-
medium:
|
|
716
|
-
low:
|
|
737
|
+
critical: chalk.red,
|
|
738
|
+
high: chalk.yellow,
|
|
739
|
+
medium: chalk.blue,
|
|
740
|
+
low: chalk.dim
|
|
717
741
|
};
|
|
718
742
|
findings.slice(0, 10).forEach((f) => {
|
|
719
|
-
const color = severityColors[f.severity] ||
|
|
743
|
+
const color = severityColors[f.severity] || chalk.white;
|
|
720
744
|
console.log(` ${color(`[${f.severity?.toUpperCase()}]`)} ${f.title}`);
|
|
721
|
-
console.log(
|
|
745
|
+
console.log(chalk.dim(` Resource: ${f.resource_type || 'N/A'} | Policy: ${f.policy || 'N/A'}`));
|
|
722
746
|
});
|
|
723
|
-
console.log(
|
|
747
|
+
console.log(chalk.dim(`\nShowing ${Math.min(10, findings.length)} of ${findings.length} findings`));
|
|
724
748
|
}
|
|
725
749
|
}
|
|
726
750
|
else if (options.dashboard) {
|
|
727
751
|
const data = await apiRequest('/v2/compliances/dashboard/cloud-stats/');
|
|
728
752
|
spinner.succeed('Dashboard loaded!');
|
|
729
|
-
console.log(
|
|
730
|
-
console.log(` Security Score: ${data.security_score >= 80 ?
|
|
731
|
-
console.log(` Total Resources: ${
|
|
732
|
-
console.log(` Open Findings: ${
|
|
753
|
+
console.log(chalk.bold('\nCloud Security Dashboard:\n'));
|
|
754
|
+
console.log(` Security Score: ${data.security_score >= 80 ? chalk.green(data.security_score) : chalk.yellow(data.security_score || 'N/A')}`);
|
|
755
|
+
console.log(` Total Resources: ${chalk.cyan(data.total_resources || 0)}`);
|
|
756
|
+
console.log(` Open Findings: ${chalk.yellow(data.open_findings || 0)}`);
|
|
733
757
|
}
|
|
734
758
|
else {
|
|
735
759
|
spinner.stop();
|
|
736
|
-
console.log(
|
|
760
|
+
console.log(chalk.yellow('Usage: aribot cloud-security [--scan [provider]] [--findings] [--dashboard]'));
|
|
737
761
|
}
|
|
738
762
|
}
|
|
739
763
|
catch (error) {
|
|
@@ -757,26 +781,26 @@ program
|
|
|
757
781
|
.option('--patterns <diagram-id>', 'Detect AI patterns in diagram')
|
|
758
782
|
.action(async (options) => {
|
|
759
783
|
if (options.methodologies) {
|
|
760
|
-
const spinner = (
|
|
784
|
+
const spinner = ora('Fetching methodologies...').start();
|
|
761
785
|
try {
|
|
762
786
|
const data = await apiRequest('/v2/threat-engine/threat-models/');
|
|
763
787
|
spinner.stop();
|
|
764
|
-
console.log(
|
|
788
|
+
console.log(chalk.bold('\nThreat Modeling Methodologies:\n'));
|
|
765
789
|
(data.available_methodologies || []).forEach((m) => {
|
|
766
|
-
console.log(` ${
|
|
790
|
+
console.log(` ${chalk.cyan(m.name.toUpperCase().padEnd(12))} ${chalk.dim(m.description)}`);
|
|
767
791
|
});
|
|
768
|
-
console.log(
|
|
792
|
+
console.log(chalk.bold('\nRisk Levels:\n'));
|
|
769
793
|
(data.risk_levels || []).forEach((r) => {
|
|
770
|
-
console.log(` ${
|
|
794
|
+
console.log(` ${chalk.yellow(r.name.padEnd(12))} ${chalk.dim(r.description)}`);
|
|
771
795
|
});
|
|
772
|
-
console.log(
|
|
796
|
+
console.log(chalk.bold('\nCompliance Frameworks:\n'));
|
|
773
797
|
(data.compliance_frameworks || []).slice(0, 10).forEach((f) => {
|
|
774
|
-
console.log(` ${
|
|
798
|
+
console.log(` ${chalk.green(f.name.padEnd(20))} ${chalk.dim(f.description)}`);
|
|
775
799
|
});
|
|
776
|
-
console.log(
|
|
800
|
+
console.log(chalk.bold('\nEngine Capabilities:\n'));
|
|
777
801
|
const caps = data.engine_capabilities || {};
|
|
778
802
|
Object.entries(caps).forEach(([key, value]) => {
|
|
779
|
-
console.log(` ${value ?
|
|
803
|
+
console.log(` ${value ? chalk.green('✓') : chalk.red('✗')} ${key.replace(/_/g, ' ')}`);
|
|
780
804
|
});
|
|
781
805
|
}
|
|
782
806
|
catch (error) {
|
|
@@ -786,24 +810,24 @@ program
|
|
|
786
810
|
return;
|
|
787
811
|
}
|
|
788
812
|
if (options.intelligence) {
|
|
789
|
-
const spinner = (
|
|
813
|
+
const spinner = ora('Fetching threat intelligence...').start();
|
|
790
814
|
try {
|
|
791
815
|
const data = await apiRequest('/v2/threat-engine/threat-intelligence/');
|
|
792
816
|
spinner.stop();
|
|
793
|
-
console.log(
|
|
817
|
+
console.log(chalk.bold('\nThreat Intelligence Summary:\n'));
|
|
794
818
|
const intel = data.threat_intelligence || {};
|
|
795
|
-
console.log(` Integration: ${intel.integration_status === 'active' ?
|
|
796
|
-
console.log(` Cache TTL: ${
|
|
797
|
-
console.log(` Real-time Feeds: ${intel.real_time_feeds ?
|
|
798
|
-
console.log(` Correlation: ${intel.correlation_engine ?
|
|
799
|
-
console.log(
|
|
819
|
+
console.log(` Integration: ${intel.integration_status === 'active' ? chalk.green('Active') : chalk.red('Inactive')}`);
|
|
820
|
+
console.log(` Cache TTL: ${chalk.cyan(intel.cache_ttl + 's')}`);
|
|
821
|
+
console.log(` Real-time Feeds: ${intel.real_time_feeds ? chalk.green('Enabled') : chalk.yellow('Disabled')}`);
|
|
822
|
+
console.log(` Correlation: ${intel.correlation_engine ? chalk.green('Enabled') : chalk.yellow('Disabled')}`);
|
|
823
|
+
console.log(chalk.bold('\nSupported Indicators:\n'));
|
|
800
824
|
(intel.supported_indicators || []).forEach((i) => {
|
|
801
|
-
console.log(` ${
|
|
825
|
+
console.log(` ${chalk.cyan('•')} ${i}`);
|
|
802
826
|
});
|
|
803
|
-
console.log(
|
|
827
|
+
console.log(chalk.bold('\nVision 2040 Features:\n'));
|
|
804
828
|
const v2040 = data.vision_2040_features || {};
|
|
805
829
|
Object.entries(v2040).forEach(([key, value]) => {
|
|
806
|
-
console.log(` ${value ?
|
|
830
|
+
console.log(` ${value ? chalk.green('✓') : chalk.red('✗')} ${key.replace(/_/g, ' ')}`);
|
|
807
831
|
});
|
|
808
832
|
}
|
|
809
833
|
catch (error) {
|
|
@@ -814,10 +838,10 @@ program
|
|
|
814
838
|
}
|
|
815
839
|
if (options.attackPaths) {
|
|
816
840
|
if (!options.diagram) {
|
|
817
|
-
console.log(
|
|
841
|
+
console.log(chalk.yellow('Usage: aribot redteam --attack-paths --diagram <diagram-id>'));
|
|
818
842
|
return;
|
|
819
843
|
}
|
|
820
|
-
const spinner = (
|
|
844
|
+
const spinner = ora('Analyzing attack paths...').start();
|
|
821
845
|
try {
|
|
822
846
|
// First get the diagram components
|
|
823
847
|
const fullId = await resolveDiagramId(options.diagram);
|
|
@@ -834,8 +858,8 @@ program
|
|
|
834
858
|
}));
|
|
835
859
|
if (components.length < 2) {
|
|
836
860
|
spinner.stop();
|
|
837
|
-
console.log(
|
|
838
|
-
console.log(
|
|
861
|
+
console.log(chalk.yellow('\nInsufficient components for attack path analysis.'));
|
|
862
|
+
console.log(chalk.dim('Upload a diagram with at least 2 connected components.'));
|
|
839
863
|
return;
|
|
840
864
|
}
|
|
841
865
|
// Generate attack paths
|
|
@@ -849,30 +873,30 @@ program
|
|
|
849
873
|
})
|
|
850
874
|
});
|
|
851
875
|
spinner.succeed('Attack path analysis complete!');
|
|
852
|
-
console.log(
|
|
853
|
-
console.log(` Source: ${
|
|
854
|
-
console.log(` Target: ${
|
|
855
|
-
console.log(` Paths Found: ${
|
|
876
|
+
console.log(chalk.bold('\nAttack Path Analysis:\n'));
|
|
877
|
+
console.log(` Source: ${chalk.cyan(data.analysis_summary?.source_node || 'N/A')}`);
|
|
878
|
+
console.log(` Target: ${chalk.cyan(data.analysis_summary?.target_node || 'N/A')}`);
|
|
879
|
+
console.log(` Paths Found: ${chalk.yellow(data.analysis_summary?.total_paths_found || 0)}`);
|
|
856
880
|
const paths = data.attack_paths || [];
|
|
857
881
|
if (paths.length > 0) {
|
|
858
|
-
console.log(
|
|
882
|
+
console.log(chalk.bold('\nIdentified Attack Paths:\n'));
|
|
859
883
|
paths.slice(0, 5).forEach((p, i) => {
|
|
860
|
-
const riskColor = p.risk_score > 0.5 ?
|
|
861
|
-
console.log(` ${
|
|
884
|
+
const riskColor = p.risk_score > 0.5 ? chalk.red : p.risk_score > 0.2 ? chalk.yellow : chalk.green;
|
|
885
|
+
console.log(` ${chalk.bold(`Path ${i + 1}:`)} ${p.source_node} → ${p.target_node}`);
|
|
862
886
|
console.log(` Risk Score: ${riskColor((p.risk_score * 100).toFixed(1) + '%')}`);
|
|
863
|
-
console.log(` Likelihood: ${
|
|
864
|
-
console.log(` Impact: ${
|
|
865
|
-
console.log(` Steps: ${
|
|
887
|
+
console.log(` Likelihood: ${chalk.cyan((p.likelihood * 100).toFixed(1) + '%')}`);
|
|
888
|
+
console.log(` Impact: ${chalk.yellow((p.impact * 100).toFixed(1) + '%')}`);
|
|
889
|
+
console.log(` Steps: ${chalk.dim(p.steps?.length || 0)}`);
|
|
866
890
|
if (p.steps?.length > 0) {
|
|
867
891
|
p.steps.forEach((s, j) => {
|
|
868
|
-
console.log(
|
|
892
|
+
console.log(chalk.dim(` ${j + 1}. ${s.from_node} → ${s.to_node}`));
|
|
869
893
|
});
|
|
870
894
|
}
|
|
871
895
|
console.log();
|
|
872
896
|
});
|
|
873
897
|
}
|
|
874
898
|
else {
|
|
875
|
-
console.log(
|
|
899
|
+
console.log(chalk.green('\n No critical attack paths identified!'));
|
|
876
900
|
}
|
|
877
901
|
}
|
|
878
902
|
catch (error) {
|
|
@@ -882,7 +906,7 @@ program
|
|
|
882
906
|
return;
|
|
883
907
|
}
|
|
884
908
|
if (options.analyze) {
|
|
885
|
-
const spinner = (
|
|
909
|
+
const spinner = ora('Running comprehensive threat analysis...').start();
|
|
886
910
|
try {
|
|
887
911
|
const fullId = await resolveDiagramId(options.analyze);
|
|
888
912
|
const diagramData = await apiRequest(`/v2/threat-modeling/diagrams/${fullId}/`);
|
|
@@ -896,29 +920,29 @@ program
|
|
|
896
920
|
})
|
|
897
921
|
});
|
|
898
922
|
spinner.succeed('Comprehensive analysis complete!');
|
|
899
|
-
console.log(
|
|
900
|
-
console.log(` Diagram: ${
|
|
923
|
+
console.log(chalk.bold('\nComprehensive Threat Analysis:\n'));
|
|
924
|
+
console.log(` Diagram: ${chalk.cyan(diagramData.name || 'N/A')}`);
|
|
901
925
|
const analysis = data.analysis || data;
|
|
902
|
-
console.log(` Risk Level: ${analysis.risk_level === 'critical' ?
|
|
903
|
-
console.log(` Risk Score: ${
|
|
904
|
-
console.log(` Threats Found: ${
|
|
926
|
+
console.log(` Risk Level: ${analysis.risk_level === 'critical' ? chalk.red(analysis.risk_level) : chalk.yellow(analysis.risk_level || 'N/A')}`);
|
|
927
|
+
console.log(` Risk Score: ${chalk.red(analysis.risk_score || analysis.overall_score || 'N/A')}`);
|
|
928
|
+
console.log(` Threats Found: ${chalk.yellow(analysis.threat_count || analysis.total_threats || 0)}`);
|
|
905
929
|
const threats = analysis.threats || data.threats || [];
|
|
906
930
|
if (threats.length > 0) {
|
|
907
|
-
console.log(
|
|
931
|
+
console.log(chalk.bold('\nTop Threats:\n'));
|
|
908
932
|
threats.slice(0, 5).forEach((t) => {
|
|
909
|
-
const severity = t.severity === 'critical' ?
|
|
933
|
+
const severity = t.severity === 'critical' ? chalk.red : t.severity === 'high' ? chalk.yellow : chalk.blue;
|
|
910
934
|
console.log(` ${severity(`[${t.severity?.toUpperCase()}]`)} ${t.title || t.name}`);
|
|
911
|
-
console.log(
|
|
935
|
+
console.log(chalk.dim(` Category: ${t.category || 'N/A'} | MITRE: ${t.mitre_id || t.mitre_mapping || 'N/A'}`));
|
|
912
936
|
});
|
|
913
937
|
}
|
|
914
938
|
const recommendations = analysis.recommendations || data.recommendations || [];
|
|
915
939
|
if (recommendations.length > 0) {
|
|
916
|
-
console.log(
|
|
940
|
+
console.log(chalk.bold('\nTop Recommendations:\n'));
|
|
917
941
|
recommendations.slice(0, 3).forEach((r) => {
|
|
918
|
-
console.log(` ${
|
|
942
|
+
console.log(` ${chalk.green('→')} ${r.title || r.description || r}`);
|
|
919
943
|
});
|
|
920
944
|
}
|
|
921
|
-
console.log(
|
|
945
|
+
console.log(chalk.dim(`\nMethodologies: ${analysis.methodologies?.join(', ') || data.methodologies?.join(', ') || 'STRIDE, PASTA, NIST'}`));
|
|
922
946
|
}
|
|
923
947
|
catch (error) {
|
|
924
948
|
spinner.fail('Comprehensive analysis failed');
|
|
@@ -927,7 +951,7 @@ program
|
|
|
927
951
|
return;
|
|
928
952
|
}
|
|
929
953
|
if (options.requirements) {
|
|
930
|
-
const spinner = (
|
|
954
|
+
const spinner = ora('Generating security requirements...').start();
|
|
931
955
|
try {
|
|
932
956
|
const fullId = await resolveDiagramId(options.requirements);
|
|
933
957
|
const diagramData = await apiRequest(`/v2/threat-modeling/diagrams/${fullId}/threats/`);
|
|
@@ -945,18 +969,18 @@ program
|
|
|
945
969
|
})
|
|
946
970
|
});
|
|
947
971
|
spinner.succeed('Security requirements generated!');
|
|
948
|
-
console.log(
|
|
949
|
-
console.log(` Total: ${
|
|
950
|
-
console.log(` Critical: ${
|
|
951
|
-
console.log(` High: ${
|
|
972
|
+
console.log(chalk.bold('\nSecurity Requirements:\n'));
|
|
973
|
+
console.log(` Total: ${chalk.cyan(data.summary?.total_requirements || 0)}`);
|
|
974
|
+
console.log(` Critical: ${chalk.red(data.summary?.critical_requirements || 0)}`);
|
|
975
|
+
console.log(` High: ${chalk.yellow(data.summary?.high_requirements || 0)}`);
|
|
952
976
|
const reqs = data.security_requirements || [];
|
|
953
977
|
if (reqs.length > 0) {
|
|
954
|
-
console.log(
|
|
978
|
+
console.log(chalk.bold('\nRequirements:\n'));
|
|
955
979
|
reqs.slice(0, 5).forEach((r, i) => {
|
|
956
|
-
const priority = r.priority === 'critical' ?
|
|
980
|
+
const priority = r.priority === 'critical' ? chalk.red : r.priority === 'high' ? chalk.yellow : chalk.blue;
|
|
957
981
|
console.log(` ${i + 1}. ${priority(`[${r.priority?.toUpperCase()}]`)} ${r.title}`);
|
|
958
|
-
console.log(
|
|
959
|
-
console.log(
|
|
982
|
+
console.log(chalk.dim(` ${r.description?.slice(0, 80) || ''}...`));
|
|
983
|
+
console.log(chalk.dim(` Category: ${r.category || 'N/A'}`));
|
|
960
984
|
});
|
|
961
985
|
}
|
|
962
986
|
}
|
|
@@ -968,7 +992,7 @@ program
|
|
|
968
992
|
}
|
|
969
993
|
// AI-powered attack path analysis
|
|
970
994
|
if (options.aiAttackPaths) {
|
|
971
|
-
const spinner = (
|
|
995
|
+
const spinner = ora('Running AI-powered attack path analysis...').start();
|
|
972
996
|
try {
|
|
973
997
|
const fullId = await resolveDiagramId(options.aiAttackPaths);
|
|
974
998
|
const diagramData = await apiRequest(`/v2/threat-modeling/diagrams/${fullId}/`);
|
|
@@ -989,34 +1013,34 @@ program
|
|
|
989
1013
|
})
|
|
990
1014
|
});
|
|
991
1015
|
spinner.succeed('AI attack path analysis complete!');
|
|
992
|
-
console.log(
|
|
993
|
-
console.log(` Diagram: ${
|
|
1016
|
+
console.log(chalk.bold('\nAI Attack Path Analysis:\n'));
|
|
1017
|
+
console.log(` Diagram: ${chalk.cyan(diagramData.name || 'N/A')}`);
|
|
994
1018
|
const analysis = data.analysis || data;
|
|
995
|
-
console.log(` Risk Level: ${analysis.risk_level === 'critical' ?
|
|
996
|
-
console.log(` AI Confidence: ${
|
|
1019
|
+
console.log(` Risk Level: ${analysis.risk_level === 'critical' ? chalk.red(analysis.risk_level) : chalk.yellow(analysis.risk_level || 'N/A')}`);
|
|
1020
|
+
console.log(` AI Confidence: ${chalk.green((analysis.confidence || analysis.ai_confidence || 0.85) * 100 + '%')}`);
|
|
997
1021
|
const attackPaths = analysis.attack_paths || data.attack_paths || [];
|
|
998
1022
|
if (attackPaths.length > 0) {
|
|
999
|
-
console.log(
|
|
1023
|
+
console.log(chalk.bold(`\nIdentified Attack Paths (${attackPaths.length}):\n`));
|
|
1000
1024
|
attackPaths.slice(0, 5).forEach((path, i) => {
|
|
1001
|
-
const riskColor = path.risk_score > 0.7 ?
|
|
1002
|
-
console.log(` ${
|
|
1025
|
+
const riskColor = path.risk_score > 0.7 ? chalk.red : path.risk_score > 0.4 ? chalk.yellow : chalk.green;
|
|
1026
|
+
console.log(` ${chalk.bold(`Path ${i + 1}:`)} ${path.name || path.description || 'Attack Vector'}`);
|
|
1003
1027
|
console.log(` Risk Score: ${riskColor((path.risk_score * 100).toFixed(0) + '%')}`);
|
|
1004
|
-
console.log(` Attack Steps: ${
|
|
1005
|
-
console.log(` Entry Point: ${
|
|
1006
|
-
console.log(` Target: ${
|
|
1028
|
+
console.log(` Attack Steps: ${chalk.cyan(path.steps?.length || path.hop_count || 'N/A')}`);
|
|
1029
|
+
console.log(` Entry Point: ${chalk.yellow(path.entry_point || path.source || 'External')}`);
|
|
1030
|
+
console.log(` Target: ${chalk.red(path.target || path.destination || 'Critical Asset')}`);
|
|
1007
1031
|
if (path.mitre_techniques?.length > 0) {
|
|
1008
|
-
console.log(` MITRE: ${
|
|
1032
|
+
console.log(` MITRE: ${chalk.dim(path.mitre_techniques.slice(0, 3).join(', '))}`);
|
|
1009
1033
|
}
|
|
1010
1034
|
});
|
|
1011
1035
|
}
|
|
1012
1036
|
else {
|
|
1013
|
-
console.log(
|
|
1037
|
+
console.log(chalk.green('\n No critical attack paths identified!'));
|
|
1014
1038
|
}
|
|
1015
1039
|
const mitigations = analysis.mitigations || data.mitigations || [];
|
|
1016
1040
|
if (mitigations.length > 0) {
|
|
1017
|
-
console.log(
|
|
1041
|
+
console.log(chalk.bold('\nAI-Recommended Mitigations:\n'));
|
|
1018
1042
|
mitigations.slice(0, 3).forEach((m) => {
|
|
1019
|
-
console.log(` ${
|
|
1043
|
+
console.log(` ${chalk.green('→')} ${m.title || m.description || m}`);
|
|
1020
1044
|
});
|
|
1021
1045
|
}
|
|
1022
1046
|
}
|
|
@@ -1028,7 +1052,7 @@ program
|
|
|
1028
1052
|
}
|
|
1029
1053
|
// AI threat prediction
|
|
1030
1054
|
if (options.aiPredict) {
|
|
1031
|
-
const spinner = (
|
|
1055
|
+
const spinner = ora('Running AI threat prediction...').start();
|
|
1032
1056
|
try {
|
|
1033
1057
|
const fullId = await resolveDiagramId(options.aiPredict);
|
|
1034
1058
|
const diagramData = await apiRequest(`/v2/threat-modeling/diagrams/${fullId}/`);
|
|
@@ -1047,26 +1071,26 @@ program
|
|
|
1047
1071
|
})
|
|
1048
1072
|
});
|
|
1049
1073
|
spinner.succeed('AI threat prediction complete!');
|
|
1050
|
-
console.log(
|
|
1051
|
-
console.log(` Diagram: ${
|
|
1052
|
-
console.log(` Model: ${
|
|
1074
|
+
console.log(chalk.bold('\nAI Threat Prediction:\n'));
|
|
1075
|
+
console.log(` Diagram: ${chalk.cyan(diagramData.name || 'N/A')}`);
|
|
1076
|
+
console.log(` Model: ${chalk.green('ML Ensemble (STRIDE + PASTA + NIST)')}`);
|
|
1053
1077
|
const predictions = data.predictions || data;
|
|
1054
|
-
console.log(` Confidence: ${
|
|
1055
|
-
console.log(` Predicted Risk: ${predictions.risk_level === 'critical' ?
|
|
1078
|
+
console.log(` Confidence: ${chalk.green((predictions.confidence || 0.92) * 100 + '%')}`);
|
|
1079
|
+
console.log(` Predicted Risk: ${predictions.risk_level === 'critical' ? chalk.red(predictions.risk_level) : chalk.yellow(predictions.risk_level || 'medium')}`);
|
|
1056
1080
|
const threats = predictions.predicted_threats || predictions.threats || [];
|
|
1057
1081
|
if (threats.length > 0) {
|
|
1058
|
-
console.log(
|
|
1082
|
+
console.log(chalk.bold('\nPredicted Threats:\n'));
|
|
1059
1083
|
threats.slice(0, 5).forEach((t) => {
|
|
1060
1084
|
const prob = t.probability || t.confidence || 0.8;
|
|
1061
|
-
const probColor = prob > 0.8 ?
|
|
1085
|
+
const probColor = prob > 0.8 ? chalk.red : prob > 0.5 ? chalk.yellow : chalk.green;
|
|
1062
1086
|
console.log(` ${probColor(`[${(prob * 100).toFixed(0)}%]`)} ${t.title || t.name}`);
|
|
1063
|
-
console.log(
|
|
1087
|
+
console.log(chalk.dim(` Category: ${t.category || 'N/A'} | Impact: ${t.impact || 'high'}`));
|
|
1064
1088
|
});
|
|
1065
1089
|
}
|
|
1066
1090
|
if (predictions.emerging_threats?.length > 0) {
|
|
1067
|
-
console.log(
|
|
1091
|
+
console.log(chalk.bold('\nEmerging Threat Patterns:\n'));
|
|
1068
1092
|
predictions.emerging_threats.slice(0, 3).forEach((t) => {
|
|
1069
|
-
console.log(` ${
|
|
1093
|
+
console.log(` ${chalk.yellow('⚠')} ${t.name || t.description || t}`);
|
|
1070
1094
|
});
|
|
1071
1095
|
}
|
|
1072
1096
|
}
|
|
@@ -1078,7 +1102,7 @@ program
|
|
|
1078
1102
|
}
|
|
1079
1103
|
// AI architecture insights
|
|
1080
1104
|
if (options.aiInsights) {
|
|
1081
|
-
const spinner = (
|
|
1105
|
+
const spinner = ora('Generating AI architecture insights...').start();
|
|
1082
1106
|
try {
|
|
1083
1107
|
const fullId = await resolveDiagramId(options.aiInsights);
|
|
1084
1108
|
const data = await apiRequest(`/v2/threat-modeling/diagrams/${fullId}/generate-ai-insights/`, {
|
|
@@ -1088,31 +1112,31 @@ program
|
|
|
1088
1112
|
})
|
|
1089
1113
|
});
|
|
1090
1114
|
spinner.succeed('AI insights generated!');
|
|
1091
|
-
console.log(
|
|
1092
|
-
console.log(` Diagram: ${
|
|
1093
|
-
console.log(` Components: ${
|
|
1094
|
-
console.log(` AI Provider: ${
|
|
1095
|
-
console.log(` Generated: ${
|
|
1115
|
+
console.log(chalk.bold('\nAI Architecture Insights:\n'));
|
|
1116
|
+
console.log(` Diagram: ${chalk.cyan(data.diagram_name || 'N/A')}`);
|
|
1117
|
+
console.log(` Components: ${chalk.white(data.component_count || 0)}`);
|
|
1118
|
+
console.log(` AI Provider: ${chalk.green(data.provider || 'ai_agent')}`);
|
|
1119
|
+
console.log(` Generated: ${chalk.gray(data.generated_at || 'now')}`);
|
|
1096
1120
|
const threats = data.threats || [];
|
|
1097
1121
|
if (threats.length > 0) {
|
|
1098
|
-
console.log(
|
|
1122
|
+
console.log(chalk.bold('\nPredicted Threats:\n'));
|
|
1099
1123
|
threats.slice(0, 5).forEach((t) => {
|
|
1100
1124
|
const severity = t.severity || 'medium';
|
|
1101
|
-
const severityColor = severity === 'critical' || severity === 'high' ?
|
|
1125
|
+
const severityColor = severity === 'critical' || severity === 'high' ? chalk.red : chalk.yellow;
|
|
1102
1126
|
console.log(` ${severityColor('!')} ${t.name || t.threat_id}: ${t.description || ''}`);
|
|
1103
1127
|
if (t.probability) {
|
|
1104
|
-
console.log(` ${
|
|
1128
|
+
console.log(` ${chalk.gray(`Probability: ${(t.probability * 100).toFixed(0)}%`)}`);
|
|
1105
1129
|
}
|
|
1106
1130
|
});
|
|
1107
1131
|
}
|
|
1108
1132
|
else {
|
|
1109
|
-
console.log(
|
|
1133
|
+
console.log(chalk.green('\n No critical threats identified.'));
|
|
1110
1134
|
}
|
|
1111
1135
|
const recommendations = data.recommendations || [];
|
|
1112
1136
|
if (recommendations.length > 0) {
|
|
1113
|
-
console.log(
|
|
1137
|
+
console.log(chalk.bold('\nAI Recommendations:\n'));
|
|
1114
1138
|
recommendations.slice(0, 5).forEach((r) => {
|
|
1115
|
-
console.log(` ${
|
|
1139
|
+
console.log(` ${chalk.cyan('→')} ${typeof r === 'string' ? r : (r.title || r.description || r)}`);
|
|
1116
1140
|
});
|
|
1117
1141
|
}
|
|
1118
1142
|
}
|
|
@@ -1124,7 +1148,7 @@ program
|
|
|
1124
1148
|
}
|
|
1125
1149
|
// AI pattern detection
|
|
1126
1150
|
if (options.patterns) {
|
|
1127
|
-
const spinner = (
|
|
1151
|
+
const spinner = ora('Detecting AI patterns...').start();
|
|
1128
1152
|
try {
|
|
1129
1153
|
const fullId = await resolveDiagramId(options.patterns);
|
|
1130
1154
|
const data = await apiRequest('/v2/threat-modeling/ai-patterns/detect/', {
|
|
@@ -1135,25 +1159,25 @@ program
|
|
|
1135
1159
|
})
|
|
1136
1160
|
});
|
|
1137
1161
|
spinner.succeed('AI pattern detection complete!');
|
|
1138
|
-
console.log(
|
|
1162
|
+
console.log(chalk.bold('\nAI Pattern Detection:\n'));
|
|
1139
1163
|
const detection = data.detection || data;
|
|
1140
|
-
console.log(` Patterns Found: ${
|
|
1141
|
-
console.log(` Security Patterns: ${
|
|
1142
|
-
console.log(` Risk Patterns: ${
|
|
1164
|
+
console.log(` Patterns Found: ${chalk.cyan(detection.total_patterns || 0)}`);
|
|
1165
|
+
console.log(` Security Patterns: ${chalk.yellow(detection.security_patterns || 0)}`);
|
|
1166
|
+
console.log(` Risk Patterns: ${chalk.red(detection.risk_patterns || 0)}`);
|
|
1143
1167
|
const patterns = detection.patterns || data.patterns || [];
|
|
1144
1168
|
if (patterns.length > 0) {
|
|
1145
|
-
console.log(
|
|
1169
|
+
console.log(chalk.bold('\nDetected Patterns:\n'));
|
|
1146
1170
|
patterns.slice(0, 5).forEach((p) => {
|
|
1147
|
-
const typeColor = p.type === 'risk' ?
|
|
1171
|
+
const typeColor = p.type === 'risk' ? chalk.red : p.type === 'security' ? chalk.green : chalk.cyan;
|
|
1148
1172
|
console.log(` ${typeColor(`[${p.type?.toUpperCase() || 'PATTERN'}]`)} ${p.name || p.title}`);
|
|
1149
|
-
console.log(
|
|
1173
|
+
console.log(chalk.dim(` Confidence: ${((p.confidence || 0.85) * 100).toFixed(0)}% | Impact: ${p.impact || 'medium'}`));
|
|
1150
1174
|
});
|
|
1151
1175
|
}
|
|
1152
1176
|
const anomalies = detection.anomalies || [];
|
|
1153
1177
|
if (anomalies.length > 0) {
|
|
1154
|
-
console.log(
|
|
1178
|
+
console.log(chalk.bold('\nDetected Anomalies:\n'));
|
|
1155
1179
|
anomalies.slice(0, 3).forEach((a) => {
|
|
1156
|
-
console.log(` ${
|
|
1180
|
+
console.log(` ${chalk.yellow('⚠')} ${a.description || a.name || a}`);
|
|
1157
1181
|
});
|
|
1158
1182
|
}
|
|
1159
1183
|
}
|
|
@@ -1164,17 +1188,17 @@ program
|
|
|
1164
1188
|
return;
|
|
1165
1189
|
}
|
|
1166
1190
|
// Default: show usage
|
|
1167
|
-
console.log(
|
|
1168
|
-
console.log(` ${
|
|
1169
|
-
console.log(` ${
|
|
1170
|
-
console.log(` ${
|
|
1171
|
-
console.log(` ${
|
|
1172
|
-
console.log(` ${
|
|
1173
|
-
console.log(
|
|
1174
|
-
console.log(` ${
|
|
1175
|
-
console.log(` ${
|
|
1176
|
-
console.log(` ${
|
|
1177
|
-
console.log(` ${
|
|
1191
|
+
console.log(chalk.bold('\nRed Team Commands:\n'));
|
|
1192
|
+
console.log(` ${chalk.cyan('aribot redteam --methodologies')} List threat modeling methodologies`);
|
|
1193
|
+
console.log(` ${chalk.cyan('aribot redteam --intelligence')} Get threat intelligence summary`);
|
|
1194
|
+
console.log(` ${chalk.cyan('aribot redteam --attack-paths -d <id>')} Analyze attack paths for diagram`);
|
|
1195
|
+
console.log(` ${chalk.cyan('aribot redteam --analyze <id>')} Comprehensive threat analysis`);
|
|
1196
|
+
console.log(` ${chalk.cyan('aribot redteam --requirements <id>')} Generate security requirements`);
|
|
1197
|
+
console.log(chalk.bold('\nAI-Powered Commands:\n'));
|
|
1198
|
+
console.log(` ${chalk.green('aribot redteam --ai-attack-paths <id>')} AI attack path analysis`);
|
|
1199
|
+
console.log(` ${chalk.green('aribot redteam --ai-predict <id>')} AI threat prediction (ML)`);
|
|
1200
|
+
console.log(` ${chalk.green('aribot redteam --ai-insights <id>')} Generate AI architecture insights`);
|
|
1201
|
+
console.log(` ${chalk.green('aribot redteam --patterns <id>')} Detect AI patterns in diagram`);
|
|
1178
1202
|
});
|
|
1179
1203
|
// AI Analysis command
|
|
1180
1204
|
program
|
|
@@ -1185,7 +1209,7 @@ program
|
|
|
1185
1209
|
.option('--recommendations <diagram-id>', 'Get AI recommendations')
|
|
1186
1210
|
.option('--cost <diagram-id>', 'Analyze costs with AI')
|
|
1187
1211
|
.action(async (options) => {
|
|
1188
|
-
const spinner = (
|
|
1212
|
+
const spinner = ora('Running AI analysis...').start();
|
|
1189
1213
|
try {
|
|
1190
1214
|
if (options.analyze) {
|
|
1191
1215
|
const fullId = await resolveDiagramId(options.analyze);
|
|
@@ -1194,10 +1218,10 @@ program
|
|
|
1194
1218
|
body: JSON.stringify({ diagram_id: fullId })
|
|
1195
1219
|
});
|
|
1196
1220
|
spinner.succeed('AI analysis complete!');
|
|
1197
|
-
console.log(
|
|
1198
|
-
console.log(` Risk Level: ${
|
|
1199
|
-
console.log(` Findings: ${
|
|
1200
|
-
console.log(` Confidence: ${
|
|
1221
|
+
console.log(chalk.bold('\nAI Analysis Results:\n'));
|
|
1222
|
+
console.log(` Risk Level: ${chalk.yellow(data.risk_level || 'N/A')}`);
|
|
1223
|
+
console.log(` Findings: ${chalk.cyan(data.findings_count || 0)}`);
|
|
1224
|
+
console.log(` Confidence: ${chalk.green((data.confidence || 0) + '%')}`);
|
|
1201
1225
|
}
|
|
1202
1226
|
else if (options.predict) {
|
|
1203
1227
|
const fullId = await resolveDiagramId(options.predict);
|
|
@@ -1206,9 +1230,9 @@ program
|
|
|
1206
1230
|
body: JSON.stringify({ diagram_id: fullId })
|
|
1207
1231
|
});
|
|
1208
1232
|
spinner.succeed('Threat prediction complete!');
|
|
1209
|
-
console.log(
|
|
1233
|
+
console.log(chalk.bold('\nPredicted Threats:\n'));
|
|
1210
1234
|
(data.predictions || []).slice(0, 5).forEach((p) => {
|
|
1211
|
-
console.log(` ${
|
|
1235
|
+
console.log(` ${chalk.red('•')} ${p.threat_type}: ${p.probability}% likelihood`);
|
|
1212
1236
|
});
|
|
1213
1237
|
}
|
|
1214
1238
|
else if (options.recommendations) {
|
|
@@ -1218,24 +1242,24 @@ program
|
|
|
1218
1242
|
body: JSON.stringify({ diagram_id: fullId })
|
|
1219
1243
|
});
|
|
1220
1244
|
spinner.succeed('Recommendations generated!');
|
|
1221
|
-
console.log(
|
|
1245
|
+
console.log(chalk.bold('\nAI Recommendations:\n'));
|
|
1222
1246
|
(data.recommendations || []).slice(0, 5).forEach((r, i) => {
|
|
1223
1247
|
console.log(` ${i + 1}. ${r.title}`);
|
|
1224
|
-
console.log(
|
|
1248
|
+
console.log(chalk.dim(` Priority: ${r.priority} | Impact: ${r.impact}`));
|
|
1225
1249
|
});
|
|
1226
1250
|
}
|
|
1227
1251
|
else if (options.cost) {
|
|
1228
1252
|
const fullId = await resolveDiagramId(options.cost);
|
|
1229
1253
|
const data = await apiRequest(`/v2/ai-agents/cost-analysis/${fullId}/`);
|
|
1230
1254
|
spinner.succeed('Cost analysis complete!');
|
|
1231
|
-
console.log(
|
|
1232
|
-
console.log(` Monthly Estimate: ${
|
|
1233
|
-
console.log(` Annual Estimate: ${
|
|
1234
|
-
console.log(` Optimization: ${
|
|
1255
|
+
console.log(chalk.bold('\nAI Cost Analysis:\n'));
|
|
1256
|
+
console.log(` Monthly Estimate: ${chalk.cyan('$' + (data.monthly_cost || 0).toLocaleString())}`);
|
|
1257
|
+
console.log(` Annual Estimate: ${chalk.cyan('$' + (data.annual_cost || 0).toLocaleString())}`);
|
|
1258
|
+
console.log(` Optimization: ${chalk.green((data.optimization_potential || 0) + '%')}`);
|
|
1235
1259
|
}
|
|
1236
1260
|
else {
|
|
1237
1261
|
spinner.stop();
|
|
1238
|
-
console.log(
|
|
1262
|
+
console.log(chalk.yellow('Usage: aribot ai [--analyze|--predict|--recommendations|--cost] <diagram-id>'));
|
|
1239
1263
|
}
|
|
1240
1264
|
}
|
|
1241
1265
|
catch (error) {
|
|
@@ -1252,7 +1276,7 @@ program
|
|
|
1252
1276
|
.option('--vulnerabilities [sbom-id]', 'Scan for vulnerabilities')
|
|
1253
1277
|
.option('--list', 'List all SBOM documents')
|
|
1254
1278
|
.action(async (options) => {
|
|
1255
|
-
const spinner = (
|
|
1279
|
+
const spinner = ora('Processing SBOM...').start();
|
|
1256
1280
|
try {
|
|
1257
1281
|
if (options.generate) {
|
|
1258
1282
|
const fullId = await resolveDiagramId(options.generate);
|
|
@@ -1261,10 +1285,10 @@ program
|
|
|
1261
1285
|
body: JSON.stringify({ diagram_id: fullId })
|
|
1262
1286
|
});
|
|
1263
1287
|
spinner.succeed('SBOM generated!');
|
|
1264
|
-
console.log(
|
|
1265
|
-
console.log(` ID: ${
|
|
1266
|
-
console.log(` Components: ${
|
|
1267
|
-
console.log(` Format: ${
|
|
1288
|
+
console.log(chalk.bold('\nSBOM Document:\n'));
|
|
1289
|
+
console.log(` ID: ${chalk.cyan(data.id || data.sbom_id)}`);
|
|
1290
|
+
console.log(` Components: ${chalk.yellow(data.component_count || 0)}`);
|
|
1291
|
+
console.log(` Format: ${chalk.dim(data.format || 'CycloneDX')}`);
|
|
1268
1292
|
}
|
|
1269
1293
|
else if (options.analyze) {
|
|
1270
1294
|
const data = await apiRequest('/v2/sbom/analyze/', {
|
|
@@ -1272,39 +1296,39 @@ program
|
|
|
1272
1296
|
body: JSON.stringify({ sbom_id: options.analyze })
|
|
1273
1297
|
});
|
|
1274
1298
|
spinner.succeed('SBOM analysis complete!');
|
|
1275
|
-
console.log(
|
|
1276
|
-
console.log(` Total Dependencies: ${
|
|
1277
|
-
console.log(` Direct: ${
|
|
1278
|
-
console.log(` Transitive: ${
|
|
1279
|
-
console.log(` Outdated: ${
|
|
1299
|
+
console.log(chalk.bold('\nDependency Analysis:\n'));
|
|
1300
|
+
console.log(` Total Dependencies: ${chalk.cyan(data.total_dependencies || 0)}`);
|
|
1301
|
+
console.log(` Direct: ${chalk.yellow(data.direct_dependencies || 0)}`);
|
|
1302
|
+
console.log(` Transitive: ${chalk.dim(data.transitive_dependencies || 0)}`);
|
|
1303
|
+
console.log(` Outdated: ${chalk.red(data.outdated_count || 0)}`);
|
|
1280
1304
|
}
|
|
1281
1305
|
else if (options.vulnerabilities) {
|
|
1282
1306
|
const data = await apiRequest('/v2/sbom/vulnerabilities/');
|
|
1283
1307
|
spinner.succeed('Vulnerability scan complete!');
|
|
1284
|
-
console.log(
|
|
1285
|
-
console.log(` Critical: ${
|
|
1286
|
-
console.log(` High: ${
|
|
1287
|
-
console.log(` Medium: ${
|
|
1288
|
-
console.log(` Low: ${
|
|
1308
|
+
console.log(chalk.bold('\nVulnerability Report:\n'));
|
|
1309
|
+
console.log(` Critical: ${chalk.red(data.critical || 0)}`);
|
|
1310
|
+
console.log(` High: ${chalk.yellow(data.high || 0)}`);
|
|
1311
|
+
console.log(` Medium: ${chalk.blue(data.medium || 0)}`);
|
|
1312
|
+
console.log(` Low: ${chalk.dim(data.low || 0)}`);
|
|
1289
1313
|
}
|
|
1290
1314
|
else if (options.list) {
|
|
1291
1315
|
const data = await apiRequest('/v2/sbom/documents/');
|
|
1292
1316
|
spinner.stop();
|
|
1293
|
-
console.log(
|
|
1317
|
+
console.log(chalk.bold('\nSBOM Documents:\n'));
|
|
1294
1318
|
// Backend returns {sbom_documents: [...]}
|
|
1295
1319
|
const docs = data.sbom_documents || data.results || (Array.isArray(data) ? data : []);
|
|
1296
1320
|
if (docs.length === 0) {
|
|
1297
|
-
console.log(
|
|
1321
|
+
console.log(chalk.dim(' No SBOM documents found'));
|
|
1298
1322
|
}
|
|
1299
1323
|
else {
|
|
1300
1324
|
docs.forEach((s) => {
|
|
1301
|
-
console.log(` ${
|
|
1325
|
+
console.log(` ${chalk.cyan(String(s.id || s.sbom_id).slice(0, 8))} ${s.name || 'Unnamed'} ${chalk.dim((s.component_count || 0) + ' components')}`);
|
|
1302
1326
|
});
|
|
1303
1327
|
}
|
|
1304
1328
|
}
|
|
1305
1329
|
else {
|
|
1306
1330
|
spinner.stop();
|
|
1307
|
-
console.log(
|
|
1331
|
+
console.log(chalk.yellow('Usage: aribot sbom [--generate <diagram-id>] [--analyze <sbom-id>] [--vulnerabilities] [--list]'));
|
|
1308
1332
|
}
|
|
1309
1333
|
}
|
|
1310
1334
|
catch (error) {
|
|
@@ -1322,24 +1346,24 @@ program
|
|
|
1322
1346
|
.option('--discover <provider-id>', 'Discover cloud resources')
|
|
1323
1347
|
.option('--health', 'Check digital twin health')
|
|
1324
1348
|
.action(async (options) => {
|
|
1325
|
-
const spinner = (
|
|
1349
|
+
const spinner = ora('Processing...').start();
|
|
1326
1350
|
try {
|
|
1327
1351
|
if (options.providers) {
|
|
1328
1352
|
const data = await apiRequest('/v2/digital-twin/providers/');
|
|
1329
1353
|
spinner.stop();
|
|
1330
|
-
console.log(
|
|
1354
|
+
console.log(chalk.bold('\nCloud Providers:\n'));
|
|
1331
1355
|
(data.results || data || []).forEach((p) => {
|
|
1332
|
-
const status = p.connected ?
|
|
1333
|
-
console.log(` ${status} ${
|
|
1356
|
+
const status = p.connected ? chalk.green('✓') : chalk.red('✗');
|
|
1357
|
+
console.log(` ${status} ${chalk.cyan(p.name)} ${chalk.dim(p.provider_type || p.type)}`);
|
|
1334
1358
|
});
|
|
1335
1359
|
}
|
|
1336
1360
|
else if (options.resources) {
|
|
1337
1361
|
const provider = typeof options.resources === 'string' ? `?provider=${options.resources}` : '';
|
|
1338
1362
|
const data = await apiRequest(`/v2/digital-twin/resources/${provider}`);
|
|
1339
1363
|
spinner.stop();
|
|
1340
|
-
console.log(
|
|
1364
|
+
console.log(chalk.bold('\nCloud Resources:\n'));
|
|
1341
1365
|
(data.results || data || []).slice(0, 20).forEach((r) => {
|
|
1342
|
-
console.log(` ${
|
|
1366
|
+
console.log(` ${chalk.cyan(r.resource_type || r.type)} ${r.name} ${chalk.dim(r.region || '')}`);
|
|
1343
1367
|
});
|
|
1344
1368
|
}
|
|
1345
1369
|
else if (options.sync) {
|
|
@@ -1348,7 +1372,7 @@ program
|
|
|
1348
1372
|
body: JSON.stringify({ provider_id: options.sync })
|
|
1349
1373
|
});
|
|
1350
1374
|
spinner.succeed('Cloud sync initiated!');
|
|
1351
|
-
console.log(` Resources discovered: ${
|
|
1375
|
+
console.log(` Resources discovered: ${chalk.cyan(data.resources_found || 0)}`);
|
|
1352
1376
|
}
|
|
1353
1377
|
else if (options.discover) {
|
|
1354
1378
|
const data = await apiRequest('/v2/digital-twin/discover/', {
|
|
@@ -1356,20 +1380,20 @@ program
|
|
|
1356
1380
|
body: JSON.stringify({ provider_id: options.discover })
|
|
1357
1381
|
});
|
|
1358
1382
|
spinner.succeed('Discovery complete!');
|
|
1359
|
-
console.log(` Resources found: ${
|
|
1383
|
+
console.log(` Resources found: ${chalk.cyan(data.resources_count || 0)}`);
|
|
1360
1384
|
}
|
|
1361
1385
|
else if (options.health) {
|
|
1362
1386
|
const data = await apiRequest('/v2/digital-twin/health/');
|
|
1363
1387
|
spinner.stop();
|
|
1364
|
-
console.log(
|
|
1365
|
-
console.log(` Status: ${data.healthy ?
|
|
1366
|
-
console.log(` Providers: ${
|
|
1367
|
-
console.log(` Resources: ${
|
|
1368
|
-
console.log(` Last Sync: ${
|
|
1388
|
+
console.log(chalk.bold('\nDigital Twin Health:\n'));
|
|
1389
|
+
console.log(` Status: ${data.healthy ? chalk.green('Healthy') : chalk.red('Unhealthy')}`);
|
|
1390
|
+
console.log(` Providers: ${chalk.cyan(data.providers_connected || 0)}`);
|
|
1391
|
+
console.log(` Resources: ${chalk.cyan(data.total_resources || 0)}`);
|
|
1392
|
+
console.log(` Last Sync: ${chalk.dim(data.last_sync || 'Never')}`);
|
|
1369
1393
|
}
|
|
1370
1394
|
else {
|
|
1371
1395
|
spinner.stop();
|
|
1372
|
-
console.log(
|
|
1396
|
+
console.log(chalk.yellow('Usage: aribot digital-twin [--providers] [--resources] [--sync <id>] [--discover <id>] [--health]'));
|
|
1373
1397
|
}
|
|
1374
1398
|
}
|
|
1375
1399
|
catch (error) {
|
|
@@ -1387,34 +1411,34 @@ program
|
|
|
1387
1411
|
.option('--scan <project-id>', 'Run a new scan')
|
|
1388
1412
|
.option('--stats', 'Get pipeline statistics')
|
|
1389
1413
|
.action(async (options) => {
|
|
1390
|
-
const spinner = (
|
|
1414
|
+
const spinner = ora('Processing...').start();
|
|
1391
1415
|
try {
|
|
1392
1416
|
if (options.projects) {
|
|
1393
1417
|
const data = await apiRequest('/v1/pipeline/projects/');
|
|
1394
1418
|
spinner.stop();
|
|
1395
|
-
console.log(
|
|
1419
|
+
console.log(chalk.bold('\nPipeline Projects:\n'));
|
|
1396
1420
|
(data.results || data || []).forEach((p) => {
|
|
1397
|
-
const status = p.last_scan_status === 'passed' ?
|
|
1398
|
-
console.log(` ${status} ${
|
|
1421
|
+
const status = p.last_scan_status === 'passed' ? chalk.green('✓') : chalk.red('✗');
|
|
1422
|
+
console.log(` ${status} ${chalk.cyan(p.name)} ${chalk.dim(p.repository || '')}`);
|
|
1399
1423
|
});
|
|
1400
1424
|
}
|
|
1401
1425
|
else if (options.scans) {
|
|
1402
1426
|
const project = typeof options.scans === 'string' ? `?project=${options.scans}` : '';
|
|
1403
1427
|
const data = await apiRequest(`/v1/pipeline/scans/${project}`);
|
|
1404
1428
|
spinner.stop();
|
|
1405
|
-
console.log(
|
|
1429
|
+
console.log(chalk.bold('\nPipeline Scans:\n'));
|
|
1406
1430
|
(data.results || data || []).slice(0, 10).forEach((s) => {
|
|
1407
|
-
const status = s.status === 'completed' ?
|
|
1408
|
-
console.log(` ${status} ${
|
|
1431
|
+
const status = s.status === 'completed' ? chalk.green('✓') : chalk.yellow('⋯');
|
|
1432
|
+
console.log(` ${status} ${chalk.cyan(String(s.id).slice(0, 8))} ${s.scan_type || 'security'} ${chalk.dim(s.created_at?.slice(0, 10) || '')}`);
|
|
1409
1433
|
});
|
|
1410
1434
|
}
|
|
1411
1435
|
else if (options.vulnerabilities) {
|
|
1412
1436
|
const project = typeof options.vulnerabilities === 'string' ? `?project=${options.vulnerabilities}` : '';
|
|
1413
1437
|
const data = await apiRequest(`/v1/pipeline/vulnerabilities/${project}`);
|
|
1414
1438
|
spinner.stop();
|
|
1415
|
-
console.log(
|
|
1439
|
+
console.log(chalk.bold('\nPipeline Vulnerabilities:\n'));
|
|
1416
1440
|
(data.results || data || []).slice(0, 10).forEach((v) => {
|
|
1417
|
-
const severity = v.severity === 'critical' ?
|
|
1441
|
+
const severity = v.severity === 'critical' ? chalk.red : v.severity === 'high' ? chalk.yellow : chalk.blue;
|
|
1418
1442
|
console.log(` ${severity(`[${v.severity?.toUpperCase()}]`)} ${v.title || v.name}`);
|
|
1419
1443
|
});
|
|
1420
1444
|
}
|
|
@@ -1424,20 +1448,20 @@ program
|
|
|
1424
1448
|
body: JSON.stringify({ project_id: options.scan })
|
|
1425
1449
|
});
|
|
1426
1450
|
spinner.succeed('Scan initiated!');
|
|
1427
|
-
console.log(` Scan ID: ${
|
|
1451
|
+
console.log(` Scan ID: ${chalk.cyan(data.id || data.scan_id)}`);
|
|
1428
1452
|
}
|
|
1429
1453
|
else if (options.stats) {
|
|
1430
1454
|
const data = await apiRequest('/v1/pipeline/stats/');
|
|
1431
1455
|
spinner.stop();
|
|
1432
|
-
console.log(
|
|
1433
|
-
console.log(` Total Projects: ${
|
|
1434
|
-
console.log(` Total Scans: ${
|
|
1435
|
-
console.log(` Vulnerabilities: ${
|
|
1436
|
-
console.log(` Pass Rate: ${
|
|
1456
|
+
console.log(chalk.bold('\nPipeline Statistics:\n'));
|
|
1457
|
+
console.log(` Total Projects: ${chalk.cyan(data.total_projects || 0)}`);
|
|
1458
|
+
console.log(` Total Scans: ${chalk.cyan(data.total_scans || 0)}`);
|
|
1459
|
+
console.log(` Vulnerabilities: ${chalk.red(data.total_vulnerabilities || 0)}`);
|
|
1460
|
+
console.log(` Pass Rate: ${chalk.green((data.pass_rate || 0) + '%')}`);
|
|
1437
1461
|
}
|
|
1438
1462
|
else {
|
|
1439
1463
|
spinner.stop();
|
|
1440
|
-
console.log(
|
|
1464
|
+
console.log(chalk.yellow('Usage: aribot pipeline [--projects] [--scans] [--vulnerabilities] [--scan <id>] [--stats]'));
|
|
1441
1465
|
}
|
|
1442
1466
|
}
|
|
1443
1467
|
catch (error) {
|
|
@@ -1454,16 +1478,16 @@ program
|
|
|
1454
1478
|
.option('--delete <key-id>', 'Delete API key')
|
|
1455
1479
|
.option('--usage', 'Show API usage')
|
|
1456
1480
|
.action(async (options) => {
|
|
1457
|
-
const spinner = (
|
|
1481
|
+
const spinner = ora('Processing...').start();
|
|
1458
1482
|
try {
|
|
1459
1483
|
if (options.list) {
|
|
1460
1484
|
const data = await apiRequest('/v1/developer/api-keys/');
|
|
1461
1485
|
spinner.stop();
|
|
1462
|
-
console.log(
|
|
1486
|
+
console.log(chalk.bold('\nAPI Keys:\n'));
|
|
1463
1487
|
(data.results || data || []).forEach((k) => {
|
|
1464
|
-
const status = (k.is_active || k.status === 'active') ?
|
|
1488
|
+
const status = (k.is_active || k.status === 'active') ? chalk.green('active') : chalk.red('inactive');
|
|
1465
1489
|
const keyPrefix = k.key_prefix || (k.key ? k.key.slice(0, 8) : 'xxxxxxxx');
|
|
1466
|
-
console.log(` ${
|
|
1490
|
+
console.log(` ${chalk.cyan(k.name)} ${chalk.dim(keyPrefix + '...')} ${status}`);
|
|
1467
1491
|
});
|
|
1468
1492
|
}
|
|
1469
1493
|
else if (options.create) {
|
|
@@ -1472,10 +1496,10 @@ program
|
|
|
1472
1496
|
body: JSON.stringify({ name: options.create })
|
|
1473
1497
|
});
|
|
1474
1498
|
spinner.succeed('API key created!');
|
|
1475
|
-
console.log(
|
|
1476
|
-
console.log(` Name: ${
|
|
1477
|
-
console.log(` Key: ${
|
|
1478
|
-
console.log(
|
|
1499
|
+
console.log(chalk.bold('\nNew API Key:\n'));
|
|
1500
|
+
console.log(` Name: ${chalk.cyan(data.name)}`);
|
|
1501
|
+
console.log(` Key: ${chalk.yellow(data.key)}`);
|
|
1502
|
+
console.log(chalk.red('\n ⚠ Save this key now - it won\'t be shown again!'));
|
|
1479
1503
|
}
|
|
1480
1504
|
else if (options.delete) {
|
|
1481
1505
|
await apiRequest(`/v1/developer/api-keys/${options.delete}/`, {
|
|
@@ -1486,14 +1510,14 @@ program
|
|
|
1486
1510
|
else if (options.usage) {
|
|
1487
1511
|
const data = await apiRequest('/v1/developer/usage/');
|
|
1488
1512
|
spinner.stop();
|
|
1489
|
-
console.log(
|
|
1490
|
-
console.log(` Today: ${
|
|
1491
|
-
console.log(` This Month: ${
|
|
1492
|
-
console.log(` Limit: ${
|
|
1513
|
+
console.log(chalk.bold('\nAPI Usage:\n'));
|
|
1514
|
+
console.log(` Today: ${chalk.cyan(data.today || 0)} requests`);
|
|
1515
|
+
console.log(` This Month: ${chalk.cyan(data.this_month || 0)} requests`);
|
|
1516
|
+
console.log(` Limit: ${chalk.dim(data.limit || 'unlimited')}`);
|
|
1493
1517
|
}
|
|
1494
1518
|
else {
|
|
1495
1519
|
spinner.stop();
|
|
1496
|
-
console.log(
|
|
1520
|
+
console.log(chalk.yellow('Usage: aribot api-keys [--list] [--create <name>] [--delete <id>] [--usage]'));
|
|
1497
1521
|
}
|
|
1498
1522
|
}
|
|
1499
1523
|
catch (error) {
|
|
@@ -1511,55 +1535,55 @@ program
|
|
|
1511
1535
|
.option('--my-templates', 'List your templates')
|
|
1512
1536
|
.option('--get <template-id>', 'Get template details')
|
|
1513
1537
|
.action(async (options) => {
|
|
1514
|
-
const spinner = (
|
|
1538
|
+
const spinner = ora('Fetching...').start();
|
|
1515
1539
|
try {
|
|
1516
1540
|
if (options.search) {
|
|
1517
1541
|
const data = await apiRequest(`/v2/marketplace/search/?q=${encodeURIComponent(options.search)}`);
|
|
1518
1542
|
spinner.stop();
|
|
1519
|
-
console.log(
|
|
1543
|
+
console.log(chalk.bold(`\nSearch Results for "${options.search}":\n`));
|
|
1520
1544
|
(data.results || data || []).slice(0, 10).forEach((t) => {
|
|
1521
|
-
console.log(` ${
|
|
1522
|
-
console.log(
|
|
1545
|
+
console.log(` ${chalk.cyan(t.title || t.name)} ${chalk.dim('$' + (t.price || 0))}`);
|
|
1546
|
+
console.log(chalk.dim(` ${t.description?.slice(0, 60) || ''}...`));
|
|
1523
1547
|
});
|
|
1524
1548
|
}
|
|
1525
1549
|
else if (options.featured) {
|
|
1526
1550
|
const data = await apiRequest('/v2/marketplace/featured/');
|
|
1527
1551
|
spinner.stop();
|
|
1528
|
-
console.log(
|
|
1552
|
+
console.log(chalk.bold('\nFeatured Templates:\n'));
|
|
1529
1553
|
(data.results || data || []).forEach((t) => {
|
|
1530
|
-
console.log(` ${
|
|
1554
|
+
console.log(` ${chalk.green('★')} ${chalk.cyan(t.title || t.name)} ${chalk.dim('$' + (t.price || 0))}`);
|
|
1531
1555
|
});
|
|
1532
1556
|
}
|
|
1533
1557
|
else if (options.categories) {
|
|
1534
1558
|
const data = await apiRequest('/v2/marketplace/categories/');
|
|
1535
1559
|
spinner.stop();
|
|
1536
|
-
console.log(
|
|
1560
|
+
console.log(chalk.bold('\nTemplate Categories:\n'));
|
|
1537
1561
|
// Backend returns {value, label} format
|
|
1538
1562
|
(data.results || data || []).forEach((c) => {
|
|
1539
|
-
console.log(` ${
|
|
1563
|
+
console.log(` ${chalk.cyan(c.label || c.name || c.value)} ${chalk.dim((c.template_count || c.count || '') + ' templates')}`);
|
|
1540
1564
|
});
|
|
1541
1565
|
}
|
|
1542
1566
|
else if (options.myTemplates) {
|
|
1543
1567
|
const data = await apiRequest('/v2/marketplace/my-templates/');
|
|
1544
1568
|
spinner.stop();
|
|
1545
|
-
console.log(
|
|
1569
|
+
console.log(chalk.bold('\nYour Templates:\n'));
|
|
1546
1570
|
(data.results || data || []).forEach((t) => {
|
|
1547
|
-
console.log(` ${
|
|
1571
|
+
console.log(` ${chalk.cyan(t.title || t.name)} ${chalk.dim(t.status)}`);
|
|
1548
1572
|
});
|
|
1549
1573
|
}
|
|
1550
1574
|
else if (options.get) {
|
|
1551
1575
|
const data = await apiRequest(`/v2/marketplace/v2/templates/${options.get}/`);
|
|
1552
1576
|
spinner.stop();
|
|
1553
|
-
console.log(
|
|
1554
|
-
console.log(` Category: ${
|
|
1555
|
-
console.log(` Price: ${
|
|
1556
|
-
console.log(` Downloads: ${
|
|
1557
|
-
console.log(` Rating: ${
|
|
1558
|
-
console.log(`\n ${
|
|
1577
|
+
console.log(chalk.bold(`\n${data.title || data.name}\n`));
|
|
1578
|
+
console.log(` Category: ${chalk.cyan(data.category)}`);
|
|
1579
|
+
console.log(` Price: ${chalk.yellow('$' + (data.price || 0))}`);
|
|
1580
|
+
console.log(` Downloads: ${chalk.dim(data.download_count || 0)}`);
|
|
1581
|
+
console.log(` Rating: ${chalk.green(data.rating || 'N/A')}`);
|
|
1582
|
+
console.log(`\n ${chalk.dim(data.description || '')}`);
|
|
1559
1583
|
}
|
|
1560
1584
|
else {
|
|
1561
1585
|
spinner.stop();
|
|
1562
|
-
console.log(
|
|
1586
|
+
console.log(chalk.yellow('Usage: aribot marketplace [--search <query>] [--featured] [--categories] [--my-templates] [--get <id>]'));
|
|
1563
1587
|
}
|
|
1564
1588
|
}
|
|
1565
1589
|
catch (error) {
|
|
@@ -1576,40 +1600,40 @@ program
|
|
|
1576
1600
|
.option('--risk', 'Show risk summary')
|
|
1577
1601
|
.option('--billing', 'Show billing information')
|
|
1578
1602
|
.action(async (options) => {
|
|
1579
|
-
const spinner = (
|
|
1603
|
+
const spinner = ora('Loading dashboard...').start();
|
|
1580
1604
|
try {
|
|
1581
1605
|
if (options.overview || (!options.recent && !options.risk && !options.billing)) {
|
|
1582
1606
|
const data = await apiRequest('/v2/threat-modeling/dashboard/');
|
|
1583
1607
|
spinner.stop();
|
|
1584
|
-
console.log(
|
|
1585
|
-
console.log(` Total Diagrams: ${
|
|
1586
|
-
console.log(` Total Threats: ${
|
|
1587
|
-
console.log(` Critical Threats: ${
|
|
1588
|
-
console.log(` Compliance Score: ${
|
|
1608
|
+
console.log(chalk.bold('\nDashboard Overview:\n'));
|
|
1609
|
+
console.log(` Total Diagrams: ${chalk.cyan(data.total_diagrams || 0)}`);
|
|
1610
|
+
console.log(` Total Threats: ${chalk.yellow(data.total_threats || 0)}`);
|
|
1611
|
+
console.log(` Critical Threats: ${chalk.red(data.critical_threats || 0)}`);
|
|
1612
|
+
console.log(` Compliance Score: ${chalk.green((data.compliance_score || 0) + '%')}`);
|
|
1589
1613
|
}
|
|
1590
1614
|
else if (options.recent) {
|
|
1591
1615
|
const data = await apiRequest('/v2/threat-modeling/dashboard/recent-activity/');
|
|
1592
1616
|
spinner.stop();
|
|
1593
|
-
console.log(
|
|
1617
|
+
console.log(chalk.bold('\nRecent Activity:\n'));
|
|
1594
1618
|
(data.activities || data || []).slice(0, 10).forEach((a) => {
|
|
1595
|
-
console.log(` ${
|
|
1619
|
+
console.log(` ${chalk.dim(a.timestamp?.slice(0, 16) || '')} ${a.action} ${chalk.cyan(a.resource || '')}`);
|
|
1596
1620
|
});
|
|
1597
1621
|
}
|
|
1598
1622
|
else if (options.risk) {
|
|
1599
1623
|
const data = await apiRequest('/v2/threat-modeling/dashboard/risk-summary/');
|
|
1600
1624
|
spinner.stop();
|
|
1601
|
-
console.log(
|
|
1602
|
-
console.log(` Overall Risk: ${data.overall_risk === 'high' ?
|
|
1603
|
-
console.log(` Open Threats: ${
|
|
1604
|
-
console.log(` Mitigated: ${
|
|
1625
|
+
console.log(chalk.bold('\nRisk Summary:\n'));
|
|
1626
|
+
console.log(` Overall Risk: ${data.overall_risk === 'high' ? chalk.red(data.overall_risk) : chalk.yellow(data.overall_risk || 'N/A')}`);
|
|
1627
|
+
console.log(` Open Threats: ${chalk.yellow(data.open_threats || 0)}`);
|
|
1628
|
+
console.log(` Mitigated: ${chalk.green(data.mitigated_threats || 0)}`);
|
|
1605
1629
|
}
|
|
1606
1630
|
else if (options.billing) {
|
|
1607
1631
|
const data = await apiRequest('/v1/developer/billing/');
|
|
1608
1632
|
spinner.stop();
|
|
1609
|
-
console.log(
|
|
1610
|
-
console.log(` Plan: ${
|
|
1611
|
-
console.log(` Usage: ${
|
|
1612
|
-
console.log(` Next Billing: ${
|
|
1633
|
+
console.log(chalk.bold('\nBilling Information:\n'));
|
|
1634
|
+
console.log(` Plan: ${chalk.cyan(data.plan || 'Free')}`);
|
|
1635
|
+
console.log(` Usage: ${chalk.yellow((data.usage || 0) + ' requests')}`);
|
|
1636
|
+
console.log(` Next Billing: ${chalk.dim(data.next_billing_date || 'N/A')}`);
|
|
1613
1637
|
}
|
|
1614
1638
|
}
|
|
1615
1639
|
catch (error) {
|