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.
- runbooks/__init__.py +1 -1
- runbooks/base.py +5 -1
- runbooks/cfat/__init__.py +8 -4
- runbooks/cfat/assessment/collectors.py +171 -14
- runbooks/cfat/assessment/compliance.py +871 -0
- runbooks/cfat/assessment/runner.py +122 -11
- runbooks/cfat/models.py +6 -2
- runbooks/common/logger.py +14 -0
- runbooks/common/rich_utils.py +451 -0
- runbooks/enterprise/__init__.py +68 -0
- runbooks/enterprise/error_handling.py +411 -0
- runbooks/enterprise/logging.py +439 -0
- runbooks/enterprise/multi_tenant.py +583 -0
- runbooks/finops/README.md +468 -241
- runbooks/finops/__init__.py +39 -3
- runbooks/finops/cli.py +83 -18
- runbooks/finops/cross_validation.py +375 -0
- runbooks/finops/dashboard_runner.py +812 -164
- runbooks/finops/enhanced_dashboard_runner.py +525 -0
- runbooks/finops/finops_dashboard.py +1892 -0
- runbooks/finops/helpers.py +485 -51
- runbooks/finops/optimizer.py +823 -0
- runbooks/finops/tests/__init__.py +19 -0
- runbooks/finops/tests/results_test_finops_dashboard.xml +1 -0
- runbooks/finops/tests/run_comprehensive_tests.py +421 -0
- runbooks/finops/tests/run_tests.py +305 -0
- runbooks/finops/tests/test_finops_dashboard.py +705 -0
- runbooks/finops/tests/test_integration.py +477 -0
- runbooks/finops/tests/test_performance.py +380 -0
- runbooks/finops/tests/test_performance_benchmarks.py +500 -0
- runbooks/finops/tests/test_reference_images_validation.py +867 -0
- runbooks/finops/tests/test_single_account_features.py +715 -0
- runbooks/finops/tests/validate_test_suite.py +220 -0
- runbooks/finops/types.py +1 -1
- runbooks/hitl/enhanced_workflow_engine.py +725 -0
- runbooks/inventory/artifacts/scale-optimize-status.txt +12 -0
- runbooks/inventory/collectors/aws_comprehensive.py +442 -0
- runbooks/inventory/collectors/enterprise_scale.py +281 -0
- runbooks/inventory/core/collector.py +172 -13
- runbooks/inventory/discovery.md +1 -1
- runbooks/inventory/list_ec2_instances.py +18 -20
- runbooks/inventory/list_ssm_parameters.py +31 -3
- runbooks/inventory/organizations_discovery.py +1269 -0
- runbooks/inventory/rich_inventory_display.py +393 -0
- runbooks/inventory/run_on_multi_accounts.py +35 -19
- runbooks/inventory/runbooks.security.report_generator.log +0 -0
- runbooks/inventory/runbooks.security.run_script.log +0 -0
- runbooks/inventory/vpc_flow_analyzer.py +1030 -0
- runbooks/main.py +2215 -119
- runbooks/metrics/dora_metrics_engine.py +599 -0
- runbooks/operate/__init__.py +2 -2
- runbooks/operate/base.py +122 -10
- runbooks/operate/deployment_framework.py +1032 -0
- runbooks/operate/deployment_validator.py +853 -0
- runbooks/operate/dynamodb_operations.py +10 -6
- runbooks/operate/ec2_operations.py +319 -11
- runbooks/operate/executive_dashboard.py +779 -0
- runbooks/operate/mcp_integration.py +750 -0
- runbooks/operate/nat_gateway_operations.py +1120 -0
- runbooks/operate/networking_cost_heatmap.py +685 -0
- runbooks/operate/privatelink_operations.py +940 -0
- runbooks/operate/s3_operations.py +10 -6
- runbooks/operate/vpc_endpoints.py +644 -0
- runbooks/operate/vpc_operations.py +1038 -0
- runbooks/remediation/__init__.py +2 -2
- runbooks/remediation/acm_remediation.py +1 -1
- runbooks/remediation/base.py +1 -1
- runbooks/remediation/cloudtrail_remediation.py +1 -1
- runbooks/remediation/cognito_remediation.py +1 -1
- runbooks/remediation/dynamodb_remediation.py +1 -1
- runbooks/remediation/ec2_remediation.py +1 -1
- runbooks/remediation/ec2_unattached_ebs_volumes.py +1 -1
- runbooks/remediation/kms_enable_key_rotation.py +1 -1
- runbooks/remediation/kms_remediation.py +1 -1
- runbooks/remediation/lambda_remediation.py +1 -1
- runbooks/remediation/multi_account.py +1 -1
- runbooks/remediation/rds_remediation.py +1 -1
- runbooks/remediation/s3_block_public_access.py +1 -1
- runbooks/remediation/s3_enable_access_logging.py +1 -1
- runbooks/remediation/s3_encryption.py +1 -1
- runbooks/remediation/s3_remediation.py +1 -1
- runbooks/remediation/vpc_remediation.py +475 -0
- runbooks/security/__init__.py +3 -1
- runbooks/security/compliance_automation.py +632 -0
- runbooks/security/report_generator.py +10 -0
- runbooks/security/run_script.py +31 -5
- runbooks/security/security_baseline_tester.py +169 -30
- runbooks/security/security_export.py +477 -0
- runbooks/validation/__init__.py +10 -0
- runbooks/validation/benchmark.py +484 -0
- runbooks/validation/cli.py +356 -0
- runbooks/validation/mcp_validator.py +768 -0
- runbooks/vpc/__init__.py +38 -0
- runbooks/vpc/config.py +212 -0
- runbooks/vpc/cost_engine.py +347 -0
- runbooks/vpc/heatmap_engine.py +605 -0
- runbooks/vpc/manager_interface.py +634 -0
- runbooks/vpc/networking_wrapper.py +1260 -0
- runbooks/vpc/rich_formatters.py +679 -0
- runbooks/vpc/tests/__init__.py +5 -0
- runbooks/vpc/tests/conftest.py +356 -0
- runbooks/vpc/tests/test_cli_integration.py +530 -0
- runbooks/vpc/tests/test_config.py +458 -0
- runbooks/vpc/tests/test_cost_engine.py +479 -0
- runbooks/vpc/tests/test_networking_wrapper.py +512 -0
- {runbooks-0.7.6.dist-info → runbooks-0.7.9.dist-info}/METADATA +40 -12
- {runbooks-0.7.6.dist-info → runbooks-0.7.9.dist-info}/RECORD +111 -50
- {runbooks-0.7.6.dist-info → runbooks-0.7.9.dist-info}/WHEEL +0 -0
- {runbooks-0.7.6.dist-info → runbooks-0.7.9.dist-info}/entry_points.txt +0 -0
- {runbooks-0.7.6.dist-info → runbooks-0.7.9.dist-info}/licenses/LICENSE +0 -0
- {runbooks-0.7.6.dist-info → runbooks-0.7.9.dist-info}/top_level.txt +0 -0
runbooks/security/run_script.py
CHANGED
@@ -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
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
66
|
-
|
67
|
-
|
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
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
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
|
-
|
135
|
+
print_info(f"Generating credential report for account {account_id}")
|
94
136
|
credential_report = common.generate_credential_report(iam_client)
|
95
137
|
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
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
|
-
|
290
|
+
print_info(f"Created results directory: {results_dir}")
|
168
291
|
else:
|
169
|
-
|
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
|
-
|
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}")
|