runbooks 1.1.4__py3-none-any.whl → 1.1.6__py3-none-any.whl
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.
- runbooks/__init__.py +31 -2
- runbooks/__init___optimized.py +18 -4
- runbooks/_platform/__init__.py +1 -5
- runbooks/_platform/core/runbooks_wrapper.py +141 -138
- runbooks/aws2/accuracy_validator.py +812 -0
- runbooks/base.py +7 -0
- runbooks/cfat/assessment/compliance.py +1 -1
- runbooks/cfat/assessment/runner.py +1 -0
- runbooks/cfat/cloud_foundations_assessment.py +227 -239
- runbooks/cli/__init__.py +1 -1
- runbooks/cli/commands/cfat.py +64 -23
- runbooks/cli/commands/finops.py +1005 -54
- runbooks/cli/commands/inventory.py +135 -91
- runbooks/cli/commands/operate.py +9 -36
- runbooks/cli/commands/security.py +42 -18
- runbooks/cli/commands/validation.py +432 -18
- runbooks/cli/commands/vpc.py +81 -17
- runbooks/cli/registry.py +22 -10
- runbooks/cloudops/__init__.py +20 -27
- runbooks/cloudops/base.py +96 -107
- runbooks/cloudops/cost_optimizer.py +544 -542
- runbooks/cloudops/infrastructure_optimizer.py +5 -4
- runbooks/cloudops/interfaces.py +224 -225
- runbooks/cloudops/lifecycle_manager.py +5 -4
- runbooks/cloudops/mcp_cost_validation.py +252 -235
- runbooks/cloudops/models.py +78 -53
- runbooks/cloudops/monitoring_automation.py +5 -4
- runbooks/cloudops/notebook_framework.py +177 -213
- runbooks/cloudops/security_enforcer.py +125 -159
- runbooks/common/accuracy_validator.py +17 -12
- runbooks/common/aws_pricing.py +349 -326
- runbooks/common/aws_pricing_api.py +211 -212
- runbooks/common/aws_profile_manager.py +40 -36
- runbooks/common/aws_utils.py +74 -79
- runbooks/common/business_logic.py +126 -104
- runbooks/common/cli_decorators.py +36 -60
- runbooks/common/comprehensive_cost_explorer_integration.py +455 -463
- runbooks/common/cross_account_manager.py +197 -204
- runbooks/common/date_utils.py +27 -39
- runbooks/common/decorators.py +29 -19
- runbooks/common/dry_run_examples.py +173 -208
- runbooks/common/dry_run_framework.py +157 -155
- runbooks/common/enhanced_exception_handler.py +15 -4
- runbooks/common/enhanced_logging_example.py +50 -64
- runbooks/common/enhanced_logging_integration_example.py +65 -37
- runbooks/common/env_utils.py +16 -16
- runbooks/common/error_handling.py +40 -38
- runbooks/common/lazy_loader.py +41 -23
- runbooks/common/logging_integration_helper.py +79 -86
- runbooks/common/mcp_cost_explorer_integration.py +476 -493
- runbooks/common/mcp_integration.py +99 -79
- runbooks/common/memory_optimization.py +140 -118
- runbooks/common/module_cli_base.py +37 -58
- runbooks/common/organizations_client.py +175 -193
- runbooks/common/patterns.py +23 -25
- runbooks/common/performance_monitoring.py +67 -71
- runbooks/common/performance_optimization_engine.py +283 -274
- runbooks/common/profile_utils.py +111 -37
- runbooks/common/rich_utils.py +315 -141
- runbooks/common/sre_performance_suite.py +177 -186
- runbooks/enterprise/__init__.py +1 -1
- runbooks/enterprise/logging.py +144 -106
- runbooks/enterprise/security.py +187 -204
- runbooks/enterprise/validation.py +43 -56
- runbooks/finops/__init__.py +26 -30
- runbooks/finops/account_resolver.py +1 -1
- runbooks/finops/advanced_optimization_engine.py +980 -0
- runbooks/finops/automation_core.py +268 -231
- runbooks/finops/business_case_config.py +184 -179
- runbooks/finops/cli.py +660 -139
- runbooks/finops/commvault_ec2_analysis.py +157 -164
- runbooks/finops/compute_cost_optimizer.py +336 -320
- runbooks/finops/config.py +20 -20
- runbooks/finops/cost_optimizer.py +484 -618
- runbooks/finops/cost_processor.py +332 -214
- runbooks/finops/dashboard_runner.py +1006 -172
- runbooks/finops/ebs_cost_optimizer.py +991 -657
- runbooks/finops/elastic_ip_optimizer.py +317 -257
- runbooks/finops/enhanced_mcp_integration.py +340 -0
- runbooks/finops/enhanced_progress.py +32 -29
- runbooks/finops/enhanced_trend_visualization.py +3 -2
- runbooks/finops/enterprise_wrappers.py +223 -285
- runbooks/finops/executive_export.py +203 -160
- runbooks/finops/helpers.py +130 -288
- runbooks/finops/iam_guidance.py +1 -1
- runbooks/finops/infrastructure/__init__.py +80 -0
- runbooks/finops/infrastructure/commands.py +506 -0
- runbooks/finops/infrastructure/load_balancer_optimizer.py +866 -0
- runbooks/finops/infrastructure/vpc_endpoint_optimizer.py +832 -0
- runbooks/finops/markdown_exporter.py +337 -174
- runbooks/finops/mcp_validator.py +1952 -0
- runbooks/finops/nat_gateway_optimizer.py +1512 -481
- runbooks/finops/network_cost_optimizer.py +657 -587
- runbooks/finops/notebook_utils.py +226 -188
- runbooks/finops/optimization_engine.py +1136 -0
- runbooks/finops/optimizer.py +19 -23
- runbooks/finops/rds_snapshot_optimizer.py +367 -411
- runbooks/finops/reservation_optimizer.py +427 -363
- runbooks/finops/scenario_cli_integration.py +64 -65
- runbooks/finops/scenarios.py +1277 -438
- runbooks/finops/schemas.py +218 -182
- runbooks/finops/snapshot_manager.py +2289 -0
- runbooks/finops/types.py +3 -3
- runbooks/finops/validation_framework.py +259 -265
- runbooks/finops/vpc_cleanup_exporter.py +189 -144
- runbooks/finops/vpc_cleanup_optimizer.py +591 -573
- runbooks/finops/workspaces_analyzer.py +171 -182
- runbooks/integration/__init__.py +89 -0
- runbooks/integration/mcp_integration.py +1920 -0
- runbooks/inventory/CLAUDE.md +816 -0
- runbooks/inventory/__init__.py +2 -2
- runbooks/inventory/aws_decorators.py +2 -3
- runbooks/inventory/check_cloudtrail_compliance.py +2 -4
- runbooks/inventory/check_controltower_readiness.py +152 -151
- runbooks/inventory/check_landingzone_readiness.py +85 -84
- runbooks/inventory/cloud_foundations_integration.py +144 -149
- runbooks/inventory/collectors/aws_comprehensive.py +1 -1
- runbooks/inventory/collectors/aws_networking.py +109 -99
- runbooks/inventory/collectors/base.py +4 -0
- runbooks/inventory/core/collector.py +495 -313
- runbooks/inventory/core/formatter.py +11 -0
- runbooks/inventory/draw_org_structure.py +8 -9
- runbooks/inventory/drift_detection_cli.py +69 -96
- runbooks/inventory/ec2_vpc_utils.py +2 -2
- runbooks/inventory/find_cfn_drift_detection.py +5 -7
- runbooks/inventory/find_cfn_orphaned_stacks.py +7 -9
- runbooks/inventory/find_cfn_stackset_drift.py +5 -6
- runbooks/inventory/find_ec2_security_groups.py +48 -42
- runbooks/inventory/find_landingzone_versions.py +4 -6
- runbooks/inventory/find_vpc_flow_logs.py +7 -9
- runbooks/inventory/inventory_mcp_cli.py +48 -46
- runbooks/inventory/inventory_modules.py +103 -91
- runbooks/inventory/list_cfn_stacks.py +9 -10
- runbooks/inventory/list_cfn_stackset_operation_results.py +1 -3
- runbooks/inventory/list_cfn_stackset_operations.py +79 -57
- runbooks/inventory/list_cfn_stacksets.py +8 -10
- runbooks/inventory/list_config_recorders_delivery_channels.py +49 -39
- runbooks/inventory/list_ds_directories.py +65 -53
- runbooks/inventory/list_ec2_availability_zones.py +2 -4
- runbooks/inventory/list_ec2_ebs_volumes.py +32 -35
- runbooks/inventory/list_ec2_instances.py +23 -28
- runbooks/inventory/list_ecs_clusters_and_tasks.py +26 -34
- runbooks/inventory/list_elbs_load_balancers.py +22 -20
- runbooks/inventory/list_enis_network_interfaces.py +26 -33
- runbooks/inventory/list_guardduty_detectors.py +2 -4
- runbooks/inventory/list_iam_policies.py +2 -4
- runbooks/inventory/list_iam_roles.py +5 -7
- runbooks/inventory/list_iam_saml_providers.py +4 -6
- runbooks/inventory/list_lambda_functions.py +38 -38
- runbooks/inventory/list_org_accounts.py +6 -8
- runbooks/inventory/list_org_accounts_users.py +55 -44
- runbooks/inventory/list_rds_db_instances.py +31 -33
- runbooks/inventory/list_rds_snapshots_aggregator.py +192 -208
- runbooks/inventory/list_route53_hosted_zones.py +3 -5
- runbooks/inventory/list_servicecatalog_provisioned_products.py +37 -41
- runbooks/inventory/list_sns_topics.py +2 -4
- runbooks/inventory/list_ssm_parameters.py +4 -7
- runbooks/inventory/list_vpc_subnets.py +2 -4
- runbooks/inventory/list_vpcs.py +7 -10
- runbooks/inventory/mcp_inventory_validator.py +554 -468
- runbooks/inventory/mcp_vpc_validator.py +359 -442
- runbooks/inventory/organizations_discovery.py +63 -55
- runbooks/inventory/recover_cfn_stack_ids.py +7 -8
- runbooks/inventory/requirements.txt +0 -1
- runbooks/inventory/rich_inventory_display.py +35 -34
- runbooks/inventory/run_on_multi_accounts.py +3 -5
- runbooks/inventory/unified_validation_engine.py +281 -253
- runbooks/inventory/verify_ec2_security_groups.py +1 -1
- runbooks/inventory/vpc_analyzer.py +735 -697
- runbooks/inventory/vpc_architecture_validator.py +293 -348
- runbooks/inventory/vpc_dependency_analyzer.py +384 -380
- runbooks/inventory/vpc_flow_analyzer.py +1 -1
- runbooks/main.py +49 -34
- runbooks/main_final.py +91 -60
- runbooks/main_minimal.py +22 -10
- runbooks/main_optimized.py +131 -100
- runbooks/main_ultra_minimal.py +7 -2
- runbooks/mcp/__init__.py +36 -0
- runbooks/mcp/integration.py +679 -0
- runbooks/monitoring/performance_monitor.py +9 -4
- runbooks/operate/dynamodb_operations.py +3 -1
- runbooks/operate/ec2_operations.py +145 -137
- runbooks/operate/iam_operations.py +146 -152
- runbooks/operate/networking_cost_heatmap.py +29 -8
- runbooks/operate/rds_operations.py +223 -254
- runbooks/operate/s3_operations.py +107 -118
- runbooks/operate/vpc_operations.py +646 -616
- runbooks/remediation/base.py +1 -1
- runbooks/remediation/commons.py +10 -7
- runbooks/remediation/commvault_ec2_analysis.py +70 -66
- runbooks/remediation/ec2_unattached_ebs_volumes.py +1 -0
- runbooks/remediation/multi_account.py +24 -21
- runbooks/remediation/rds_snapshot_list.py +86 -60
- runbooks/remediation/remediation_cli.py +92 -146
- runbooks/remediation/universal_account_discovery.py +83 -79
- runbooks/remediation/workspaces_list.py +46 -41
- runbooks/security/__init__.py +19 -0
- runbooks/security/assessment_runner.py +1150 -0
- runbooks/security/baseline_checker.py +812 -0
- runbooks/security/cloudops_automation_security_validator.py +509 -535
- runbooks/security/compliance_automation_engine.py +17 -17
- runbooks/security/config/__init__.py +2 -2
- runbooks/security/config/compliance_config.py +50 -50
- runbooks/security/config_template_generator.py +63 -76
- runbooks/security/enterprise_security_framework.py +1 -1
- runbooks/security/executive_security_dashboard.py +519 -508
- runbooks/security/multi_account_security_controls.py +959 -1210
- runbooks/security/real_time_security_monitor.py +422 -444
- runbooks/security/security_baseline_tester.py +1 -1
- runbooks/security/security_cli.py +143 -112
- runbooks/security/test_2way_validation.py +439 -0
- runbooks/security/two_way_validation_framework.py +852 -0
- runbooks/sre/production_monitoring_framework.py +167 -177
- runbooks/tdd/__init__.py +15 -0
- runbooks/tdd/cli.py +1071 -0
- runbooks/utils/__init__.py +14 -17
- runbooks/utils/logger.py +7 -2
- runbooks/utils/version_validator.py +50 -47
- runbooks/validation/__init__.py +6 -6
- runbooks/validation/cli.py +9 -3
- runbooks/validation/comprehensive_2way_validator.py +745 -704
- runbooks/validation/mcp_validator.py +906 -228
- runbooks/validation/terraform_citations_validator.py +104 -115
- runbooks/validation/terraform_drift_detector.py +461 -454
- runbooks/vpc/README.md +617 -0
- runbooks/vpc/__init__.py +8 -1
- runbooks/vpc/analyzer.py +577 -0
- runbooks/vpc/cleanup_wrapper.py +476 -413
- runbooks/vpc/cli_cloudtrail_commands.py +339 -0
- runbooks/vpc/cli_mcp_validation_commands.py +480 -0
- runbooks/vpc/cloudtrail_audit_integration.py +717 -0
- runbooks/vpc/config.py +92 -97
- runbooks/vpc/cost_engine.py +411 -148
- runbooks/vpc/cost_explorer_integration.py +553 -0
- runbooks/vpc/cross_account_session.py +101 -106
- runbooks/vpc/enhanced_mcp_validation.py +917 -0
- runbooks/vpc/eni_gate_validator.py +961 -0
- runbooks/vpc/heatmap_engine.py +185 -160
- runbooks/vpc/mcp_no_eni_validator.py +680 -639
- runbooks/vpc/nat_gateway_optimizer.py +358 -0
- runbooks/vpc/networking_wrapper.py +15 -8
- runbooks/vpc/pdca_remediation_planner.py +528 -0
- runbooks/vpc/performance_optimized_analyzer.py +219 -231
- runbooks/vpc/runbooks_adapter.py +1167 -241
- runbooks/vpc/tdd_red_phase_stubs.py +601 -0
- runbooks/vpc/test_data_loader.py +358 -0
- runbooks/vpc/tests/conftest.py +314 -4
- runbooks/vpc/tests/test_cleanup_framework.py +1022 -0
- runbooks/vpc/tests/test_cost_engine.py +0 -2
- runbooks/vpc/topology_generator.py +326 -0
- runbooks/vpc/unified_scenarios.py +1297 -1124
- runbooks/vpc/vpc_cleanup_integration.py +1943 -1115
- runbooks-1.1.6.dist-info/METADATA +327 -0
- runbooks-1.1.6.dist-info/RECORD +489 -0
- runbooks/finops/README.md +0 -414
- runbooks/finops/accuracy_cross_validator.py +0 -647
- runbooks/finops/business_cases.py +0 -950
- runbooks/finops/dashboard_router.py +0 -922
- runbooks/finops/ebs_optimizer.py +0 -973
- runbooks/finops/embedded_mcp_validator.py +0 -1629
- runbooks/finops/enhanced_dashboard_runner.py +0 -527
- runbooks/finops/finops_dashboard.py +0 -584
- runbooks/finops/finops_scenarios.py +0 -1218
- runbooks/finops/legacy_migration.py +0 -730
- runbooks/finops/multi_dashboard.py +0 -1519
- runbooks/finops/single_dashboard.py +0 -1113
- runbooks/finops/unlimited_scenarios.py +0 -393
- runbooks-1.1.4.dist-info/METADATA +0 -800
- runbooks-1.1.4.dist-info/RECORD +0 -468
- {runbooks-1.1.4.dist-info → runbooks-1.1.6.dist-info}/WHEEL +0 -0
- {runbooks-1.1.4.dist-info → runbooks-1.1.6.dist-info}/entry_points.txt +0 -0
- {runbooks-1.1.4.dist-info → runbooks-1.1.6.dist-info}/licenses/LICENSE +0 -0
- {runbooks-1.1.4.dist-info → runbooks-1.1.6.dist-info}/top_level.txt +0 -0
@@ -31,8 +31,14 @@ from botocore.exceptions import ClientError
|
|
31
31
|
|
32
32
|
# Import runbooks enterprise standards
|
33
33
|
from runbooks.common.rich_utils import (
|
34
|
-
console,
|
35
|
-
|
34
|
+
console,
|
35
|
+
print_header,
|
36
|
+
print_success,
|
37
|
+
print_warning,
|
38
|
+
print_error,
|
39
|
+
create_table,
|
40
|
+
create_progress_bar,
|
41
|
+
create_panel,
|
36
42
|
)
|
37
43
|
from runbooks.common.profile_utils import get_profile_for_operation
|
38
44
|
|
@@ -44,6 +50,7 @@ class CFATFinding:
|
|
44
50
|
"""
|
45
51
|
CFAT assessment finding compatible with both Python and JavaScript engines
|
46
52
|
"""
|
53
|
+
|
47
54
|
check_id: str
|
48
55
|
category: str
|
49
56
|
severity: str # HIGH, MEDIUM, LOW
|
@@ -53,28 +60,29 @@ class CFATFinding:
|
|
53
60
|
estimated_effort: str # HOURS, DAYS, WEEKS
|
54
61
|
compliance_frameworks: List[str] = field(default_factory=list)
|
55
62
|
resources_affected: List[str] = field(default_factory=list)
|
56
|
-
|
63
|
+
|
57
64
|
@classmethod
|
58
|
-
def from_javascript_result(cls, js_result: Dict[str, Any]) ->
|
65
|
+
def from_javascript_result(cls, js_result: Dict[str, Any]) -> "CFATFinding":
|
59
66
|
"""Create finding from JavaScript CFAT result"""
|
60
67
|
return cls(
|
61
|
-
check_id=js_result.get(
|
62
|
-
category=js_result.get(
|
63
|
-
severity=js_result.get(
|
64
|
-
title=js_result.get(
|
65
|
-
description=js_result.get(
|
66
|
-
remediation=js_result.get(
|
67
|
-
estimated_effort=js_result.get(
|
68
|
-
compliance_frameworks=js_result.get(
|
69
|
-
resources_affected=js_result.get(
|
68
|
+
check_id=js_result.get("checkId", "unknown"),
|
69
|
+
category=js_result.get("category", "general"),
|
70
|
+
severity=js_result.get("severity", "MEDIUM"),
|
71
|
+
title=js_result.get("title", ""),
|
72
|
+
description=js_result.get("description", ""),
|
73
|
+
remediation=js_result.get("remediation", ""),
|
74
|
+
estimated_effort=js_result.get("estimatedEffort", "UNKNOWN"),
|
75
|
+
compliance_frameworks=js_result.get("complianceFrameworks", []),
|
76
|
+
resources_affected=js_result.get("resourcesAffected", []),
|
70
77
|
)
|
71
78
|
|
72
79
|
|
73
|
-
@dataclass
|
80
|
+
@dataclass
|
74
81
|
class CFATAssessmentResult:
|
75
82
|
"""
|
76
83
|
Comprehensive CFAT assessment result
|
77
84
|
"""
|
85
|
+
|
78
86
|
assessment_id: str
|
79
87
|
timestamp: datetime
|
80
88
|
assessment_type: str # 'python', 'javascript', 'dual'
|
@@ -85,29 +93,29 @@ class CFATAssessmentResult:
|
|
85
93
|
summary_stats: Dict[str, int] = field(default_factory=dict)
|
86
94
|
execution_time: float = 0.0
|
87
95
|
artifacts_directory: Optional[str] = None
|
88
|
-
|
96
|
+
|
89
97
|
def __post_init__(self):
|
90
98
|
"""Calculate summary statistics"""
|
91
99
|
if self.findings:
|
92
100
|
self.summary_stats = {
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
101
|
+
"total_findings": len(self.findings),
|
102
|
+
"high_severity": len([f for f in self.findings if f.severity == "HIGH"]),
|
103
|
+
"medium_severity": len([f for f in self.findings if f.severity == "MEDIUM"]),
|
104
|
+
"low_severity": len([f for f in self.findings if f.severity == "LOW"]),
|
105
|
+
"categories": len(set(f.category for f in self.findings)),
|
98
106
|
}
|
99
107
|
|
100
108
|
|
101
109
|
class CloudFoundationsCFATIntegration:
|
102
110
|
"""
|
103
111
|
Cloud Foundations CFAT Integration
|
104
|
-
|
112
|
+
|
105
113
|
Provides dual-engine Cloud Foundation Assessment capability:
|
106
114
|
1. JavaScript engine (cloud-foundations-templates/cfat/)
|
107
115
|
2. Python engine integration (existing runbooks/cfat/)
|
108
116
|
3. Unified reporting and export formats
|
109
117
|
4. CloudShell execution compatibility
|
110
|
-
|
118
|
+
|
111
119
|
Key Features:
|
112
120
|
- Dual assessment engine execution
|
113
121
|
- Project management exports (Jira/Asana CSV)
|
@@ -115,67 +123,73 @@ class CloudFoundationsCFATIntegration:
|
|
115
123
|
- Enterprise reporting with Rich CLI
|
116
124
|
- MCP validation integration
|
117
125
|
"""
|
118
|
-
|
126
|
+
|
119
127
|
def __init__(self, profile: Optional[str] = None):
|
120
128
|
"""Initialize CFAT integration with profile management"""
|
121
129
|
self.profile = get_profile_for_operation("management", profile)
|
122
130
|
self.session = boto3.Session(profile_name=self.profile)
|
123
|
-
|
131
|
+
|
124
132
|
# JavaScript engine configuration
|
125
133
|
self.js_engine_path = Path(__file__).parent / "cloud_foundations_js"
|
126
134
|
self.ensure_js_engine_available()
|
127
|
-
|
135
|
+
|
128
136
|
print_success(f"Initialized Cloud Foundations CFAT Integration with profile: {self.profile}")
|
129
|
-
|
137
|
+
|
130
138
|
def ensure_js_engine_available(self):
|
131
139
|
"""Ensure JavaScript engine is available for execution"""
|
132
140
|
# In real implementation, this would extract/setup the JS engine
|
133
141
|
# For now, create placeholder structure
|
134
142
|
self.js_engine_path.mkdir(exist_ok=True)
|
135
|
-
|
136
|
-
async def run_comprehensive_assessment(
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
143
|
+
|
144
|
+
async def run_comprehensive_assessment(
|
145
|
+
self,
|
146
|
+
cloudshell_mode: bool = False,
|
147
|
+
include_python: bool = True,
|
148
|
+
include_javascript: bool = True,
|
149
|
+
output_directory: Optional[str] = None,
|
150
|
+
) -> CFATAssessmentResult:
|
141
151
|
"""
|
142
152
|
Run comprehensive CFAT assessment using available engines
|
143
|
-
|
153
|
+
|
144
154
|
Args:
|
145
155
|
cloudshell_mode: Enable CloudShell compatibility optimizations
|
146
156
|
include_python: Include Python-based assessment
|
147
|
-
include_javascript: Include JavaScript-based assessment
|
157
|
+
include_javascript: Include JavaScript-based assessment
|
148
158
|
output_directory: Directory for assessment artifacts
|
149
|
-
|
159
|
+
|
150
160
|
Returns:
|
151
161
|
Comprehensive assessment result with findings from all engines
|
152
162
|
"""
|
153
163
|
print_header("Cloud Foundations Assessment", __version__)
|
154
|
-
|
164
|
+
|
155
165
|
if not (include_python or include_javascript):
|
156
166
|
raise ValueError("At least one assessment engine must be enabled")
|
157
|
-
|
167
|
+
|
158
168
|
# Initialize assessment result
|
159
169
|
account_info = self._get_account_info()
|
160
170
|
assessment_result = CFATAssessmentResult(
|
161
171
|
assessment_id=f"cfat-{datetime.now().strftime('%Y%m%d-%H%M%S')}",
|
162
172
|
timestamp=datetime.now(),
|
163
|
-
assessment_type=
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
173
|
+
assessment_type="dual"
|
174
|
+
if (include_python and include_javascript)
|
175
|
+
else "python"
|
176
|
+
if include_python
|
177
|
+
else "javascript",
|
178
|
+
account_id=account_info["account_id"],
|
179
|
+
account_name=account_info["account_name"],
|
180
|
+
organization_id=account_info.get("organization_id"),
|
181
|
+
artifacts_directory=output_directory or tempfile.mkdtemp(prefix="cfat-assessment-"),
|
168
182
|
)
|
169
|
-
|
183
|
+
|
170
184
|
start_time = datetime.now()
|
171
|
-
|
185
|
+
|
172
186
|
try:
|
173
187
|
with create_progress_bar() as progress:
|
174
188
|
total_tasks = (1 if include_python else 0) + (1 if include_javascript else 0) + 2
|
175
189
|
assessment_task = progress.add_task("Running comprehensive assessment...", total=total_tasks)
|
176
|
-
|
190
|
+
|
177
191
|
all_findings = []
|
178
|
-
|
192
|
+
|
179
193
|
# Run Python assessment if enabled
|
180
194
|
if include_python:
|
181
195
|
try:
|
@@ -185,7 +199,7 @@ class CloudFoundationsCFATIntegration:
|
|
185
199
|
progress.advance(assessment_task)
|
186
200
|
except Exception as e:
|
187
201
|
print_warning(f"Python assessment failed: {e}")
|
188
|
-
|
202
|
+
|
189
203
|
# Run JavaScript assessment if enabled
|
190
204
|
if include_javascript:
|
191
205
|
try:
|
@@ -195,82 +209,84 @@ class CloudFoundationsCFATIntegration:
|
|
195
209
|
progress.advance(assessment_task)
|
196
210
|
except Exception as e:
|
197
211
|
print_warning(f"JavaScript assessment failed: {e}")
|
198
|
-
|
212
|
+
|
199
213
|
# Consolidate findings
|
200
214
|
assessment_result.findings = self._consolidate_findings(all_findings)
|
201
215
|
progress.advance(assessment_task)
|
202
|
-
|
216
|
+
|
203
217
|
# Generate artifacts
|
204
218
|
await self._generate_assessment_artifacts(assessment_result)
|
205
219
|
progress.advance(assessment_task)
|
206
|
-
|
220
|
+
|
207
221
|
assessment_result.execution_time = (datetime.now() - start_time).total_seconds()
|
208
|
-
print_success(
|
209
|
-
|
222
|
+
print_success(
|
223
|
+
f"Assessment completed in {assessment_result.execution_time:.1f}s with {len(assessment_result.findings)} findings"
|
224
|
+
)
|
225
|
+
|
210
226
|
return assessment_result
|
211
|
-
|
227
|
+
|
212
228
|
except Exception as e:
|
213
229
|
print_error(f"Assessment execution failed: {e}")
|
214
230
|
raise
|
215
|
-
|
231
|
+
|
216
232
|
async def _run_python_assessment(self, cloudshell_mode: bool) -> List[CFATFinding]:
|
217
233
|
"""
|
218
234
|
Run Python-based CFAT assessment
|
219
235
|
Integration with existing runbooks CFAT module
|
220
236
|
"""
|
221
237
|
findings = []
|
222
|
-
|
238
|
+
|
223
239
|
try:
|
224
240
|
# Import existing CFAT functionality
|
225
241
|
from runbooks.cfat.cfat_runner import CFATRunner
|
226
|
-
|
242
|
+
|
227
243
|
cfat_runner = CFATRunner(profile=self.profile)
|
228
244
|
python_results = await cfat_runner.run_assessment()
|
229
|
-
|
245
|
+
|
230
246
|
# Convert Python results to standardized findings
|
231
|
-
for result in python_results.get(
|
247
|
+
for result in python_results.get("findings", []):
|
232
248
|
finding = CFATFinding(
|
233
|
-
check_id=result.get(
|
234
|
-
category=result.get(
|
235
|
-
severity=result.get(
|
236
|
-
title=result.get(
|
237
|
-
description=result.get(
|
238
|
-
remediation=result.get(
|
239
|
-
estimated_effort=result.get(
|
249
|
+
check_id=result.get("check_id", "python-check"),
|
250
|
+
category=result.get("category", "governance"),
|
251
|
+
severity=result.get("severity", "MEDIUM"),
|
252
|
+
title=result.get("title", ""),
|
253
|
+
description=result.get("description", ""),
|
254
|
+
remediation=result.get("remediation", ""),
|
255
|
+
estimated_effort=result.get("effort", "UNKNOWN"),
|
240
256
|
)
|
241
257
|
findings.append(finding)
|
242
|
-
|
258
|
+
|
243
259
|
except ImportError:
|
244
260
|
print_warning("Existing Python CFAT module not available, skipping Python assessment")
|
245
261
|
except Exception as e:
|
246
262
|
print_error(f"Python assessment execution failed: {e}")
|
247
|
-
|
263
|
+
|
248
264
|
return findings
|
249
|
-
|
265
|
+
|
250
266
|
async def _run_javascript_assessment(self, cloudshell_mode: bool) -> List[CFATFinding]:
|
251
267
|
"""
|
252
268
|
Run JavaScript-based CFAT assessment
|
253
269
|
Based on cloud-foundations-templates/cfat/ engine
|
254
270
|
"""
|
255
271
|
findings = []
|
256
|
-
|
272
|
+
|
257
273
|
try:
|
258
274
|
# Prepare JavaScript execution environment
|
259
275
|
js_command = self._prepare_javascript_command(cloudshell_mode)
|
260
|
-
|
276
|
+
|
261
277
|
# Execute JavaScript assessment
|
262
278
|
result = subprocess.run(
|
263
279
|
js_command,
|
264
280
|
capture_output=True,
|
265
281
|
text=True,
|
266
282
|
timeout=300, # 5 minute timeout
|
267
|
-
cwd=self.js_engine_path
|
283
|
+
cwd=self.js_engine_path,
|
268
284
|
)
|
269
|
-
|
285
|
+
|
270
286
|
if result.returncode == 0:
|
271
287
|
# Parse JavaScript assessment output
|
272
288
|
js_results = self._parse_javascript_output(result.stdout)
|
273
|
-
|
289
|
+
|
274
290
|
# Convert to standardized findings
|
275
291
|
for js_result in js_results:
|
276
292
|
finding = CFATFinding.from_javascript_result(js_result)
|
@@ -278,14 +294,14 @@ class CloudFoundationsCFATIntegration:
|
|
278
294
|
else:
|
279
295
|
print_warning(f"JavaScript assessment returned non-zero exit code: {result.returncode}")
|
280
296
|
print_warning(f"stderr: {result.stderr}")
|
281
|
-
|
297
|
+
|
282
298
|
except subprocess.TimeoutExpired:
|
283
299
|
print_error("JavaScript assessment timed out after 5 minutes")
|
284
300
|
except Exception as e:
|
285
301
|
print_error(f"JavaScript assessment execution failed: {e}")
|
286
|
-
|
302
|
+
|
287
303
|
return findings
|
288
|
-
|
304
|
+
|
289
305
|
def _prepare_javascript_command(self, cloudshell_mode: bool) -> List[str]:
|
290
306
|
"""
|
291
307
|
Prepare JavaScript execution command
|
@@ -293,32 +309,27 @@ class CloudFoundationsCFATIntegration:
|
|
293
309
|
"""
|
294
310
|
if cloudshell_mode:
|
295
311
|
# CloudShell optimized execution
|
296
|
-
return [
|
297
|
-
'bash', '-c',
|
298
|
-
f'cd {self.js_engine_path} && AWS_PROFILE={self.profile} node app.js'
|
299
|
-
]
|
312
|
+
return ["bash", "-c", f"cd {self.js_engine_path} && AWS_PROFILE={self.profile} node app.js"]
|
300
313
|
else:
|
301
314
|
# Local execution
|
302
|
-
return [
|
303
|
-
|
304
|
-
]
|
305
|
-
|
315
|
+
return ["node", "app.js"]
|
316
|
+
|
306
317
|
def _parse_javascript_output(self, stdout: str) -> List[Dict[str, Any]]:
|
307
318
|
"""Parse JavaScript assessment output to structured findings"""
|
308
319
|
# In real implementation, this would parse the actual JS CFAT output format
|
309
320
|
# For demonstration, return mock structure
|
310
321
|
return [
|
311
322
|
{
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
323
|
+
"checkId": "js-org-001",
|
324
|
+
"category": "organization",
|
325
|
+
"severity": "HIGH",
|
326
|
+
"title": "Organization Structure Assessment",
|
327
|
+
"description": "Organization structure needs improvement",
|
328
|
+
"remediation": "Implement proper OU structure",
|
329
|
+
"estimatedEffort": "DAYS",
|
319
330
|
}
|
320
331
|
]
|
321
|
-
|
332
|
+
|
322
333
|
def _consolidate_findings(self, all_findings: List[CFATFinding]) -> List[CFATFinding]:
|
323
334
|
"""
|
324
335
|
Consolidate findings from multiple engines, removing duplicates
|
@@ -327,14 +338,14 @@ class CloudFoundationsCFATIntegration:
|
|
327
338
|
# In real implementation, would use more sophisticated matching
|
328
339
|
seen_checks = set()
|
329
340
|
consolidated = []
|
330
|
-
|
341
|
+
|
331
342
|
for finding in all_findings:
|
332
343
|
if finding.check_id not in seen_checks:
|
333
344
|
seen_checks.add(finding.check_id)
|
334
345
|
consolidated.append(finding)
|
335
|
-
|
346
|
+
|
336
347
|
return consolidated
|
337
|
-
|
348
|
+
|
338
349
|
async def _generate_assessment_artifacts(self, assessment: CFATAssessmentResult):
|
339
350
|
"""
|
340
351
|
Generate comprehensive assessment artifacts
|
@@ -342,46 +353,46 @@ class CloudFoundationsCFATIntegration:
|
|
342
353
|
"""
|
343
354
|
artifacts_path = Path(assessment.artifacts_directory)
|
344
355
|
artifacts_path.mkdir(exist_ok=True)
|
345
|
-
|
356
|
+
|
346
357
|
# Generate detailed report
|
347
358
|
await self._generate_detailed_report(assessment, artifacts_path)
|
348
|
-
|
359
|
+
|
349
360
|
# Generate CSV exports
|
350
361
|
await self._generate_csv_exports(assessment, artifacts_path)
|
351
|
-
|
362
|
+
|
352
363
|
# Generate project management imports
|
353
364
|
await self._generate_project_management_exports(assessment, artifacts_path)
|
354
|
-
|
365
|
+
|
355
366
|
# Create assessment archive
|
356
367
|
await self._create_assessment_archive(assessment, artifacts_path)
|
357
|
-
|
368
|
+
|
358
369
|
async def _generate_detailed_report(self, assessment: CFATAssessmentResult, artifacts_path: Path):
|
359
370
|
"""Generate detailed text report"""
|
360
371
|
report_file = artifacts_path / "cfat-detailed-report.txt"
|
361
|
-
|
362
|
-
with open(report_file,
|
363
|
-
f.write("="*80 + "\n")
|
372
|
+
|
373
|
+
with open(report_file, "w") as f:
|
374
|
+
f.write("=" * 80 + "\n")
|
364
375
|
f.write("CLOUD FOUNDATIONS ASSESSMENT REPORT\n")
|
365
|
-
f.write("="*80 + "\n")
|
376
|
+
f.write("=" * 80 + "\n")
|
366
377
|
f.write(f"Assessment ID: {assessment.assessment_id}\n")
|
367
378
|
f.write(f"Timestamp: {assessment.timestamp}\n")
|
368
379
|
f.write(f"Account: {assessment.account_name} ({assessment.account_id})\n")
|
369
380
|
f.write(f"Assessment Type: {assessment.assessment_type}\n")
|
370
381
|
f.write(f"Execution Time: {assessment.execution_time:.1f}s\n")
|
371
382
|
f.write("\n")
|
372
|
-
|
383
|
+
|
373
384
|
f.write("EXECUTIVE SUMMARY\n")
|
374
|
-
f.write("-"*40 + "\n")
|
385
|
+
f.write("-" * 40 + "\n")
|
375
386
|
f.write(f"Total Findings: {assessment.summary_stats.get('total_findings', 0)}\n")
|
376
387
|
f.write(f"High Severity: {assessment.summary_stats.get('high_severity', 0)}\n")
|
377
388
|
f.write(f"Medium Severity: {assessment.summary_stats.get('medium_severity', 0)}\n")
|
378
389
|
f.write(f"Low Severity: {assessment.summary_stats.get('low_severity', 0)}\n")
|
379
390
|
f.write(f"Categories: {assessment.summary_stats.get('categories', 0)}\n")
|
380
391
|
f.write("\n")
|
381
|
-
|
392
|
+
|
382
393
|
f.write("DETAILED FINDINGS\n")
|
383
|
-
f.write("-"*40 + "\n")
|
384
|
-
|
394
|
+
f.write("-" * 40 + "\n")
|
395
|
+
|
385
396
|
for finding in assessment.findings:
|
386
397
|
f.write(f"Finding: {finding.title}\n")
|
387
398
|
f.write(f"Category: {finding.category}\n")
|
@@ -389,28 +400,30 @@ class CloudFoundationsCFATIntegration:
|
|
389
400
|
f.write(f"Description: {finding.description}\n")
|
390
401
|
f.write(f"Remediation: {finding.remediation}\n")
|
391
402
|
f.write(f"Estimated Effort: {finding.estimated_effort}\n")
|
392
|
-
f.write("-"*40 + "\n")
|
393
|
-
|
403
|
+
f.write("-" * 40 + "\n")
|
404
|
+
|
394
405
|
async def _generate_csv_exports(self, assessment: CFATAssessmentResult, artifacts_path: Path):
|
395
406
|
"""Generate CSV exports for analysis"""
|
396
407
|
csv_file = artifacts_path / "cfat-findings.csv"
|
397
|
-
|
398
|
-
with open(csv_file,
|
399
|
-
fieldnames = [
|
408
|
+
|
409
|
+
with open(csv_file, "w", newline="") as csvfile:
|
410
|
+
fieldnames = ["check_id", "category", "severity", "title", "description", "remediation", "estimated_effort"]
|
400
411
|
writer = csv.DictWriter(csvfile, fieldnames=fieldnames)
|
401
|
-
|
412
|
+
|
402
413
|
writer.writeheader()
|
403
414
|
for finding in assessment.findings:
|
404
|
-
writer.writerow(
|
405
|
-
|
406
|
-
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
|
413
|
-
|
415
|
+
writer.writerow(
|
416
|
+
{
|
417
|
+
"check_id": finding.check_id,
|
418
|
+
"category": finding.category,
|
419
|
+
"severity": finding.severity,
|
420
|
+
"title": finding.title,
|
421
|
+
"description": finding.description,
|
422
|
+
"remediation": finding.remediation,
|
423
|
+
"estimated_effort": finding.estimated_effort,
|
424
|
+
}
|
425
|
+
)
|
426
|
+
|
414
427
|
async def _generate_project_management_exports(self, assessment: CFATAssessmentResult, artifacts_path: Path):
|
415
428
|
"""
|
416
429
|
Generate project management import files
|
@@ -418,155 +431,135 @@ class CloudFoundationsCFATIntegration:
|
|
418
431
|
"""
|
419
432
|
# Jira import
|
420
433
|
jira_file = artifacts_path / "jira-import.csv"
|
421
|
-
with open(jira_file,
|
422
|
-
fieldnames = [
|
434
|
+
with open(jira_file, "w", newline="") as csvfile:
|
435
|
+
fieldnames = ["Summary", "Issue Type", "Priority", "Description", "Labels", "Epic Link"]
|
423
436
|
writer = csv.DictWriter(csvfile, fieldnames=fieldnames)
|
424
|
-
|
437
|
+
|
425
438
|
writer.writeheader()
|
426
439
|
for finding in assessment.findings:
|
427
|
-
writer.writerow(
|
428
|
-
|
429
|
-
|
430
|
-
|
431
|
-
|
432
|
-
|
433
|
-
|
434
|
-
|
435
|
-
|
440
|
+
writer.writerow(
|
441
|
+
{
|
442
|
+
"Summary": finding.title,
|
443
|
+
"Issue Type": "Task",
|
444
|
+
"Priority": self._map_severity_to_jira_priority(finding.severity),
|
445
|
+
"Description": f"{finding.description}\n\nRemediation: {finding.remediation}",
|
446
|
+
"Labels": f"cfat,{finding.category},{finding.severity.lower()}",
|
447
|
+
"Epic Link": "Cloud Foundations Assessment",
|
448
|
+
}
|
449
|
+
)
|
450
|
+
|
436
451
|
# Asana import
|
437
452
|
asana_file = artifacts_path / "asana-import.csv"
|
438
|
-
with open(asana_file,
|
439
|
-
fieldnames = [
|
453
|
+
with open(asana_file, "w", newline="") as csvfile:
|
454
|
+
fieldnames = ["Name", "Notes", "Priority", "Tags", "Projects"]
|
440
455
|
writer = csv.DictWriter(csvfile, fieldnames=fieldnames)
|
441
|
-
|
456
|
+
|
442
457
|
writer.writeheader()
|
443
458
|
for finding in assessment.findings:
|
444
|
-
writer.writerow(
|
445
|
-
|
446
|
-
|
447
|
-
|
448
|
-
|
449
|
-
|
450
|
-
|
451
|
-
|
459
|
+
writer.writerow(
|
460
|
+
{
|
461
|
+
"Name": finding.title,
|
462
|
+
"Notes": f"{finding.description}\n\nRemediation: {finding.remediation}",
|
463
|
+
"Priority": self._map_severity_to_asana_priority(finding.severity),
|
464
|
+
"Tags": f"cfat,{finding.category}",
|
465
|
+
"Projects": "Cloud Foundations Assessment",
|
466
|
+
}
|
467
|
+
)
|
468
|
+
|
452
469
|
def _map_severity_to_jira_priority(self, severity: str) -> str:
|
453
470
|
"""Map CFAT severity to Jira priority"""
|
454
|
-
mapping = {
|
455
|
-
|
456
|
-
|
457
|
-
'LOW': 'Low'
|
458
|
-
}
|
459
|
-
return mapping.get(severity, 'Medium')
|
460
|
-
|
471
|
+
mapping = {"HIGH": "High", "MEDIUM": "Medium", "LOW": "Low"}
|
472
|
+
return mapping.get(severity, "Medium")
|
473
|
+
|
461
474
|
def _map_severity_to_asana_priority(self, severity: str) -> str:
|
462
475
|
"""Map CFAT severity to Asana priority"""
|
463
|
-
mapping = {
|
464
|
-
|
465
|
-
|
466
|
-
'LOW': 'Low'
|
467
|
-
}
|
468
|
-
return mapping.get(severity, 'Medium')
|
469
|
-
|
476
|
+
mapping = {"HIGH": "High", "MEDIUM": "Medium", "LOW": "Low"}
|
477
|
+
return mapping.get(severity, "Medium")
|
478
|
+
|
470
479
|
async def _create_assessment_archive(self, assessment: CFATAssessmentResult, artifacts_path: Path):
|
471
480
|
"""Create zip archive of all assessment artifacts"""
|
472
481
|
archive_file = artifacts_path / "assessment.zip"
|
473
|
-
|
474
|
-
with zipfile.ZipFile(archive_file,
|
475
|
-
for file_path in artifacts_path.glob(
|
476
|
-
if file_path.name !=
|
482
|
+
|
483
|
+
with zipfile.ZipFile(archive_file, "w", zipfile.ZIP_DEFLATED) as zipf:
|
484
|
+
for file_path in artifacts_path.glob("*"):
|
485
|
+
if file_path.name != "assessment.zip": # Don't include the zip itself
|
477
486
|
zipf.write(file_path, file_path.name)
|
478
|
-
|
487
|
+
|
479
488
|
print_success(f"Assessment archive created: {archive_file}")
|
480
|
-
|
489
|
+
|
481
490
|
def _get_account_info(self) -> Dict[str, str]:
|
482
491
|
"""Get current account information"""
|
483
492
|
try:
|
484
|
-
sts = self.session.client(
|
493
|
+
sts = self.session.client("sts")
|
485
494
|
identity = sts.get_caller_identity()
|
486
|
-
|
487
|
-
account_id = identity[
|
488
|
-
|
495
|
+
|
496
|
+
account_id = identity["Account"]
|
497
|
+
|
489
498
|
# Try to get account name from organizations
|
490
499
|
account_name = account_id # Default to account ID
|
491
500
|
org_id = None
|
492
|
-
|
501
|
+
|
493
502
|
try:
|
494
|
-
orgs = self.session.client(
|
503
|
+
orgs = self.session.client("organizations")
|
495
504
|
account = orgs.describe_account(AccountId=account_id)
|
496
|
-
account_name = account[
|
497
|
-
|
505
|
+
account_name = account["Account"]["Name"]
|
506
|
+
|
498
507
|
org_info = orgs.describe_organization()
|
499
|
-
org_id = org_info[
|
508
|
+
org_id = org_info["Organization"]["Id"]
|
500
509
|
except ClientError:
|
501
510
|
pass # Not in organization or no permission
|
502
|
-
|
503
|
-
return {
|
504
|
-
|
505
|
-
'account_name': account_name,
|
506
|
-
'organization_id': org_id
|
507
|
-
}
|
508
|
-
|
511
|
+
|
512
|
+
return {"account_id": account_id, "account_name": account_name, "organization_id": org_id}
|
513
|
+
|
509
514
|
except ClientError as e:
|
510
515
|
print_error(f"Failed to get account information: {e}")
|
511
516
|
raise
|
512
|
-
|
517
|
+
|
513
518
|
def display_assessment_summary(self, assessment: CFATAssessmentResult):
|
514
519
|
"""
|
515
520
|
Display assessment summary with Rich CLI formatting
|
516
521
|
"""
|
517
522
|
print_header("Assessment Results Summary", __version__)
|
518
|
-
|
523
|
+
|
519
524
|
# Executive summary table
|
520
|
-
summary_table = create_table(
|
521
|
-
|
522
|
-
caption=f"Assessment ID: {assessment.assessment_id}"
|
523
|
-
)
|
524
|
-
|
525
|
+
summary_table = create_table(title="Executive Summary", caption=f"Assessment ID: {assessment.assessment_id}")
|
526
|
+
|
525
527
|
summary_table.add_column("Metric", style="cyan")
|
526
528
|
summary_table.add_column("Value", justify="right", style="green")
|
527
529
|
summary_table.add_column("Impact", style="yellow")
|
528
|
-
|
530
|
+
|
529
531
|
impact_levels = {
|
530
|
-
|
531
|
-
|
532
|
-
|
533
|
-
|
532
|
+
"total_findings": "Requires attention",
|
533
|
+
"high_severity": "Immediate action",
|
534
|
+
"medium_severity": "Plan remediation",
|
535
|
+
"low_severity": "Schedule improvement",
|
534
536
|
}
|
535
|
-
|
537
|
+
|
536
538
|
for metric, value in assessment.summary_stats.items():
|
537
|
-
impact = impact_levels.get(metric,
|
538
|
-
summary_table.add_row(
|
539
|
-
|
540
|
-
str(value),
|
541
|
-
impact
|
542
|
-
)
|
543
|
-
|
539
|
+
impact = impact_levels.get(metric, "Review needed")
|
540
|
+
summary_table.add_row(metric.replace("_", " ").title(), str(value), impact)
|
541
|
+
|
544
542
|
console.print(summary_table)
|
545
|
-
|
543
|
+
|
546
544
|
# Findings by category
|
547
545
|
if assessment.findings:
|
548
546
|
category_stats = {}
|
549
547
|
for finding in assessment.findings:
|
550
548
|
if finding.category not in category_stats:
|
551
|
-
category_stats[finding.category] = {
|
549
|
+
category_stats[finding.category] = {"high": 0, "medium": 0, "low": 0}
|
552
550
|
category_stats[finding.category][finding.severity.lower()] += 1
|
553
|
-
|
551
|
+
|
554
552
|
category_table = create_table(title="Findings by Category")
|
555
553
|
category_table.add_column("Category", style="cyan")
|
556
554
|
category_table.add_column("High", justify="right", style="red")
|
557
555
|
category_table.add_column("Medium", justify="right", style="yellow")
|
558
556
|
category_table.add_column("Low", justify="right", style="green")
|
559
|
-
|
557
|
+
|
560
558
|
for category, stats in category_stats.items():
|
561
|
-
category_table.add_row(
|
562
|
-
|
563
|
-
str(stats['high']),
|
564
|
-
str(stats['medium']),
|
565
|
-
str(stats['low'])
|
566
|
-
)
|
567
|
-
|
559
|
+
category_table.add_row(category.title(), str(stats["high"]), str(stats["medium"]), str(stats["low"]))
|
560
|
+
|
568
561
|
console.print(category_table)
|
569
|
-
|
562
|
+
|
570
563
|
# Assessment details panel
|
571
564
|
details_text = f"""
|
572
565
|
Assessment Type: {assessment.assessment_type}
|
@@ -574,12 +567,8 @@ Account: {assessment.account_name} ({assessment.account_id})
|
|
574
567
|
Execution Time: {assessment.execution_time:.1f} seconds
|
575
568
|
Artifacts: {assessment.artifacts_directory}
|
576
569
|
"""
|
577
|
-
|
578
|
-
details_panel = create_panel(
|
579
|
-
details_text,
|
580
|
-
title="Assessment Details",
|
581
|
-
style="blue"
|
582
|
-
)
|
570
|
+
|
571
|
+
details_panel = create_panel(details_text, title="Assessment Details", style="blue")
|
583
572
|
console.print(details_panel)
|
584
573
|
|
585
574
|
|
@@ -588,34 +577,32 @@ async def main():
|
|
588
577
|
Demonstration of CFAT Cloud Foundations integration
|
589
578
|
"""
|
590
579
|
import argparse
|
591
|
-
|
592
|
-
parser = argparse.ArgumentParser(
|
593
|
-
|
594
|
-
)
|
595
|
-
parser.add_argument(
|
596
|
-
parser.add_argument(
|
597
|
-
parser.add_argument(
|
598
|
-
|
599
|
-
parser.add_argument('--output-dir', help='Output directory for artifacts')
|
600
|
-
|
580
|
+
|
581
|
+
parser = argparse.ArgumentParser(description="CFAT Cloud Foundations Integration - Dual Engine Assessment")
|
582
|
+
parser.add_argument("--profile", help="AWS profile to use")
|
583
|
+
parser.add_argument("--cloudshell", action="store_true", help="Enable CloudShell compatibility mode")
|
584
|
+
parser.add_argument("--python-only", action="store_true", help="Run Python assessment only")
|
585
|
+
parser.add_argument("--javascript-only", action="store_true", help="Run JavaScript assessment only")
|
586
|
+
parser.add_argument("--output-dir", help="Output directory for artifacts")
|
587
|
+
|
601
588
|
args = parser.parse_args()
|
602
|
-
|
589
|
+
|
603
590
|
try:
|
604
591
|
cfat_integration = CloudFoundationsCFATIntegration(profile=args.profile)
|
605
|
-
|
592
|
+
|
606
593
|
# Run comprehensive assessment
|
607
594
|
result = await cfat_integration.run_comprehensive_assessment(
|
608
595
|
cloudshell_mode=args.cloudshell,
|
609
596
|
include_python=not args.javascript_only,
|
610
597
|
include_javascript=not args.python_only,
|
611
|
-
output_directory=args.output_dir
|
598
|
+
output_directory=args.output_dir,
|
612
599
|
)
|
613
|
-
|
600
|
+
|
614
601
|
# Display results
|
615
602
|
cfat_integration.display_assessment_summary(result)
|
616
|
-
|
603
|
+
|
617
604
|
print_success("CFAT Cloud Foundations integration demonstration completed")
|
618
|
-
|
605
|
+
|
619
606
|
except Exception as e:
|
620
607
|
print_error(f"CFAT integration demonstration failed: {e}")
|
621
608
|
raise
|
@@ -623,4 +610,5 @@ async def main():
|
|
623
610
|
|
624
611
|
if __name__ == "__main__":
|
625
612
|
import asyncio
|
626
|
-
|
613
|
+
|
614
|
+
asyncio.run(main())
|