runbooks 0.7.6__py3-none-any.whl → 0.7.9__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 (111) hide show
  1. runbooks/__init__.py +1 -1
  2. runbooks/base.py +5 -1
  3. runbooks/cfat/__init__.py +8 -4
  4. runbooks/cfat/assessment/collectors.py +171 -14
  5. runbooks/cfat/assessment/compliance.py +871 -0
  6. runbooks/cfat/assessment/runner.py +122 -11
  7. runbooks/cfat/models.py +6 -2
  8. runbooks/common/logger.py +14 -0
  9. runbooks/common/rich_utils.py +451 -0
  10. runbooks/enterprise/__init__.py +68 -0
  11. runbooks/enterprise/error_handling.py +411 -0
  12. runbooks/enterprise/logging.py +439 -0
  13. runbooks/enterprise/multi_tenant.py +583 -0
  14. runbooks/finops/README.md +468 -241
  15. runbooks/finops/__init__.py +39 -3
  16. runbooks/finops/cli.py +83 -18
  17. runbooks/finops/cross_validation.py +375 -0
  18. runbooks/finops/dashboard_runner.py +812 -164
  19. runbooks/finops/enhanced_dashboard_runner.py +525 -0
  20. runbooks/finops/finops_dashboard.py +1892 -0
  21. runbooks/finops/helpers.py +485 -51
  22. runbooks/finops/optimizer.py +823 -0
  23. runbooks/finops/tests/__init__.py +19 -0
  24. runbooks/finops/tests/results_test_finops_dashboard.xml +1 -0
  25. runbooks/finops/tests/run_comprehensive_tests.py +421 -0
  26. runbooks/finops/tests/run_tests.py +305 -0
  27. runbooks/finops/tests/test_finops_dashboard.py +705 -0
  28. runbooks/finops/tests/test_integration.py +477 -0
  29. runbooks/finops/tests/test_performance.py +380 -0
  30. runbooks/finops/tests/test_performance_benchmarks.py +500 -0
  31. runbooks/finops/tests/test_reference_images_validation.py +867 -0
  32. runbooks/finops/tests/test_single_account_features.py +715 -0
  33. runbooks/finops/tests/validate_test_suite.py +220 -0
  34. runbooks/finops/types.py +1 -1
  35. runbooks/hitl/enhanced_workflow_engine.py +725 -0
  36. runbooks/inventory/artifacts/scale-optimize-status.txt +12 -0
  37. runbooks/inventory/collectors/aws_comprehensive.py +442 -0
  38. runbooks/inventory/collectors/enterprise_scale.py +281 -0
  39. runbooks/inventory/core/collector.py +172 -13
  40. runbooks/inventory/discovery.md +1 -1
  41. runbooks/inventory/list_ec2_instances.py +18 -20
  42. runbooks/inventory/list_ssm_parameters.py +31 -3
  43. runbooks/inventory/organizations_discovery.py +1269 -0
  44. runbooks/inventory/rich_inventory_display.py +393 -0
  45. runbooks/inventory/run_on_multi_accounts.py +35 -19
  46. runbooks/inventory/runbooks.security.report_generator.log +0 -0
  47. runbooks/inventory/runbooks.security.run_script.log +0 -0
  48. runbooks/inventory/vpc_flow_analyzer.py +1030 -0
  49. runbooks/main.py +2215 -119
  50. runbooks/metrics/dora_metrics_engine.py +599 -0
  51. runbooks/operate/__init__.py +2 -2
  52. runbooks/operate/base.py +122 -10
  53. runbooks/operate/deployment_framework.py +1032 -0
  54. runbooks/operate/deployment_validator.py +853 -0
  55. runbooks/operate/dynamodb_operations.py +10 -6
  56. runbooks/operate/ec2_operations.py +319 -11
  57. runbooks/operate/executive_dashboard.py +779 -0
  58. runbooks/operate/mcp_integration.py +750 -0
  59. runbooks/operate/nat_gateway_operations.py +1120 -0
  60. runbooks/operate/networking_cost_heatmap.py +685 -0
  61. runbooks/operate/privatelink_operations.py +940 -0
  62. runbooks/operate/s3_operations.py +10 -6
  63. runbooks/operate/vpc_endpoints.py +644 -0
  64. runbooks/operate/vpc_operations.py +1038 -0
  65. runbooks/remediation/__init__.py +2 -2
  66. runbooks/remediation/acm_remediation.py +1 -1
  67. runbooks/remediation/base.py +1 -1
  68. runbooks/remediation/cloudtrail_remediation.py +1 -1
  69. runbooks/remediation/cognito_remediation.py +1 -1
  70. runbooks/remediation/dynamodb_remediation.py +1 -1
  71. runbooks/remediation/ec2_remediation.py +1 -1
  72. runbooks/remediation/ec2_unattached_ebs_volumes.py +1 -1
  73. runbooks/remediation/kms_enable_key_rotation.py +1 -1
  74. runbooks/remediation/kms_remediation.py +1 -1
  75. runbooks/remediation/lambda_remediation.py +1 -1
  76. runbooks/remediation/multi_account.py +1 -1
  77. runbooks/remediation/rds_remediation.py +1 -1
  78. runbooks/remediation/s3_block_public_access.py +1 -1
  79. runbooks/remediation/s3_enable_access_logging.py +1 -1
  80. runbooks/remediation/s3_encryption.py +1 -1
  81. runbooks/remediation/s3_remediation.py +1 -1
  82. runbooks/remediation/vpc_remediation.py +475 -0
  83. runbooks/security/__init__.py +3 -1
  84. runbooks/security/compliance_automation.py +632 -0
  85. runbooks/security/report_generator.py +10 -0
  86. runbooks/security/run_script.py +31 -5
  87. runbooks/security/security_baseline_tester.py +169 -30
  88. runbooks/security/security_export.py +477 -0
  89. runbooks/validation/__init__.py +10 -0
  90. runbooks/validation/benchmark.py +484 -0
  91. runbooks/validation/cli.py +356 -0
  92. runbooks/validation/mcp_validator.py +768 -0
  93. runbooks/vpc/__init__.py +38 -0
  94. runbooks/vpc/config.py +212 -0
  95. runbooks/vpc/cost_engine.py +347 -0
  96. runbooks/vpc/heatmap_engine.py +605 -0
  97. runbooks/vpc/manager_interface.py +634 -0
  98. runbooks/vpc/networking_wrapper.py +1260 -0
  99. runbooks/vpc/rich_formatters.py +679 -0
  100. runbooks/vpc/tests/__init__.py +5 -0
  101. runbooks/vpc/tests/conftest.py +356 -0
  102. runbooks/vpc/tests/test_cli_integration.py +530 -0
  103. runbooks/vpc/tests/test_config.py +458 -0
  104. runbooks/vpc/tests/test_cost_engine.py +479 -0
  105. runbooks/vpc/tests/test_networking_wrapper.py +512 -0
  106. {runbooks-0.7.6.dist-info → runbooks-0.7.9.dist-info}/METADATA +40 -12
  107. {runbooks-0.7.6.dist-info → runbooks-0.7.9.dist-info}/RECORD +111 -50
  108. {runbooks-0.7.6.dist-info → runbooks-0.7.9.dist-info}/WHEEL +0 -0
  109. {runbooks-0.7.6.dist-info → runbooks-0.7.9.dist-info}/entry_points.txt +0 -0
  110. {runbooks-0.7.6.dist-info → runbooks-0.7.9.dist-info}/licenses/LICENSE +0 -0
  111. {runbooks-0.7.6.dist-info → runbooks-0.7.9.dist-info}/top_level.txt +0 -0
