runbooks 0.7.9__py3-none-any.whl → 0.9.0__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.
Files changed (95) hide show
  1. runbooks/__init__.py +1 -1
  2. runbooks/cfat/README.md +12 -1
  3. runbooks/cfat/__init__.py +1 -1
  4. runbooks/cfat/assessment/runner.py +42 -34
  5. runbooks/cfat/models.py +1 -1
  6. runbooks/common/__init__.py +152 -0
  7. runbooks/common/accuracy_validator.py +1039 -0
  8. runbooks/common/context_logger.py +440 -0
  9. runbooks/common/cross_module_integration.py +594 -0
  10. runbooks/common/enhanced_exception_handler.py +1108 -0
  11. runbooks/common/enterprise_audit_integration.py +634 -0
  12. runbooks/common/mcp_integration.py +539 -0
  13. runbooks/common/performance_monitor.py +387 -0
  14. runbooks/common/profile_utils.py +216 -0
  15. runbooks/common/rich_utils.py +171 -0
  16. runbooks/feedback/user_feedback_collector.py +440 -0
  17. runbooks/finops/README.md +339 -451
  18. runbooks/finops/__init__.py +4 -21
  19. runbooks/finops/account_resolver.py +279 -0
  20. runbooks/finops/accuracy_cross_validator.py +638 -0
  21. runbooks/finops/aws_client.py +721 -36
  22. runbooks/finops/budget_integration.py +313 -0
  23. runbooks/finops/cli.py +59 -5
  24. runbooks/finops/cost_processor.py +211 -37
  25. runbooks/finops/dashboard_router.py +900 -0
  26. runbooks/finops/dashboard_runner.py +990 -232
  27. runbooks/finops/embedded_mcp_validator.py +288 -0
  28. runbooks/finops/enhanced_dashboard_runner.py +8 -7
  29. runbooks/finops/enhanced_progress.py +327 -0
  30. runbooks/finops/enhanced_trend_visualization.py +423 -0
  31. runbooks/finops/finops_dashboard.py +29 -1880
  32. runbooks/finops/helpers.py +509 -196
  33. runbooks/finops/iam_guidance.py +400 -0
  34. runbooks/finops/markdown_exporter.py +466 -0
  35. runbooks/finops/multi_dashboard.py +1502 -0
  36. runbooks/finops/optimizer.py +15 -15
  37. runbooks/finops/profile_processor.py +2 -2
  38. runbooks/finops/runbooks.inventory.organizations_discovery.log +0 -0
  39. runbooks/finops/runbooks.security.report_generator.log +0 -0
  40. runbooks/finops/runbooks.security.run_script.log +0 -0
  41. runbooks/finops/runbooks.security.security_export.log +0 -0
  42. runbooks/finops/service_mapping.py +195 -0
  43. runbooks/finops/single_dashboard.py +710 -0
  44. runbooks/finops/tests/test_reference_images_validation.py +1 -1
  45. runbooks/inventory/README.md +12 -1
  46. runbooks/inventory/core/collector.py +157 -29
  47. runbooks/inventory/list_ec2_instances.py +9 -6
  48. runbooks/inventory/list_ssm_parameters.py +10 -10
  49. runbooks/inventory/organizations_discovery.py +210 -164
  50. runbooks/inventory/rich_inventory_display.py +74 -107
  51. runbooks/inventory/run_on_multi_accounts.py +13 -13
  52. runbooks/main.py +740 -134
  53. runbooks/metrics/dora_metrics_engine.py +711 -17
  54. runbooks/monitoring/performance_monitor.py +433 -0
  55. runbooks/operate/README.md +394 -0
  56. runbooks/operate/base.py +215 -47
  57. runbooks/operate/ec2_operations.py +7 -5
  58. runbooks/operate/privatelink_operations.py +1 -1
  59. runbooks/operate/vpc_endpoints.py +1 -1
  60. runbooks/remediation/README.md +489 -13
  61. runbooks/remediation/commons.py +8 -4
  62. runbooks/security/ENTERPRISE_SECURITY_FRAMEWORK.md +506 -0
  63. runbooks/security/README.md +12 -1
  64. runbooks/security/__init__.py +164 -33
  65. runbooks/security/compliance_automation.py +12 -10
  66. runbooks/security/compliance_automation_engine.py +1021 -0
  67. runbooks/security/enterprise_security_framework.py +931 -0
  68. runbooks/security/enterprise_security_policies.json +293 -0
  69. runbooks/security/integration_test_enterprise_security.py +879 -0
  70. runbooks/security/module_security_integrator.py +641 -0
  71. runbooks/security/report_generator.py +1 -1
  72. runbooks/security/run_script.py +4 -8
  73. runbooks/security/security_baseline_tester.py +36 -49
  74. runbooks/security/security_export.py +99 -120
  75. runbooks/sre/README.md +472 -0
  76. runbooks/sre/__init__.py +33 -0
  77. runbooks/sre/mcp_reliability_engine.py +1049 -0
  78. runbooks/sre/performance_optimization_engine.py +1032 -0
  79. runbooks/sre/reliability_monitoring_framework.py +1011 -0
  80. runbooks/validation/__init__.py +2 -2
  81. runbooks/validation/benchmark.py +154 -149
  82. runbooks/validation/cli.py +159 -147
  83. runbooks/validation/mcp_validator.py +265 -236
  84. runbooks/vpc/README.md +478 -0
  85. runbooks/vpc/__init__.py +2 -2
  86. runbooks/vpc/manager_interface.py +366 -351
  87. runbooks/vpc/networking_wrapper.py +62 -33
  88. runbooks/vpc/rich_formatters.py +22 -8
  89. {runbooks-0.7.9.dist-info → runbooks-0.9.0.dist-info}/METADATA +136 -54
  90. {runbooks-0.7.9.dist-info → runbooks-0.9.0.dist-info}/RECORD +94 -55
  91. {runbooks-0.7.9.dist-info → runbooks-0.9.0.dist-info}/entry_points.txt +1 -1
  92. runbooks/finops/cross_validation.py +0 -375
  93. {runbooks-0.7.9.dist-info → runbooks-0.9.0.dist-info}/WHEEL +0 -0
  94. {runbooks-0.7.9.dist-info → runbooks-0.9.0.dist-info}/licenses/LICENSE +0 -0
  95. {runbooks-0.7.9.dist-info → runbooks-0.9.0.dist-info}/top_level.txt +0 -0
