runbooks 0.2.3__py3-none-any.whl → 0.6.1__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.
- conftest.py +26 -0
- jupyter-agent/.env.template +2 -0
- jupyter-agent/.gitattributes +35 -0
- jupyter-agent/README.md +16 -0
- jupyter-agent/app.py +256 -0
- jupyter-agent/cloudops-agent.png +0 -0
- jupyter-agent/ds-system-prompt.txt +154 -0
- jupyter-agent/jupyter-agent.png +0 -0
- jupyter-agent/llama3_template.jinja +123 -0
- jupyter-agent/requirements.txt +9 -0
- jupyter-agent/utils.py +409 -0
- runbooks/__init__.py +71 -3
- runbooks/__main__.py +13 -0
- runbooks/aws/ec2_describe_instances.py +1 -1
- runbooks/aws/ec2_run_instances.py +8 -2
- runbooks/aws/ec2_start_stop_instances.py +17 -4
- runbooks/aws/ec2_unused_volumes.py +5 -1
- runbooks/aws/s3_create_bucket.py +4 -2
- runbooks/aws/s3_list_objects.py +6 -1
- runbooks/aws/tagging_lambda_handler.py +13 -2
- runbooks/aws/tags.json +12 -0
- runbooks/base.py +353 -0
- runbooks/cfat/README.md +49 -0
- runbooks/cfat/__init__.py +74 -0
- runbooks/cfat/app.ts +644 -0
- runbooks/cfat/assessment/__init__.py +40 -0
- runbooks/cfat/assessment/asana-import.csv +39 -0
- runbooks/cfat/assessment/cfat-checks.csv +31 -0
- runbooks/cfat/assessment/cfat.txt +520 -0
- runbooks/cfat/assessment/collectors.py +200 -0
- runbooks/cfat/assessment/jira-import.csv +39 -0
- runbooks/cfat/assessment/runner.py +387 -0
- runbooks/cfat/assessment/validators.py +290 -0
- runbooks/cfat/cli.py +103 -0
- runbooks/cfat/docs/asana-import.csv +24 -0
- runbooks/cfat/docs/cfat-checks.csv +31 -0
- runbooks/cfat/docs/cfat.txt +335 -0
- runbooks/cfat/docs/checks-output.png +0 -0
- runbooks/cfat/docs/cloudshell-console-run.png +0 -0
- runbooks/cfat/docs/cloudshell-download.png +0 -0
- runbooks/cfat/docs/cloudshell-output.png +0 -0
- runbooks/cfat/docs/downloadfile.png +0 -0
- runbooks/cfat/docs/jira-import.csv +24 -0
- runbooks/cfat/docs/open-cloudshell.png +0 -0
- runbooks/cfat/docs/report-header.png +0 -0
- runbooks/cfat/models.py +1026 -0
- runbooks/cfat/package-lock.json +5116 -0
- runbooks/cfat/package.json +38 -0
- runbooks/cfat/report.py +496 -0
- runbooks/cfat/reporting/__init__.py +46 -0
- runbooks/cfat/reporting/exporters.py +337 -0
- runbooks/cfat/reporting/formatters.py +496 -0
- runbooks/cfat/reporting/templates.py +135 -0
- runbooks/cfat/run-assessment.sh +23 -0
- runbooks/cfat/runner.py +69 -0
- runbooks/cfat/src/actions/check-cloudtrail-existence.ts +43 -0
- runbooks/cfat/src/actions/check-config-existence.ts +37 -0
- runbooks/cfat/src/actions/check-control-tower.ts +37 -0
- runbooks/cfat/src/actions/check-ec2-existence.ts +46 -0
- runbooks/cfat/src/actions/check-iam-users.ts +50 -0
- runbooks/cfat/src/actions/check-legacy-cur.ts +30 -0
- runbooks/cfat/src/actions/check-org-cloudformation.ts +30 -0
- runbooks/cfat/src/actions/check-vpc-existence.ts +43 -0
- runbooks/cfat/src/actions/create-asanaimport.ts +14 -0
- runbooks/cfat/src/actions/create-backlog.ts +372 -0
- runbooks/cfat/src/actions/create-jiraimport.ts +15 -0
- runbooks/cfat/src/actions/create-report.ts +616 -0
- runbooks/cfat/src/actions/define-account-type.ts +51 -0
- runbooks/cfat/src/actions/get-enabled-org-policy-types.ts +40 -0
- runbooks/cfat/src/actions/get-enabled-org-services.ts +26 -0
- runbooks/cfat/src/actions/get-idc-info.ts +34 -0
- runbooks/cfat/src/actions/get-org-da-accounts.ts +34 -0
- runbooks/cfat/src/actions/get-org-details.ts +35 -0
- runbooks/cfat/src/actions/get-org-member-accounts.ts +44 -0
- runbooks/cfat/src/actions/get-org-ous.ts +35 -0
- runbooks/cfat/src/actions/get-regions.ts +22 -0
- runbooks/cfat/src/actions/zip-assessment.ts +27 -0
- runbooks/cfat/src/types/index.d.ts +147 -0
- runbooks/cfat/tests/__init__.py +141 -0
- runbooks/cfat/tests/test_cli.py +340 -0
- runbooks/cfat/tests/test_integration.py +290 -0
- runbooks/cfat/tests/test_models.py +505 -0
- runbooks/cfat/tests/test_reporting.py +354 -0
- runbooks/cfat/tsconfig.json +16 -0
- runbooks/cfat/webpack.config.cjs +27 -0
- runbooks/config.py +260 -0
- runbooks/finops/__init__.py +88 -0
- runbooks/finops/aws_client.py +245 -0
- runbooks/finops/cli.py +151 -0
- runbooks/finops/cost_processor.py +410 -0
- runbooks/finops/dashboard_runner.py +448 -0
- runbooks/finops/helpers.py +355 -0
- runbooks/finops/main.py +14 -0
- runbooks/finops/profile_processor.py +174 -0
- runbooks/finops/types.py +66 -0
- runbooks/finops/visualisations.py +80 -0
- runbooks/inventory/.gitignore +354 -0
- runbooks/inventory/ArgumentsClass.py +261 -0
- runbooks/inventory/Inventory_Modules.py +6130 -0
- runbooks/inventory/LandingZone/delete_lz.py +1075 -0
- runbooks/inventory/README.md +1320 -0
- runbooks/inventory/__init__.py +62 -0
- runbooks/inventory/account_class.py +532 -0
- runbooks/inventory/all_my_instances_wrapper.py +123 -0
- runbooks/inventory/aws_decorators.py +201 -0
- runbooks/inventory/cfn_move_stack_instances.py +1526 -0
- runbooks/inventory/check_cloudtrail_compliance.py +614 -0
- runbooks/inventory/check_controltower_readiness.py +1107 -0
- runbooks/inventory/check_landingzone_readiness.py +711 -0
- runbooks/inventory/cloudtrail.md +727 -0
- runbooks/inventory/collectors/__init__.py +20 -0
- runbooks/inventory/collectors/aws_compute.py +518 -0
- runbooks/inventory/collectors/aws_networking.py +275 -0
- runbooks/inventory/collectors/base.py +222 -0
- runbooks/inventory/core/__init__.py +19 -0
- runbooks/inventory/core/collector.py +303 -0
- runbooks/inventory/core/formatter.py +296 -0
- runbooks/inventory/delete_s3_buckets_objects.py +169 -0
- runbooks/inventory/discovery.md +81 -0
- runbooks/inventory/draw_org_structure.py +748 -0
- runbooks/inventory/ec2_vpc_utils.py +341 -0
- runbooks/inventory/find_cfn_drift_detection.py +272 -0
- runbooks/inventory/find_cfn_orphaned_stacks.py +719 -0
- runbooks/inventory/find_cfn_stackset_drift.py +733 -0
- runbooks/inventory/find_ec2_security_groups.py +669 -0
- runbooks/inventory/find_landingzone_versions.py +201 -0
- runbooks/inventory/find_vpc_flow_logs.py +1221 -0
- runbooks/inventory/inventory.sh +659 -0
- runbooks/inventory/list_cfn_stacks.py +558 -0
- runbooks/inventory/list_cfn_stackset_operation_results.py +252 -0
- runbooks/inventory/list_cfn_stackset_operations.py +734 -0
- runbooks/inventory/list_cfn_stacksets.py +453 -0
- runbooks/inventory/list_config_recorders_delivery_channels.py +681 -0
- runbooks/inventory/list_ds_directories.py +354 -0
- runbooks/inventory/list_ec2_availability_zones.py +286 -0
- runbooks/inventory/list_ec2_ebs_volumes.py +244 -0
- runbooks/inventory/list_ec2_instances.py +425 -0
- runbooks/inventory/list_ecs_clusters_and_tasks.py +562 -0
- runbooks/inventory/list_elbs_load_balancers.py +411 -0
- runbooks/inventory/list_enis_network_interfaces.py +526 -0
- runbooks/inventory/list_guardduty_detectors.py +568 -0
- runbooks/inventory/list_iam_policies.py +404 -0
- runbooks/inventory/list_iam_roles.py +518 -0
- runbooks/inventory/list_iam_saml_providers.py +359 -0
- runbooks/inventory/list_lambda_functions.py +882 -0
- runbooks/inventory/list_org_accounts.py +446 -0
- runbooks/inventory/list_org_accounts_users.py +354 -0
- runbooks/inventory/list_rds_db_instances.py +406 -0
- runbooks/inventory/list_route53_hosted_zones.py +318 -0
- runbooks/inventory/list_servicecatalog_provisioned_products.py +575 -0
- runbooks/inventory/list_sns_topics.py +360 -0
- runbooks/inventory/list_ssm_parameters.py +402 -0
- runbooks/inventory/list_vpc_subnets.py +433 -0
- runbooks/inventory/list_vpcs.py +422 -0
- runbooks/inventory/lockdown_cfn_stackset_role.py +224 -0
- runbooks/inventory/models/__init__.py +24 -0
- runbooks/inventory/models/account.py +192 -0
- runbooks/inventory/models/inventory.py +309 -0
- runbooks/inventory/models/resource.py +247 -0
- runbooks/inventory/recover_cfn_stack_ids.py +205 -0
- runbooks/inventory/requirements.txt +12 -0
- runbooks/inventory/run_on_multi_accounts.py +211 -0
- runbooks/inventory/tests/common_test_data.py +3661 -0
- runbooks/inventory/tests/common_test_functions.py +204 -0
- runbooks/inventory/tests/script_test_data.py +0 -0
- runbooks/inventory/tests/setup.py +24 -0
- runbooks/inventory/tests/src.py +18 -0
- runbooks/inventory/tests/test_cfn_describe_stacks.py +208 -0
- runbooks/inventory/tests/test_ec2_describe_instances.py +162 -0
- runbooks/inventory/tests/test_inventory_modules.py +55 -0
- runbooks/inventory/tests/test_lambda_list_functions.py +86 -0
- runbooks/inventory/tests/test_moto_integration_example.py +273 -0
- runbooks/inventory/tests/test_org_list_accounts.py +49 -0
- runbooks/inventory/update_aws_actions.py +173 -0
- runbooks/inventory/update_cfn_stacksets.py +1215 -0
- runbooks/inventory/update_cloudwatch_logs_retention_policy.py +294 -0
- runbooks/inventory/update_iam_roles_cross_accounts.py +478 -0
- runbooks/inventory/update_s3_public_access_block.py +539 -0
- runbooks/inventory/utils/__init__.py +23 -0
- runbooks/inventory/utils/aws_helpers.py +510 -0
- runbooks/inventory/utils/threading_utils.py +493 -0
- runbooks/inventory/utils/validation.py +682 -0
- runbooks/inventory/verify_ec2_security_groups.py +1430 -0
- runbooks/main.py +785 -0
- runbooks/organizations/__init__.py +12 -0
- runbooks/organizations/manager.py +374 -0
- runbooks/security_baseline/README.md +324 -0
- runbooks/security_baseline/checklist/alternate_contacts.py +8 -1
- runbooks/security_baseline/checklist/bucket_public_access.py +4 -1
- runbooks/security_baseline/checklist/cloudwatch_alarm_configuration.py +9 -2
- runbooks/security_baseline/checklist/guardduty_enabled.py +9 -2
- runbooks/security_baseline/checklist/multi_region_instance_usage.py +5 -1
- runbooks/security_baseline/checklist/root_access_key.py +6 -1
- runbooks/security_baseline/config-origin.json +1 -1
- runbooks/security_baseline/config.json +1 -1
- runbooks/security_baseline/permission.json +1 -1
- runbooks/security_baseline/report_generator.py +10 -2
- runbooks/security_baseline/report_template_en.html +8 -8
- runbooks/security_baseline/report_template_jp.html +8 -8
- runbooks/security_baseline/report_template_kr.html +13 -13
- runbooks/security_baseline/report_template_vn.html +8 -8
- runbooks/security_baseline/requirements.txt +7 -0
- runbooks/security_baseline/run_script.py +8 -2
- runbooks/security_baseline/security_baseline_tester.py +10 -2
- runbooks/security_baseline/utils/common.py +5 -1
- runbooks/utils/__init__.py +204 -0
- runbooks-0.6.1.dist-info/METADATA +373 -0
- runbooks-0.6.1.dist-info/RECORD +237 -0
- {runbooks-0.2.3.dist-info → runbooks-0.6.1.dist-info}/WHEEL +1 -1
- runbooks-0.6.1.dist-info/entry_points.txt +7 -0
- runbooks-0.6.1.dist-info/licenses/LICENSE +201 -0
- runbooks-0.6.1.dist-info/top_level.txt +3 -0
- runbooks/python101/calculator.py +0 -34
- runbooks/python101/config.py +0 -1
- runbooks/python101/exceptions.py +0 -16
- runbooks/python101/file_manager.py +0 -218
- runbooks/python101/toolkit.py +0 -153
- runbooks-0.2.3.dist-info/METADATA +0 -435
- runbooks-0.2.3.dist-info/RECORD +0 -61
- runbooks-0.2.3.dist-info/entry_points.txt +0 -3
- runbooks-0.2.3.dist-info/top_level.txt +0 -1
@@ -0,0 +1,337 @@
|
|
1
|
+
"""
|
2
|
+
Export Integrations for Cloud Foundations Assessment.
|
3
|
+
|
4
|
+
This module provides integration with popular project management
|
5
|
+
and ticketing systems for automated task creation and tracking:
|
6
|
+
|
7
|
+
- Jira integration for development workflows
|
8
|
+
- Asana integration for project management
|
9
|
+
- ServiceNow integration for enterprise ITSM
|
10
|
+
- Generic CSV/JSON exports for custom integrations
|
11
|
+
|
12
|
+
Each exporter formats assessment findings as actionable tasks
|
13
|
+
with appropriate priority, assignee, and remediation guidance.
|
14
|
+
"""
|
15
|
+
|
16
|
+
import csv
|
17
|
+
import json
|
18
|
+
from abc import ABC, abstractmethod
|
19
|
+
from datetime import datetime
|
20
|
+
from io import StringIO
|
21
|
+
from typing import Any, Dict, List, Optional
|
22
|
+
|
23
|
+
from loguru import logger
|
24
|
+
|
25
|
+
from runbooks.cfat.models import AssessmentReport, AssessmentResult, Severity
|
26
|
+
|
27
|
+
|
28
|
+
class BaseExporter(ABC):
|
29
|
+
"""Base class for assessment result exporters."""
|
30
|
+
|
31
|
+
@abstractmethod
|
32
|
+
def export(self, report: AssessmentReport, output_path: Optional[str] = None) -> str:
|
33
|
+
"""Export assessment results to target system."""
|
34
|
+
pass
|
35
|
+
|
36
|
+
@abstractmethod
|
37
|
+
def get_exporter_name(self) -> str:
|
38
|
+
"""Get exporter name."""
|
39
|
+
pass
|
40
|
+
|
41
|
+
def _map_severity_to_priority(self, severity: Severity) -> str:
|
42
|
+
"""Map assessment severity to priority level."""
|
43
|
+
mapping = {Severity.CRITICAL: "Critical", Severity.WARNING: "High", Severity.INFO: "Medium"}
|
44
|
+
return mapping.get(severity, "Medium")
|
45
|
+
|
46
|
+
|
47
|
+
class JiraExporter(BaseExporter):
|
48
|
+
"""Jira integration for creating tickets from assessment findings."""
|
49
|
+
|
50
|
+
def get_exporter_name(self) -> str:
|
51
|
+
return "jira"
|
52
|
+
|
53
|
+
def export(self, report: AssessmentReport, output_path: Optional[str] = None) -> str:
|
54
|
+
"""
|
55
|
+
Export assessment findings as Jira-compatible CSV.
|
56
|
+
|
57
|
+
Args:
|
58
|
+
report: Assessment report to export
|
59
|
+
output_path: Output file path (optional)
|
60
|
+
|
61
|
+
Returns:
|
62
|
+
CSV content for Jira import
|
63
|
+
"""
|
64
|
+
logger.info("Exporting assessment results for Jira import")
|
65
|
+
|
66
|
+
# Jira CSV headers
|
67
|
+
headers = ["Summary", "Issue Type", "Priority", "Description", "Labels", "Components", "Assignee", "Reporter"]
|
68
|
+
|
69
|
+
rows = []
|
70
|
+
failed_results = report.get_failed_results()
|
71
|
+
|
72
|
+
for result in failed_results:
|
73
|
+
# Create Jira-compatible task
|
74
|
+
summary = f"[CFAT] {result.check_name}: {result.message[:100]}"
|
75
|
+
|
76
|
+
description = self._create_jira_description(result, report)
|
77
|
+
|
78
|
+
priority = self._map_severity_to_priority(result.severity)
|
79
|
+
|
80
|
+
labels = f"cfat,{result.check_category},{result.severity.value.lower()}"
|
81
|
+
|
82
|
+
row = {
|
83
|
+
"Summary": summary,
|
84
|
+
"Issue Type": "Task",
|
85
|
+
"Priority": priority,
|
86
|
+
"Description": description,
|
87
|
+
"Labels": labels,
|
88
|
+
"Components": f"AWS-{result.check_category.upper()}",
|
89
|
+
"Assignee": "", # To be assigned
|
90
|
+
"Reporter": "CFAT-Assessment-Tool",
|
91
|
+
}
|
92
|
+
rows.append(row)
|
93
|
+
|
94
|
+
# Generate CSV
|
95
|
+
output = StringIO()
|
96
|
+
writer = csv.DictWriter(output, fieldnames=headers)
|
97
|
+
writer.writeheader()
|
98
|
+
writer.writerows(rows)
|
99
|
+
|
100
|
+
csv_content = output.getvalue()
|
101
|
+
|
102
|
+
if output_path:
|
103
|
+
with open(output_path, "w", encoding="utf-8") as f:
|
104
|
+
f.write(csv_content)
|
105
|
+
logger.info(f"Jira export saved to: {output_path}")
|
106
|
+
|
107
|
+
return csv_content
|
108
|
+
|
109
|
+
def _create_jira_description(self, result: AssessmentResult, report: AssessmentReport) -> str:
|
110
|
+
"""Create detailed Jira task description."""
|
111
|
+
lines = [
|
112
|
+
f"*Assessment Finding*: {result.finding_id}",
|
113
|
+
f"*Severity*: {result.severity.value}",
|
114
|
+
f"*Category*: {result.check_category}",
|
115
|
+
f"*Status*: {result.status.value}",
|
116
|
+
"",
|
117
|
+
"*Description*:",
|
118
|
+
result.message,
|
119
|
+
"",
|
120
|
+
"*AWS Resource*:",
|
121
|
+
result.resource_arn or "N/A",
|
122
|
+
"",
|
123
|
+
"*Recommendations*:",
|
124
|
+
]
|
125
|
+
|
126
|
+
for rec in result.recommendations:
|
127
|
+
lines.append(f"• {rec}")
|
128
|
+
|
129
|
+
lines.extend(
|
130
|
+
[
|
131
|
+
"",
|
132
|
+
f"*Assessment Details*:",
|
133
|
+
f"• Account ID: {report.account_id}",
|
134
|
+
f"• Region: {report.region}",
|
135
|
+
f"• Assessment Date: {report.timestamp.strftime('%Y-%m-%d %H:%M:%S')}",
|
136
|
+
f"• Execution Time: {result.execution_time:.2f}s",
|
137
|
+
]
|
138
|
+
)
|
139
|
+
|
140
|
+
return "\n".join(lines)
|
141
|
+
|
142
|
+
|
143
|
+
class AsanaExporter(BaseExporter):
|
144
|
+
"""Asana integration for project task creation."""
|
145
|
+
|
146
|
+
def get_exporter_name(self) -> str:
|
147
|
+
return "asana"
|
148
|
+
|
149
|
+
def export(self, report: AssessmentReport, output_path: Optional[str] = None) -> str:
|
150
|
+
"""
|
151
|
+
Export assessment findings as Asana-compatible CSV.
|
152
|
+
|
153
|
+
Args:
|
154
|
+
report: Assessment report to export
|
155
|
+
output_path: Output file path (optional)
|
156
|
+
|
157
|
+
Returns:
|
158
|
+
CSV content for Asana import
|
159
|
+
"""
|
160
|
+
logger.info("Exporting assessment results for Asana import")
|
161
|
+
|
162
|
+
# Asana CSV headers
|
163
|
+
headers = ["Name", "Notes", "Priority", "Tags", "Projects", "Due Date", "Assignee"]
|
164
|
+
|
165
|
+
rows = []
|
166
|
+
failed_results = report.get_failed_results()
|
167
|
+
|
168
|
+
for result in failed_results:
|
169
|
+
# Create Asana-compatible task
|
170
|
+
name = f"[CFAT] Fix {result.check_name}"
|
171
|
+
|
172
|
+
notes = self._create_asana_notes(result, report)
|
173
|
+
|
174
|
+
priority = self._map_severity_to_priority(result.severity)
|
175
|
+
|
176
|
+
tags = f"cfat,{result.check_category},{result.severity.value.lower()}"
|
177
|
+
|
178
|
+
# Set due date based on severity
|
179
|
+
due_date = self._calculate_due_date(result.severity)
|
180
|
+
|
181
|
+
row = {
|
182
|
+
"Name": name,
|
183
|
+
"Notes": notes,
|
184
|
+
"Priority": priority,
|
185
|
+
"Tags": tags,
|
186
|
+
"Projects": "AWS Security Remediation",
|
187
|
+
"Due Date": due_date,
|
188
|
+
"Assignee": "",
|
189
|
+
}
|
190
|
+
rows.append(row)
|
191
|
+
|
192
|
+
# Generate CSV
|
193
|
+
output = StringIO()
|
194
|
+
writer = csv.DictWriter(output, fieldnames=headers)
|
195
|
+
writer.writeheader()
|
196
|
+
writer.writerows(rows)
|
197
|
+
|
198
|
+
csv_content = output.getvalue()
|
199
|
+
|
200
|
+
if output_path:
|
201
|
+
with open(output_path, "w", encoding="utf-8") as f:
|
202
|
+
f.write(csv_content)
|
203
|
+
logger.info(f"Asana export saved to: {output_path}")
|
204
|
+
|
205
|
+
return csv_content
|
206
|
+
|
207
|
+
def _create_asana_notes(self, result: AssessmentResult, report: AssessmentReport) -> str:
|
208
|
+
"""Create Asana task notes."""
|
209
|
+
lines = [f"Finding: {result.finding_id}", f"Issue: {result.message}", "", "Recommended Actions:"]
|
210
|
+
|
211
|
+
for rec in result.recommendations:
|
212
|
+
lines.append(f"- {rec}")
|
213
|
+
|
214
|
+
return "\n".join(lines)
|
215
|
+
|
216
|
+
def _calculate_due_date(self, severity: Severity) -> str:
|
217
|
+
"""Calculate due date based on severity."""
|
218
|
+
from datetime import timedelta
|
219
|
+
|
220
|
+
today = datetime.now()
|
221
|
+
|
222
|
+
if severity == Severity.CRITICAL:
|
223
|
+
due = today + timedelta(days=7) # 1 week
|
224
|
+
elif severity == Severity.WARNING:
|
225
|
+
due = today + timedelta(days=30) # 1 month
|
226
|
+
else:
|
227
|
+
due = today + timedelta(days=90) # 3 months
|
228
|
+
|
229
|
+
return due.strftime("%Y-%m-%d")
|
230
|
+
|
231
|
+
|
232
|
+
class ServiceNowExporter(BaseExporter):
|
233
|
+
"""ServiceNow integration for enterprise ITSM."""
|
234
|
+
|
235
|
+
def get_exporter_name(self) -> str:
|
236
|
+
return "servicenow"
|
237
|
+
|
238
|
+
def export(self, report: AssessmentReport, output_path: Optional[str] = None) -> str:
|
239
|
+
"""
|
240
|
+
Export assessment findings as ServiceNow JSON.
|
241
|
+
|
242
|
+
Args:
|
243
|
+
report: Assessment report to export
|
244
|
+
output_path: Output file path (optional)
|
245
|
+
|
246
|
+
Returns:
|
247
|
+
JSON content for ServiceNow import
|
248
|
+
"""
|
249
|
+
logger.info("Exporting assessment results for ServiceNow import")
|
250
|
+
|
251
|
+
incidents = []
|
252
|
+
failed_results = report.get_failed_results()
|
253
|
+
|
254
|
+
for result in failed_results:
|
255
|
+
incident = {
|
256
|
+
"short_description": f"CFAT: {result.check_name}",
|
257
|
+
"description": result.message,
|
258
|
+
"category": "Security",
|
259
|
+
"subcategory": result.check_category.upper(),
|
260
|
+
"priority": self._map_severity_to_snow_priority(result.severity),
|
261
|
+
"impact": self._map_severity_to_impact(result.severity),
|
262
|
+
"urgency": self._map_severity_to_urgency(result.severity),
|
263
|
+
"assignment_group": "Cloud Security Team",
|
264
|
+
"work_notes": "\n".join(result.recommendations),
|
265
|
+
"cmdb_ci": result.resource_arn or report.account_id,
|
266
|
+
"business_service": "AWS Infrastructure",
|
267
|
+
}
|
268
|
+
incidents.append(incident)
|
269
|
+
|
270
|
+
export_data = {
|
271
|
+
"incidents": incidents,
|
272
|
+
"metadata": {
|
273
|
+
"assessment_id": f"cfat-{report.timestamp.strftime('%Y%m%d-%H%M%S')}",
|
274
|
+
"account_id": report.account_id,
|
275
|
+
"assessment_date": report.timestamp.isoformat(),
|
276
|
+
"total_findings": len(failed_results),
|
277
|
+
"compliance_score": report.summary.compliance_score,
|
278
|
+
},
|
279
|
+
}
|
280
|
+
|
281
|
+
json_content = json.dumps(export_data, indent=2, default=str)
|
282
|
+
|
283
|
+
if output_path:
|
284
|
+
with open(output_path, "w", encoding="utf-8") as f:
|
285
|
+
f.write(json_content)
|
286
|
+
logger.info(f"ServiceNow export saved to: {output_path}")
|
287
|
+
|
288
|
+
return json_content
|
289
|
+
|
290
|
+
def _map_severity_to_snow_priority(self, severity: Severity) -> str:
|
291
|
+
"""Map severity to ServiceNow priority."""
|
292
|
+
mapping = {Severity.CRITICAL: "1 - Critical", Severity.WARNING: "2 - High", Severity.INFO: "3 - Moderate"}
|
293
|
+
return mapping.get(severity, "3 - Moderate")
|
294
|
+
|
295
|
+
def _map_severity_to_impact(self, severity: Severity) -> str:
|
296
|
+
"""Map severity to ServiceNow impact."""
|
297
|
+
mapping = {Severity.CRITICAL: "1 - High", Severity.WARNING: "2 - Medium", Severity.INFO: "3 - Low"}
|
298
|
+
return mapping.get(severity, "3 - Low")
|
299
|
+
|
300
|
+
def _map_severity_to_urgency(self, severity: Severity) -> str:
|
301
|
+
"""Map severity to ServiceNow urgency."""
|
302
|
+
mapping = {Severity.CRITICAL: "1 - High", Severity.WARNING: "2 - Medium", Severity.INFO: "3 - Low"}
|
303
|
+
return mapping.get(severity, "3 - Low")
|
304
|
+
|
305
|
+
|
306
|
+
# Exporter registry
|
307
|
+
EXPORTERS = {
|
308
|
+
"jira": JiraExporter,
|
309
|
+
"asana": AsanaExporter,
|
310
|
+
"servicenow": ServiceNowExporter,
|
311
|
+
}
|
312
|
+
|
313
|
+
|
314
|
+
def get_exporter(exporter_name: str) -> Optional[BaseExporter]:
|
315
|
+
"""
|
316
|
+
Get exporter instance by name.
|
317
|
+
|
318
|
+
Args:
|
319
|
+
exporter_name: Name of the exporter
|
320
|
+
|
321
|
+
Returns:
|
322
|
+
Exporter instance or None if not found
|
323
|
+
"""
|
324
|
+
exporter_class = EXPORTERS.get(exporter_name.lower())
|
325
|
+
if exporter_class:
|
326
|
+
return exporter_class()
|
327
|
+
return None
|
328
|
+
|
329
|
+
|
330
|
+
def list_available_exporters() -> List[str]:
|
331
|
+
"""
|
332
|
+
Get list of available exporters.
|
333
|
+
|
334
|
+
Returns:
|
335
|
+
List of available exporter names
|
336
|
+
"""
|
337
|
+
return list(EXPORTERS.keys())
|