@@ -16,6 +16,14 @@ import argparse
16
16
  import sys
17
17
 
18
18
  from runbooks.utils.logger import configure_logger
19
+ from runbooks.common.rich_utils import (
20
+ console,
21
+ create_panel,
22
+ print_error,
23
+ print_info,
24
+ print_success,
25
+ print_warning,
26
+ )
19
27
 
20
28
  from .security_baseline_tester import SecurityBaselineTester
21
29
 
@@ -65,17 +73,35 @@ def main():
65
73
  try:
66
74
  args = parse_arguments()
67
75
 
68
- logger.info("Starting AWS Security Baseline Tester...")
69
- logger.info(f"Using AWS profile: {args.profile}")
70
- logger.info(f"Report language: {args.language}")
71
- logger.info(f"Output directory: {args.output}")
76
+ # Display startup information with Rich formatting
77
+ startup_info = f"""[bold cyan]AWS Security Baseline Tester[/bold cyan]
78
+
79
+ [green]Configuration:[/green]
80
+ [cyan]AWS Profile:[/cyan] {args.profile}
81
+ [cyan]Language:[/cyan] {args.language}
82
+ [cyan]Output Directory:[/cyan] {args.output or './results'}
83
+
84
+ [dim]Starting comprehensive security assessment...[/dim]"""
85
+
86
+ console.print(create_panel(
87
+ startup_info,
88
+ title="🔒 Security Baseline Tester",
89
+ border_style="cyan"
90
+ ))
91
+
92
+ print_info("Initializing AWS Security Baseline Tester...")
93
+ print_info(f"Using AWS profile: {args.profile}")
94
+ print_info(f"Report language: {args.language}")
95
+ print_info(f"Output directory: {args.output or './results'}")
72
96
 