@@ -7,7 +7,7 @@ with multiple format support (JSON, CSV, PDF) and multi-language reporting.
7
7
 
8
8
  Features:
9
9
  - JSON export for dashboard integration
10
- - CSV export for spreadsheet analysis
10
+ - CSV export for spreadsheet analysis
11
11
  - PDF export for executive reports
12
12
  - Multi-language export support (EN, JP, KR, VN)
13
13
  - Rich CLI progress tracking
@@ -43,68 +43,60 @@ logger = configure_logger(__name__)
43
43
  class SecurityExporter:
44
44
  """
45
45
  Enterprise security assessment export functionality with Rich CLI.
46
-
46
+
47
47
  Supports JSON, CSV, and PDF exports with multi-language capabilities
48
48
  and professional formatting for enterprise compliance requirements.
49
49
  """
50
-
50
+
51
51
  def __init__(self, output_dir: Optional[str] = None):
52
52
  self.output_dir = Path(output_dir) if output_dir else Path.cwd() / "security-exports"
53
53
  self.supported_formats = ["json", "csv", "pdf"]
54
54
  self.supported_languages = ["EN", "JP", "KR", "VN"]
55
-
55
+
56
56
  # Ensure output directory exists
57
57
  self.output_dir.mkdir(parents=True, exist_ok=True)
58
-
58
+
59
59
  def export_security_results(
60
- self,
61
- account_id: str,
62
- results: Dict[str, List],
63
- language: str = "EN",
64
- formats: List[str] = None
60
+ self, account_id: str, results: Dict[str, List], language: str = "EN", formats: List[str] = None
65
61
  ) -> Dict[str, str]:
66
62
  """
67
63
  Export security assessment results in multiple formats.
68
-
64
+
69
65
  Args:
70
66
  account_id: AWS account ID
71
67
  results: Security assessment results
72
68
  language: Report language (EN, JP, KR, VN)
73
69
  formats: Export formats (json, csv, pdf)
74
-
70
+
75
71
  Returns:
76
72
  Dictionary mapping format to file path
77
73
  """
78
74
  if formats is None:
79
75
  formats = ["json", "csv"]
80
-
76
+
81
77
  export_results = {}
82
78
  timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
83
-
79
+
84
80
  # Display export startup
85
81
  export_info = f"""[bold cyan]Security Export Configuration[/bold cyan]
86
82
 
87
83
  [green]Account ID:[/green] {account_id}
88
84
  [green]Language:[/green] {language}
89
- [green]Formats:[/green] {', '.join(formats)}
85
+ [green]Formats:[/green] {", ".join(formats)}
90
86
  [green]Output Directory:[/green] {self.output_dir}
91
87
 
92
88
  [dim]Exporting assessment results...[/dim]"""
93
-
94
- console.print(create_panel(
95
- export_info,
96
- title="📤 Security Data Export",
97
- border_style="cyan"
98
- ))
99
-
89
+
90
+ console.print(create_panel(export_info, title="📤 Security Data Export", border_style="cyan"))
91
+
100
92
  with create_progress_bar(description="Exporting Data") as progress:
101
93
  export_task = progress.add_task("Processing exports...", total=len(formats))
102
-
94
+
103
95
  for format_type in formats:
104
96
  if format_type not in self.supported_formats:
105
97
  print_warning(f"Unsupported format: {format_type}")
106
98
  continue
107
-
99
+
108
100
  try:
109
101
  if format_type == "json":
110
102
  file_path = self._export_json(account_id, results, language, timestamp)
@@ -112,25 +104,25 @@ class SecurityExporter:
112
104
  file_path = self._export_csv(account_id, results, language, timestamp)
113
105
  elif format_type == "pdf":
114
106
  file_path = self._export_pdf(account_id, results, language, timestamp)
115
-
107
+
116
108
  export_results[format_type] = str(file_path)
117
109
  print_success(f"Exported {format_type.upper()}: {file_path}")
118
-
110
+
119
111
  except Exception as e:
120
112
  print_error(f"Failed to export {format_type}: {e}")
121
113
  logger.error(f"Export failed for {format_type}: {e}", exc_info=True)
122
-
114
+
123
115
  progress.update(export_task, advance=1)
124
-
116
+
125
117
  # Display export summary
126
118
  self._display_export_summary(export_results, account_id)
127
119
  return export_results
128
-
120
+
129
121
  def _export_json(self, account_id: str, results: Dict[str, List], language: str, timestamp: str) -> Path:
130
122
  """Export results to JSON format for dashboard integration."""
131
123
  filename = f"security-assessment-{account_id}-{timestamp}.json"
132
124
  file_path = self.output_dir / filename
133
-
125
+
134
126
  # Transform results for JSON export
135
127
  json_data = {
136
128
  "metadata": {
@@ -138,56 +130,56 @@ class SecurityExporter:
138
130
  "assessment_date": datetime.now().isoformat(),
139
131
  "language": language,
140
132
  "export_format": "json",
141
- "version": "0.7.8"
133
+ "version": "0.7.8",
142
134
  },
143
135
  "summary": self._calculate_summary_stats(results),
144
136
  "findings": self._transform_findings_for_json(results),
145
137
  "compliance_frameworks": {
146
138
  "aws_well_architected": self._map_to_wa_framework(results),
147
139
  "soc2": self._map_to_soc2_framework(results),
148
- "enterprise_baseline": self._map_to_enterprise_framework(results)
149
- }
140
+ "enterprise_baseline": self._map_to_enterprise_framework(results),
141
+ },
150
142
  }
151
-
143
+
152
144
  with file_path.open("w", encoding="utf-8") as f:
153
145
  json.dump(json_data, f, indent=2, ensure_ascii=False)
154
-
146
+
155
147
  return file_path
