runbooks 0.2.5__py3-none-any.whl → 0.7.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 (249) hide show
  1. conftest.py +26 -0
  2. jupyter-agent/.env +2 -0
  3. jupyter-agent/.env.template +2 -0
  4. jupyter-agent/.gitattributes +35 -0
  5. jupyter-agent/.gradio/certificate.pem +31 -0
  6. jupyter-agent/README.md +16 -0
  7. jupyter-agent/__main__.log +8 -0
  8. jupyter-agent/app.py +256 -0
  9. jupyter-agent/cloudops-agent.png +0 -0
  10. jupyter-agent/ds-system-prompt.txt +154 -0
  11. jupyter-agent/jupyter-agent.png +0 -0
  12. jupyter-agent/llama3_template.jinja +123 -0
  13. jupyter-agent/requirements.txt +9 -0
  14. jupyter-agent/tmp/4ojbs8a02ir/jupyter-agent.ipynb +68 -0
  15. jupyter-agent/tmp/cm5iasgpm3p/jupyter-agent.ipynb +91 -0
  16. jupyter-agent/tmp/crqbsseag5/jupyter-agent.ipynb +91 -0
  17. jupyter-agent/tmp/hohanq1u097/jupyter-agent.ipynb +57 -0
  18. jupyter-agent/tmp/jns1sam29wm/jupyter-agent.ipynb +53 -0
  19. jupyter-agent/tmp/jupyter-agent.ipynb +27 -0
  20. jupyter-agent/utils.py +409 -0
  21. runbooks/__init__.py +71 -3
  22. runbooks/__main__.py +13 -0
  23. runbooks/aws/ec2_describe_instances.py +1 -1
  24. runbooks/aws/ec2_run_instances.py +8 -2
  25. runbooks/aws/ec2_start_stop_instances.py +17 -4
  26. runbooks/aws/ec2_unused_volumes.py +5 -1
  27. runbooks/aws/s3_create_bucket.py +4 -2
  28. runbooks/aws/s3_list_objects.py +6 -1
  29. runbooks/aws/tagging_lambda_handler.py +13 -2
  30. runbooks/aws/tags.json +12 -0
  31. runbooks/base.py +353 -0
  32. runbooks/cfat/README.md +49 -0
  33. runbooks/cfat/__init__.py +74 -0
  34. runbooks/cfat/app.ts +644 -0
  35. runbooks/cfat/assessment/__init__.py +40 -0
  36. runbooks/cfat/assessment/asana-import.csv +39 -0
  37. runbooks/cfat/assessment/cfat-checks.csv +31 -0
  38. runbooks/cfat/assessment/cfat.txt +520 -0
  39. runbooks/cfat/assessment/collectors.py +200 -0
  40. runbooks/cfat/assessment/jira-import.csv +39 -0
  41. runbooks/cfat/assessment/runner.py +387 -0
  42. runbooks/cfat/assessment/validators.py +290 -0
  43. runbooks/cfat/cli.py +103 -0
  44. runbooks/cfat/docs/asana-import.csv +24 -0
  45. runbooks/cfat/docs/cfat-checks.csv +31 -0
  46. runbooks/cfat/docs/cfat.txt +335 -0
  47. runbooks/cfat/docs/checks-output.png +0 -0
  48. runbooks/cfat/docs/cloudshell-console-run.png +0 -0
  49. runbooks/cfat/docs/cloudshell-download.png +0 -0
  50. runbooks/cfat/docs/cloudshell-output.png +0 -0
  51. runbooks/cfat/docs/downloadfile.png +0 -0
  52. runbooks/cfat/docs/jira-import.csv +24 -0
  53. runbooks/cfat/docs/open-cloudshell.png +0 -0
  54. runbooks/cfat/docs/report-header.png +0 -0
  55. runbooks/cfat/models.py +1026 -0
  56. runbooks/cfat/package-lock.json +5116 -0
  57. runbooks/cfat/package.json +38 -0
  58. runbooks/cfat/report.py +496 -0
  59. runbooks/cfat/reporting/__init__.py +46 -0
  60. runbooks/cfat/reporting/exporters.py +337 -0
  61. runbooks/cfat/reporting/formatters.py +496 -0
  62. runbooks/cfat/reporting/templates.py +135 -0
  63. runbooks/cfat/run-assessment.sh +23 -0
  64. runbooks/cfat/runner.py +69 -0
  65. runbooks/cfat/src/actions/check-cloudtrail-existence.ts +43 -0
  66. runbooks/cfat/src/actions/check-config-existence.ts +37 -0
  67. runbooks/cfat/src/actions/check-control-tower.ts +37 -0
  68. runbooks/cfat/src/actions/check-ec2-existence.ts +46 -0
  69. runbooks/cfat/src/actions/check-iam-users.ts +50 -0
  70. runbooks/cfat/src/actions/check-legacy-cur.ts +30 -0
  71. runbooks/cfat/src/actions/check-org-cloudformation.ts +30 -0
  72. runbooks/cfat/src/actions/check-vpc-existence.ts +43 -0
  73. runbooks/cfat/src/actions/create-asanaimport.ts +14 -0
  74. runbooks/cfat/src/actions/create-backlog.ts +372 -0
  75. runbooks/cfat/src/actions/create-jiraimport.ts +15 -0
  76. runbooks/cfat/src/actions/create-report.ts +616 -0
  77. runbooks/cfat/src/actions/define-account-type.ts +51 -0
  78. runbooks/cfat/src/actions/get-enabled-org-policy-types.ts +40 -0
  79. runbooks/cfat/src/actions/get-enabled-org-services.ts +26 -0
  80. runbooks/cfat/src/actions/get-idc-info.ts +34 -0
  81. runbooks/cfat/src/actions/get-org-da-accounts.ts +34 -0
  82. runbooks/cfat/src/actions/get-org-details.ts +35 -0
  83. runbooks/cfat/src/actions/get-org-member-accounts.ts +44 -0
  84. runbooks/cfat/src/actions/get-org-ous.ts +35 -0
  85. runbooks/cfat/src/actions/get-regions.ts +22 -0
  86. runbooks/cfat/src/actions/zip-assessment.ts +27 -0
  87. runbooks/cfat/src/types/index.d.ts +147 -0
  88. runbooks/cfat/tests/__init__.py +141 -0
  89. runbooks/cfat/tests/test_cli.py +340 -0
  90. runbooks/cfat/tests/test_integration.py +290 -0
  91. runbooks/cfat/tests/test_models.py +505 -0
  92. runbooks/cfat/tests/test_reporting.py +354 -0
  93. runbooks/cfat/tsconfig.json +16 -0
  94. runbooks/cfat/webpack.config.cjs +27 -0
  95. runbooks/config.py +260 -0
  96. runbooks/finops/README.md +337 -0
  97. runbooks/finops/__init__.py +86 -0
  98. runbooks/finops/aws_client.py +245 -0
  99. runbooks/finops/cli.py +151 -0
  100. runbooks/finops/cost_processor.py +410 -0
  101. runbooks/finops/dashboard_runner.py +448 -0
  102. runbooks/finops/helpers.py +355 -0
  103. runbooks/finops/main.py +14 -0
  104. runbooks/finops/profile_processor.py +174 -0
  105. runbooks/finops/types.py +66 -0
  106. runbooks/finops/visualisations.py +80 -0
  107. runbooks/inventory/.gitignore +354 -0
  108. runbooks/inventory/ArgumentsClass.py +261 -0
  109. runbooks/inventory/FAILED_SCRIPTS_TROUBLESHOOTING.md +619 -0
  110. runbooks/inventory/Inventory_Modules.py +6130 -0
  111. runbooks/inventory/LandingZone/delete_lz.py +1075 -0
  112. runbooks/inventory/PASSED_SCRIPTS_GUIDE.md +738 -0
  113. runbooks/inventory/README.md +1320 -0
  114. runbooks/inventory/__init__.py +62 -0
  115. runbooks/inventory/account_class.py +532 -0
  116. runbooks/inventory/all_my_instances_wrapper.py +123 -0
  117. runbooks/inventory/aws_decorators.py +201 -0
  118. runbooks/inventory/aws_organization.png +0 -0
  119. runbooks/inventory/cfn_move_stack_instances.py +1526 -0
  120. runbooks/inventory/check_cloudtrail_compliance.py +614 -0
  121. runbooks/inventory/check_controltower_readiness.py +1107 -0
  122. runbooks/inventory/check_landingzone_readiness.py +711 -0
  123. runbooks/inventory/cloudtrail.md +727 -0
  124. runbooks/inventory/collectors/__init__.py +20 -0
  125. runbooks/inventory/collectors/aws_compute.py +518 -0
  126. runbooks/inventory/collectors/aws_networking.py +275 -0
  127. runbooks/inventory/collectors/base.py +222 -0
  128. runbooks/inventory/core/__init__.py +19 -0
  129. runbooks/inventory/core/collector.py +303 -0
  130. runbooks/inventory/core/formatter.py +296 -0
  131. runbooks/inventory/delete_s3_buckets_objects.py +169 -0
  132. runbooks/inventory/discovery.md +81 -0
  133. runbooks/inventory/draw_org_structure.py +748 -0
  134. runbooks/inventory/ec2_vpc_utils.py +341 -0
  135. runbooks/inventory/find_cfn_drift_detection.py +272 -0
  136. runbooks/inventory/find_cfn_orphaned_stacks.py +719 -0
  137. runbooks/inventory/find_cfn_stackset_drift.py +733 -0
  138. runbooks/inventory/find_ec2_security_groups.py +669 -0
  139. runbooks/inventory/find_landingzone_versions.py +201 -0
  140. runbooks/inventory/find_vpc_flow_logs.py +1221 -0
  141. runbooks/inventory/inventory.sh +659 -0
  142. runbooks/inventory/list_cfn_stacks.py +558 -0
  143. runbooks/inventory/list_cfn_stackset_operation_results.py +252 -0
  144. runbooks/inventory/list_cfn_stackset_operations.py +734 -0
  145. runbooks/inventory/list_cfn_stacksets.py +453 -0
  146. runbooks/inventory/list_config_recorders_delivery_channels.py +681 -0
  147. runbooks/inventory/list_ds_directories.py +354 -0
  148. runbooks/inventory/list_ec2_availability_zones.py +286 -0
  149. runbooks/inventory/list_ec2_ebs_volumes.py +244 -0
  150. runbooks/inventory/list_ec2_instances.py +425 -0
  151. runbooks/inventory/list_ecs_clusters_and_tasks.py +562 -0
  152. runbooks/inventory/list_elbs_load_balancers.py +411 -0
  153. runbooks/inventory/list_enis_network_interfaces.py +526 -0
  154. runbooks/inventory/list_guardduty_detectors.py +568 -0
  155. runbooks/inventory/list_iam_policies.py +404 -0
  156. runbooks/inventory/list_iam_roles.py +518 -0
  157. runbooks/inventory/list_iam_saml_providers.py +359 -0
  158. runbooks/inventory/list_lambda_functions.py +882 -0
  159. runbooks/inventory/list_org_accounts.py +446 -0
  160. runbooks/inventory/list_org_accounts_users.py +354 -0
  161. runbooks/inventory/list_rds_db_instances.py +406 -0
  162. runbooks/inventory/list_route53_hosted_zones.py +318 -0
  163. runbooks/inventory/list_servicecatalog_provisioned_products.py +575 -0
  164. runbooks/inventory/list_sns_topics.py +360 -0
  165. runbooks/inventory/list_ssm_parameters.py +402 -0
  166. runbooks/inventory/list_vpc_subnets.py +433 -0
  167. runbooks/inventory/list_vpcs.py +422 -0
  168. runbooks/inventory/lockdown_cfn_stackset_role.py +224 -0
  169. runbooks/inventory/models/__init__.py +24 -0
  170. runbooks/inventory/models/account.py +192 -0
  171. runbooks/inventory/models/inventory.py +309 -0
  172. runbooks/inventory/models/resource.py +247 -0
  173. runbooks/inventory/recover_cfn_stack_ids.py +205 -0
  174. runbooks/inventory/requirements.txt +12 -0
  175. runbooks/inventory/run_on_multi_accounts.py +211 -0
  176. runbooks/inventory/tests/common_test_data.py +3661 -0
  177. runbooks/inventory/tests/common_test_functions.py +204 -0
  178. runbooks/inventory/tests/setup.py +24 -0
  179. runbooks/inventory/tests/src.py +18 -0
  180. runbooks/inventory/tests/test_cfn_describe_stacks.py +208 -0
  181. runbooks/inventory/tests/test_ec2_describe_instances.py +162 -0
  182. runbooks/inventory/tests/test_inventory_modules.py +55 -0
  183. runbooks/inventory/tests/test_lambda_list_functions.py +86 -0
  184. runbooks/inventory/tests/test_moto_integration_example.py +273 -0
  185. runbooks/inventory/tests/test_org_list_accounts.py +49 -0
  186. runbooks/inventory/update_aws_actions.py +173 -0
  187. runbooks/inventory/update_cfn_stacksets.py +1215 -0
  188. runbooks/inventory/update_cloudwatch_logs_retention_policy.py +294 -0
  189. runbooks/inventory/update_iam_roles_cross_accounts.py +478 -0
  190. runbooks/inventory/update_s3_public_access_block.py +539 -0
  191. runbooks/inventory/utils/__init__.py +23 -0
  192. runbooks/inventory/utils/aws_helpers.py +510 -0
  193. runbooks/inventory/utils/threading_utils.py +493 -0
  194. runbooks/inventory/utils/validation.py +682 -0
  195. runbooks/inventory/verify_ec2_security_groups.py +1430 -0
  196. runbooks/main.py +1004 -0
  197. runbooks/organizations/__init__.py +12 -0
  198. runbooks/organizations/manager.py +374 -0
  199. runbooks/security/README.md +447 -0
  200. runbooks/security/__init__.py +71 -0
  201. runbooks/{security_baseline → security}/checklist/alternate_contacts.py +8 -1
  202. runbooks/{security_baseline → security}/checklist/bucket_public_access.py +4 -1
  203. runbooks/{security_baseline → security}/checklist/cloudwatch_alarm_configuration.py +9 -2
  204. runbooks/{security_baseline → security}/checklist/guardduty_enabled.py +9 -2
  205. runbooks/{security_baseline → security}/checklist/multi_region_instance_usage.py +5 -1
  206. runbooks/{security_baseline → security}/checklist/root_access_key.py +6 -1
  207. runbooks/{security_baseline → security}/config-origin.json +1 -1
  208. runbooks/{security_baseline → security}/config.json +1 -1
  209. runbooks/{security_baseline → security}/permission.json +1 -1
  210. runbooks/{security_baseline → security}/report_generator.py +10 -2
  211. runbooks/{security_baseline → security}/report_template_en.html +7 -7
  212. runbooks/{security_baseline → security}/report_template_jp.html +7 -7
  213. runbooks/{security_baseline → security}/report_template_kr.html +12 -12
  214. runbooks/{security_baseline → security}/report_template_vn.html +7 -7
  215. runbooks/{security_baseline → security}/run_script.py +8 -2
  216. runbooks/{security_baseline → security}/security_baseline_tester.py +12 -4
  217. runbooks/{security_baseline → security}/utils/common.py +5 -1
  218. runbooks/utils/__init__.py +204 -0
  219. runbooks-0.7.0.dist-info/METADATA +375 -0
  220. runbooks-0.7.0.dist-info/RECORD +249 -0
  221. {runbooks-0.2.5.dist-info → runbooks-0.7.0.dist-info}/WHEEL +1 -1
  222. runbooks-0.7.0.dist-info/entry_points.txt +7 -0
  223. runbooks-0.7.0.dist-info/licenses/LICENSE +201 -0
  224. runbooks-0.7.0.dist-info/top_level.txt +3 -0
  225. runbooks/python101/calculator.py +0 -34
  226. runbooks/python101/config.py +0 -1
  227. runbooks/python101/exceptions.py +0 -16
  228. runbooks/python101/file_manager.py +0 -218
  229. runbooks/python101/toolkit.py +0 -153
  230. runbooks-0.2.5.dist-info/METADATA +0 -439
  231. runbooks-0.2.5.dist-info/RECORD +0 -61
  232. runbooks-0.2.5.dist-info/entry_points.txt +0 -3
  233. runbooks-0.2.5.dist-info/top_level.txt +0 -1
  234. /runbooks/{security_baseline/__init__.py → inventory/tests/script_test_data.py} +0 -0
  235. /runbooks/{security_baseline → security}/checklist/__init__.py +0 -0
  236. /runbooks/{security_baseline → security}/checklist/account_level_bucket_public_access.py +0 -0
  237. /runbooks/{security_baseline → security}/checklist/direct_attached_policy.py +0 -0
  238. /runbooks/{security_baseline → security}/checklist/iam_password_policy.py +0 -0
  239. /runbooks/{security_baseline → security}/checklist/iam_user_mfa.py +0 -0
  240. /runbooks/{security_baseline → security}/checklist/multi_region_trail.py +0 -0
  241. /runbooks/{security_baseline → security}/checklist/root_mfa.py +0 -0
  242. /runbooks/{security_baseline → security}/checklist/root_usage.py +0 -0
  243. /runbooks/{security_baseline → security}/checklist/trail_enabled.py +0 -0
  244. /runbooks/{security_baseline → security}/checklist/trusted_advisor.py +0 -0
  245. /runbooks/{security_baseline → security}/utils/__init__.py +0 -0
  246. /runbooks/{security_baseline → security}/utils/enums.py +0 -0
  247. /runbooks/{security_baseline → security}/utils/language.py +0 -0
  248. /runbooks/{security_baseline → security}/utils/level_const.py +0 -0
  249. /runbooks/{security_baseline → security}/utils/permission_list.py +0 -0
@@ -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())