73
97
  ## Instantiate and run the Security Baseline Tester
74
98
  tester = SecurityBaselineTester(args.profile, args.language, args.output)
75
99
  tester.run()
76
100
 
77
- logger.info("AWS Security Baseline testing completed successfully.")
101
+ print_success("AWS Security Baseline testing completed successfully!")
102
+
78
103
  except Exception as e:
104
+ print_error(f"An unexpected error occurred: {e}", exception=e)
79
105
  logger.error(f"An unexpected error occurred: {e}", exc_info=True)
80
106
  sys.exit(1)
81
107
 
@@ -5,26 +5,43 @@ import logging
5
5
  import os
6
6
  from concurrent.futures import ThreadPoolExecutor, as_completed
7
7
  from pathlib import Path
8
+ from typing import Any, Dict, List, Optional
8
9
 
9
10
  import boto3
10
11
  import botocore
11
12
 
13
+ from runbooks.common.rich_utils import (
14
+ STATUS_INDICATORS,
15
+ console,
16
+ create_panel,
17
+ create_progress_bar,
18
+ create_table,
19
+ print_error,
20
+ print_info,
21
+ print_status,
22
+ print_success,
23
+ print_warning,
24
+ )
25
+
12
26
  from . import (
13
27
  checklist, # noqa: F403
14
28
  report_generator,
15
29
  )
30
+ from .security_export import SecurityExporter
16
31
  from .utils import common, language, level_const
17
32
 
18
33
  # from .utils.language import get_translator
19
34
 
20
35
 
21
36
  class SecurityBaselineTester:
22
- def __init__(self, profile, lang_code, output_dir):
37
+ def __init__(self, profile, lang_code, output_dir, export_formats: List[str] = None):
23
38
  self.profile = profile
24
39
  self.language = lang_code
25
40
  self.output = output_dir
41
+ self.export_formats = export_formats or ["json", "csv"]
26
42
  self.session = self._create_session()
27
43
  self.config = self._load_config()
44
+ self.exporter = SecurityExporter(output_dir)
28
45
  ## Call module 'language' and pass the string 'lang_code'
29
46
  self.translator = language.get_translator("main", lang_code)
30
47
 
@@ -50,22 +67,41 @@ class SecurityBaselineTester:
50
67
  raise
51
68
 
52
69
  def run(self):
70
+ """Execute the security baseline assessment with Rich CLI output."""
53
71
  try:
72
+ # Print security assessment header
73
+ console.print(create_panel(
74
+ "[bold cyan]AWS Security Baseline Assessment[/bold cyan]\n\n"
75
+ f"[dim]Profile: {self.profile} | Language: {self.language}[/dim]",
76
+ title="🛡️ Starting Security Assessment",
77
+ border_style="cyan"
78
+ ))
79
+
54
80
  self._validate_session()
55
81
  caller_identity = self._get_caller_identity()
56
82
  self._print_auditor_info(caller_identity)
57
83
 
58
- logging.info(self.translator.translate("start_test"))
84
+ print_info("Initiating comprehensive security baseline tests...")
59
85
 
60
86
  account_id, results = self._execute_tests()
61
87
  self._generate_report(account_id, results)