156
-
148
+
157
149
  def _export_csv(self, account_id: str, results: Dict[str, List], language: str, timestamp: str) -> Path:
158
150
  """Export results to CSV format for spreadsheet analysis."""
159
151
  filename = f"security-findings-{account_id}-{timestamp}.csv"
160
152
  file_path = self.output_dir / filename
161
-
153
+
162
154
  with file_path.open("w", newline="", encoding="utf-8") as f:
163
155
  writer = csv.writer(f)
164
-
156
+
165
157
  # CSV Headers
166
158
  headers = [
167
159
  "Finding_ID",
168
- "Status_Level",
160
+ "Status_Level",
169
161
  "Title",
170
162
  "Message",
171
163
  "Severity",
172
164
  "Compliance_Framework",
173
165
  "Remediation_Available",
174
- "Assessment_Date"
166
+ "Assessment_Date",
175
167
  ]
176
168
  writer.writerow(headers)
177
-
169
+
178
170
  # Write findings data
179
171
  finding_id = 1
180
172
  assessment_date = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
181
-
173
+
182
174
  for level, findings in results.items():
183
175
  for finding in findings:
184
- if hasattr(finding, 'to_dict'):
176
+ if hasattr(finding, "to_dict"):
185
177
  finding_dict = finding.to_dict()
186
178
  elif isinstance(finding, dict):
187
179
  finding_dict = finding
188
180
  else:
189
181
  continue
190
-
182
+
191
183
  row = [
192
184
  f"SEC-{finding_id:04d}",
193
185
  level,
@@ -196,65 +188,65 @@ class SecurityExporter:
196
188
  self._map_level_to_severity(level),
197
189
  "AWS Security Baseline",
198
190
  "Manual" if level in ["Danger", "Warning"] else "N/A",
199
- assessment_date
191
+ assessment_date,
200
192
  ]
201
193
  writer.writerow(row)
202
194
  finding_id += 1
203
-
195
+
204
196
  return file_path
205
-
197
+
206
198
  def _export_pdf(self, account_id: str, results: Dict[str, List], language: str, timestamp: str) -> Path:
207
199
  """Export results to PDF format for executive reports."""
208
200
  filename = f"security-executive-report-{account_id}-{timestamp}.pdf"
209
201
  file_path = self.output_dir / filename
210
-
202
+
211
203
  # For now, create a placeholder PDF export
212
204
  # In production, this would use a proper PDF library like reportlab
213
205
  html_content = self._generate_executive_html(account_id, results, language)
214
-
206
+
215
207
  # Write HTML version as PDF placeholder
216
- html_path = file_path.with_suffix('.html')
208
+ html_path = file_path.with_suffix(".html")
217
209
  with html_path.open("w", encoding="utf-8") as f:
218
210
  f.write(html_content)
219
-
211
+
220
212
  print_info(f"PDF export created as HTML: {html_path}")
221
213
  return html_path
222
-
214
+
223
215
  def _calculate_summary_stats(self, results: Dict[str, List]) -> Dict[str, Any]:
224
216
  """Calculate summary statistics for the assessment."""
225
217
  total_checks = sum(len(findings) for findings in results.values())
226
218
  critical_issues = len(results.get("Danger", []))
227
219
  warnings = len(results.get("Warning", []))
228
220
  successful = len(results.get("Success", []))
229
-
221
+
230
222
  if total_checks > 0:
231
223
  security_score = (successful / total_checks) * 100
232
224
  else:
233
225
  security_score = 0
234
-
226
+
235
227
  return {
236
228
  "total_checks": total_checks,
237
229
  "critical_issues": critical_issues,
238
230
  "warnings": warnings,
239
231
  "successful_checks": successful,
240
232
  "security_score": round(security_score, 1),
241
- "compliance_status": "COMPLIANT" if security_score >= 80 else "NON_COMPLIANT"
233
+ "compliance_status": "COMPLIANT" if security_score >= 80 else "NON_COMPLIANT",
242
234
  }
243
-
235
+
244
236
  def _transform_findings_for_json(self, results: Dict[str, List]) -> List[Dict[str, Any]]:
245
237
  """Transform findings into structured JSON format."""
246
238
  findings = []
247
239
  finding_id = 1
248
-
240
+
249
241
  for level, level_findings in results.items():
250
242
  for finding in level_findings:
251
- if hasattr(finding, 'to_dict'):
243
+ if hasattr(finding, "to_dict"):
252
244
  finding_dict = finding.to_dict()
253
245
  elif isinstance(finding, dict):
254
246
  finding_dict = finding
255
247
  else:
256
248
  continue
257
-
249
+
258
250
  structured_finding = {
259
251
  "finding_id": f"SEC-{finding_id:04d}",
260
252
  "status": level,
@@ -264,25 +256,19 @@ class SecurityExporter:
264
256
  "result_columns": finding_dict.get("result_cols", []),
265
257
  "result_rows": finding_dict.get("result_rows", []),
266
258
  "remediation_required": level in ["Danger", "Warning"],
267
- "compliance_impact": self._assess_compliance_impact(level)
259
+ "compliance_impact": self._assess_compliance_impact(level),
268
260
  }
269
-
261
+
270
262
  findings.append(structured_finding)
271
263
  finding_id += 1
272
-
264
+
273
265
  return findings
274
-
266
+
275
267
  def _map_level_to_severity(self, level: str) -> str:
276
268
  """Map security level to severity classification."""
277
- mapping = {
278
- "Danger": "CRITICAL",
279
- "Warning": "HIGH",
280
- "Success": "PASS",
281
- "Info": "INFO",
282
- "Error": "ERROR"
283
- }
269
+ mapping = {"Danger": "CRITICAL", "Warning": "HIGH", "Success": "PASS", "Info": "INFO", "Error": "ERROR"}
284
270
  return mapping.get(level, "UNKNOWN")
285
-
271
+
286
272
  def _assess_compliance_impact(self, level: str) -> str:
287
273
  """Assess compliance impact of finding."""
288
274
  if level == "Danger":
@@ -291,7 +277,7 @@ class SecurityExporter:
291
277
  return "MEDIUM"
292
278
  else:
293
279
  return "LOW"
294
-
280
+
295
281
  def _map_to_wa_framework(self, results: Dict[str, List]) -> Dict[str, Any]:
296
282
  """Map findings to AWS Well-Architected framework."""
297
283
  return {
@@ -301,10 +287,10 @@ class SecurityExporter:
301
287
  "identity_access_management": self._assess_iam_findings(results),
302
288
  "detective_controls": self._assess_detective_findings(results),
303
289
  "infrastructure_protection": self._assess_infrastructure_findings(results),
304
- "data_protection": self._assess_data_findings(results)
305
- }
290
+ "data_protection": self._assess_data_findings(results),
291
+ },
306
292
  }
307
-
293
+
308
294
  def _map_to_soc2_framework(self, results: Dict[str, List]) -> Dict[str, Any]:
309
295
  """Map findings to SOC2 compliance framework."""
310
296
  return {
@@ -314,10 +300,10 @@ class SecurityExporter:
314
300
  "security": self._assess_soc2_security(results),
315
301
  "availability": self._assess_soc2_availability(results),
316
302
  "processing_integrity": self._assess_soc2_processing(results),
317
- "confidentiality": self._assess_soc2_confidentiality(results)
318
- }
303
+ "confidentiality": self._assess_soc2_confidentiality(results),
304
+ },
319
305
  }
320
-
306
+
321
307
  def _map_to_enterprise_framework(self, results: Dict[str, List]) -> Dict[str, Any]:
322
308
  """Map findings to enterprise baseline framework."""
323
309
  return {
@@ -327,72 +313,72 @@ class SecurityExporter:
327
313
  "access_controls": self._assess_access_controls(results),
328
314
  "monitoring_logging": self._assess_monitoring_controls(results),
329
315
  "encryption_protection": self._assess_encryption_controls(results),
330
- "incident_response": self._assess_incident_controls(results)
331
- }
316
+ "incident_response": self._assess_incident_controls(results),
317
+ },
332
318
  }
