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,463 @@
1
+ import json
2
+ import logging
3
+ from typing import Any
4
+
5
+ import neo4j
6
+ import requests
7
+
8
+ from cartography.client.core.tx import load
9
+ from cartography.graph.job import GraphJob
10
+ from cartography.intel.spacelift.util import call_spacelift_api
11
+ from cartography.models.spacelift.run import SpaceliftRunSchema
12
+ from cartography.models.spacelift.spaceliftgitcommit import SpaceliftGitCommitSchema
13
+ from cartography.models.spacelift.user import SpaceliftUserSchema
14
+ from cartography.util import timeit
15
+
16
+ logger = logging.getLogger(__name__)
17
+
18
+ # GraphQL query to fetch all managed entities from all stacks
19
+ GET_ENTITIES_QUERY = """
20
+ query {
21
+ stacks {
22
+ entities {
23
+ type
24
+ creator {
25
+ id
26
+ }
27
+ updater {
28
+ id
29
+ }
30
+ vendor {
31
+ __typename
32
+ ... on EntityVendorTerraform {
33
+ terraform {
34
+ __typename
35
+ ... on TerraformResource {
36
+ values
37
+ }
38
+ }
39
+ }
40
+ }
41
+ }
42
+ }
43
+ }
44
+ """
45
+
46
+ # GraphQL query to fetch runs nested under stacks
47
+ # Note: Runs don't have a top-level query, they're nested under stacks
48
+ GET_RUNS_QUERY = """
49
+ query {
50
+ stacks {
51
+ id
52
+ runs {
53
+ id
54
+ type
55
+ state
56
+ commit {
57
+ hash
58
+ authorLogin
59
+ authorName
60
+ message
61
+ timestamp
62
+ url
63
+ }
64
+ branch
65
+ createdAt
66
+ finished
67
+ triggeredBy
68
+ }
69
+ }
70
+ }
71
+ """
72
+
73
+
74
+ @timeit
75
+ def get_entities(session: requests.Session, api_endpoint: str) -> list[dict[str, Any]]:
76
+ logger.info("Fetching Spacelift entities")
77
+
78
+ response = call_spacelift_api(session, api_endpoint, GET_ENTITIES_QUERY)
79
+ stacks = response.get("data", {}).get("stacks", [])
80
+
81
+ # Flatten entities from all stacks into a single list
82
+ all_entities = []
83
+ for stack in stacks:
84
+ all_entities.extend(stack.get("entities", []))
85
+
86
+ logger.info(
87
+ f"Retrieved {len(all_entities)} Spacelift entities from {len(stacks)} stacks"
88
+ )
89
+ return all_entities
90
+
91
+
92
+ def transform_entities_to_run_map(
93
+ entities_data: list[dict[str, Any]],
94
+ ) -> dict[str, list[dict[str, str]]]:
95
+ """
96
+ This function processes entities to extract EC2 instance IDs and maps them to
97
+ the runs that created or updated them.
98
+ """
99
+ logger.info(f"Transforming {len(entities_data)} entities into run-to-instances map")
100
+
101
+ run_to_instances: dict[str, list[dict[str, str]]] = {}
102
+
103
+ for entity in entities_data:
104
+ # Right now we just cover ec2
105
+ entity_type = entity.get("type")
106
+ if entity_type != "aws_instance":
107
+ continue
108
+
109
+ # NOTE: Right now we only cover terraform resources but we can extend this to cover other vendors in the future.
110
+ vendor = entity.get("vendor", {})
111
+ if vendor.get("__typename") != "EntityVendorTerraform":
112
+ continue
113
+
114
+ terraform_data = vendor.get("terraform", {})
115
+ if terraform_data.get("__typename") != "TerraformResource":
116
+ continue
117
+
118
+ values_json = terraform_data.get("values")
119
+ try:
120
+ values = json.loads(values_json)
121
+ instance_id = values["id"]
122
+ except (json.JSONDecodeError, KeyError) as e:
123
+ logger.warning(f"Failed to parse entity values:{e}")
124
+ continue
125
+
126
+ logger.info(f"Found EC2 instance from entity: {instance_id}")
127
+
128
+ # Map to creator run
129
+ creator = entity.get("creator")
130
+ if creator and creator.get("id"):
131
+ creator_run_id = creator["id"]
132
+ if creator_run_id not in run_to_instances:
133
+ run_to_instances[creator_run_id] = []
134
+ run_to_instances[creator_run_id].append(
135
+ {
136
+ "instance_id": instance_id,
137
+ "action": "create",
138
+ }
139
+ )
140
+ logger.info(
141
+ f"Mapped instance {instance_id} to creator run {creator_run_id}"
142
+ )
143
+
144
+ # Map to updater run
145
+ updater = entity.get("updater")
146
+ if updater and updater.get("id"):
147
+ updater_run_id = updater["id"]
148
+ # Don't duplicate if updater is same as creator
149
+ if updater_run_id != creator.get("id") if creator else True:
150
+ if updater_run_id not in run_to_instances:
151
+ run_to_instances[updater_run_id] = []
152
+ run_to_instances[updater_run_id].append(
153
+ {
154
+ "instance_id": instance_id,
155
+ "action": "update",
156
+ }
157
+ )
158
+ logger.info(
159
+ f"Mapped instance {instance_id} to updater run {updater_run_id}"
160
+ )
161
+
162
+ logger.info(
163
+ f"Built run-to-instances map with {len(run_to_instances)} runs affecting EC2 instances"
164
+ )
165
+ return run_to_instances
166
+
167
+
168
+ @timeit
169
+ def get_runs(session: requests.Session, api_endpoint: str) -> list[dict[str, Any]]:
170
+ logger.info("Fetching Spacelift runs")
171
+
172
+ response = call_spacelift_api(session, api_endpoint, GET_RUNS_QUERY)
173
+ stacks = response.get("data", {}).get("stacks", [])
174
+
175
+ # Flatten runs from all stacks and add the stack ID to each run
176
+ all_runs = []
177
+ for stack in stacks:
178
+ stack_id = stack["id"]
179
+ for run in stack.get("runs", []):
180
+ # Add the stack ID to each run
181
+ run["stack"] = stack_id
182
+ all_runs.append(run)
183
+
184
+ logger.info(f"Retrieved {len(all_runs)} Spacelift runs from {len(stacks)} stacks")
185
+ return all_runs
186
+
187
+
188
+ def transform_runs(
189
+ runs_data: list[dict[str, Any]],
190
+ run_to_instances_map: dict[str, list[dict[str, str]]],
191
+ account_id: str,
192
+ ) -> list[dict[str, Any]]:
193
+
194
+ logger.info(f"Transforming {len(runs_data)} runs")
195
+
196
+ result: list[dict[str, Any]] = []
197
+
198
+ for run in runs_data:
199
+ run_id = run["id"]
200
+
201
+ # Look up affected EC2 instances for this run
202
+ affected_instances = run_to_instances_map.get(run_id, [])
203
+ affected_instance_ids = [inst["instance_id"] for inst in affected_instances]
204
+
205
+ if affected_instance_ids:
206
+ logger.info(f"Run {run_id} affects instances: {affected_instance_ids}")
207
+
208
+ # Extract commit hash from nested commit object
209
+ commit = run.get("commit", {})
210
+ commit_hash = commit.get("hash") if commit else None
211
+
212
+ transformed_run = {
213
+ "id": run_id,
214
+ "run_type": run.get("type"),
215
+ "state": run.get("state"),
216
+ "commit_sha": commit_hash,
217
+ "branch": run.get("branch"),
218
+ "created_at": run.get("createdAt"),
219
+ "stack_id": run.get("stack"),
220
+ "triggered_by_user_id": run.get("triggeredBy"),
221
+ "affected_instance_ids": affected_instance_ids,
222
+ "spacelift_account_id": account_id,
223
+ }
224
+
225
+ result.append(transformed_run)
226
+
227
+ logger.info(
228
+ f"Transformed {len(result)} runs ({sum(1 for r in result if r['affected_instance_ids'])} affecting EC2 instances)"
229
+ )
230
+ return result
231
+
232
+
233
+ def extract_users_from_runs(runs_data: list[dict[str, Any]]) -> list[dict[str, Any]]:
234
+ """
235
+ Extract unique user information from runs' triggeredBy field.
236
+ """
237
+ logger.info("Extracting users from runs")
238
+
239
+ user_emails = set()
240
+
241
+ for run in runs_data:
242
+ triggered_by = run.get("triggeredBy")
243
+
244
+ if triggered_by:
245
+ user_emails.add(triggered_by)
246
+
247
+ # Create user dictionaries with appropriate user_type
248
+ users = []
249
+ for user_id in sorted(user_emails):
250
+ is_email = "@" in user_id
251
+ users.append(
252
+ {
253
+ "id": user_id,
254
+ "username": user_id,
255
+ "email": user_id if is_email else None,
256
+ "name": user_id.split("@")[0] if is_email else user_id,
257
+ "user_type": "human" if is_email else "system",
258
+ }
259
+ )
260
+
261
+ logger.info(f"Extracted {len(users)} unique users from {len(runs_data)} runs")
262
+ return users
263
+
264
+
265
+ def extract_commits_from_runs(
266
+ runs_data: list[dict[str, Any]], account_id: str
267
+ ) -> list[dict[str, Any]]:
268
+ """
269
+ Extract unique commit information from runs.
270
+ Links commits to the users/systems that triggered runs using those commits.
271
+ """
272
+ logger.info("Extracting commits from runs")
273
+
274
+ # Use dict to deduplicate by SHA
275
+ commits_by_sha: dict[str, dict[str, Any]] = {}
276
+
277
+ for run in runs_data:
278
+ commit = run.get("commit")
279
+
280
+ # Skip runs without commit data
281
+ if not commit or not commit.get("hash"):
282
+ continue
283
+
284
+ sha = commit["hash"]
285
+ triggered_by = run.get("triggeredBy")
286
+
287
+ # Skip if we've already seen this commit (first triggeredBy wins)
288
+ if sha in commits_by_sha:
289
+ continue
290
+
291
+ commits_by_sha[sha] = {
292
+ "sha": sha,
293
+ "message": commit.get("message"),
294
+ "timestamp": commit.get("timestamp"),
295
+ "url": commit.get("url"),
296
+ "author_login": commit.get("authorLogin"),
297
+ "author_name": commit.get("authorName"),
298
+ "author_user_id": triggered_by,
299
+ "spacelift_account_id": account_id,
300
+ }
301
+
302
+ logger.debug(
303
+ f"Extracted commit {sha} (by {commit.get('authorLogin')}) confirmed by {triggered_by}"
304
+ )
305
+
306
+ commits = list(commits_by_sha.values())
307
+ logger.info(f"Extracted {len(commits)} unique commits from {len(runs_data)} runs")
308
+ return commits
309
+
310
+
311
+ def load_users(
312
+ neo4j_session: neo4j.Session,
313
+ users_data: list[dict[str, Any]],
314
+ update_tag: int,
315
+ account_id: str,
316
+ ) -> None:
317
+ """
318
+ Load Spacelift users data into Neo4j using the data model.
319
+ """
320
+
321
+ # Add spacelift_account_id to each user for the relationship
322
+ for user in users_data:
323
+ user["spacelift_account_id"] = account_id
324
+
325
+ load(
326
+ neo4j_session,
327
+ SpaceliftUserSchema(),
328
+ users_data,
329
+ lastupdated=update_tag,
330
+ spacelift_account_id=account_id,
331
+ )
332
+
333
+ logger.info(f"Loaded {len(users_data)} Spacelift users")
334
+
335
+
336
+ def load_runs(
337
+ neo4j_session: neo4j.Session,
338
+ runs_data: list[dict[str, Any]],
339
+ update_tag: int,
340
+ account_id: str,
341
+ ) -> None:
342
+ """
343
+ Load Spacelift runs data into Neo4j using the data model.
344
+ """
345
+ load(
346
+ neo4j_session,
347
+ SpaceliftRunSchema(),
348
+ runs_data,
349
+ lastupdated=update_tag,
350
+ spacelift_account_id=account_id,
351
+ )
352
+
353
+ logger.info(f"Loaded {len(runs_data)} Spacelift runs")
354
+
355
+
356
+ def load_commits(
357
+ neo4j_session: neo4j.Session,
358
+ commits_data: list[dict[str, Any]],
359
+ update_tag: int,
360
+ account_id: str,
361
+ ) -> None:
362
+ """
363
+ Load Git commit data into Neo4j using the data model.
364
+ """
365
+
366
+ load(
367
+ neo4j_session,
368
+ SpaceliftGitCommitSchema(),
369
+ commits_data,
370
+ lastupdated=update_tag,
371
+ spacelift_account_id=account_id,
372
+ )
373
+
374
+ logger.info(f"Loaded {len(commits_data)} Git commits")
375
+
376
+
377
+ @timeit
378
+ def cleanup_users(
379
+ neo4j_session: neo4j.Session,
380
+ common_job_parameters: dict[str, Any],
381
+ ) -> None:
382
+ """
383
+ Remove stale Spacelift user data from Neo4j.
384
+ """
385
+ from cartography.models.spacelift.user import SpaceliftUserSchema
386
+
387
+ logger.debug("Running SpaceliftUser cleanup job")
388
+ GraphJob.from_node_schema(SpaceliftUserSchema(), common_job_parameters).run(
389
+ neo4j_session
390
+ )
391
+
392
+
393
+ @timeit
394
+ def cleanup_runs(
395
+ neo4j_session: neo4j.Session,
396
+ common_job_parameters: dict[str, Any],
397
+ ) -> None:
398
+
399
+ logger.debug("Running SpaceliftRun cleanup job")
400
+ GraphJob.from_node_schema(SpaceliftRunSchema(), common_job_parameters).run(
401
+ neo4j_session
402
+ )
403
+
404
+
405
+ @timeit
406
+ def cleanup_commits(
407
+ neo4j_session: neo4j.Session,
408
+ common_job_parameters: dict[str, Any],
409
+ ) -> None:
410
+
411
+ logger.debug("Running GitCommit cleanup job")
412
+ GraphJob.from_node_schema(SpaceliftGitCommitSchema(), common_job_parameters).run(
413
+ neo4j_session
414
+ )
415
+
416
+
417
+ @timeit
418
+ def sync_runs(
419
+ neo4j_session: neo4j.Session,
420
+ spacelift_session: requests.Session,
421
+ api_endpoint: str,
422
+ account_id: str,
423
+ common_job_parameters: dict[str, Any],
424
+ ) -> None:
425
+ """
426
+ Sync Spacelift runs, commits, and users to Neo4j.
427
+ Users are extracted from runs' triggeredBy field.
428
+ Commits are linked to the users who triggered deployments using those commits.
429
+ """
430
+
431
+ entities_raw_data = get_entities(spacelift_session, api_endpoint)
432
+ runs_raw_data = get_runs(spacelift_session, api_endpoint)
433
+
434
+ run_to_instances_map = transform_entities_to_run_map(entities_raw_data)
435
+ transformed_runs = transform_runs(runs_raw_data, run_to_instances_map, account_id)
436
+
437
+ extracted_users = extract_users_from_runs(runs_raw_data)
438
+
439
+ extracted_commits = extract_commits_from_runs(runs_raw_data, account_id)
440
+
441
+ load_users(
442
+ neo4j_session, extracted_users, common_job_parameters["UPDATE_TAG"], account_id
443
+ )
444
+
445
+ load_commits(
446
+ neo4j_session,
447
+ extracted_commits,
448
+ common_job_parameters["UPDATE_TAG"],
449
+ account_id,
450
+ )
451
+
452
+ load_runs(
453
+ neo4j_session, transformed_runs, common_job_parameters["UPDATE_TAG"], account_id
454
+ )
455
+
456
+ cleanup_users(neo4j_session, common_job_parameters)
457
+ cleanup_commits(neo4j_session, common_job_parameters)
458
+ cleanup_runs(neo4j_session, common_job_parameters)
459
+
460
+ logger.info(
461
+ f"Synced {len(extracted_users)} users, {len(extracted_commits)} commits, "
462
+ f"and {len(transformed_runs)} Spacelift runs"
463
+ )
@@ -0,0 +1,112 @@
1
+ import logging
2
+ from typing import Any
3
+
4
+ import neo4j
5
+ import requests
6
+
7
+ from cartography.client.core.tx import load
8
+ from cartography.graph.job import GraphJob
9
+ from cartography.intel.spacelift.util import call_spacelift_api
10
+ from cartography.models.spacelift.space import SpaceliftSpaceSchema
11
+ from cartography.util import timeit
12
+
13
+ logger = logging.getLogger(__name__)
14
+
15
+ # GraphQL query to fetch all spaces (direct array, no pagination)
16
+ GET_SPACES_QUERY = """
17
+ query {
18
+ spaces {
19
+ id
20
+ name
21
+ description
22
+ parentSpace
23
+ }
24
+ }
25
+ """
26
+
27
+
28
+ @timeit
29
+ def get_spaces(session: requests.Session, api_endpoint: str) -> list[dict[str, Any]]:
30
+ logger.info("Fetching Spacelift spaces")
31
+
32
+ response = call_spacelift_api(session, api_endpoint, GET_SPACES_QUERY)
33
+ spaces_data = response.get("data", {}).get("spaces", [])
34
+
35
+ logger.info(f"Retrieved {len(spaces_data)} Spacelift spaces")
36
+ return spaces_data
37
+
38
+
39
+ def transform_spaces(
40
+ spaces_data: list[dict[str, Any]], account_id: str
41
+ ) -> list[dict[str, Any]]:
42
+
43
+ result: list[dict[str, Any]] = []
44
+
45
+ for space in spaces_data:
46
+ parent_space = space.get("parentSpace")
47
+ # A space is root if it has no parent space
48
+ is_root = parent_space is None
49
+
50
+ transformed_space = {
51
+ "id": space["id"],
52
+ "name": space.get("name"),
53
+ "description": space.get("description"),
54
+ "is_root": is_root,
55
+ "spacelift_account_id": account_id,
56
+ "parent_spacelift_account_id": account_id if is_root else None,
57
+ "parent_space_id": parent_space,
58
+ }
59
+
60
+ result.append(transformed_space)
61
+
62
+ logger.info(f"Transformed {len(result)} spaces")
63
+ return result
64
+
65
+
66
+ def load_spaces(
67
+ neo4j_session: neo4j.Session,
68
+ spaces_data: list[dict[str, Any]],
69
+ update_tag: int,
70
+ account_id: str,
71
+ ) -> None:
72
+ load(
73
+ neo4j_session,
74
+ SpaceliftSpaceSchema(),
75
+ spaces_data,
76
+ lastupdated=update_tag,
77
+ spacelift_account_id=account_id,
78
+ )
79
+
80
+ logger.info(f"Loaded {len(spaces_data)} Spacelift spaces")
81
+
82
+
83
+ @timeit
84
+ def cleanup_spaces(
85
+ neo4j_session: neo4j.Session,
86
+ common_job_parameters: dict[str, Any],
87
+ ) -> None:
88
+ logger.debug("Running SpaceliftSpace cleanup job")
89
+ GraphJob.from_node_schema(SpaceliftSpaceSchema(), common_job_parameters).run(
90
+ neo4j_session
91
+ )
92
+
93
+
94
+ @timeit
95
+ def sync_spaces(
96
+ neo4j_session: neo4j.Session,
97
+ spacelift_session: requests.Session,
98
+ api_endpoint: str,
99
+ account_id: str,
100
+ common_job_parameters: dict[str, Any],
101
+ ) -> None:
102
+ spaces_raw_data = get_spaces(spacelift_session, api_endpoint)
103
+ transformed_spaces = transform_spaces(spaces_raw_data, account_id)
104
+ load_spaces(
105
+ neo4j_session,
106
+ transformed_spaces,
107
+ common_job_parameters["UPDATE_TAG"],
108
+ account_id,
109
+ )
110
+ cleanup_spaces(neo4j_session, common_job_parameters)
111
+
112
+ logger.info(f"Synced {len(transformed_spaces)} Spacelift spaces")
@@ -0,0 +1,119 @@
1
+ import logging
2
+ from typing import Any
3
+
4
+ import neo4j
5
+ import requests
6
+
7
+ from cartography.client.core.tx import load
8
+ from cartography.graph.job import GraphJob
9
+ from cartography.intel.spacelift.util import call_spacelift_api
10
+ from cartography.models.spacelift.stack import SpaceliftStackSchema
11
+ from cartography.util import timeit
12
+
13
+ logger = logging.getLogger(__name__)
14
+
15
+ # GraphQL query to fetch all stacks (direct array, no pagination)
16
+ GET_STACKS_QUERY = """
17
+ query {
18
+ stacks {
19
+ id
20
+ name
21
+ description
22
+ state
23
+ administrative
24
+ repository
25
+ branch
26
+ projectRoot
27
+ space
28
+ }
29
+ }
30
+ """
31
+
32
+
33
+ @timeit
34
+ def get_stacks(session: requests.Session, api_endpoint: str) -> list[dict[str, Any]]:
35
+ logger.info("Fetching Spacelift stacks")
36
+
37
+ response = call_spacelift_api(session, api_endpoint, GET_STACKS_QUERY)
38
+ stacks_data = response.get("data", {}).get("stacks", [])
39
+
40
+ logger.info(f"Retrieved {len(stacks_data)} Spacelift stacks")
41
+ return stacks_data
42
+
43
+
44
+ def transform_stacks(
45
+ stacks_data: list[dict[str, Any]], account_id: str
46
+ ) -> list[dict[str, Any]]:
47
+
48
+ result: list[dict[str, Any]] = []
49
+
50
+ for stack in stacks_data:
51
+ transformed_stack = {
52
+ "id": stack["id"],
53
+ "name": stack.get("name"),
54
+ "description": stack.get("description"),
55
+ "state": stack.get("state"),
56
+ "administrative": stack.get("administrative"),
57
+ "repository": stack.get("repository"),
58
+ "branch": stack.get("branch"),
59
+ "project_root": stack.get("projectRoot"),
60
+ "space_id": stack.get("space"),
61
+ "spacelift_account_id": account_id,
62
+ }
63
+
64
+ result.append(transformed_stack)
65
+
66
+ logger.info(f"Transformed {len(result)} stacks")
67
+ return result
68
+
69
+
70
+ def load_stacks(
71
+ neo4j_session: neo4j.Session,
72
+ stacks_data: list[dict[str, Any]],
73
+ update_tag: int,
74
+ account_id: str,
75
+ ) -> None:
76
+
77
+ load(
78
+ neo4j_session,
79
+ SpaceliftStackSchema(),
80
+ stacks_data,
81
+ lastupdated=update_tag,
82
+ spacelift_account_id=account_id,
83
+ )
84
+
85
+ logger.info(f"Loaded {len(stacks_data)} Spacelift stacks")
86
+
87
+
88
+ @timeit
89
+ def cleanup_stacks(
90
+ neo4j_session: neo4j.Session,
91
+ common_job_parameters: dict[str, Any],
92
+ ) -> None:
93
+
94
+ logger.debug("Running SpaceliftStack cleanup job")
95
+ GraphJob.from_node_schema(SpaceliftStackSchema(), common_job_parameters).run(
96
+ neo4j_session
97
+ )
98
+
99
+
100
+ @timeit
101
+ def sync_stacks(
102
+ neo4j_session: neo4j.Session,
103
+ spacelift_session: requests.Session,
104
+ api_endpoint: str,
105
+ account_id: str,
106
+ common_job_parameters: dict[str, Any],
107
+ ) -> None:
108
+
109
+ stacks_raw_data = get_stacks(spacelift_session, api_endpoint)
110
+ transformed_stacks = transform_stacks(stacks_raw_data, account_id)
111
+ load_stacks(
112
+ neo4j_session,
113
+ transformed_stacks,
114
+ common_job_parameters["UPDATE_TAG"],
115
+ account_id,
116
+ )
117
+ cleanup_stacks(neo4j_session, common_job_parameters)
118
+
119
+ logger.info(f"Synced {len(transformed_stacks)} Spacelift stacks")