62
-
63
- logging.info(self.translator.translate("test_completed"))
88
+
89
+ # Export results in multiple formats
90
+ if self.export_formats:
91
+ print_info("Exporting security assessment results...")
92
+ self.exporter.export_security_results(
93
+ account_id=account_id,
94
+ results=results,
95
+ language=self.language,
96
+ formats=self.export_formats
97
+ )
98
+
99
+ print_success("Security baseline assessment completed successfully!")
100
+
64
101
  except Exception as e:
65
- logging.error(
66
- f"An error occurred during the security baseline test: {str(e)}",
67
- exc_info=True,
68
- )
102
+ print_error(f"Security baseline test failed: {str(e)}", exception=e)
103
+ logging.error(f"An error occurred during the security baseline test: {str(e)}", exc_info=True)
104
+ raise
69
105
 
70
106
  def _validate_session(self):
71
107
  if self.session.region_name is None:
@@ -79,35 +115,122 @@ class SecurityBaselineTester:
79
115
  raise
80
116
 
81
117
  def _print_auditor_info(self, caller_identity):
82
- logging.info("==================== AUDITOR INFO ====================")
83
- logging.info(f"USER ID : {caller_identity['UserId']}")
84
- logging.info(f"ACCOUNT : {caller_identity['Account']}")
85
- logging.info(f"ARN : {caller_identity['Arn']}")
86
- logging.info("=====================================================")
118
+ """Display auditor information with Rich formatting."""
119
+ auditor_info = f"""[bold cyan]User ID:[/bold cyan] {caller_identity['UserId']}
120
+ [bold cyan]Account:[/bold cyan] {caller_identity['Account']}
121
+ [bold cyan]ARN:[/bold cyan] {caller_identity['Arn']}"""
122
+
123
+ console.print(create_panel(
124
+ auditor_info,
125
+ title="🔐 Security Assessment Context",
126
+ border_style="cyan",
127
+ padding=1
128
+ ))
87
129
 
88
130
  def _execute_tests(self):
89
131
  iam_client = self.session.client("iam")
90
132
  sts_client = self.session.client("sts")
91
133
 
92
134
  account_id = common.get_account_id(sts_client)
93
- logging.info(self.translator.translate("request_credential_report"))
135
+ print_info(f"Generating credential report for account {account_id}")
94
136
  credential_report = common.generate_credential_report(iam_client)
95
137
 
96
- with ThreadPoolExecutor(max_workers=self.config.get("max_workers", 5)) as executor:
97
- futures = {
98
- executor.submit(self._run_check, check_name, credential_report): check_name
99
- for check_name in self.config.get("checks", [])
100
- }
101
-
102
- results = {
103
- level: [] for level in ["Success", "Warning", "Danger", "Error", "Info"] if isinstance(level, str)
104
- }
105
- for future in as_completed(futures):
106
- result = future.result()
107
- results[result.level].append(result)
108
-
138
+ # Create progress bar for security checks
139
+ checks = self.config.get("checks", [])
140
+ total_checks = len(checks)
141
+
142
+ with create_progress_bar(description="Security Assessment") as progress:
143
+ task = progress.add_task("Running security checks...", total=total_checks)
144
+
145
+ with ThreadPoolExecutor(max_workers=self.config.get("max_workers", 5)) as executor:
146
+ futures = {
147
+ executor.submit(self._run_check, check_name, credential_report): check_name
148
+ for check_name in checks
149
+ }
150
+
151
+ results = {
152
+ level: [] for level in ["Success", "Warning", "Danger", "Error", "Info"] if isinstance(level, str)
153
+ }
154
+ completed_checks = 0
155
+
156
+ for future in as_completed(futures):
157
+ result = future.result()
158
+ results[result.level].append(result)
159
+ completed_checks += 1
160
+ progress.update(task, completed=completed_checks)
161
+
162
+ # Display security assessment summary
163
+ self._display_security_summary(results, total_checks)
109
164
  return account_id, results
110
165
 