333
-
319
+
334
320
  def _calculate_framework_score(self, results: Dict[str, List], framework: str) -> float:
335
321
  """Calculate compliance score for specific framework."""
336
322
  # Simplified scoring - in production would map specific checks to framework requirements
337
323
  total_checks = sum(len(findings) for findings in results.values())
338
324
  successful = len(results.get("Success", []))
339
-
325
+
340
326
  if total_checks > 0:
341
327
  return round((successful / total_checks) * 100, 1)
342
328
  return 0.0
343
-
329
+
344
330
  def _assess_iam_findings(self, results: Dict[str, List]) -> Dict[str, Any]:
345
331
  """Assess IAM-related findings."""
346
332
  return {"status": "ASSESSED", "findings_count": len(results.get("Success", [])), "risk_level": "LOW"}
347
-
333
+
348
334
  def _assess_detective_findings(self, results: Dict[str, List]) -> Dict[str, Any]:
349
335
  """Assess detective control findings."""
350
336
  return {"status": "ASSESSED", "findings_count": len(results.get("Warning", [])), "risk_level": "MEDIUM"}
351
-
337
+
352
338
  def _assess_infrastructure_findings(self, results: Dict[str, List]) -> Dict[str, Any]:
353
339
  """Assess infrastructure protection findings."""
354
340
  return {"status": "ASSESSED", "findings_count": len(results.get("Danger", [])), "risk_level": "HIGH"}
355
-
341
+
356
342
  def _assess_data_findings(self, results: Dict[str, List]) -> Dict[str, Any]:
357
- """Assess data protection findings."""
343
+ """Assess data protection findings."""
358
344
  return {"status": "ASSESSED", "findings_count": len(results.get("Info", [])), "risk_level": "LOW"}
359
-
345
+
360
346
  def _assess_soc2_security(self, results: Dict[str, List]) -> Dict[str, Any]:
361
347
  """Assess SOC2 security criteria."""
362
348
  return {"status": "COMPLIANT", "findings_count": 0, "risk_level": "LOW"}
363
-
349
+
364
350
  def _assess_soc2_availability(self, results: Dict[str, List]) -> Dict[str, Any]:
365
351
  """Assess SOC2 availability criteria."""
366
352
  return {"status": "COMPLIANT", "findings_count": 0, "risk_level": "LOW"}
367
-
353
+
368
354
  def _assess_soc2_processing(self, results: Dict[str, List]) -> Dict[str, Any]:
369
355
  """Assess SOC2 processing integrity."""
370
356
  return {"status": "COMPLIANT", "findings_count": 0, "risk_level": "LOW"}
371
-
357
+
372
358
  def _assess_soc2_confidentiality(self, results: Dict[str, List]) -> Dict[str, Any]:
373
359
  """Assess SOC2 confidentiality criteria."""
374
360
  return {"status": "COMPLIANT", "findings_count": 0, "risk_level": "LOW"}
375
-
361
+
376
362
  def _assess_access_controls(self, results: Dict[str, List]) -> Dict[str, Any]:
377
363
  """Assess access control compliance."""
378
364
  return {"status": "COMPLIANT", "findings_count": 0, "risk_level": "LOW"}
379
-
365
+
380
366
  def _assess_monitoring_controls(self, results: Dict[str, List]) -> Dict[str, Any]:
381
367
  """Assess monitoring and logging controls."""
382
368
  return {"status": "COMPLIANT", "findings_count": 0, "risk_level": "LOW"}
383
-
369
+
384
370
  def _assess_encryption_controls(self, results: Dict[str, List]) -> Dict[str, Any]:
385
371
  """Assess encryption and protection controls."""
386
372
  return {"status": "COMPLIANT", "findings_count": 0, "risk_level": "LOW"}
387
-
373
+
388
374
  def _assess_incident_controls(self, results: Dict[str, List]) -> Dict[str, Any]:
389
375
  """Assess incident response controls."""
390
376
  return {"status": "COMPLIANT", "findings_count": 0, "risk_level": "LOW"}
