@ayurak/aribot-cli 1.2.0 → 1.3.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.
- package/README.md +56 -1
- package/dist/cli.js +435 -66
- package/dist/sdk.d.ts +8 -1
- package/dist/sdk.js +32 -10
- package/package.json +7 -2
- package/src/cli.ts +466 -68
- package/src/sdk.ts +35 -10
- package/tests/e2e.test.ts +244 -0
- package/tests/sdk.test.ts +500 -0
- package/vitest.config.ts +17 -0
package/dist/cli.js
CHANGED
|
@@ -693,71 +693,370 @@ program
|
|
|
693
693
|
.option('--findings', 'List security findings')
|
|
694
694
|
.option('--dashboard', 'Show cloud security dashboard')
|
|
695
695
|
.option('-s, --severity <level>', 'Filter findings by severity (critical, high, medium, low)')
|
|
696
|
+
.option('--dynamic-scan <account-id>', 'Run dynamic cloud scan')
|
|
697
|
+
.option('--unified-scan', 'Run unified scan with scope')
|
|
698
|
+
.option('--scope <type>', 'Scan scope: account, standard, control, policy, diagram, component', 'account')
|
|
699
|
+
.option('--scope-id <id>', 'ID for the scan scope')
|
|
700
|
+
.option('--rules', 'List scanner rules')
|
|
701
|
+
.option('--create-rule', 'Create a new scanner rule (interactive)')
|
|
702
|
+
.option('--sync-rules', 'Sync rules from cloud providers')
|
|
703
|
+
.option('--scanner-stats', 'Show scanner statistics')
|
|
704
|
+
.option('--remediate <policy-id>', 'Execute remediation for a policy')
|
|
705
|
+
.option('--remediate-preview <policy-id>', 'Preview remediation without applying')
|
|
706
|
+
.option('--account-id <id>', 'Cloud account ID for operations')
|
|
696
707
|
.action(async (options) => {
|
|
697
708
|
const spinner = ora('Scanning cloud security...').start();
|
|
698
709
|
try {
|
|
699
710
|
if (options.scan) {
|
|
700
711
|
const provider = typeof options.scan === 'string' ? options.scan : undefined;
|
|
701
|
-
//
|
|
702
|
-
|
|
712
|
+
// Get violations from unified-violations endpoint
|
|
713
|
+
let violationsUrl = '/v2/compliances/unified-violations/?limit=100';
|
|
714
|
+
if (provider) {
|
|
715
|
+
violationsUrl += `&platform=${provider}`;
|
|
716
|
+
}
|
|
717
|
+
const violationsData = await apiRequest(violationsUrl);
|
|
718
|
+
const violationsList = Array.isArray(violationsData) ? violationsData : (violationsData.results || violationsData.violations || []);
|
|
719
|
+
const totalViolations = violationsData.count || violationsList.length;
|
|
720
|
+
// Get cloud accounts count
|
|
721
|
+
let totalAccounts = 0;
|
|
722
|
+
try {
|
|
723
|
+
const customersData = await apiRequest('/v1/customers/');
|
|
724
|
+
const customers = customersData.results || [];
|
|
725
|
+
totalAccounts = customers.reduce((sum, c) => sum + (c.ac_count || 0), 0);
|
|
726
|
+
}
|
|
727
|
+
catch { /* ignore */ }
|
|
728
|
+
// Count by severity and platform
|
|
729
|
+
const severityCounts = { critical: 0, high: 0, medium: 0, low: 0 };
|
|
730
|
+
const platformCounts = {};
|
|
731
|
+
violationsList.forEach((v) => {
|
|
732
|
+
const sev = (v.severity || 'medium').toLowerCase();
|
|
733
|
+
if (sev in severityCounts)
|
|
734
|
+
severityCounts[sev]++;
|
|
735
|
+
const platform = (v.platform || 'unknown').toLowerCase();
|
|
736
|
+
platformCounts[platform] = (platformCounts[platform] || 0) + 1;
|
|
737
|
+
});
|
|
703
738
|
spinner.succeed('Cloud security scan complete!');
|
|
704
739
|
console.log(chalk.bold('\nCloud Security Posture:\n'));
|
|
705
|
-
|
|
706
|
-
console.log(`
|
|
707
|
-
console.log(`
|
|
708
|
-
console.log(`
|
|
709
|
-
console.log(`
|
|
710
|
-
console.log(` Critical Issues: ${chalk.red(stats.critical_findings || stats.critical || 0)}`);
|
|
740
|
+
console.log(` Connected Accounts: ${chalk.cyan(totalAccounts)}`);
|
|
741
|
+
console.log(` Total Violations: ${chalk.yellow(totalViolations)}`);
|
|
742
|
+
console.log(` Critical Issues: ${chalk.red(severityCounts.critical)}`);
|
|
743
|
+
console.log(` High Issues: ${chalk.yellow(severityCounts.high)}`);
|
|
744
|
+
console.log(` Medium Issues: ${chalk.blue(severityCounts.medium)}`);
|
|
711
745
|
if (provider) {
|
|
712
|
-
console.log(`\n Provider:
|
|
746
|
+
console.log(`\n Provider: ${chalk.cyan(provider.toUpperCase())}`);
|
|
713
747
|
}
|
|
714
|
-
// Show
|
|
715
|
-
if (
|
|
716
|
-
console.log(chalk.bold('\nBy
|
|
717
|
-
Object.entries(
|
|
718
|
-
|
|
748
|
+
// Show platform breakdown if scanning all
|
|
749
|
+
if (Object.keys(platformCounts).length > 0 && !provider) {
|
|
750
|
+
console.log(chalk.bold('\nBy Platform:\n'));
|
|
751
|
+
Object.entries(platformCounts)
|
|
752
|
+
.sort((a, b) => b[1] - a[1])
|
|
753
|
+
.forEach(([p, count]) => {
|
|
754
|
+
console.log(` ${chalk.cyan(p.toUpperCase().padEnd(10))} ${count} violations`);
|
|
719
755
|
});
|
|
720
756
|
}
|
|
721
757
|
}
|
|
722
758
|
else if (options.findings) {
|
|
723
|
-
// Use
|
|
724
|
-
let url = '/v2/compliances/
|
|
759
|
+
// Use unified violations endpoint
|
|
760
|
+
let url = '/v2/compliances/unified-violations/?limit=20';
|
|
725
761
|
if (options.severity) {
|
|
726
762
|
url += `&severity=${options.severity}`;
|
|
727
763
|
}
|
|
728
764
|
const data = await apiRequest(url);
|
|
729
765
|
spinner.stop();
|
|
730
766
|
console.log(chalk.bold('\nCloud Security Findings:\n'));
|
|
731
|
-
const findings = data.results || data.findings || [];
|
|
767
|
+
const findings = Array.isArray(data) ? data : (data.results || data.violations || data.findings || []);
|
|
732
768
|
if (findings.length === 0) {
|
|
733
769
|
console.log(chalk.green(' No open findings! Your cloud is secure.'));
|
|
734
770
|
}
|
|
735
771
|
else {
|
|
736
772
|
const severityColors = {
|
|
737
773
|
critical: chalk.red,
|
|
774
|
+
crit: chalk.red,
|
|
738
775
|
high: chalk.yellow,
|
|
739
776
|
medium: chalk.blue,
|
|
740
777
|
low: chalk.dim
|
|
741
778
|
};
|
|
742
779
|
findings.slice(0, 10).forEach((f) => {
|
|
743
|
-
const
|
|
744
|
-
|
|
745
|
-
|
|
780
|
+
const sev = (f.severity || 'medium').toLowerCase();
|
|
781
|
+
const color = severityColors[sev] || chalk.white;
|
|
782
|
+
const ruleName = f.rule_name || f.title || 'Unknown';
|
|
783
|
+
const resource = f.resource_name || f.resource_type || 'N/A';
|
|
784
|
+
const platform = (f.platform || 'N/A').toUpperCase();
|
|
785
|
+
console.log(` ${color(`[${sev.toUpperCase()}]`)} ${ruleName}`);
|
|
786
|
+
console.log(chalk.dim(` Resource: ${resource} | Platform: ${platform}`));
|
|
746
787
|
});
|
|
747
|
-
|
|
788
|
+
const totalCount = data.count || findings.length;
|
|
789
|
+
console.log(chalk.dim(`\nShowing ${Math.min(10, findings.length)} of ${totalCount} findings`));
|
|
748
790
|
}
|
|
749
791
|
}
|
|
750
792
|
else if (options.dashboard) {
|
|
751
|
-
|
|
793
|
+
// Get dashboard trends and violations count
|
|
794
|
+
const dashboardData = await apiRequest('/v2/compliances/dashboard/trends/');
|
|
795
|
+
const violationsData = await apiRequest('/v2/compliances/unified-violations/?limit=1');
|
|
796
|
+
const violationsCount = violationsData.count || (Array.isArray(violationsData) ? violationsData.length : 0);
|
|
752
797
|
spinner.succeed('Dashboard loaded!');
|
|
753
798
|
console.log(chalk.bold('\nCloud Security Dashboard:\n'));
|
|
754
|
-
console.log(`
|
|
755
|
-
console.log(`
|
|
756
|
-
console.log(`
|
|
799
|
+
console.log(` Fix Velocity: ${chalk.green(dashboardData.fix_velocity || 'N/A')}`);
|
|
800
|
+
console.log(` Time Range: ${chalk.cyan(dashboardData.time_range || '7d')}`);
|
|
801
|
+
console.log(` Resolved Issues: ${chalk.green(dashboardData.total_resolved || 0)}`);
|
|
802
|
+
console.log(` Avg Resolution: ${dashboardData.avg_resolution_time_hours || 'N/A'}h`);
|
|
803
|
+
console.log(` Open Violations: ${chalk.yellow(violationsCount)}`);
|
|
804
|
+
// Show severity breakdown if available
|
|
805
|
+
if (dashboardData.by_severity) {
|
|
806
|
+
console.log(chalk.bold('\nResolved by Severity:\n'));
|
|
807
|
+
const sevColors = { critical: chalk.red, high: chalk.yellow, medium: chalk.blue, low: chalk.dim };
|
|
808
|
+
Object.entries(dashboardData.by_severity).forEach(([sev, count]) => {
|
|
809
|
+
const color = sevColors[sev.toLowerCase()] || chalk.white;
|
|
810
|
+
console.log(` ${color(sev.toUpperCase().padEnd(10))} ${count}`);
|
|
811
|
+
});
|
|
812
|
+
}
|
|
813
|
+
}
|
|
814
|
+
else if (options.dynamicScan) {
|
|
815
|
+
// Dynamic cloud scan
|
|
816
|
+
spinner.text = 'Running dynamic cloud scan...';
|
|
817
|
+
const data = await apiRequest('/v2/compliances/dynamic-scan/execute/', {
|
|
818
|
+
method: 'POST',
|
|
819
|
+
body: JSON.stringify({ account_id: parseInt(options.dynamicScan) })
|
|
820
|
+
});
|
|
821
|
+
spinner.succeed('Dynamic scan initiated!');
|
|
822
|
+
console.log(chalk.bold('\nDynamic Scan Results:\n'));
|
|
823
|
+
console.log(` Scan ID: ${chalk.cyan(data.scan_id || 'N/A')}`);
|
|
824
|
+
console.log(` Account ID: ${options.dynamicScan}`);
|
|
825
|
+
console.log(` Status: ${chalk.green(data.status || 'initiated')}`);
|
|
826
|
+
console.log(` Started At: ${chalk.dim(data.started_at || 'now')}`);
|
|
827
|
+
if (data.findings_count) {
|
|
828
|
+
console.log(` Findings: ${chalk.yellow(data.findings_count)}`);
|
|
829
|
+
}
|
|
830
|
+
if (data.async) {
|
|
831
|
+
console.log(chalk.dim('\nScan running asynchronously. Check status with --dashboard'));
|
|
832
|
+
}
|
|
833
|
+
}
|
|
834
|
+
else if (options.unifiedScan) {
|
|
835
|
+
// Unified scan with scope
|
|
836
|
+
if (!options.scopeId && options.scope !== 'account') {
|
|
837
|
+
spinner.fail('--scope-id required for non-account scopes');
|
|
838
|
+
return;
|
|
839
|
+
}
|
|
840
|
+
spinner.text = `Running unified scan (${options.scope})...`;
|
|
841
|
+
const scanData = { scope: options.scope };
|
|
842
|
+
if (options.scope === 'account') {
|
|
843
|
+
scanData.account_id = options.accountId || (options.scopeId ? parseInt(options.scopeId) : undefined);
|
|
844
|
+
}
|
|
845
|
+
else if (options.scope === 'standard') {
|
|
846
|
+
scanData.standard_id = options.scopeId;
|
|
847
|
+
}
|
|
848
|
+
else if (options.scope === 'control') {
|
|
849
|
+
scanData.control_id = options.scopeId;
|
|
850
|
+
}
|
|
851
|
+
else if (options.scope === 'policy') {
|
|
852
|
+
scanData.policy_id = options.scopeId;
|
|
853
|
+
}
|
|
854
|
+
else if (options.scope === 'diagram') {
|
|
855
|
+
scanData.diagram_id = options.scopeId;
|
|
856
|
+
}
|
|
857
|
+
else if (options.scope === 'component') {
|
|
858
|
+
scanData.component_id = options.scopeId;
|
|
859
|
+
}
|
|
860
|
+
if (options.accountId) {
|
|
861
|
+
scanData.account_id = parseInt(options.accountId);
|
|
862
|
+
}
|
|
863
|
+
const data = await apiRequest('/v2/compliances/scan/execute/', {
|
|
864
|
+
method: 'POST',
|
|
865
|
+
body: JSON.stringify(scanData)
|
|
866
|
+
});
|
|
867
|
+
spinner.succeed('Unified scan complete!');
|
|
868
|
+
console.log(chalk.bold(`\nUnified Scan Results (${options.scope}):\n`));
|
|
869
|
+
console.log(` Scan ID: ${chalk.cyan(data.scan_id || 'N/A')}`);
|
|
870
|
+
console.log(` Scope: ${options.scope}`);
|
|
871
|
+
console.log(` Status: ${chalk.green(data.status || 'completed')}`);
|
|
872
|
+
const results = data.results || data;
|
|
873
|
+
console.log(` Total Checked: ${results.total_checks || results.total || 0}`);
|
|
874
|
+
console.log(` Passed: ${chalk.green(results.passed || 0)}`);
|
|
875
|
+
console.log(` Failed: ${chalk.red(results.failed || 0)}`);
|
|
876
|
+
if (results.compliance_score !== undefined) {
|
|
877
|
+
const score = results.compliance_score;
|
|
878
|
+
const scoreColor = score >= 80 ? chalk.green : score >= 60 ? chalk.yellow : chalk.red;
|
|
879
|
+
console.log(` Score: ${scoreColor(score + '%')}`);
|
|
880
|
+
}
|
|
881
|
+
}
|
|
882
|
+
else if (options.rules) {
|
|
883
|
+
// List scanner rules
|
|
884
|
+
spinner.text = 'Fetching scanner rules...';
|
|
885
|
+
let url = '/v2/compliances/scanner-rules/';
|
|
886
|
+
if (options.severity) {
|
|
887
|
+
url += `?severity=${options.severity}`;
|
|
888
|
+
}
|
|
889
|
+
const data = await apiRequest(url);
|
|
890
|
+
spinner.stop();
|
|
891
|
+
const rulesList = data.results || data.rules || [];
|
|
892
|
+
console.log(chalk.bold(`\nScanner Rules (${rulesList.length} total):\n`));
|
|
893
|
+
if (rulesList.length === 0) {
|
|
894
|
+
console.log(chalk.dim(' No scanner rules found.'));
|
|
895
|
+
}
|
|
896
|
+
else {
|
|
897
|
+
const severityColors = {
|
|
898
|
+
critical: chalk.red,
|
|
899
|
+
high: chalk.yellow,
|
|
900
|
+
medium: chalk.blue,
|
|
901
|
+
low: chalk.dim
|
|
902
|
+
};
|
|
903
|
+
rulesList.slice(0, 20).forEach((r) => {
|
|
904
|
+
const color = severityColors[r.severity] || chalk.white;
|
|
905
|
+
const enabled = r.enabled !== false ? chalk.green('✓') : chalk.red('✗');
|
|
906
|
+
console.log(` ${enabled} ${chalk.cyan((r.id + '').slice(0, 8).padEnd(8))} ${color(`[${(r.severity || 'medium').toUpperCase()}]`)} ${(r.name || 'N/A').slice(0, 40)}`);
|
|
907
|
+
console.log(chalk.dim(` Provider: ${r.provider || 'N/A'}`));
|
|
908
|
+
});
|
|
909
|
+
}
|
|
910
|
+
}
|
|
911
|
+
else if (options.createRule) {
|
|
912
|
+
// Interactive rule creation
|
|
913
|
+
spinner.stop();
|
|
914
|
+
console.log(chalk.bold('\nCreate Scanner Rule\n'));
|
|
915
|
+
const answers = await inquirer.prompt([
|
|
916
|
+
{ type: 'input', name: 'name', message: 'Rule name:' },
|
|
917
|
+
{ type: 'input', name: 'description', message: 'Description:' },
|
|
918
|
+
{ type: 'list', name: 'severity', message: 'Severity:', choices: ['critical', 'high', 'medium', 'low'], default: 'medium' },
|
|
919
|
+
{ type: 'list', name: 'provider', message: 'Provider:', choices: ['aws', 'azure', 'gcp', 'custom'], default: 'custom' }
|
|
920
|
+
]);
|
|
921
|
+
const createSpinner = ora('Creating rule...').start();
|
|
922
|
+
const data = await apiRequest('/v2/compliances/scanner-rules/', {
|
|
923
|
+
method: 'POST',
|
|
924
|
+
body: JSON.stringify({
|
|
925
|
+
name: answers.name,
|
|
926
|
+
description: answers.description,
|
|
927
|
+
severity: answers.severity,
|
|
928
|
+
provider: answers.provider,
|
|
929
|
+
enabled: true
|
|
930
|
+
})
|
|
931
|
+
});
|
|
932
|
+
createSpinner.succeed('Rule created successfully!');
|
|
933
|
+
console.log(` ID: ${chalk.cyan(data.id || 'N/A')}`);
|
|
934
|
+
console.log(` Name: ${data.name || answers.name}`);
|
|
935
|
+
console.log(` Severity: ${data.severity || answers.severity}`);
|
|
936
|
+
}
|
|
937
|
+
else if (options.syncRules) {
|
|
938
|
+
// Sync rules from cloud providers
|
|
939
|
+
spinner.text = 'Syncing rules from cloud providers...';
|
|
940
|
+
const data = await apiRequest('/v2/compliances/scanner-rules/sync_from_providers/', {
|
|
941
|
+
method: 'POST',
|
|
942
|
+
body: JSON.stringify({})
|
|
943
|
+
});
|
|
944
|
+
spinner.succeed('Rules synced successfully!');
|
|
945
|
+
console.log(chalk.bold('\nSync Results:\n'));
|
|
946
|
+
console.log(` AWS Rules: ${chalk.cyan(data.aws_rules || data.aws || 0)}`);
|
|
947
|
+
console.log(` Azure Rules: ${chalk.cyan(data.azure_rules || data.azure || 0)}`);
|
|
948
|
+
console.log(` GCP Rules: ${chalk.cyan(data.gcp_rules || data.gcp || 0)}`);
|
|
949
|
+
console.log(` Total: ${chalk.green(data.total || data.total_synced || 0)}`);
|
|
950
|
+
}
|
|
951
|
+
else if (options.scannerStats) {
|
|
952
|
+
// Scanner statistics
|
|
953
|
+
spinner.text = 'Fetching scanner statistics...';
|
|
954
|
+
const data = await apiRequest('/v2/compliances/scanner-rules/statistics/');
|
|
955
|
+
spinner.stop();
|
|
956
|
+
console.log(chalk.bold('\nScanner Statistics:\n'));
|
|
957
|
+
const stats = data.statistics || data;
|
|
958
|
+
console.log(` Total Rules: ${chalk.cyan(stats.total_rules || 0)}`);
|
|
959
|
+
console.log(` Active Rules: ${chalk.green(stats.active_rules || stats.enabled || 0)}`);
|
|
960
|
+
console.log(` Total Scans: ${stats.total_scans || 0}`);
|
|
961
|
+
console.log(` Findings Today: ${chalk.yellow(stats.findings_today || 0)}`);
|
|
962
|
+
if (stats.by_severity) {
|
|
963
|
+
console.log(chalk.bold('\nRules by Severity:\n'));
|
|
964
|
+
const severityColors = {
|
|
965
|
+
critical: chalk.red,
|
|
966
|
+
high: chalk.yellow,
|
|
967
|
+
medium: chalk.blue,
|
|
968
|
+
low: chalk.dim
|
|
969
|
+
};
|
|
970
|
+
Object.entries(stats.by_severity).forEach(([sev, count]) => {
|
|
971
|
+
const color = severityColors[sev] || chalk.white;
|
|
972
|
+
console.log(` ${color(sev.toUpperCase().padEnd(10))} ${count}`);
|
|
973
|
+
});
|
|
974
|
+
}
|
|
975
|
+
if (stats.by_provider) {
|
|
976
|
+
console.log(chalk.bold('\nRules by Provider:\n'));
|
|
977
|
+
Object.entries(stats.by_provider).forEach(([prov, count]) => {
|
|
978
|
+
console.log(` ${chalk.cyan(prov.toUpperCase().padEnd(10))} ${count}`);
|
|
979
|
+
});
|
|
980
|
+
}
|
|
981
|
+
}
|
|
982
|
+
else if (options.remediate) {
|
|
983
|
+
// Execute remediation
|
|
984
|
+
if (!options.accountId) {
|
|
985
|
+
spinner.fail('--account-id required for remediation');
|
|
986
|
+
return;
|
|
987
|
+
}
|
|
988
|
+
spinner.text = 'Executing remediation...';
|
|
989
|
+
const data = await apiRequest('/v2/compliances/remediation-execution/execute/', {
|
|
990
|
+
method: 'POST',
|
|
991
|
+
body: JSON.stringify({
|
|
992
|
+
policy_id: options.remediate,
|
|
993
|
+
account_id: parseInt(options.accountId)
|
|
994
|
+
})
|
|
995
|
+
});
|
|
996
|
+
spinner.succeed('Remediation executed!');
|
|
997
|
+
console.log(chalk.bold('\nRemediation Results:\n'));
|
|
998
|
+
console.log(` Policy ID: ${chalk.cyan(options.remediate)}`);
|
|
999
|
+
console.log(` Account ID: ${options.accountId}`);
|
|
1000
|
+
console.log(` Status: ${chalk.green(data.status || 'completed')}`);
|
|
1001
|
+
console.log(` Resources: ${data.resources_affected || 0}`);
|
|
1002
|
+
if (data.changes && data.changes.length > 0) {
|
|
1003
|
+
console.log(chalk.bold('\nChanges Applied:\n'));
|
|
1004
|
+
data.changes.slice(0, 5).forEach((change) => {
|
|
1005
|
+
console.log(` ${chalk.green('✓')} ${change.description || change}`);
|
|
1006
|
+
});
|
|
1007
|
+
}
|
|
1008
|
+
}
|
|
1009
|
+
else if (options.remediatePreview) {
|
|
1010
|
+
// Preview remediation
|
|
1011
|
+
if (!options.accountId) {
|
|
1012
|
+
spinner.fail('--account-id required for remediation preview');
|
|
1013
|
+
return;
|
|
1014
|
+
}
|
|
1015
|
+
spinner.text = 'Generating remediation preview...';
|
|
1016
|
+
const data = await apiRequest('/v2/compliances/remediation-execution/preview/', {
|
|
1017
|
+
method: 'POST',
|
|
1018
|
+
body: JSON.stringify({
|
|
1019
|
+
policy_id: options.remediatePreview,
|
|
1020
|
+
account_id: parseInt(options.accountId)
|
|
1021
|
+
})
|
|
1022
|
+
});
|
|
1023
|
+
spinner.stop();
|
|
1024
|
+
console.log(chalk.bold('\nRemediation Preview:\n'));
|
|
1025
|
+
console.log(` Policy ID: ${chalk.cyan(options.remediatePreview)}`);
|
|
1026
|
+
console.log(` Account ID: ${options.accountId}`);
|
|
1027
|
+
console.log(` Resources: ${chalk.yellow(data.resources_affected || 0)}`);
|
|
1028
|
+
console.log(` Estimated Time: ${chalk.dim(data.estimated_time || 'N/A')}`);
|
|
1029
|
+
const changes = data.planned_changes || data.changes || [];
|
|
1030
|
+
if (changes.length > 0) {
|
|
1031
|
+
console.log(chalk.bold('\nPlanned Changes:\n'));
|
|
1032
|
+
changes.slice(0, 10).forEach((change) => {
|
|
1033
|
+
console.log(` ${chalk.yellow('→')} ${change.description || change.action || change}`);
|
|
1034
|
+
if (change.resource) {
|
|
1035
|
+
console.log(chalk.dim(` Resource: ${change.resource}`));
|
|
1036
|
+
}
|
|
1037
|
+
});
|
|
1038
|
+
}
|
|
1039
|
+
console.log(chalk.dim('\nRun with --remediate to apply these changes'));
|
|
757
1040
|
}
|
|
758
1041
|
else {
|
|
759
1042
|
spinner.stop();
|
|
760
|
-
console.log(chalk.
|
|
1043
|
+
console.log(chalk.bold('Cloud Security Posture Management (CSPM/CNAPP)\n'));
|
|
1044
|
+
console.log(chalk.bold('Basic Commands'));
|
|
1045
|
+
console.log(` ${chalk.cyan('aribot cloud-security --scan')} Scan all cloud accounts`);
|
|
1046
|
+
console.log(` ${chalk.cyan('aribot cloud-security --scan aws')} Scan AWS only`);
|
|
1047
|
+
console.log(` ${chalk.cyan('aribot cloud-security --findings')} List security findings`);
|
|
1048
|
+
console.log(` ${chalk.cyan('aribot cloud-security --dashboard')} Show security dashboard`);
|
|
1049
|
+
console.log(chalk.bold('\nScanner Commands'));
|
|
1050
|
+
console.log(` ${chalk.green('aribot cloud-security --dynamic-scan <account-id>')} Run dynamic scan`);
|
|
1051
|
+
console.log(` ${chalk.green('aribot cloud-security --unified-scan --scope account --account-id 123')}`);
|
|
1052
|
+
console.log(` ${chalk.green('aribot cloud-security --unified-scan --scope standard --scope-id CIS-AWS')}`);
|
|
1053
|
+
console.log(` ${chalk.green('aribot cloud-security --rules')} List scanner rules`);
|
|
1054
|
+
console.log(` ${chalk.green('aribot cloud-security --create-rule')} Create custom rule`);
|
|
1055
|
+
console.log(` ${chalk.green('aribot cloud-security --sync-rules')} Sync from providers`);
|
|
1056
|
+
console.log(` ${chalk.green('aribot cloud-security --scanner-stats')} Show statistics`);
|
|
1057
|
+
console.log(chalk.bold('\nRemediation Commands'));
|
|
1058
|
+
console.log(` ${chalk.yellow('aribot cloud-security --remediate-preview <policy-id> --account-id 123')}`);
|
|
1059
|
+
console.log(` ${chalk.yellow('aribot cloud-security --remediate <policy-id> --account-id 123')}`);
|
|
761
1060
|
}
|
|
762
1061
|
}
|
|
763
1062
|
catch (error) {
|
|
@@ -787,8 +1086,44 @@ program
|
|
|
787
1086
|
.action(async (options) => {
|
|
788
1087
|
if (options.methodologies) {
|
|
789
1088
|
const spinner = ora('Fetching methodologies...').start();
|
|
1089
|
+
// Static fallback data for when backend engine is unavailable
|
|
1090
|
+
const fallbackData = {
|
|
1091
|
+
available_methodologies: [
|
|
1092
|
+
{ name: 'STRIDE', description: 'Spoofing, Tampering, Repudiation, Info Disclosure, DoS, Elevation' },
|
|
1093
|
+
{ name: 'PASTA', description: 'Process for Attack Simulation and Threat Analysis' },
|
|
1094
|
+
{ name: 'NIST', description: 'NIST Cybersecurity Framework threat modeling' },
|
|
1095
|
+
{ name: 'MITRE ATT&CK', description: 'Adversarial tactics, techniques, and common knowledge' },
|
|
1096
|
+
{ name: 'OWASP', description: 'Open Web Application Security Project methodology' },
|
|
1097
|
+
{ name: 'AI_ENHANCED', description: 'AI-powered predictive threat analysis' },
|
|
1098
|
+
],
|
|
1099
|
+
risk_levels: [
|
|
1100
|
+
{ name: 'Critical', description: 'Immediate action required - system compromise likely' },
|
|
1101
|
+
{ name: 'High', description: 'Significant risk - address within 24-48 hours' },
|
|
1102
|
+
{ name: 'Medium', description: 'Moderate risk - address within 1-2 weeks' },
|
|
1103
|
+
{ name: 'Low', description: 'Minor risk - address in regular maintenance' },
|
|
1104
|
+
{ name: 'Info', description: 'Informational finding - no immediate action needed' },
|
|
1105
|
+
],
|
|
1106
|
+
compliance_frameworks: [
|
|
1107
|
+
{ name: 'SOC2', description: 'Service Organization Control 2' },
|
|
1108
|
+
{ name: 'ISO27001', description: 'Information Security Management' },
|
|
1109
|
+
{ name: 'PCI-DSS', description: 'Payment Card Industry Data Security Standard' },
|
|
1110
|
+
{ name: 'GDPR', description: 'General Data Protection Regulation' },
|
|
1111
|
+
{ name: 'HIPAA', description: 'Health Insurance Portability and Accountability' },
|
|
1112
|
+
{ name: 'NIST-CSF', description: 'NIST Cybersecurity Framework' },
|
|
1113
|
+
{ name: 'CIS', description: 'Center for Internet Security Controls' },
|
|
1114
|
+
{ name: 'FedRAMP', description: 'Federal Risk and Authorization Management' },
|
|
1115
|
+
],
|
|
1116
|
+
engine_capabilities: {
|
|
1117
|
+
multi_framework_analysis: true,
|
|
1118
|
+
ai_threat_prediction: true,
|
|
1119
|
+
real_time_scanning: true,
|
|
1120
|
+
attack_path_analysis: true,
|
|
1121
|
+
compliance_mapping: true,
|
|
1122
|
+
automated_remediation: true,
|
|
1123
|
+
}
|
|
1124
|
+
};
|
|
790
1125
|
try {
|
|
791
|
-
const data = await apiRequest('/v2/threat-engine/threat-models/');
|
|
1126
|
+
const data = await apiRequest('/v2/threat-modeling/threat-engine/threat-models/');
|
|
792
1127
|
spinner.stop();
|
|
793
1128
|
console.log(chalk.bold('\nThreat Modeling Methodologies:\n'));
|
|
794
1129
|
(data.available_methodologies || []).forEach((m) => {
|
|
@@ -809,15 +1144,38 @@ program
|
|
|
809
1144
|
});
|
|
810
1145
|
}
|
|
811
1146
|
catch (error) {
|
|
812
|
-
|
|
813
|
-
|
|
1147
|
+
// Use fallback data for 404/503 errors (endpoint not deployed or engine unavailable)
|
|
1148
|
+
if (error.message?.includes('404') || error.message?.includes('503') || error.message?.includes('unavailable') || error.message?.includes('Not Found')) {
|
|
1149
|
+
spinner.stop();
|
|
1150
|
+
console.log(chalk.dim('(Using cached methodology data)\n'));
|
|
1151
|
+
console.log(chalk.bold('\nThreat Modeling Methodologies:\n'));
|
|
1152
|
+
fallbackData.available_methodologies.forEach((m) => {
|
|
1153
|
+
console.log(` ${chalk.cyan(m.name.toUpperCase().padEnd(14))} ${chalk.dim(m.description)}`);
|
|
1154
|
+
});
|
|
1155
|
+
console.log(chalk.bold('\nRisk Levels:\n'));
|
|
1156
|
+
fallbackData.risk_levels.forEach((r) => {
|
|
1157
|
+
console.log(` ${chalk.yellow(r.name.padEnd(12))} ${chalk.dim(r.description)}`);
|
|
1158
|
+
});
|
|
1159
|
+
console.log(chalk.bold('\nCompliance Frameworks:\n'));
|
|
1160
|
+
fallbackData.compliance_frameworks.forEach((f) => {
|
|
1161
|
+
console.log(` ${chalk.green(f.name.padEnd(12))} ${chalk.dim(f.description)}`);
|
|
1162
|
+
});
|
|
1163
|
+
console.log(chalk.bold('\nEngine Capabilities:\n'));
|
|
1164
|
+
Object.entries(fallbackData.engine_capabilities).forEach(([key, value]) => {
|
|
1165
|
+
console.log(` ${value ? chalk.green('✓') : chalk.red('✗')} ${key.replace(/_/g, ' ')}`);
|
|
1166
|
+
});
|
|
1167
|
+
}
|
|
1168
|
+
else {
|
|
1169
|
+
spinner.fail('Failed to fetch methodologies');
|
|
1170
|
+
console.error(error);
|
|
1171
|
+
}
|
|
814
1172
|
}
|
|
815
1173
|
return;
|
|
816
1174
|
}
|
|
817
1175
|
if (options.intelligence) {
|
|
818
1176
|
const spinner = ora('Fetching threat intelligence...').start();
|
|
819
1177
|
try {
|
|
820
|
-
const data = await apiRequest('/v2/threat-engine/threat-intelligence/');
|
|
1178
|
+
const data = await apiRequest('/v2/threat-modeling/threat-engine/threat-intelligence/');
|
|
821
1179
|
spinner.stop();
|
|
822
1180
|
console.log(chalk.bold('\nThreat Intelligence Summary:\n'));
|
|
823
1181
|
const intel = data.threat_intelligence || {};
|
|
@@ -867,34 +1225,36 @@ program
|
|
|
867
1225
|
console.log(chalk.dim('Upload a diagram with at least 2 connected components.'));
|
|
868
1226
|
return;
|
|
869
1227
|
}
|
|
870
|
-
// Generate attack paths
|
|
871
|
-
const data = await apiRequest(
|
|
1228
|
+
// Generate attack paths using the correct v2 endpoint (same as frontend)
|
|
1229
|
+
const data = await apiRequest(`/v2/threat-modeling/diagrams/${fullId}/generate-attack-paths/`, {
|
|
872
1230
|
method: 'POST',
|
|
873
1231
|
body: JSON.stringify({
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
target_node: components[components.length - 1]?.id || ''
|
|
1232
|
+
scope: 'single',
|
|
1233
|
+
include_compliance: true,
|
|
1234
|
+
save: true
|
|
878
1235
|
})
|
|
879
1236
|
});
|
|
880
1237
|
spinner.succeed('Attack path analysis complete!');
|
|
1238
|
+
// Response matches frontend: { status: 'success', paths: [...] }
|
|
1239
|
+
const paths = data.paths || data.attack_paths || [];
|
|
881
1240
|
console.log(chalk.bold('\nAttack Path Analysis:\n'));
|
|
882
|
-
console.log(`
|
|
883
|
-
console.log(`
|
|
884
|
-
console.log(` Paths Found: ${chalk.yellow(data.analysis_summary?.total_paths_found || 0)}`);
|
|
885
|
-
const paths = data.attack_paths || [];
|
|
1241
|
+
console.log(` Paths Found: ${chalk.yellow(paths.length)}`);
|
|
1242
|
+
console.log(` Status: ${chalk.cyan(data.status || 'completed')}`);
|
|
886
1243
|
if (paths.length > 0) {
|
|
887
1244
|
console.log(chalk.bold('\nIdentified Attack Paths:\n'));
|
|
888
1245
|
paths.slice(0, 5).forEach((p, i) => {
|
|
889
|
-
const
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
console.log(`
|
|
893
|
-
console.log(`
|
|
894
|
-
console.log(`
|
|
1246
|
+
const riskScore = p.risk_score || 0;
|
|
1247
|
+
const riskColor = riskScore >= 80 ? chalk.red : riskScore >= 60 ? chalk.yellow : chalk.green;
|
|
1248
|
+
const riskLabel = riskScore >= 80 ? 'CRITICAL' : riskScore >= 60 ? 'HIGH' : riskScore >= 40 ? 'MEDIUM' : 'LOW';
|
|
1249
|
+
console.log(` ${chalk.bold(`Path ${i + 1}:`)} ${p.title || 'Attack Path'}`);
|
|
1250
|
+
console.log(` Risk: ${riskColor(riskLabel)} (${riskScore}%)`);
|
|
1251
|
+
console.log(` Description: ${chalk.dim(p.description || 'N/A')}`);
|
|
1252
|
+
console.log(` Steps: ${chalk.cyan(p.steps?.length || 0)}`);
|
|
1253
|
+
console.log(` Mitigations: ${chalk.green(p.mitigations?.length || 0)}`);
|
|
895
1254
|
if (p.steps?.length > 0) {
|
|
1255
|
+
console.log(chalk.dim(' Attack Chain:'));
|
|
896
1256
|
p.steps.forEach((s, j) => {
|
|
897
|
-
console.log(chalk.dim(` ${j + 1}. ${s.from_node} → ${s.to_node}`));
|
|
1257
|
+
console.log(chalk.dim(` ${j + 1}. ${s.action || s.from_node} → ${s.target_component_name || s.to_node}`));
|
|
898
1258
|
});
|
|
899
1259
|
}
|
|
900
1260
|
console.log();
|
|
@@ -966,7 +1326,7 @@ program
|
|
|
966
1326
|
severity: t.severity,
|
|
967
1327
|
category: t.category
|
|
968
1328
|
}));
|
|
969
|
-
const data = await apiRequest('/v2/threat-engine/security-requirements/', {
|
|
1329
|
+
const data = await apiRequest('/v2/threat-modeling/threat-engine/security-requirements/', {
|
|
970
1330
|
method: 'POST',
|
|
971
1331
|
body: JSON.stringify({
|
|
972
1332
|
threats,
|
|
@@ -1627,17 +1987,17 @@ program
|
|
|
1627
1987
|
const spinner = ora('Processing...').start();
|
|
1628
1988
|
try {
|
|
1629
1989
|
if (options.providers) {
|
|
1630
|
-
const data = await apiRequest('/v2/digital-twin/providers/');
|
|
1990
|
+
const data = await apiRequest('/v2/threat-modeling/digital-twin/providers/');
|
|
1631
1991
|
spinner.stop();
|
|
1632
1992
|
console.log(chalk.bold('\nCloud Providers:\n'));
|
|
1633
1993
|
(data.results || data || []).forEach((p) => {
|
|
1634
1994
|
const status = p.connected ? chalk.green('✓') : chalk.red('✗');
|
|
1635
|
-
console.log(` ${status} ${chalk.cyan(p.name)} ${chalk.dim(p.provider_type || p.type)}`);
|
|
1995
|
+
console.log(` ${status} ${chalk.cyan(p.name)} ${chalk.dim(p.display_name || p.provider_type || p.type || '')}`);
|
|
1636
1996
|
});
|
|
1637
1997
|
}
|
|
1638
1998
|
else if (options.resources) {
|
|
1639
1999
|
const provider = typeof options.resources === 'string' ? `?provider=${options.resources}` : '';
|
|
1640
|
-
const data = await apiRequest(`/v2/digital-twin/resources/${provider}`);
|
|
2000
|
+
const data = await apiRequest(`/v2/threat-modeling/digital-twin/resources/${provider}`);
|
|
1641
2001
|
spinner.stop();
|
|
1642
2002
|
console.log(chalk.bold('\nCloud Resources:\n'));
|
|
1643
2003
|
(data.results || data || []).slice(0, 20).forEach((r) => {
|
|
@@ -1645,7 +2005,7 @@ program
|
|
|
1645
2005
|
});
|
|
1646
2006
|
}
|
|
1647
2007
|
else if (options.sync) {
|
|
1648
|
-
const data = await apiRequest('/v2/digital-twin/sync/', {
|
|
2008
|
+
const data = await apiRequest('/v2/threat-modeling/digital-twin/sync/', {
|
|
1649
2009
|
method: 'POST',
|
|
1650
2010
|
body: JSON.stringify({ provider_id: options.sync })
|
|
1651
2011
|
});
|
|
@@ -1653,7 +2013,7 @@ program
|
|
|
1653
2013
|
console.log(` Resources discovered: ${chalk.cyan(data.resources_found || 0)}`);
|
|
1654
2014
|
}
|
|
1655
2015
|
else if (options.discover) {
|
|
1656
|
-
const data = await apiRequest('/v2/digital-twin/discover/', {
|
|
2016
|
+
const data = await apiRequest('/v2/threat-modeling/digital-twin/discover/', {
|
|
1657
2017
|
method: 'POST',
|
|
1658
2018
|
body: JSON.stringify({ provider_id: options.discover })
|
|
1659
2019
|
});
|
|
@@ -1661,7 +2021,7 @@ program
|
|
|
1661
2021
|
console.log(` Resources found: ${chalk.cyan(data.resources_count || 0)}`);
|
|
1662
2022
|
}
|
|
1663
2023
|
else if (options.health) {
|
|
1664
|
-
const data = await apiRequest('/v2/digital-twin/health/');
|
|
2024
|
+
const data = await apiRequest('/v2/threat-modeling/digital-twin/health/');
|
|
1665
2025
|
spinner.stop();
|
|
1666
2026
|
console.log(chalk.bold('\nDigital Twin Health:\n'));
|
|
1667
2027
|
console.log(` Status: ${data.healthy ? chalk.green('Healthy') : chalk.red('Unhealthy')}`);
|
|
@@ -1881,29 +2241,38 @@ program
|
|
|
1881
2241
|
const spinner = ora('Loading dashboard...').start();
|
|
1882
2242
|
try {
|
|
1883
2243
|
if (options.overview || (!options.recent && !options.risk && !options.billing)) {
|
|
1884
|
-
|
|
2244
|
+
// Use v1 dashboard endpoint which exists
|
|
2245
|
+
const data = await apiRequest('/v1/tm/dashboard/status-progress-compliance/');
|
|
1885
2246
|
spinner.stop();
|
|
1886
2247
|
console.log(chalk.bold('\nDashboard Overview:\n'));
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
console.log(`
|
|
1890
|
-
console.log(`
|
|
2248
|
+
const overall = data.overall_compliance || {};
|
|
2249
|
+
const status = overall.status || {};
|
|
2250
|
+
console.log(` Unresolved: ${chalk.yellow(status.unresolved || 0)}`);
|
|
2251
|
+
console.log(` In Progress: ${chalk.cyan(status.in_progress || 0)}`);
|
|
2252
|
+
console.log(` Resolved: ${chalk.green(status.resolved || 0)}`);
|
|
2253
|
+
console.log(` With Resource: ${chalk.blue((overall.with_resource || 0) + '%')}`);
|
|
1891
2254
|
}
|
|
1892
2255
|
else if (options.recent) {
|
|
1893
|
-
|
|
2256
|
+
// Use v1 dashboard activity endpoint
|
|
2257
|
+
const data = await apiRequest('/v1/tm/dashboard/status-progress-compliance/');
|
|
1894
2258
|
spinner.stop();
|
|
1895
2259
|
console.log(chalk.bold('\nRecent Activity:\n'));
|
|
1896
|
-
|
|
1897
|
-
|
|
2260
|
+
const progress = data.status_progress || [];
|
|
2261
|
+
progress.slice(0, 7).forEach((p) => {
|
|
2262
|
+
console.log(` ${chalk.dim(p.date)} (${p.day}) Unresolved: ${chalk.yellow(p.statuses?.unresolved || 0)} Resolved: ${chalk.green(p.statuses?.resolved || 0)}`);
|
|
1898
2263
|
});
|
|
1899
2264
|
}
|
|
1900
2265
|
else if (options.risk) {
|
|
1901
|
-
|
|
2266
|
+
// Use v1 dashboard for risk summary
|
|
2267
|
+
const data = await apiRequest('/v1/tm/dashboard/status-progress-compliance/');
|
|
1902
2268
|
spinner.stop();
|
|
1903
2269
|
console.log(chalk.bold('\nRisk Summary:\n'));
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
2270
|
+
const status = data.overall_compliance?.status || {};
|
|
2271
|
+
const total = (status.unresolved || 0) + (status.in_progress || 0) + (status.resolved || 0);
|
|
2272
|
+
const riskLevel = status.unresolved > total * 0.5 ? 'high' : status.unresolved > total * 0.2 ? 'medium' : 'low';
|
|
2273
|
+
console.log(` Overall Risk: ${riskLevel === 'high' ? chalk.red(riskLevel) : chalk.yellow(riskLevel)}`);
|
|
2274
|
+
console.log(` Open Threats: ${chalk.yellow(status.unresolved || 0)}`);
|
|
2275
|
+
console.log(` Mitigated: ${chalk.green(status.resolved || 0)}`);
|
|
1907
2276
|
}
|
|
1908
2277
|
else if (options.billing) {
|
|
1909
2278
|
const data = await apiRequest('/v1/developer/billing/');
|