166
+ def _display_security_summary(self, results: Dict[str, List], total_checks: int):
167
+ """Display security assessment summary with Rich formatting."""
168
+ # Create summary table
169
+ summary_table = create_table(
170
+ title="🛡️ Security Assessment Summary",
171
+ columns=[
172
+ {"name": "Status", "style": "bold", "justify": "left"},
173
+ {"name": "Count", "style": "bold", "justify": "center"},
174
+ {"name": "Percentage", "style": "dim", "justify": "right"},
175
+ ]
176
+ )
177
+
178
+ # Calculate statistics
179
+ for level in ["Danger", "Warning", "Success", "Info", "Error"]:
180
+ count = len(results.get(level, []))
181
+ if total_checks > 0:
182
+ percentage = (count / total_checks) * 100
183
+ percentage_str = f"{percentage:.1f}%"
184
+ else:
185
+ percentage_str = "0%"
186
+
187
+ # Style based on level
188
+ if level == "Danger":
189
+ status_text = f"🔴 {level}"
190
+ style = "error"
191
+ elif level == "Warning":
192
+ status_text = f"🟡 {level}"
193
+ style = "warning"
194
+ elif level == "Success":
195
+ status_text = f"🟢 {level}"
196
+ style = "success"
197
+ elif level == "Info":
198
+ status_text = f"🔵 {level}"
199
+ style = "info"
200
+ else: # Error
201
+ status_text = f"❌ {level}"
202
+ style = "critical"
203
+
204
+ summary_table.add_row(status_text, str(count), percentage_str, style=style)
205
+
206
+ console.print(summary_table)
207
+
208
+ # Calculate overall security score
209
+ total_issues = len(results.get("Danger", [])) + len(results.get("Warning", []))
210
+ total_success = len(results.get("Success", []))
211
+
212
+ if total_checks > 0:
213
+ security_score = (total_success / total_checks) * 100
214
+ if security_score >= 90:
215
+ score_style = "success"
216
+ score_icon = "🛡️"
217
+ elif security_score >= 75:
218
+ score_style = "warning"
219
+ score_icon = "⚠️"
220
+ else:
221
+ score_style = "error"
222
+ score_icon = "🚨"
223
+
224
+ score_summary = f"""[bold {score_style}]{score_icon} Overall Security Score: {security_score:.1f}%[/bold {score_style}]
225
+
226
+ [dim]Total Checks: {total_checks} | Issues Found: {total_issues} | Successful: {total_success}[/dim]"""
227
+
228
+ console.print(create_panel(
229
+ score_summary,
230
+ title="Security Posture Assessment",
231
+ border_style=score_style
232
+ ))
233
+
111
234
  def _run_check(self, check_name, credential_report):
112
235
  # check_module = __import__(f"checklist.{check_name}", fromlist=[check_name])
113
236
  check_module = importlib.import_module(f"runbooks.security.checklist.{check_name}")
@@ -164,9 +287,9 @@ class SecurityBaselineTester:
164
287
 
165
288
  if not results_dir.exists():
166
289
  results_dir.mkdir(parents=True, exist_ok=True)
167
- logging.info(self.translator.translate("results_folder_created"))
290
+ print_info(f"Created results directory: {results_dir}")
168
291
  else:
169
- logging.info(self.translator.translate("results_folder_already_exists"))
292
+ print_info(f"Using existing results directory: {results_dir}")
170
293
 
171
294
  return results_dir
172
295
 
@@ -198,4 +321,20 @@ class SecurityBaselineTester:
198
321
  with report_path.open("w", encoding="utf-8") as file:
199
322
  file.write(html_report)
200
323
 
201
- logging.info(self.translator.translate("generate_result_report"))
324
+ # Display report generation success with Rich formatting
325
+ report_success = f"""[bold green]Security Report Generated Successfully[/bold green]
326
+
327
+ [cyan]Report Location:[/cyan] {report_path}
328
+ [cyan]Account ID:[/cyan] {account_id}
329
+ [cyan]Language:[/cyan] {self.language}
330
+ [cyan]Report Time:[/cyan] {current_time}
331
+
332
+ [dim]Open the HTML report in your browser to view detailed findings.[/dim]"""
333
+
334
+ console.print(create_panel(
335
+ report_success,
336
+ title="📊 Report Generation Complete",
337
+ border_style="green"
338
+ ))
339
+
340
+ print_success(f"HTML report saved to: {report_path}")