391
-
377
+
392
378
  def _generate_executive_html(self, account_id: str, results: Dict[str, List], language: str) -> str:
393
379
  """Generate executive HTML report."""
394
380
  summary = self._calculate_summary_stats(results)
395
-
381
+
396
382
  html_content = f"""
397
383
  <!DOCTYPE html>
398
384
  <html lang="{language.lower()}">
@@ -413,30 +399,30 @@ class SecurityExporter:
413
399
  <div class="header">
414
400
  <h1>🛡️ Security Assessment Executive Report</h1>
415
401
  <p><strong>Account ID:</strong> {account_id}</p>
416
- <p><strong>Assessment Date:</strong> {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}</p>
402
+ <p><strong>Assessment Date:</strong> {datetime.now().strftime("%Y-%m-%d %H:%M:%S")}</p>
417
403
  <p><strong>Language:</strong> {language}</p>
418
404
  </div>
419
405
 
420
406
  <div class="summary">
421
407
  <h2>Executive Summary</h2>
422
- <p><strong>Security Score:</strong> {summary['security_score']}%</p>
423
- <p><strong>Total Checks:</strong> {summary['total_checks']}</p>
424
- <p class="critical"><strong>Critical Issues:</strong> {summary['critical_issues']}</p>
425
- <p class="warning"><strong>Warnings:</strong> {summary['warnings']}</p>
426
- <p class="success"><strong>Successful Checks:</strong> {summary['successful_checks']}</p>
427
- <p><strong>Compliance Status:</strong> {summary['compliance_status']}</p>
408
+ <p><strong>Security Score:</strong> {summary["security_score"]}%</p>
409
+ <p><strong>Total Checks:</strong> {summary["total_checks"]}</p>
410
+ <p class="critical"><strong>Critical Issues:</strong> {summary["critical_issues"]}</p>
411
+ <p class="warning"><strong>Warnings:</strong> {summary["warnings"]}</p>
412
+ <p class="success"><strong>Successful Checks:</strong> {summary["successful_checks"]}</p>
413
+ <p><strong>Compliance Status:</strong> {summary["compliance_status"]}</p>
428
414
  </div>
429
415
  </body>
430
416
  </html>
431
417
  """
432
418
  return html_content
433
-
419
+
434
420
  def _display_export_summary(self, export_results: Dict[str, str], account_id: str):
435
421
  """Display export summary with Rich formatting."""
436
422
  if not export_results:
437
423
  print_warning("No files were exported successfully")
438
424
  return
439
-
425
+
440
426
  # Create export summary table
441
427
  summary_table = create_table(
442
428
  title="📁 Export Summary",
@@ -444,19 +430,16 @@ class SecurityExporter:
444
430
  {"name": "Format", "style": "bold cyan", "justify": "left"},
445
431
  {"name": "File Path", "style": "dim", "justify": "left"},
446
432
  {"name": "Status", "style": "bold", "justify": "center"},
447
- ]
433
+ ],
448
434
  )
449
-
435
+
450
436
  for format_type, file_path in export_results.items():
451
437
  summary_table.add_row(
452
- format_type.upper(),
453
- str(file_path),
454
- f"{STATUS_INDICATORS['success']} Exported",
455
- style="success"
438
+ format_type.upper(), str(file_path), f"{STATUS_INDICATORS['success']} Exported", style="success"
456
439
  )
457
-
440
+
458
441
  console.print(summary_table)
459
-
442
+
460
443
  # Display final export summary
461
444
  export_summary = f"""[bold green]Security Data Export Complete[/bold green]
462
445
 
@@ -466,12 +449,8 @@ class SecurityExporter:
466
449
 
467
450
  [dim]All exports completed successfully. Files are ready for analysis.[/dim]"""
468
451
 
469
- console.print(create_panel(
470
- export_summary,
471
- title="✅ Export Complete",
472
- border_style="green"
473
- ))
452
+ console.print(create_panel(export_summary, title="✅ Export Complete", border_style="green"))
474
453
 
475
454
 
476
455
  # Export functionality for external use
477
- __all__ = ["SecurityExporter"]
456
+ __all__ = ["SecurityExporter"]