cartography 0.104.0rc2__py3-none-any.whl → 0.123.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 (642) hide show
  1. cartography/_version.py +16 -3
  2. cartography/cli.py +466 -5
  3. cartography/client/aws/__init__.py +19 -0
  4. cartography/client/aws/ecr.py +51 -0
  5. cartography/client/core/tx.py +357 -8
  6. cartography/config.py +153 -0
  7. cartography/data/azure_permission_relationships.yaml +20 -0
  8. cartography/data/gcp_permission_relationships.yaml +21 -0
  9. cartography/data/indexes.cypher +0 -186
  10. cartography/data/jobs/analysis/aws_ec2_keypair_analysis.json +2 -2
  11. cartography/data/jobs/analysis/keycloak_inheritance.json +30 -0
  12. cartography/data/jobs/cleanup/gcp_compute_vpc_cleanup.json +0 -12
  13. cartography/data/jobs/cleanup/github_repos_cleanup.json +2 -0
  14. cartography/driftdetect/cli.py +3 -2
  15. cartography/graph/cleanupbuilder.py +198 -41
  16. cartography/graph/job.py +54 -6
  17. cartography/graph/querybuilder.py +528 -27
  18. cartography/graph/statement.py +5 -1
  19. cartography/intel/airbyte/__init__.py +105 -0
  20. cartography/intel/airbyte/connections.py +120 -0
  21. cartography/intel/airbyte/destinations.py +81 -0
  22. cartography/intel/airbyte/organizations.py +59 -0
  23. cartography/intel/airbyte/sources.py +78 -0
  24. cartography/intel/airbyte/tags.py +64 -0
  25. cartography/intel/airbyte/users.py +106 -0
  26. cartography/intel/airbyte/util.py +122 -0
  27. cartography/intel/airbyte/workspaces.py +63 -0
  28. cartography/intel/aws/__init__.py +24 -9
  29. cartography/intel/aws/acm.py +124 -0
  30. cartography/intel/aws/apigateway.py +253 -22
  31. cartography/intel/aws/apigatewayv2.py +116 -0
  32. cartography/intel/aws/cloudtrail.py +17 -39
  33. cartography/intel/aws/cloudtrail_management_events.py +962 -0
  34. cartography/intel/aws/cloudwatch.py +150 -4
  35. cartography/intel/aws/codebuild.py +132 -0
  36. cartography/intel/aws/cognito.py +201 -0
  37. cartography/intel/aws/config.py +7 -3
  38. cartography/intel/aws/ec2/elastic_ip_addresses.py +3 -1
  39. cartography/intel/aws/ec2/instances.py +25 -1
  40. cartography/intel/aws/ec2/internet_gateways.py +4 -2
  41. cartography/intel/aws/ec2/load_balancer_v2s.py +11 -5
  42. cartography/intel/aws/ec2/network_interfaces.py +5 -1
  43. cartography/intel/aws/ec2/reserved_instances.py +3 -1
  44. cartography/intel/aws/ec2/security_groups.py +140 -122
  45. cartography/intel/aws/ec2/snapshots.py +47 -84
  46. cartography/intel/aws/ec2/subnets.py +37 -63
  47. cartography/intel/aws/ec2/tgw.py +11 -5
  48. cartography/intel/aws/ec2/volumes.py +1 -1
  49. cartography/intel/aws/ec2/vpc.py +140 -124
  50. cartography/intel/aws/ec2/vpc_peerings.py +262 -125
  51. cartography/intel/aws/ecr.py +269 -98
  52. cartography/intel/aws/ecr_image_layers.py +923 -0
  53. cartography/intel/aws/ecs.py +251 -380
  54. cartography/intel/aws/efs.py +179 -11
  55. cartography/intel/aws/elasticache.py +102 -79
  56. cartography/intel/aws/elasticsearch.py +13 -4
  57. cartography/intel/aws/eventbridge.py +164 -0
  58. cartography/intel/aws/glue.py +181 -0
  59. cartography/intel/aws/guardduty.py +443 -0
  60. cartography/intel/aws/iam.py +750 -493
  61. cartography/intel/aws/identitycenter.py +605 -83
  62. cartography/intel/aws/inspector.py +221 -105
  63. cartography/intel/aws/kms.py +173 -201
  64. cartography/intel/aws/lambda_function.py +272 -189
  65. cartography/intel/aws/organizations.py +10 -9
  66. cartography/intel/aws/permission_relationships.py +10 -20
  67. cartography/intel/aws/rds.py +337 -446
  68. cartography/intel/aws/redshift.py +9 -4
  69. cartography/intel/aws/resourcegroupstaggingapi.py +78 -19
  70. cartography/intel/aws/resources.py +18 -0
  71. cartography/intel/aws/route53.py +386 -332
  72. cartography/intel/aws/s3.py +322 -14
  73. cartography/intel/aws/secretsmanager.py +81 -49
  74. cartography/intel/aws/securityhub.py +3 -1
  75. cartography/intel/aws/sns.py +62 -2
  76. cartography/intel/aws/sqs.py +36 -90
  77. cartography/intel/aws/ssm.py +3 -5
  78. cartography/intel/azure/__init__.py +202 -48
  79. cartography/intel/azure/aks.py +175 -0
  80. cartography/intel/azure/app_service.py +105 -0
  81. cartography/intel/azure/compute.py +59 -112
  82. cartography/intel/azure/container_instances.py +95 -0
  83. cartography/intel/azure/cosmosdb.py +222 -361
  84. cartography/intel/azure/data_factory.py +85 -0
  85. cartography/intel/azure/data_factory_dataset.py +128 -0
  86. cartography/intel/azure/data_factory_linked_service.py +119 -0
  87. cartography/intel/azure/data_factory_pipeline.py +142 -0
  88. cartography/intel/azure/data_lake.py +124 -0
  89. cartography/intel/azure/event_grid.py +94 -0
  90. cartography/intel/azure/functions.py +124 -0
  91. cartography/intel/azure/load_balancers.py +263 -0
  92. cartography/intel/azure/logic_apps.py +101 -0
  93. cartography/intel/azure/monitor.py +105 -0
  94. cartography/intel/azure/network.py +467 -0
  95. cartography/intel/azure/permission_relationships.py +466 -0
  96. cartography/intel/azure/rbac.py +309 -0
  97. cartography/intel/azure/resource_groups.py +82 -0
  98. cartography/intel/azure/security_center.py +106 -0
  99. cartography/intel/azure/sql.py +145 -292
  100. cartography/intel/azure/storage.py +185 -262
  101. cartography/intel/azure/subscription.py +21 -43
  102. cartography/intel/azure/tenant.py +39 -30
  103. cartography/intel/azure/util/common.py +13 -0
  104. cartography/intel/azure/util/credentials.py +49 -174
  105. cartography/intel/azure/util/tag.py +41 -0
  106. cartography/intel/create_indexes.py +2 -1
  107. cartography/intel/crowdstrike/spotlight.py +5 -2
  108. cartography/intel/dns.py +5 -2
  109. cartography/intel/entra/__init__.py +100 -1
  110. cartography/intel/entra/app_role_assignments.py +284 -0
  111. cartography/intel/entra/applications.py +182 -0
  112. cartography/intel/entra/federation/__init__.py +0 -0
  113. cartography/intel/entra/federation/aws_identity_center.py +77 -0
  114. cartography/intel/entra/groups.py +198 -0
  115. cartography/intel/entra/ou.py +48 -24
  116. cartography/intel/entra/service_principals.py +217 -0
  117. cartography/intel/entra/users.py +105 -57
  118. cartography/intel/gcp/__init__.py +334 -396
  119. cartography/intel/gcp/bigtable_app_profile.py +101 -0
  120. cartography/intel/gcp/bigtable_backup.py +91 -0
  121. cartography/intel/gcp/bigtable_cluster.py +93 -0
  122. cartography/intel/gcp/bigtable_instance.py +86 -0
  123. cartography/intel/gcp/bigtable_table.py +87 -0
  124. cartography/intel/gcp/cai.py +292 -0
  125. cartography/intel/gcp/clients.py +112 -0
  126. cartography/intel/gcp/compute.py +128 -119
  127. cartography/intel/gcp/crm/__init__.py +0 -0
  128. cartography/intel/gcp/crm/folders.py +114 -0
  129. cartography/intel/gcp/crm/orgs.py +70 -0
  130. cartography/intel/gcp/crm/projects.py +120 -0
  131. cartography/intel/gcp/dns.py +83 -169
  132. cartography/intel/gcp/gke.py +72 -113
  133. cartography/intel/gcp/iam.py +111 -91
  134. cartography/intel/gcp/permission_relationships.py +394 -0
  135. cartography/intel/gcp/policy_bindings.py +225 -0
  136. cartography/intel/gcp/storage.py +75 -159
  137. cartography/intel/github/__init__.py +62 -25
  138. cartography/intel/github/commits.py +423 -0
  139. cartography/intel/github/repos.py +463 -85
  140. cartography/intel/github/teams.py +3 -3
  141. cartography/intel/github/users.py +5 -0
  142. cartography/intel/github/util.py +12 -0
  143. cartography/intel/googleworkspace/__init__.py +193 -0
  144. cartography/intel/googleworkspace/devices.py +254 -0
  145. cartography/intel/googleworkspace/groups.py +568 -0
  146. cartography/intel/googleworkspace/oauth_apps.py +259 -0
  147. cartography/intel/googleworkspace/tenant.py +85 -0
  148. cartography/intel/googleworkspace/users.py +138 -0
  149. cartography/intel/gsuite/__init__.py +17 -9
  150. cartography/intel/gsuite/groups.py +291 -0
  151. cartography/intel/gsuite/users.py +142 -0
  152. cartography/intel/jamf/computers.py +7 -1
  153. cartography/intel/keycloak/__init__.py +153 -0
  154. cartography/intel/keycloak/authenticationexecutions.py +322 -0
  155. cartography/intel/keycloak/authenticationflows.py +77 -0
  156. cartography/intel/keycloak/clients.py +187 -0
  157. cartography/intel/keycloak/groups.py +126 -0
  158. cartography/intel/keycloak/identityproviders.py +94 -0
  159. cartography/intel/keycloak/organizations.py +163 -0
  160. cartography/intel/keycloak/realms.py +61 -0
  161. cartography/intel/keycloak/roles.py +202 -0
  162. cartography/intel/keycloak/scopes.py +73 -0
  163. cartography/intel/keycloak/users.py +70 -0
  164. cartography/intel/keycloak/util.py +47 -0
  165. cartography/intel/kubernetes/__init__.py +60 -14
  166. cartography/intel/kubernetes/clusters.py +86 -0
  167. cartography/intel/kubernetes/eks.py +402 -0
  168. cartography/intel/kubernetes/namespaces.py +59 -57
  169. cartography/intel/kubernetes/pods.py +168 -75
  170. cartography/intel/kubernetes/rbac.py +597 -0
  171. cartography/intel/kubernetes/secrets.py +95 -45
  172. cartography/intel/kubernetes/services.py +131 -67
  173. cartography/intel/kubernetes/util.py +142 -14
  174. cartography/intel/oci/iam.py +23 -9
  175. cartography/intel/oci/organizations.py +3 -1
  176. cartography/intel/oci/utils.py +28 -5
  177. cartography/intel/okta/applications.py +15 -5
  178. cartography/intel/okta/awssaml.py +14 -10
  179. cartography/intel/okta/factors.py +3 -1
  180. cartography/intel/okta/groups.py +5 -2
  181. cartography/intel/okta/organization.py +3 -1
  182. cartography/intel/okta/origins.py +3 -1
  183. cartography/intel/okta/roles.py +5 -2
  184. cartography/intel/okta/users.py +10 -2
  185. cartography/intel/ontology/__init__.py +44 -0
  186. cartography/intel/ontology/devices.py +54 -0
  187. cartography/intel/ontology/users.py +54 -0
  188. cartography/intel/ontology/utils.py +176 -0
  189. cartography/intel/pagerduty/escalation_policies.py +13 -6
  190. cartography/intel/pagerduty/schedules.py +9 -4
  191. cartography/intel/pagerduty/services.py +7 -3
  192. cartography/intel/pagerduty/teams.py +5 -2
  193. cartography/intel/pagerduty/users.py +3 -1
  194. cartography/intel/pagerduty/vendors.py +3 -1
  195. cartography/intel/scaleway/__init__.py +127 -0
  196. cartography/intel/scaleway/iam/__init__.py +0 -0
  197. cartography/intel/scaleway/iam/apikeys.py +71 -0
  198. cartography/intel/scaleway/iam/applications.py +71 -0
  199. cartography/intel/scaleway/iam/groups.py +71 -0
  200. cartography/intel/scaleway/iam/users.py +71 -0
  201. cartography/intel/scaleway/instances/__init__.py +0 -0
  202. cartography/intel/scaleway/instances/flexibleips.py +86 -0
  203. cartography/intel/scaleway/instances/instances.py +92 -0
  204. cartography/intel/scaleway/projects.py +79 -0
  205. cartography/intel/scaleway/storage/__init__.py +0 -0
  206. cartography/intel/scaleway/storage/snapshots.py +86 -0
  207. cartography/intel/scaleway/storage/volumes.py +84 -0
  208. cartography/intel/scaleway/utils.py +37 -0
  209. cartography/intel/sentinelone/__init__.py +75 -0
  210. cartography/intel/sentinelone/account.py +140 -0
  211. cartography/intel/sentinelone/agent.py +139 -0
  212. cartography/intel/sentinelone/api.py +124 -0
  213. cartography/intel/sentinelone/application.py +248 -0
  214. cartography/intel/sentinelone/cve.py +119 -0
  215. cartography/intel/sentinelone/utils.py +28 -0
  216. cartography/intel/slack/__init__.py +78 -0
  217. cartography/intel/slack/channels.py +80 -0
  218. cartography/intel/slack/groups.py +90 -0
  219. cartography/intel/slack/teams.py +65 -0
  220. cartography/intel/slack/users.py +57 -0
  221. cartography/intel/slack/utils.py +29 -0
  222. cartography/intel/spacelift/__init__.py +161 -0
  223. cartography/intel/spacelift/account.py +73 -0
  224. cartography/intel/spacelift/ec2_ownership.py +280 -0
  225. cartography/intel/spacelift/runs.py +463 -0
  226. cartography/intel/spacelift/spaces.py +112 -0
  227. cartography/intel/spacelift/stacks.py +119 -0
  228. cartography/intel/spacelift/util.py +122 -0
  229. cartography/intel/spacelift/workerpools.py +131 -0
  230. cartography/intel/spacelift/workers.py +128 -0
  231. cartography/intel/trivy/__init__.py +272 -0
  232. cartography/intel/trivy/scanner.py +386 -0
  233. cartography/models/airbyte/__init__.py +0 -0
  234. cartography/models/airbyte/connection.py +138 -0
  235. cartography/models/airbyte/destination.py +75 -0
  236. cartography/models/airbyte/organization.py +19 -0
  237. cartography/models/airbyte/source.py +75 -0
  238. cartography/models/airbyte/stream.py +74 -0
  239. cartography/models/airbyte/tag.py +69 -0
  240. cartography/models/airbyte/user.py +115 -0
  241. cartography/models/airbyte/workspace.py +46 -0
  242. cartography/models/anthropic/apikey.py +4 -0
  243. cartography/models/anthropic/user.py +4 -0
  244. cartography/models/aws/acm/__init__.py +0 -0
  245. cartography/models/aws/acm/certificate.py +75 -0
  246. cartography/models/aws/apigateway/__init__.py +0 -0
  247. cartography/models/aws/apigateway/apigatewaydeployment.py +74 -0
  248. cartography/models/aws/apigateway/apigatewayintegration.py +79 -0
  249. cartography/models/aws/apigateway/apigatewaymethod.py +74 -0
  250. cartography/models/aws/apigatewayv2/__init__.py +0 -0
  251. cartography/models/aws/apigatewayv2/apigatewayv2.py +53 -0
  252. cartography/models/aws/cloudtrail/management_events.py +153 -0
  253. cartography/models/aws/cloudtrail/trail.py +45 -0
  254. cartography/models/aws/cloudwatch/log_metric_filter.py +79 -0
  255. cartography/models/aws/cloudwatch/metric_alarm.py +53 -0
  256. cartography/models/aws/codebuild/__init__.py +0 -0
  257. cartography/models/aws/codebuild/project.py +49 -0
  258. cartography/models/aws/cognito/__init__.py +0 -0
  259. cartography/models/aws/cognito/identity_pool.py +70 -0
  260. cartography/models/aws/cognito/user_pool.py +47 -0
  261. cartography/models/aws/dynamodb/tables.py +2 -0
  262. cartography/models/aws/ec2/instances.py +25 -1
  263. cartography/models/aws/ec2/networkinterfaces.py +4 -0
  264. cartography/models/aws/ec2/security_group_rules.py +109 -0
  265. cartography/models/aws/ec2/security_groups.py +90 -0
  266. cartography/models/aws/ec2/snapshots.py +58 -0
  267. cartography/models/aws/ec2/subnet_instance.py +2 -0
  268. cartography/models/aws/ec2/subnet_networkinterface.py +2 -0
  269. cartography/models/aws/ec2/subnets.py +65 -0
  270. cartography/models/aws/ec2/volumes.py +20 -0
  271. cartography/models/aws/ec2/vpc.py +46 -0
  272. cartography/models/aws/ec2/vpc_cidr.py +102 -0
  273. cartography/models/aws/ec2/vpc_peering.py +157 -0
  274. cartography/models/aws/ecr/__init__.py +0 -0
  275. cartography/models/aws/ecr/image.py +146 -0
  276. cartography/models/aws/ecr/image_layer.py +107 -0
  277. cartography/models/aws/ecr/repository.py +72 -0
  278. cartography/models/aws/ecr/repository_image.py +95 -0
  279. cartography/models/aws/ecs/__init__.py +0 -0
  280. cartography/models/aws/ecs/clusters.py +64 -0
  281. cartography/models/aws/ecs/container_definitions.py +93 -0
  282. cartography/models/aws/ecs/container_instances.py +84 -0
  283. cartography/models/aws/ecs/containers.py +101 -0
  284. cartography/models/aws/ecs/services.py +134 -0
  285. cartography/models/aws/ecs/task_definitions.py +135 -0
  286. cartography/models/aws/ecs/tasks.py +134 -0
  287. cartography/models/aws/efs/access_point.py +77 -0
  288. cartography/models/aws/efs/file_system.py +60 -0
  289. cartography/models/aws/efs/mount_target.py +29 -2
  290. cartography/models/aws/elasticache/__init__.py +0 -0
  291. cartography/models/aws/elasticache/cluster.py +65 -0
  292. cartography/models/aws/elasticache/topic.py +67 -0
  293. cartography/models/aws/eventbridge/__init__.py +0 -0
  294. cartography/models/aws/eventbridge/rule.py +77 -0
  295. cartography/models/aws/eventbridge/target.py +71 -0
  296. cartography/models/aws/glue/__init__.py +0 -0
  297. cartography/models/aws/glue/connection.py +51 -0
  298. cartography/models/aws/glue/job.py +69 -0
  299. cartography/models/aws/guardduty/__init__.py +1 -0
  300. cartography/models/aws/guardduty/detectors.py +50 -0
  301. cartography/models/aws/guardduty/findings.py +121 -0
  302. cartography/models/aws/iam/access_key.py +103 -0
  303. cartography/models/aws/iam/account_role.py +24 -0
  304. cartography/models/aws/iam/federated_principal.py +60 -0
  305. cartography/models/aws/iam/group.py +60 -0
  306. cartography/models/aws/iam/group_membership.py +27 -0
  307. cartography/models/aws/iam/inline_policy.py +78 -0
  308. cartography/models/aws/iam/managed_policy.py +51 -0
  309. cartography/models/aws/iam/policy_statement.py +57 -0
  310. cartography/models/aws/iam/role.py +83 -0
  311. cartography/models/aws/iam/root_principal.py +52 -0
  312. cartography/models/aws/iam/service_principal.py +30 -0
  313. cartography/models/aws/iam/sts_assumerole_allow.py +38 -0
  314. cartography/models/aws/iam/user.py +59 -0
  315. cartography/models/aws/identitycenter/awsidentitycenter.py +1 -0
  316. cartography/models/aws/identitycenter/awspermissionset.py +70 -0
  317. cartography/models/aws/identitycenter/awssogroup.py +70 -0
  318. cartography/models/aws/identitycenter/awsssouser.py +49 -9
  319. cartography/models/aws/inspector/findings.py +37 -0
  320. cartography/models/aws/inspector/packages.py +1 -31
  321. cartography/models/aws/kms/__init__.py +0 -0
  322. cartography/models/aws/kms/aliases.py +86 -0
  323. cartography/models/aws/kms/grants.py +65 -0
  324. cartography/models/aws/kms/keys.py +88 -0
  325. cartography/models/aws/lambda_function/__init__.py +0 -0
  326. cartography/models/aws/lambda_function/alias.py +74 -0
  327. cartography/models/aws/lambda_function/event_source_mapping.py +88 -0
  328. cartography/models/aws/lambda_function/lambda_function.py +91 -0
  329. cartography/models/aws/lambda_function/layer.py +72 -0
  330. cartography/models/aws/rds/__init__.py +0 -0
  331. cartography/models/aws/rds/cluster.py +91 -0
  332. cartography/models/aws/rds/event_subscription.py +146 -0
  333. cartography/models/aws/rds/instance.py +156 -0
  334. cartography/models/aws/rds/snapshot.py +108 -0
  335. cartography/models/aws/rds/subnet_group.py +101 -0
  336. cartography/models/aws/route53/__init__.py +0 -0
  337. cartography/models/aws/route53/dnsrecord.py +235 -0
  338. cartography/models/aws/route53/nameserver.py +63 -0
  339. cartography/models/aws/route53/subzone.py +40 -0
  340. cartography/models/aws/route53/zone.py +47 -0
  341. cartography/models/aws/s3/notification.py +24 -0
  342. cartography/models/aws/secretsmanager/secret.py +106 -0
  343. cartography/models/aws/secretsmanager/secret_version.py +0 -2
  344. cartography/models/aws/sns/topic_subscription.py +74 -0
  345. cartography/models/aws/sqs/__init__.py +0 -0
  346. cartography/models/aws/sqs/queue.py +89 -0
  347. cartography/models/azure/__init__.py +0 -0
  348. cartography/models/azure/aks_cluster.py +54 -0
  349. cartography/models/azure/aks_nodepool.py +54 -0
  350. cartography/models/azure/app_service.py +59 -0
  351. cartography/models/azure/container_instance.py +57 -0
  352. cartography/models/azure/cosmosdb/__init__.py +0 -0
  353. cartography/models/azure/cosmosdb/account.py +77 -0
  354. cartography/models/azure/cosmosdb/accountfailoverpolicy.py +77 -0
  355. cartography/models/azure/cosmosdb/cassandrakeyspace.py +82 -0
  356. cartography/models/azure/cosmosdb/cassandratable.py +81 -0
  357. cartography/models/azure/cosmosdb/corspolicy.py +74 -0
  358. cartography/models/azure/cosmosdb/dblocation.py +120 -0
  359. cartography/models/azure/cosmosdb/mongodbcollection.py +82 -0
  360. cartography/models/azure/cosmosdb/mongodbdatabase.py +78 -0
  361. cartography/models/azure/cosmosdb/privateendpointconnection.py +81 -0
  362. cartography/models/azure/cosmosdb/sqlcontainer.py +88 -0
  363. cartography/models/azure/cosmosdb/sqldatabase.py +78 -0
  364. cartography/models/azure/cosmosdb/tableresource.py +76 -0
  365. cartography/models/azure/cosmosdb/virtualnetworkrule.py +78 -0
  366. cartography/models/azure/data_factory/__init__.py +0 -0
  367. cartography/models/azure/data_factory/data_factory.py +51 -0
  368. cartography/models/azure/data_factory/data_factory_dataset.py +94 -0
  369. cartography/models/azure/data_factory/data_factory_linked_service.py +78 -0
  370. cartography/models/azure/data_factory/data_factory_pipeline.py +93 -0
  371. cartography/models/azure/data_lake_filesystem.py +51 -0
  372. cartography/models/azure/event_grid_topic.py +57 -0
  373. cartography/models/azure/function_app.py +59 -0
  374. cartography/models/azure/load_balancer/__init__.py +0 -0
  375. cartography/models/azure/load_balancer/load_balancer.py +49 -0
  376. cartography/models/azure/load_balancer/load_balancer_backend_pool.py +73 -0
  377. cartography/models/azure/load_balancer/load_balancer_frontend_ip.py +75 -0
  378. cartography/models/azure/load_balancer/load_balancer_inbound_nat_rule.py +78 -0
  379. cartography/models/azure/load_balancer/load_balancer_rule.py +108 -0
  380. cartography/models/azure/logic_apps.py +56 -0
  381. cartography/models/azure/monitor.py +54 -0
  382. cartography/models/azure/network_interface.py +112 -0
  383. cartography/models/azure/network_security_group.py +50 -0
  384. cartography/models/azure/permission_relationships.py +60 -0
  385. cartography/models/azure/principal.py +41 -0
  386. cartography/models/azure/public_ip_address.py +50 -0
  387. cartography/models/azure/rbac.py +268 -0
  388. cartography/models/azure/resource_groups.py +52 -0
  389. cartography/models/azure/security_center.py +50 -0
  390. cartography/models/azure/sql/__init__.py +0 -0
  391. cartography/models/azure/sql/databasethreatdetectionpolicy.py +85 -0
  392. cartography/models/azure/sql/elasticpool.py +77 -0
  393. cartography/models/azure/sql/failovergroup.py +73 -0
  394. cartography/models/azure/sql/recoverabledatabase.py +75 -0
  395. cartography/models/azure/sql/replicationlink.py +81 -0
  396. cartography/models/azure/sql/restorabledroppeddatabase.py +82 -0
  397. cartography/models/azure/sql/restorepoint.py +74 -0
  398. cartography/models/azure/sql/serveradadministrator.py +74 -0
  399. cartography/models/azure/sql/serverdnsalias.py +71 -0
  400. cartography/models/azure/sql/sqldatabase.py +85 -0
  401. cartography/models/azure/sql/sqlserver.py +50 -0
  402. cartography/models/azure/sql/transparentdataencryption.py +76 -0
  403. cartography/models/azure/storage/__init__.py +0 -0
  404. cartography/models/azure/storage/account.py +59 -0
  405. cartography/models/azure/storage/blobcontainer.py +85 -0
  406. cartography/models/azure/storage/blobservice.py +71 -0
  407. cartography/models/azure/storage/fileservice.py +71 -0
  408. cartography/models/azure/storage/fileshare.py +82 -0
  409. cartography/models/azure/storage/queue.py +71 -0
  410. cartography/models/azure/storage/queueservice.py +73 -0
  411. cartography/models/azure/storage/table.py +72 -0
  412. cartography/models/azure/storage/tableservice.py +73 -0
  413. cartography/models/azure/subnet.py +101 -0
  414. cartography/models/azure/subscription.py +47 -0
  415. cartography/models/azure/tags/__init__.py +0 -0
  416. cartography/models/azure/tags/storage_tag.py +40 -0
  417. cartography/models/azure/tags/tag.py +37 -0
  418. cartography/models/azure/tenant.py +17 -0
  419. cartography/models/azure/virtual_network.py +49 -0
  420. cartography/models/azure/vm/__init__.py +0 -0
  421. cartography/models/azure/vm/datadisk.py +80 -0
  422. cartography/models/azure/vm/disk.py +55 -0
  423. cartography/models/azure/vm/snapshot.py +56 -0
  424. cartography/models/azure/vm/virtualmachine.py +59 -0
  425. cartography/models/bigfix/bigfix_computer.py +1 -1
  426. cartography/models/cloudflare/member.py +4 -0
  427. cartography/models/core/common.py +1 -0
  428. cartography/models/core/nodes.py +15 -2
  429. cartography/models/core/relationships.py +44 -0
  430. cartography/models/crowdstrike/hosts.py +1 -1
  431. cartography/models/digitalocean/droplet.py +2 -0
  432. cartography/models/duo/endpoint.py +1 -1
  433. cartography/models/duo/phone.py +2 -2
  434. cartography/models/duo/user.py +4 -0
  435. cartography/models/entra/app_role_assignment.py +115 -0
  436. cartography/models/entra/application.py +49 -0
  437. cartography/models/entra/entra_user_to_aws_sso.py +41 -0
  438. cartography/models/entra/group.py +117 -0
  439. cartography/models/entra/service_principal.py +104 -0
  440. cartography/models/entra/user.py +42 -51
  441. cartography/models/gcp/__init__.py +0 -0
  442. cartography/models/gcp/bigtable/__init__.py +0 -0
  443. cartography/models/gcp/bigtable/app_profile.py +94 -0
  444. cartography/models/gcp/bigtable/backup.py +91 -0
  445. cartography/models/gcp/bigtable/cluster.py +73 -0
  446. cartography/models/gcp/bigtable/instance.py +52 -0
  447. cartography/models/gcp/bigtable/table.py +69 -0
  448. cartography/models/gcp/compute/__init__.py +0 -0
  449. cartography/models/gcp/compute/subnet.py +74 -0
  450. cartography/models/gcp/compute/vpc.py +50 -0
  451. cartography/models/gcp/crm/__init__.py +0 -0
  452. cartography/models/gcp/crm/folders.py +98 -0
  453. cartography/models/gcp/crm/organizations.py +21 -0
  454. cartography/models/gcp/crm/projects.py +100 -0
  455. cartography/models/gcp/dns.py +109 -0
  456. cartography/models/gcp/gke.py +69 -0
  457. cartography/models/gcp/iam.py +3 -0
  458. cartography/models/gcp/permission_relationships.py +61 -0
  459. cartography/models/gcp/policy_bindings.py +93 -0
  460. cartography/models/gcp/storage/__init__.py +0 -0
  461. cartography/models/gcp/storage/bucket.py +119 -0
  462. cartography/models/github/commits.py +63 -0
  463. cartography/models/github/dependencies.py +73 -0
  464. cartography/models/github/manifests.py +49 -0
  465. cartography/models/github/users.py +10 -0
  466. cartography/models/googleworkspace/__init__.py +0 -0
  467. cartography/models/googleworkspace/device.py +132 -0
  468. cartography/models/googleworkspace/group.py +382 -0
  469. cartography/models/googleworkspace/oauth_app.py +124 -0
  470. cartography/models/googleworkspace/tenant.py +30 -0
  471. cartography/models/googleworkspace/user.py +113 -0
  472. cartography/models/gsuite/__init__.py +0 -0
  473. cartography/models/gsuite/group.py +218 -0
  474. cartography/models/gsuite/tenant.py +29 -0
  475. cartography/models/gsuite/user.py +107 -0
  476. cartography/models/kandji/device.py +1 -2
  477. cartography/models/keycloak/__init__.py +0 -0
  478. cartography/models/keycloak/authenticationexecution.py +160 -0
  479. cartography/models/keycloak/authenticationflow.py +54 -0
  480. cartography/models/keycloak/client.py +179 -0
  481. cartography/models/keycloak/group.py +101 -0
  482. cartography/models/keycloak/identityprovider.py +89 -0
  483. cartography/models/keycloak/organization.py +116 -0
  484. cartography/models/keycloak/organizationdomain.py +73 -0
  485. cartography/models/keycloak/realm.py +173 -0
  486. cartography/models/keycloak/role.py +126 -0
  487. cartography/models/keycloak/scope.py +73 -0
  488. cartography/models/keycloak/user.py +55 -0
  489. cartography/models/kubernetes/__init__.py +0 -0
  490. cartography/models/kubernetes/clusterrolebindings.py +138 -0
  491. cartography/models/kubernetes/clusterroles.py +52 -0
  492. cartography/models/kubernetes/clusters.py +26 -0
  493. cartography/models/kubernetes/containers.py +133 -0
  494. cartography/models/kubernetes/groups.py +107 -0
  495. cartography/models/kubernetes/namespaces.py +51 -0
  496. cartography/models/kubernetes/oidc.py +51 -0
  497. cartography/models/kubernetes/pods.py +80 -0
  498. cartography/models/kubernetes/rolebindings.py +159 -0
  499. cartography/models/kubernetes/roles.py +76 -0
  500. cartography/models/kubernetes/secrets.py +79 -0
  501. cartography/models/kubernetes/serviceaccounts.py +77 -0
  502. cartography/models/kubernetes/services.py +108 -0
  503. cartography/models/kubernetes/users.py +105 -0
  504. cartography/models/lastpass/user.py +4 -0
  505. cartography/models/ontology/__init__.py +0 -0
  506. cartography/models/ontology/device.py +137 -0
  507. cartography/models/ontology/mapping/__init__.py +76 -0
  508. cartography/models/ontology/mapping/data/__init__.py +0 -0
  509. cartography/models/ontology/mapping/data/apikeys.py +93 -0
  510. cartography/models/ontology/mapping/data/computeinstance.py +95 -0
  511. cartography/models/ontology/mapping/data/containers.py +88 -0
  512. cartography/models/ontology/mapping/data/databases.py +182 -0
  513. cartography/models/ontology/mapping/data/devices.py +194 -0
  514. cartography/models/ontology/mapping/data/thirdpartyapps.py +140 -0
  515. cartography/models/ontology/mapping/data/useraccounts.py +416 -0
  516. cartography/models/ontology/mapping/data/users.py +63 -0
  517. cartography/models/ontology/mapping/specs.py +85 -0
  518. cartography/models/ontology/user.py +51 -0
  519. cartography/models/openai/adminapikey.py +4 -0
  520. cartography/models/openai/apikey.py +4 -0
  521. cartography/models/openai/user.py +4 -0
  522. cartography/models/scaleway/__init__.py +0 -0
  523. cartography/models/scaleway/iam/__init__.py +0 -0
  524. cartography/models/scaleway/iam/apikey.py +100 -0
  525. cartography/models/scaleway/iam/application.py +52 -0
  526. cartography/models/scaleway/iam/group.py +95 -0
  527. cartography/models/scaleway/iam/user.py +64 -0
  528. cartography/models/scaleway/instance/__init__.py +0 -0
  529. cartography/models/scaleway/instance/flexibleip.py +52 -0
  530. cartography/models/scaleway/instance/instance.py +120 -0
  531. cartography/models/scaleway/organization.py +19 -0
  532. cartography/models/scaleway/project.py +48 -0
  533. cartography/models/scaleway/storage/__init__.py +0 -0
  534. cartography/models/scaleway/storage/snapshot.py +78 -0
  535. cartography/models/scaleway/storage/volume.py +51 -0
  536. cartography/models/sentinelone/__init__.py +1 -0
  537. cartography/models/sentinelone/account.py +40 -0
  538. cartography/models/sentinelone/agent.py +50 -0
  539. cartography/models/sentinelone/application.py +44 -0
  540. cartography/models/sentinelone/application_version.py +96 -0
  541. cartography/models/sentinelone/cve.py +73 -0
  542. cartography/models/slack/__init__.py +0 -0
  543. cartography/models/slack/channels.py +92 -0
  544. cartography/models/slack/group.py +129 -0
  545. cartography/models/slack/team.py +22 -0
  546. cartography/models/slack/user.py +62 -0
  547. cartography/models/snipeit/asset.py +2 -0
  548. cartography/models/snipeit/user.py +4 -0
  549. cartography/models/spacelift/__init__.py +0 -0
  550. cartography/models/spacelift/cloudtrailevent.py +120 -0
  551. cartography/models/spacelift/run.py +162 -0
  552. cartography/models/spacelift/space.py +131 -0
  553. cartography/models/spacelift/spaceliftaccount.py +31 -0
  554. cartography/models/spacelift/spaceliftgitcommit.py +157 -0
  555. cartography/models/spacelift/stack.py +96 -0
  556. cartography/models/spacelift/user.py +63 -0
  557. cartography/models/spacelift/worker.py +97 -0
  558. cartography/models/spacelift/workerpool.py +90 -0
  559. cartography/models/tailscale/device.py +2 -1
  560. cartography/models/tailscale/user.py +6 -1
  561. cartography/models/trivy/__init__.py +0 -0
  562. cartography/models/trivy/findings.py +66 -0
  563. cartography/models/trivy/fix.py +66 -0
  564. cartography/models/trivy/package.py +71 -0
  565. cartography/rules/README.md +1 -0
  566. cartography/rules/__init__.py +0 -0
  567. cartography/rules/cli.py +261 -0
  568. cartography/rules/data/__init__.py +0 -0
  569. cartography/rules/data/rules/__init__.py +46 -0
  570. cartography/rules/data/rules/cloud_security_product_deactivated.py +49 -0
  571. cartography/rules/data/rules/compute_instance_exposed.py +51 -0
  572. cartography/rules/data/rules/database_instance_exposed.py +53 -0
  573. cartography/rules/data/rules/delegation_boundary_modifiable.py +90 -0
  574. cartography/rules/data/rules/identity_administration_privileges.py +100 -0
  575. cartography/rules/data/rules/inactive_user_active_accounts.py +48 -0
  576. cartography/rules/data/rules/malicious_npm_dependencies_shai_hulud.py +2222 -0
  577. cartography/rules/data/rules/mfa_missing.py +46 -0
  578. cartography/rules/data/rules/object_storage_public.py +100 -0
  579. cartography/rules/data/rules/policy_administration_privileges.py +104 -0
  580. cartography/rules/data/rules/unmanaged_accounts.py +43 -0
  581. cartography/rules/data/rules/workload_identity_admin_capabilities.py +193 -0
  582. cartography/rules/formatters.py +108 -0
  583. cartography/rules/runners.py +216 -0
  584. cartography/rules/spec/__init__.py +0 -0
  585. cartography/rules/spec/model.py +267 -0
  586. cartography/rules/spec/result.py +38 -0
  587. cartography/sync.py +25 -5
  588. cartography/util.py +101 -31
  589. {cartography-0.104.0rc2.dist-info → cartography-0.123.0.dist-info}/METADATA +61 -22
  590. cartography-0.123.0.dist-info/RECORD +856 -0
  591. {cartography-0.104.0rc2.dist-info → cartography-0.123.0.dist-info}/entry_points.txt +1 -0
  592. cartography/data/jobs/cleanup/aws_dns_cleanup.json +0 -65
  593. cartography/data/jobs/cleanup/aws_import_account_access_key_cleanup.json +0 -17
  594. cartography/data/jobs/cleanup/aws_import_ec2_security_groupinfo_cleanup.json +0 -24
  595. cartography/data/jobs/cleanup/aws_import_groups_cleanup.json +0 -13
  596. cartography/data/jobs/cleanup/aws_import_identity_center_cleanup.json +0 -16
  597. cartography/data/jobs/cleanup/aws_import_lambda_cleanup.json +0 -50
  598. cartography/data/jobs/cleanup/aws_import_principals_cleanup.json +0 -30
  599. cartography/data/jobs/cleanup/aws_import_rds_clusters_cleanup.json +0 -23
  600. cartography/data/jobs/cleanup/aws_import_rds_instances_cleanup.json +0 -47
  601. cartography/data/jobs/cleanup/aws_import_rds_snapshots_cleanup.json +0 -23
  602. cartography/data/jobs/cleanup/aws_import_roles_cleanup.json +0 -13
  603. cartography/data/jobs/cleanup/aws_import_secrets_cleanup.json +0 -8
  604. cartography/data/jobs/cleanup/aws_import_snapshots_cleanup.json +0 -30
  605. cartography/data/jobs/cleanup/aws_import_users_cleanup.json +0 -8
  606. cartography/data/jobs/cleanup/aws_import_vpc_cleanup.json +0 -23
  607. cartography/data/jobs/cleanup/aws_import_vpc_peering_cleanup.json +0 -45
  608. cartography/data/jobs/cleanup/aws_kms_details.json +0 -10
  609. cartography/data/jobs/cleanup/azure_cosmosdb_cassandra_keyspace_cleanup.json +0 -25
  610. cartography/data/jobs/cleanup/azure_cosmosdb_cors_details.json +0 -15
  611. cartography/data/jobs/cleanup/azure_cosmosdb_mongodb_database_cleanup.json +0 -25
  612. cartography/data/jobs/cleanup/azure_cosmosdb_sql_database_cleanup.json +0 -25
  613. cartography/data/jobs/cleanup/azure_cosmosdb_table_resources_cleanup.json +0 -15
  614. cartography/data/jobs/cleanup/azure_database_account_cleanup.json +0 -85
  615. cartography/data/jobs/cleanup/azure_import_disks_cleanup.json +0 -15
  616. cartography/data/jobs/cleanup/azure_import_snapshots_cleanup.json +0 -15
  617. cartography/data/jobs/cleanup/azure_import_virtual_machines_cleanup.json +0 -25
  618. cartography/data/jobs/cleanup/azure_sql_server_cleanup.json +0 -125
  619. cartography/data/jobs/cleanup/azure_storage_account_cleanup.json +0 -95
  620. cartography/data/jobs/cleanup/azure_subscriptions_cleanup.json +0 -14
  621. cartography/data/jobs/cleanup/azure_tenant_cleanup.json +0 -9
  622. cartography/data/jobs/cleanup/gcp_compute_vpc_subnet_cleanup.json +0 -35
  623. cartography/data/jobs/cleanup/gcp_crm_folder_cleanup.json +0 -23
  624. cartography/data/jobs/cleanup/gcp_crm_organization_cleanup.json +0 -17
  625. cartography/data/jobs/cleanup/gcp_crm_project_cleanup.json +0 -23
  626. cartography/data/jobs/cleanup/gcp_dns_cleanup.json +0 -29
  627. cartography/data/jobs/cleanup/gcp_gke_cluster_cleanup.json +0 -17
  628. cartography/data/jobs/cleanup/gcp_storage_bucket_cleanup.json +0 -29
  629. cartography/data/jobs/cleanup/gsuite_ingest_groups_cleanup.json +0 -23
  630. cartography/data/jobs/cleanup/gsuite_ingest_users_cleanup.json +0 -11
  631. cartography/data/jobs/cleanup/kubernetes_import_cleanup.json +0 -70
  632. cartography/intel/gcp/crm.py +0 -355
  633. cartography/intel/gsuite/api.py +0 -342
  634. cartography-0.104.0rc2.dist-info/RECORD +0 -455
  635. /cartography/data/jobs/{analysis → scoped_analysis}/aws_s3acl_analysis.json +0 -0
  636. /cartography/models/aws/{apigateway.py → apigateway/apigateway.py} +0 -0
  637. /cartography/models/aws/{apigatewaycertificate.py → apigateway/apigatewaycertificate.py} +0 -0
  638. /cartography/models/aws/{apigatewayresource.py → apigateway/apigatewayresource.py} +0 -0
  639. /cartography/models/aws/{apigatewaystage.py → apigateway/apigatewaystage.py} +0 -0
  640. {cartography-0.104.0rc2.dist-info → cartography-0.123.0.dist-info}/WHEEL +0 -0
  641. {cartography-0.104.0rc2.dist-info → cartography-0.123.0.dist-info}/licenses/LICENSE +0 -0
  642. {cartography-0.104.0rc2.dist-info → cartography-0.123.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,962 @@
1
+ import json
2
+ import logging
3
+ from datetime import datetime
4
+ from datetime import timedelta
5
+ from typing import Any
6
+ from typing import Dict
7
+ from typing import List
8
+
9
+ import boto3
10
+ import neo4j
11
+
12
+ from cartography.client.core.tx import load_matchlinks
13
+ from cartography.graph.job import GraphJob
14
+ from cartography.intel.aws.ec2.util import get_botocore_config
15
+ from cartography.models.aws.cloudtrail.management_events import AssumedRoleMatchLink
16
+ from cartography.models.aws.cloudtrail.management_events import (
17
+ AssumedRoleWithSAMLMatchLink,
18
+ )
19
+ from cartography.models.aws.cloudtrail.management_events import (
20
+ GitHubRepoAssumeRoleWithWebIdentityMatchLink,
21
+ )
22
+ from cartography.util import aws_handle_regions
23
+ from cartography.util import timeit
24
+
25
+ logger = logging.getLogger(__name__)
26
+
27
+
28
+ @timeit
29
+ @aws_handle_regions
30
+ def get_assume_role_events(
31
+ boto3_session: boto3.Session, region: str, lookback_hours: int
32
+ ) -> List[Dict[str, Any]]:
33
+ """
34
+ Fetch CloudTrail AssumeRole events from the specified time period.
35
+
36
+ Focuses specifically on standard AssumeRole events, excluding SAML and WebIdentity variants.
37
+
38
+ :type boto3_session: boto3.Session
39
+ :param boto3_session: The boto3 session to use for API calls
40
+ :type region: str
41
+ :param region: The AWS region to fetch events from
42
+ :type lookback_hours: int
43
+ :param lookback_hours: Number of hours back to retrieve events from
44
+ :rtype: List[Dict[str, Any]]
45
+ :return: List of CloudTrail AssumeRole events
46
+ """
47
+ client = boto3_session.client(
48
+ "cloudtrail", region_name=region, config=get_botocore_config()
49
+ )
50
+
51
+ # Calculate time range
52
+ end_time = datetime.utcnow()
53
+ start_time = end_time - timedelta(hours=lookback_hours)
54
+
55
+ logger.info(
56
+ f"Fetching CloudTrail AssumeRole events for region '{region}' "
57
+ f"from {start_time} to {end_time} ({lookback_hours} hours)"
58
+ )
59
+
60
+ paginator = client.get_paginator("lookup_events")
61
+
62
+ page_iterator = paginator.paginate(
63
+ LookupAttributes=[
64
+ {"AttributeKey": "EventName", "AttributeValue": "AssumeRole"}
65
+ ],
66
+ StartTime=start_time,
67
+ EndTime=end_time,
68
+ PaginationConfig={
69
+ "MaxItems": 10000, # Reasonable limit to prevent excessive API calls
70
+ "PageSize": 50, # CloudTrail API limit per page
71
+ },
72
+ )
73
+
74
+ all_events = []
75
+ for page in page_iterator:
76
+ all_events.extend(page.get("Events", []))
77
+
78
+ logger.info(f"Retrieved {len(all_events)} AssumeRole events from region '{region}'")
79
+
80
+ return all_events
81
+
82
+
83
+ @timeit
84
+ @aws_handle_regions
85
+ def get_saml_role_events(
86
+ boto3_session: boto3.Session, region: str, lookback_hours: int
87
+ ) -> List[Dict[str, Any]]:
88
+ """
89
+ Fetch CloudTrail AssumeRoleWithSAML events from the specified time period.
90
+
91
+ Focuses specifically on SAML-based role assumption events.
92
+
93
+ :type boto3_session: boto3.Session
94
+ :param boto3_session: The boto3 session to use for API calls
95
+ :type region: str
96
+ :param region: The AWS region to fetch events from
97
+ :type lookback_hours: int
98
+ :param lookback_hours: Number of hours back to retrieve events from
99
+ :rtype: List[Dict[str, Any]]
100
+ :return: List of CloudTrail AssumeRoleWithSAML events
101
+ """
102
+ client = boto3_session.client(
103
+ "cloudtrail", region_name=region, config=get_botocore_config()
104
+ )
105
+
106
+ # Calculate time range
107
+ end_time = datetime.utcnow()
108
+ start_time = end_time - timedelta(hours=lookback_hours)
109
+
110
+ logger.info(
111
+ f"Fetching CloudTrail AssumeRoleWithSAML events for region '{region}' "
112
+ f"from {start_time} to {end_time} ({lookback_hours} hours)"
113
+ )
114
+
115
+ paginator = client.get_paginator("lookup_events")
116
+
117
+ page_iterator = paginator.paginate(
118
+ LookupAttributes=[
119
+ {"AttributeKey": "EventName", "AttributeValue": "AssumeRoleWithSAML"}
120
+ ],
121
+ StartTime=start_time,
122
+ EndTime=end_time,
123
+ PaginationConfig={
124
+ "MaxItems": 10000, # Reasonable limit to prevent excessive API calls
125
+ "PageSize": 50, # CloudTrail API limit per page
126
+ },
127
+ )
128
+
129
+ all_events = []
130
+ for page in page_iterator:
131
+ all_events.extend(page.get("Events", []))
132
+
133
+ logger.info(
134
+ f"Retrieved {len(all_events)} AssumeRoleWithSAML events from region '{region}'"
135
+ )
136
+
137
+ return all_events
138
+
139
+
140
+ @timeit
141
+ @aws_handle_regions
142
+ def get_web_identity_role_events(
143
+ boto3_session: boto3.Session, region: str, lookback_hours: int
144
+ ) -> List[Dict[str, Any]]:
145
+ """
146
+ Fetch CloudTrail AssumeRoleWithWebIdentity events from the specified time period.
147
+
148
+ Focuses specifically on WebIdentity-based role assumption events.
149
+
150
+ :type boto3_session: boto3.Session
151
+ :param boto3_session: The boto3 session to use for API calls
152
+ :type region: str
153
+ :param region: The AWS region to fetch events from
154
+ :type lookback_hours: int
155
+ :param lookback_hours: Number of hours back to retrieve events from
156
+ :rtype: List[Dict[str, Any]]
157
+ :return: List of CloudTrail AssumeRoleWithWebIdentity events
158
+ """
159
+ client = boto3_session.client(
160
+ "cloudtrail", region_name=region, config=get_botocore_config()
161
+ )
162
+
163
+ # Calculate time range
164
+ end_time = datetime.utcnow()
165
+ start_time = end_time - timedelta(hours=lookback_hours)
166
+
167
+ logger.info(
168
+ f"Fetching CloudTrail AssumeRoleWithWebIdentity events for region '{region}' "
169
+ f"from {start_time} to {end_time} ({lookback_hours} hours)"
170
+ )
171
+
172
+ paginator = client.get_paginator("lookup_events")
173
+
174
+ page_iterator = paginator.paginate(
175
+ LookupAttributes=[
176
+ {"AttributeKey": "EventName", "AttributeValue": "AssumeRoleWithWebIdentity"}
177
+ ],
178
+ StartTime=start_time,
179
+ EndTime=end_time,
180
+ PaginationConfig={
181
+ "MaxItems": 10000, # Reasonable limit to prevent excessive API calls
182
+ "PageSize": 50, # CloudTrail API limit per page
183
+ },
184
+ )
185
+
186
+ all_events = []
187
+ for page in page_iterator:
188
+ all_events.extend(page.get("Events", []))
189
+
190
+ logger.info(
191
+ f"Retrieved {len(all_events)} AssumeRoleWithWebIdentity events from region '{region}'"
192
+ )
193
+
194
+ return all_events
195
+
196
+
197
+ @timeit
198
+ def transform_assume_role_events_to_role_assumptions(
199
+ events: List[Dict[str, Any]],
200
+ ) -> List[Dict[str, Any]]:
201
+ """
202
+ Transform raw CloudTrail AssumeRole events into aggregated role assumption relationships.
203
+
204
+ Focuses specifically on standard AssumeRole events, providing optimized processing
205
+ for the most common role assumption scenario.
206
+
207
+ This function performs the complete transformation pipeline:
208
+ 1. Extract role assumption events from CloudTrail AssumeRole data
209
+ 2. Aggregate events by (source_principal, destination_principal) pairs
210
+ 3. Return aggregated relationships ready for loading
211
+
212
+ :type events: List[Dict[str, Any]]
213
+ :param events: List of raw CloudTrail AssumeRole events from lookup_events API
214
+ :rtype: List[Dict[str, Any]]
215
+ :return: List of aggregated role assumption relationships ready for loading
216
+ """
217
+ aggregated: Dict[tuple, Dict[str, Any]] = {}
218
+ logger.info(
219
+ f"Transforming {len(events)} CloudTrail AssumeRole events to role assumptions"
220
+ )
221
+
222
+ for event in events:
223
+
224
+ cloudtrail_event = json.loads(event["CloudTrailEvent"])
225
+
226
+ # Skip events with null requestParameters since we can't extract roleArn
227
+ if not cloudtrail_event.get("requestParameters"):
228
+ logger.debug(
229
+ f"Skipping CloudTrail AssumeRole event due to missing requestParameters. Event: {event.get('EventId', 'unknown')}"
230
+ )
231
+ continue
232
+
233
+ if cloudtrail_event.get("userIdentity", {}).get("arn"):
234
+ source_principal = cloudtrail_event["userIdentity"]["arn"]
235
+ destination_principal = cloudtrail_event["requestParameters"]["roleArn"]
236
+ else:
237
+ logger.debug(
238
+ f"Skipping CloudTrail AssumeRole event due to missing UserIdentity.arn. Event: {event.get('EventId', 'unknown')}"
239
+ )
240
+ continue
241
+
242
+ destination_principal = cloudtrail_event["requestParameters"]["roleArn"]
243
+
244
+ normalized_source_principal = _convert_assumed_role_arn_to_role_arn(
245
+ source_principal
246
+ )
247
+ normalized_destination_principal = _convert_assumed_role_arn_to_role_arn(
248
+ destination_principal
249
+ )
250
+ event_time = event.get("EventTime")
251
+
252
+ key = (normalized_source_principal, normalized_destination_principal)
253
+
254
+ if key in aggregated:
255
+ aggregated[key]["times_used"] += 1
256
+ # Handle None values safely for time comparisons
257
+ if event_time:
258
+ existing_first = aggregated[key]["first_seen_in_time_window"]
259
+ existing_last = aggregated[key]["last_used"]
260
+
261
+ if existing_first is None or event_time < existing_first:
262
+ aggregated[key]["first_seen_in_time_window"] = event_time
263
+ if existing_last is None or event_time > existing_last:
264
+ aggregated[key]["last_used"] = event_time
265
+ else:
266
+ aggregated[key] = {
267
+ "source_principal_arn": normalized_source_principal,
268
+ "destination_principal_arn": normalized_destination_principal,
269
+ "times_used": 1,
270
+ "first_seen_in_time_window": event_time,
271
+ "last_used": event_time,
272
+ }
273
+
274
+ return list(aggregated.values())
275
+
276
+
277
+ @timeit
278
+ def transform_saml_role_events_to_role_assumptions(
279
+ events: List[Dict[str, Any]],
280
+ ) -> List[Dict[str, Any]]:
281
+ """
282
+ Transform raw CloudTrail AssumeRoleWithSAML events into aggregated role assumption relationships.
283
+
284
+ Focuses specifically on SAML-based role assumption events, providing optimized processing
285
+ for federated identity scenarios.
286
+
287
+ This function performs the complete transformation pipeline:
288
+ 1. Extract role assumption events from CloudTrail AssumeRoleWithSAML data
289
+ 2. Aggregate events by (source_principal, destination_principal) pairs
290
+ 3. Return aggregated relationships ready for loading
291
+
292
+ :type events: List[Dict[str, Any]]
293
+ :param events: List of raw CloudTrail AssumeRoleWithSAML events from lookup_events API
294
+ :rtype: List[Dict[str, Any]]
295
+ :return: List of aggregated SAML role assumption relationships ready for loading.
296
+ Each dict contains keys: source_principal_arn, destination_principal_arn,
297
+ times_used, first_seen_in_time_window, last_used
298
+ """
299
+ aggregated: Dict[tuple, Dict[str, Any]] = {}
300
+ logger.info(
301
+ f"Transforming {len(events)} CloudTrail AssumeRoleWithSAML events to role assumptions"
302
+ )
303
+
304
+ for event in events:
305
+
306
+ cloudtrail_event = json.loads(event["CloudTrailEvent"])
307
+
308
+ # Skip events with null requestParameters since we can't extract roleArn
309
+ if not cloudtrail_event.get("requestParameters"):
310
+ logger.debug(
311
+ f"Skipping CloudTrail AssumeRoleWithSAML event due to missing requestParameters. Event: {event.get('EventId', 'unknown')}"
312
+ )
313
+ continue
314
+
315
+ response_elements = cloudtrail_event.get("responseElements", {})
316
+ assumed_role_user = response_elements.get("assumedRoleUser", {})
317
+
318
+ if assumed_role_user.get("arn"):
319
+ assumed_role_arn = assumed_role_user["arn"]
320
+ # Extract username from assumed role ARN: arn:aws:sts::account:assumed-role/RoleName/username
321
+ source_principal = assumed_role_arn.split("/")[-1]
322
+ destination_principal = cloudtrail_event["requestParameters"]["roleArn"]
323
+ else:
324
+ logger.debug(
325
+ f"Skipping CloudTrail AssumeRoleWithSAML event due to missing assumedRoleUser.arn. Event: {event.get('EventId', 'unknown')}"
326
+ )
327
+ continue
328
+
329
+ event_time = event.get("EventTime")
330
+
331
+ key = (source_principal, destination_principal)
332
+
333
+ if key in aggregated:
334
+ aggregated[key]["times_used"] += 1
335
+ # Handle None values safely for time comparisons
336
+ if event_time:
337
+ existing_first = aggregated[key]["first_seen_in_time_window"]
338
+ existing_last = aggregated[key]["last_used"]
339
+
340
+ if existing_first is None or event_time < existing_first:
341
+ aggregated[key]["first_seen_in_time_window"] = event_time
342
+ if existing_last is None or event_time > existing_last:
343
+ aggregated[key]["last_used"] = event_time
344
+ else:
345
+ aggregated[key] = {
346
+ "source_principal_arn": source_principal,
347
+ "destination_principal_arn": destination_principal,
348
+ "times_used": 1,
349
+ "first_seen_in_time_window": event_time,
350
+ "last_used": event_time,
351
+ }
352
+
353
+ return list(aggregated.values())
354
+
355
+
356
+ @timeit
357
+ def transform_web_identity_role_events_to_role_assumptions(
358
+ events: List[Dict[str, Any]],
359
+ ) -> List[Dict[str, Any]]:
360
+ """
361
+ Transform raw CloudTrail AssumeRoleWithWebIdentity events into aggregated role assumption relationships.
362
+
363
+ Focuses specifically on WebIdentity-based role assumption events, providing optimized processing
364
+ for federated web identity scenarios.
365
+
366
+ This function performs the complete transformation pipeline:
367
+ 1. Extract role assumption events from CloudTrail AssumeRoleWithWebIdentity data
368
+ 2. Aggregate events by (source_principal, destination_principal) pairs
369
+ 3. Return aggregated relationships ready for loading
370
+
371
+ :type events: List[Dict[str, Any]]
372
+ :param events: List of raw CloudTrail AssumeRoleWithWebIdentity events from lookup_events API
373
+ :rtype: List[Dict[str, Any]]
374
+ :return: List of aggregated WebIdentity role assumption relationships ready for loading.
375
+ Each dict contains keys: source_repo_fullname, destination_principal_arn,
376
+ times_used, first_seen_in_time_window, last_used
377
+ """
378
+ github_aggregated: Dict[tuple, Dict[str, Any]] = {}
379
+ logger.info(
380
+ f"Transforming {len(events)} CloudTrail AssumeRoleWithWebIdentity events to role assumptions"
381
+ )
382
+
383
+ for event in events:
384
+
385
+ cloudtrail_event = json.loads(event["CloudTrailEvent"])
386
+
387
+ # Skip events with null requestParameters since we can't extract roleArn
388
+ if not cloudtrail_event.get("requestParameters"):
389
+ logger.debug(
390
+ f"Skipping CloudTrail AssumeRoleWithWebIdentity event due to missing requestParameters. Event: {event.get('EventId', 'unknown')}"
391
+ )
392
+ continue
393
+
394
+ user_identity = cloudtrail_event.get("userIdentity", {})
395
+
396
+ if user_identity.get("type") == "WebIdentityUser" and user_identity.get(
397
+ "userName"
398
+ ):
399
+ identity_provider = user_identity.get("identityProvider", "unknown")
400
+ destination_principal = cloudtrail_event["requestParameters"]["roleArn"]
401
+ event_time = event.get("EventTime")
402
+
403
+ # Only process GitHub Actions events
404
+ if "token.actions.githubusercontent.com" in identity_provider:
405
+ # Extract GitHub repo fullname from userName format: "repo:{organization}/{repository}:{context}"
406
+ user_name = user_identity.get("userName", "")
407
+ if not user_name:
408
+ logger.debug(
409
+ f"Missing userName in GitHub WebIdentity event: {event.get('EventId', 'unknown')}"
410
+ )
411
+ continue
412
+
413
+ github_repo = _extract_github_repo_from_username(user_name)
414
+ key = (github_repo, destination_principal)
415
+
416
+ if key in github_aggregated:
417
+ github_aggregated[key]["times_used"] += 1
418
+ # Handle None values safely for time comparisons
419
+ if event_time:
420
+ existing_first = github_aggregated[key][
421
+ "first_seen_in_time_window"
422
+ ]
423
+ existing_last = github_aggregated[key]["last_used"]
424
+
425
+ if existing_first is None or event_time < existing_first:
426
+ github_aggregated[key][
427
+ "first_seen_in_time_window"
428
+ ] = event_time
429
+ if existing_last is None or event_time > existing_last:
430
+ github_aggregated[key]["last_used"] = event_time
431
+ else:
432
+ github_aggregated[key] = {
433
+ "source_repo_fullname": github_repo,
434
+ "destination_principal_arn": destination_principal,
435
+ "times_used": 1,
436
+ "first_seen_in_time_window": event_time,
437
+ "last_used": event_time,
438
+ }
439
+ else:
440
+ # Skip non-GitHub events for now
441
+ continue
442
+ else:
443
+ continue
444
+ # Return aggregated relationships directly
445
+ return list(github_aggregated.values())
446
+
447
+
448
+ @timeit
449
+ def load_role_assumptions(
450
+ neo4j_session: neo4j.Session,
451
+ aggregated_role_assumptions: List[Dict[str, Any]],
452
+ current_aws_account_id: str,
453
+ aws_update_tag: int,
454
+ ) -> None:
455
+ """
456
+ Load aggregated role assumption relationships into Neo4j using MatchLink pattern.
457
+
458
+ Creates direct ASSUMED_ROLE relationships with aggregated properties:
459
+ (AWSUser|AWSRole|AWSPrincipal)-[:ASSUMED_ROLE {lastupdated, times_used, first_seen_in_time_window, last_used}]->(AWSRole)
460
+
461
+ Assumes that both source principals and destination roles already exist in the graph.
462
+
463
+ :type neo4j_session: neo4j.Session
464
+ :param neo4j_session: The Neo4j session to use for database operations
465
+ :type aggregated_role_assumptions: List[Dict[str, Any]]
466
+ :param aggregated_role_assumptions: List of aggregated role assumption relationships from transform function
467
+ :type current_aws_account_id: str
468
+ :param current_aws_account_id: The AWS account ID being synced
469
+ :type aws_update_tag: int
470
+ :param aws_update_tag: Timestamp tag for tracking data freshness
471
+ :rtype: None
472
+ """
473
+ # Use MatchLink to create relationships between existing nodes
474
+ matchlink_schema = AssumedRoleMatchLink()
475
+
476
+ load_matchlinks(
477
+ neo4j_session,
478
+ matchlink_schema,
479
+ aggregated_role_assumptions,
480
+ lastupdated=aws_update_tag,
481
+ _sub_resource_label="AWSAccount",
482
+ _sub_resource_id=current_aws_account_id,
483
+ )
484
+
485
+ logger.info(
486
+ f"Successfully loaded {len(aggregated_role_assumptions)} role assumption relationships"
487
+ )
488
+
489
+
490
+ @timeit
491
+ def load_saml_role_assumptions(
492
+ neo4j_session: neo4j.Session,
493
+ aggregated_role_assumptions: List[Dict[str, Any]],
494
+ current_aws_account_id: str,
495
+ aws_update_tag: int,
496
+ ) -> None:
497
+ """
498
+ Load aggregated SAML role assumption relationships into Neo4j using MatchLink pattern.
499
+
500
+ Creates direct ASSUMED_ROLE_WITH_SAML relationships with aggregated properties:
501
+ (AWSRole)-[:ASSUMED_ROLE_WITH_SAML {lastupdated, times_used, first_seen_in_time_window, last_used}]->(AWSRole)
502
+
503
+ Assumes that both source principals and destination roles already exist in the graph.
504
+
505
+ :type neo4j_session: neo4j.Session
506
+ :param neo4j_session: The Neo4j session to use for database operations
507
+ :type aggregated_role_assumptions: List[Dict[str, Any]]
508
+ :param aggregated_role_assumptions: List of aggregated SAML role assumption relationships from transform function
509
+ :type current_aws_account_id: str
510
+ :param current_aws_account_id: The AWS account ID being synced
511
+ :type aws_update_tag: int
512
+ :param aws_update_tag: Timestamp tag for tracking data freshness
513
+ :rtype: None
514
+ """
515
+ # Use MatchLink to create relationships between existing nodes
516
+ matchlink_schema = AssumedRoleWithSAMLMatchLink()
517
+
518
+ load_matchlinks(
519
+ neo4j_session,
520
+ matchlink_schema,
521
+ aggregated_role_assumptions,
522
+ lastupdated=aws_update_tag,
523
+ _sub_resource_label="AWSAccount",
524
+ _sub_resource_id=current_aws_account_id,
525
+ )
526
+
527
+ logger.info(
528
+ f"Successfully loaded {len(aggregated_role_assumptions)} SAML role assumption relationships"
529
+ )
530
+
531
+
532
+ @timeit
533
+ def load_web_identity_role_assumptions(
534
+ neo4j_session: neo4j.Session,
535
+ aggregated_role_assumptions: List[Dict[str, Any]],
536
+ current_aws_account_id: str,
537
+ aws_update_tag: int,
538
+ ) -> None:
539
+ """
540
+ Load aggregated WebIdentity role assumption relationships into Neo4j using MatchLink pattern.
541
+
542
+ Creates direct ASSUMED_ROLE_WITH_WEB_IDENTITY relationships with aggregated properties:
543
+ (GitHubRepository)-[:ASSUMED_ROLE_WITH_WEB_IDENTITY {lastupdated, times_used, first_seen_in_time_window, last_used}]->(AWSRole)
544
+
545
+ Assumes that both source principals and destination roles already exist in the graph.
546
+
547
+ :type neo4j_session: neo4j.Session
548
+ :param neo4j_session: The Neo4j session to use for database operations
549
+ :type aggregated_role_assumptions: List[Dict[str, Any]]
550
+ :param aggregated_role_assumptions: List of aggregated WebIdentity role assumption relationships from transform function
551
+ :type current_aws_account_id: str
552
+ :param current_aws_account_id: The AWS account ID being synced
553
+ :type aws_update_tag: int
554
+ :param aws_update_tag: Timestamp tag for tracking data freshness
555
+ :rtype: None
556
+ """
557
+ # Use MatchLink to create relationships between existing nodes
558
+ matchlink_schema = GitHubRepoAssumeRoleWithWebIdentityMatchLink()
559
+
560
+ load_matchlinks(
561
+ neo4j_session,
562
+ matchlink_schema,
563
+ aggregated_role_assumptions,
564
+ lastupdated=aws_update_tag,
565
+ _sub_resource_label="AWSAccount",
566
+ _sub_resource_id=current_aws_account_id,
567
+ )
568
+
569
+ logger.info(
570
+ f"Successfully loaded {len(aggregated_role_assumptions)} WebIdentity role assumption relationships"
571
+ )
572
+
573
+
574
+ def _convert_assumed_role_arn_to_role_arn(assumed_role_arn: str) -> str:
575
+ """
576
+ Convert an assumed role ARN to the original role ARN.
577
+
578
+ Example:
579
+ Input: "arn:aws:sts::123456789012:assumed-role/MyRole/session-name"
580
+ Output: "arn:aws:iam::123456789012:role/MyRole"
581
+ """
582
+
583
+ # Split the ARN into parts
584
+ arn_parts = assumed_role_arn.split(":")
585
+ if len(arn_parts) >= 6 and arn_parts[2] == "sts" and "assumed-role" in arn_parts[5]:
586
+ # Extract account ID and role name
587
+ account_id = arn_parts[4]
588
+ resource_part = arn_parts[5] # "assumed-role/MyRole/session-name"
589
+ role_name = resource_part.split("/")[1] # Extract "MyRole"
590
+
591
+ # Construct the IAM role ARN
592
+ return f"arn:aws:iam::{account_id}:role/{role_name}"
593
+
594
+ # Return original ARN if conversion fails
595
+ return assumed_role_arn
596
+
597
+
598
+ def _extract_github_repo_from_username(user_name: str) -> str:
599
+ """
600
+ Extract GitHub repository fullname from CloudTrail userName field.
601
+
602
+ GitHub Actions CloudTrail events have userName in the format:
603
+ "repo:{organization}/{repository}:{context}"
604
+ """
605
+ if not user_name:
606
+ return ""
607
+
608
+ parts = user_name.split(":")
609
+
610
+ # Need at least 3 parts: ["repo", "{organization}/{repository}", "{context}"]
611
+ if len(parts) < 3 or parts[0] != "repo":
612
+ return ""
613
+
614
+ # Extract "{organization}/{repository}"
615
+ repo_fullname = parts[1]
616
+
617
+ # Validate it looks like "{organization}/{repository}" format
618
+ if repo_fullname.count("/") != 1:
619
+ return ""
620
+
621
+ # Ensure both organization and repo exist
622
+ owner, repo = repo_fullname.split("/")
623
+ if not owner or not repo:
624
+ return ""
625
+
626
+ return repo_fullname
627
+
628
+
629
+ @timeit
630
+ def cleanup(
631
+ neo4j_session: neo4j.Session, current_aws_account_id: str, update_tag: int
632
+ ) -> None:
633
+ """
634
+ Run CloudTrail management events cleanup job to remove stale ASSUMED_ROLE relationships.
635
+
636
+ :type neo4j_session: neo4j.Session
637
+ :param neo4j_session: The Neo4j session to use for database operations
638
+ :type current_aws_account_id: str
639
+ :param current_aws_account_id: The AWS account ID being synced
640
+ :type update_tag: int
641
+ :param update_tag: Timestamp tag for tracking data freshness
642
+ :rtype: None
643
+ """
644
+ logger.info("Running CloudTrail management events cleanup job.")
645
+
646
+ matchlink_schema = AssumedRoleMatchLink()
647
+ cleanup_job = GraphJob.from_matchlink(
648
+ matchlink_schema,
649
+ "AWSAccount",
650
+ current_aws_account_id,
651
+ update_tag,
652
+ )
653
+ cleanup_job.run(neo4j_session)
654
+
655
+
656
+ @timeit
657
+ def sync_assume_role_events(
658
+ neo4j_session: neo4j.Session,
659
+ boto3_session: boto3.Session,
660
+ regions: List[str],
661
+ current_aws_account_id: str,
662
+ update_tag: int,
663
+ common_job_parameters: Dict[str, Any],
664
+ ) -> None:
665
+ """
666
+ Sync CloudTrail management events to create ASSUMED_ROLE relationships.
667
+
668
+ This function orchestrates the complete process:
669
+ 1. Fetch CloudTrail management events region by region
670
+ 2. Transform events into role assumption records per region
671
+ 3. Load role assumption relationships into Neo4j for each region
672
+ 4. Run cleanup after processing all regions
673
+
674
+ The resulting graph contains direct relationships like:
675
+ (AWSUser|AWSRole|AWSPrincipal)-[:ASSUMED_ROLE {times_used, first_seen_in_time_window, last_used, lastupdated}]->(AWSRole)
676
+
677
+ :type neo4j_session: neo4j.Session
678
+ :param neo4j_session: The Neo4j session
679
+ :type boto3_session: boto3.Session
680
+ :param boto3_session: The boto3 session to use for API calls
681
+ :type regions: List[str]
682
+ :param regions: List of AWS regions to sync
683
+ :type current_aws_account_id: str
684
+ :param current_aws_account_id: The AWS account ID being synced
685
+ :type aws_update_tag: int
686
+ :param aws_update_tag: Timestamp tag for tracking data freshness
687
+ :rtype: None
688
+ """
689
+ # Extract lookback hours from common_job_parameters (set by CLI parameter)
690
+ lookback_hours = common_job_parameters.get(
691
+ "aws_cloudtrail_management_events_lookback_hours"
692
+ )
693
+
694
+ if not lookback_hours:
695
+ logger.info(
696
+ "CloudTrail management events sync skipped - no lookback period specified"
697
+ )
698
+ return
699
+
700
+ logger.info(
701
+ f"Syncing {len(regions)} regions with {lookback_hours} hour lookback period"
702
+ )
703
+
704
+ total_role_assumptions = 0
705
+
706
+ # Process events region by region
707
+ for region in regions:
708
+ logger.info(f"Processing CloudTrail events for region {region}")
709
+
710
+ # Process AssumeRole events specifically
711
+ logger.info(f"Fetching AssumeRole events specifically for region {region}")
712
+ assume_role_events = get_assume_role_events(
713
+ boto3_session=boto3_session,
714
+ region=region,
715
+ lookback_hours=lookback_hours,
716
+ )
717
+
718
+ # Transform AssumeRole events to role assumptions
719
+ assume_role_assumptions = transform_assume_role_events_to_role_assumptions(
720
+ events=assume_role_events,
721
+ )
722
+
723
+ # Load AssumeRole assumptions for this region
724
+ load_role_assumptions(
725
+ neo4j_session=neo4j_session,
726
+ aggregated_role_assumptions=assume_role_assumptions,
727
+ current_aws_account_id=current_aws_account_id,
728
+ aws_update_tag=update_tag,
729
+ )
730
+ total_role_assumptions += len(assume_role_assumptions)
731
+ logger.info(
732
+ f"Loaded {len(assume_role_assumptions)} AssumeRole assumptions for region {region}"
733
+ )
734
+
735
+ # Run cleanup for stale relationships after processing all regions
736
+ cleanup(neo4j_session, current_aws_account_id, update_tag)
737
+
738
+ logger.info(
739
+ f"CloudTrail management events sync completed successfully. "
740
+ f"Processed {total_role_assumptions} total role assumption events across {len(regions)} regions."
741
+ )
742
+
743
+
744
+ @timeit
745
+ def sync_saml_role_events(
746
+ neo4j_session: neo4j.Session,
747
+ boto3_session: boto3.Session,
748
+ regions: List[str],
749
+ current_aws_account_id: str,
750
+ update_tag: int,
751
+ common_job_parameters: Dict[str, Any],
752
+ ) -> None:
753
+ """
754
+ Sync CloudTrail SAML management events to create ASSUMED_ROLE_WITH_SAML relationships.
755
+
756
+ This function orchestrates the complete process:
757
+ 1. Fetch CloudTrail SAML management events region by region
758
+ 2. Transform events into role assumption records per region
759
+ 3. Load role assumption relationships into Neo4j for each region
760
+
761
+ The resulting graph contains direct relationships like:
762
+ (AWSRole)-[:ASSUMED_ROLE_WITH_SAML {times_used, first_seen_in_time_window, last_used, lastupdated}]->(AWSRole)
763
+
764
+ :type neo4j_session: neo4j.Session
765
+ :param neo4j_session: The Neo4j session
766
+ :type boto3_session: boto3.Session
767
+ :param boto3_session: The boto3 session to use for API calls
768
+ :type regions: List[str]
769
+ :param regions: List of AWS regions to sync
770
+ :type current_aws_account_id: str
771
+ :param current_aws_account_id: The AWS account ID being synced
772
+ :type update_tag: int
773
+ :param update_tag: Timestamp tag for tracking data freshness
774
+ :rtype: None
775
+ """
776
+ # Extract lookback hours from common_job_parameters (set by CLI parameter)
777
+ lookback_hours = common_job_parameters.get(
778
+ "aws_cloudtrail_management_events_lookback_hours"
779
+ )
780
+
781
+ if not lookback_hours:
782
+ logger.info(
783
+ "CloudTrail SAML management events sync skipped - no lookback period specified"
784
+ )
785
+ return
786
+
787
+ logger.info(
788
+ f"Syncing SAML events for {len(regions)} regions with {lookback_hours} hour lookback period"
789
+ )
790
+
791
+ total_saml_role_assumptions = 0
792
+
793
+ # Process events region by region
794
+ for region in regions:
795
+ logger.info(f"Processing CloudTrail SAML events for region {region}")
796
+
797
+ # Process AssumeRoleWithSAML events specifically
798
+ logger.info(
799
+ f"Fetching AssumeRoleWithSAML events specifically for region {region}"
800
+ )
801
+ saml_role_events = get_saml_role_events(
802
+ boto3_session=boto3_session,
803
+ region=region,
804
+ lookback_hours=lookback_hours,
805
+ )
806
+
807
+ # Transform AssumeRoleWithSAML events to role assumptions
808
+ saml_role_assumptions = transform_saml_role_events_to_role_assumptions(
809
+ events=saml_role_events,
810
+ )
811
+
812
+ # Load SAML role assumptions for this region
813
+ load_saml_role_assumptions(
814
+ neo4j_session=neo4j_session,
815
+ aggregated_role_assumptions=saml_role_assumptions,
816
+ current_aws_account_id=current_aws_account_id,
817
+ aws_update_tag=update_tag,
818
+ )
819
+ total_saml_role_assumptions += len(saml_role_assumptions)
820
+ logger.info(
821
+ f"Loaded {len(saml_role_assumptions)} SAML role assumptions for region {region}"
822
+ )
823
+
824
+ logger.info(
825
+ f"CloudTrail SAML management events sync completed successfully. "
826
+ f"Processed {total_saml_role_assumptions} total SAML role assumption events across {len(regions)} regions."
827
+ )
828
+
829
+
830
+ @timeit
831
+ def sync_web_identity_role_events(
832
+ neo4j_session: neo4j.Session,
833
+ boto3_session: boto3.Session,
834
+ regions: List[str],
835
+ current_aws_account_id: str,
836
+ update_tag: int,
837
+ common_job_parameters: Dict[str, Any],
838
+ ) -> None:
839
+ """
840
+ Sync CloudTrail WebIdentity management events to create ASSUMED_ROLE_WITH_WEB_IDENTITY relationships.
841
+
842
+ This function orchestrates the complete process:
843
+ 1. Fetch CloudTrail WebIdentity management events region by region
844
+ 2. Transform events into role assumption records per region
845
+ 3. Load role assumption relationships into Neo4j for each region
846
+
847
+ The resulting graph contains direct relationships like:
848
+ (GitHubRepository)-[:ASSUMED_ROLE_WITH_WEB_IDENTITY {times_used, first_seen_in_time_window, last_used, lastupdated}]->(AWSRole)
849
+
850
+ :type neo4j_session: neo4j.Session
851
+ :param neo4j_session: The Neo4j session
852
+ :type boto3_session: boto3.Session
853
+ :param boto3_session: The boto3 session to use for API calls
854
+ :type regions: List[str]
855
+ :param regions: List of AWS regions to sync
856
+ :type current_aws_account_id: str
857
+ :param current_aws_account_id: The AWS account ID being synced
858
+ :type update_tag: int
859
+ :param update_tag: Timestamp tag for tracking data freshness
860
+ :rtype: None
861
+ """
862
+ # Extract lookback hours from common_job_parameters (set by CLI parameter)
863
+ lookback_hours = common_job_parameters.get(
864
+ "aws_cloudtrail_management_events_lookback_hours"
865
+ )
866
+
867
+ if not lookback_hours:
868
+ logger.info(
869
+ "CloudTrail WebIdentity management events sync skipped - no lookback period specified"
870
+ )
871
+ return
872
+
873
+ logger.info(
874
+ f"Syncing WebIdentity events for {len(regions)} regions with {lookback_hours} hour lookback period"
875
+ )
876
+
877
+ total_web_identity_role_assumptions = 0
878
+
879
+ # Process events region by region
880
+ for region in regions:
881
+ logger.info(f"Processing CloudTrail WebIdentity events for region {region}")
882
+
883
+ # Process AssumeRoleWithWebIdentity events specifically
884
+ logger.info(
885
+ f"Fetching AssumeRoleWithWebIdentity events specifically for region {region}"
886
+ )
887
+ web_identity_role_events = get_web_identity_role_events(
888
+ boto3_session=boto3_session,
889
+ region=region,
890
+ lookback_hours=lookback_hours,
891
+ )
892
+
893
+ # Transform AssumeRoleWithWebIdentity events to role assumptions
894
+ web_identity_role_assumptions = (
895
+ transform_web_identity_role_events_to_role_assumptions(
896
+ events=web_identity_role_events,
897
+ )
898
+ )
899
+
900
+ # Load WebIdentity role assumptions for this region
901
+ load_web_identity_role_assumptions(
902
+ neo4j_session=neo4j_session,
903
+ aggregated_role_assumptions=web_identity_role_assumptions,
904
+ current_aws_account_id=current_aws_account_id,
905
+ aws_update_tag=update_tag,
906
+ )
907
+ total_web_identity_role_assumptions += len(web_identity_role_assumptions)
908
+ logger.info(
909
+ f"Loaded {len(web_identity_role_assumptions)} WebIdentity role assumptions for region {region}"
910
+ )
911
+
912
+ logger.info(
913
+ f"CloudTrail WebIdentity management events sync completed successfully. "
914
+ f"Processed {total_web_identity_role_assumptions} total WebIdentity role assumption events across {len(regions)} regions."
915
+ )
916
+
917
+
918
+ # Main sync function for when we decide to add more event types
919
+ @timeit
920
+ def sync(
921
+ neo4j_session: neo4j.Session,
922
+ boto3_session: boto3.Session,
923
+ regions: List[str],
924
+ current_aws_account_id: str,
925
+ update_tag: int,
926
+ common_job_parameters: Dict[str, Any],
927
+ ) -> None:
928
+ """
929
+ Main sync function for CloudTrail management events.
930
+
931
+ Syncs AssumeRole, AssumeRoleWithSAML, and AssumeRoleWithWebIdentity events to create separate
932
+ relationship types in the graph for security analysis.
933
+ """
934
+ # Sync regular AssumeRole events
935
+ sync_assume_role_events(
936
+ neo4j_session=neo4j_session,
937
+ boto3_session=boto3_session,
938
+ regions=regions,
939
+ current_aws_account_id=current_aws_account_id,
940
+ update_tag=update_tag,
941
+ common_job_parameters=common_job_parameters,
942
+ )
943
+
944
+ # Sync SAML AssumeRoleWithSAML events
945
+ sync_saml_role_events(
946
+ neo4j_session=neo4j_session,
947
+ boto3_session=boto3_session,
948
+ regions=regions,
949
+ current_aws_account_id=current_aws_account_id,
950
+ update_tag=update_tag,
951
+ common_job_parameters=common_job_parameters,
952
+ )
953
+
954
+ # Sync WebIdentity AssumeRoleWithWebIdentity events
955
+ sync_web_identity_role_events(
956
+ neo4j_session=neo4j_session,
957
+ boto3_session=boto3_session,
958
+ regions=regions,
959
+ current_aws_account_id=current_aws_account_id,
960
+ update_tag=update_tag,
961
+ common_job_parameters=common_job_parameters,
962
+ )