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,122 @@
1
+ """
2
+ Utility functions for Spacelift GraphQL API interactions.
3
+ """
4
+
5
+ import logging
6
+ from typing import Any
7
+
8
+ import requests
9
+
10
+ logger = logging.getLogger(__name__)
11
+
12
+ # Timeout for API calls: (connection timeout, read timeout) in seconds
13
+ _TIMEOUT = (60, 60)
14
+
15
+ # GraphQL mutation to exchange API key ID and secret for a JWT token
16
+ _TOKEN_EXCHANGE_MUTATION = """
17
+ mutation GetSpaceliftToken($id: ID!, $secret: String!) {
18
+ apiKeyUser(id: $id, secret: $secret) {
19
+ jwt
20
+ }
21
+ }
22
+ """
23
+
24
+
25
+ def call_spacelift_api(
26
+ session: requests.Session,
27
+ api_endpoint: str,
28
+ query: str,
29
+ variables: dict[str, Any] | None = None,
30
+ ) -> dict[str, Any]:
31
+ """
32
+ Make a GraphQL query to the Spacelift API.
33
+ """
34
+ logger.debug(f"Making GraphQL request to {api_endpoint}")
35
+
36
+ # Prepare the GraphQL request payload
37
+ payload: dict[str, Any] = {"query": query}
38
+ if variables:
39
+ payload["variables"] = variables
40
+
41
+ # Make the POST request to the GraphQL endpoint
42
+ response = session.post(
43
+ api_endpoint,
44
+ json=payload,
45
+ timeout=_TIMEOUT,
46
+ )
47
+
48
+ # Raise an exception for HTTP errors (4xx, 5xx)
49
+ response.raise_for_status()
50
+
51
+ # Parse the JSON response
52
+ result = response.json()
53
+
54
+ # Check for GraphQL errors in the response
55
+ if "errors" in result:
56
+ error_messages = [
57
+ error.get("message", "Unknown error") for error in result["errors"]
58
+ ]
59
+ error_string = "; ".join(error_messages)
60
+ raise ValueError(f"GraphQL query failed: {error_string}")
61
+
62
+ return result
63
+
64
+
65
+ def get_spacelift_token(api_endpoint: str, key_id: str, key_secret: str) -> str:
66
+ """
67
+ Exchange Spacelift API key ID and secret for a JWT bearer token.
68
+
69
+ This uses the Spacelift GraphQL API's apiKeyUser mutation to obtain a JWT token
70
+ that can be used for authenticated requests. This is the recommended authentication
71
+ method as it avoids storing long-lived tokens.
72
+
73
+ :param api_endpoint: Spacelift GraphQL API endpoint (e.g., https://your-account.app.spacelift.io/graphql)
74
+ :param key_id: Spacelift API key ID (26-character ULID)
75
+ :param key_secret: Spacelift API key secret
76
+ :return: JWT bearer token string
77
+ :raises: requests.exceptions.RequestException if the API request fails
78
+ :raises: ValueError if the response doesn't contain a valid token
79
+ """
80
+ logger.info("Exchanging Spacelift API key for JWT token")
81
+
82
+ payload = {
83
+ "query": _TOKEN_EXCHANGE_MUTATION,
84
+ "variables": {
85
+ "id": key_id,
86
+ "secret": key_secret,
87
+ },
88
+ }
89
+
90
+ try:
91
+ response = requests.post(
92
+ api_endpoint,
93
+ json=payload,
94
+ headers={"Content-Type": "application/json"},
95
+ timeout=_TIMEOUT,
96
+ )
97
+ response.raise_for_status()
98
+
99
+ data: dict[str, Any] = response.json()
100
+
101
+ # Check for GraphQL errors
102
+ if "errors" in data:
103
+ error_messages = [
104
+ error.get("message", "Unknown error") for error in data["errors"]
105
+ ]
106
+ error_string = "; ".join(error_messages)
107
+ raise ValueError(f"Token exchange failed: {error_string}")
108
+
109
+ # Extract JWT token from response
110
+ jwt = data.get("data", {}).get("apiKeyUser", {}).get("jwt")
111
+ if not jwt:
112
+ raise ValueError("Token exchange response did not contain a JWT token")
113
+
114
+ logger.info("Successfully obtained JWT token from Spacelift API")
115
+ return jwt
116
+
117
+ except requests.exceptions.RequestException as e:
118
+ logger.error(f"Failed to exchange Spacelift API key for token: {e}")
119
+ raise
120
+ except (KeyError, ValueError) as e:
121
+ logger.error(f"Invalid response from Spacelift token exchange: {e}")
122
+ raise
@@ -0,0 +1,131 @@
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.workerpool import SpaceliftWorkerPoolSchema
11
+ from cartography.util import timeit
12
+
13
+ logger = logging.getLogger(__name__)
14
+
15
+ # GraphQL query to fetch all worker pools (direct array, no pagination)
16
+ GET_WORKER_POOLS_QUERY = """
17
+ query {
18
+ workerPools {
19
+ id
20
+ name
21
+ description
22
+ space
23
+ }
24
+ }
25
+ """
26
+
27
+
28
+ @timeit
29
+ def get_worker_pools(
30
+ session: requests.Session, api_endpoint: str
31
+ ) -> list[dict[str, Any]]:
32
+
33
+ logger.info("Fetching Spacelift worker pools")
34
+
35
+ response = call_spacelift_api(session, api_endpoint, GET_WORKER_POOLS_QUERY)
36
+ worker_pools_data = response.get("data", {}).get("workerPools", [])
37
+
38
+ logger.info(f"Retrieved {len(worker_pools_data)} Spacelift worker pools")
39
+ return worker_pools_data
40
+
41
+
42
+ def transform_worker_pools(
43
+ worker_pools_data: list[dict[str, Any]], account_id: str
44
+ ) -> list[dict[str, Any]]:
45
+ result: list[dict[str, Any]] = []
46
+
47
+ for pool in worker_pools_data:
48
+ transformed_pool = {
49
+ "id": pool["id"],
50
+ "name": pool.get("name"),
51
+ "description": pool.get("description"),
52
+ "pool_type": None, # WorkerPool type doesn't have a 'type' field in the API
53
+ "space_id": pool.get("space"),
54
+ "spacelift_account_id": account_id,
55
+ }
56
+
57
+ result.append(transformed_pool)
58
+
59
+ logger.info(f"Transformed {len(result)} worker pools")
60
+ return result
61
+
62
+
63
+ def load_worker_pools(
64
+ neo4j_session: neo4j.Session,
65
+ worker_pools_data: list[dict[str, Any]],
66
+ update_tag: int,
67
+ account_id: str,
68
+ ) -> None:
69
+ """
70
+ Load Spacelift worker pools data into Neo4j using the data model.
71
+ """
72
+ load(
73
+ neo4j_session,
74
+ SpaceliftWorkerPoolSchema(),
75
+ worker_pools_data,
76
+ lastupdated=update_tag,
77
+ spacelift_account_id=account_id,
78
+ )
79
+
80
+ logger.info(f"Loaded {len(worker_pools_data)} Spacelift worker pools")
81
+
82
+
83
+ @timeit
84
+ def cleanup_worker_pools(
85
+ neo4j_session: neo4j.Session,
86
+ common_job_parameters: dict[str, Any],
87
+ ) -> None:
88
+ """
89
+ Remove stale Spacelift worker pool data from Neo4j.
90
+ """
91
+ logger.debug("Running SpaceliftWorkerPool cleanup job")
92
+ GraphJob.from_node_schema(SpaceliftWorkerPoolSchema(), common_job_parameters).run(
93
+ neo4j_session
94
+ )
95
+
96
+
97
+ @timeit
98
+ def sync_worker_pools(
99
+ neo4j_session: neo4j.Session,
100
+ spacelift_session: requests.Session,
101
+ api_endpoint: str,
102
+ account_id: str,
103
+ common_job_parameters: dict[str, Any],
104
+ ) -> None:
105
+ """
106
+ Sync Spacelift worker pools data to Neo4j.
107
+
108
+ :param neo4j_session: Neo4j session
109
+ :param spacelift_session: Authenticated requests session for Spacelift API
110
+ :param api_endpoint: Spacelift GraphQL API endpoint
111
+ :param account_id: The Spacelift account ID
112
+ :param common_job_parameters: Common job parameters containing UPDATE_TAG
113
+ """
114
+ # 1. GET - Fetch data from Spacelift API
115
+ worker_pools_raw_data = get_worker_pools(spacelift_session, api_endpoint)
116
+
117
+ # 2. TRANSFORM - Shape data for ingestion
118
+ transformed_worker_pools = transform_worker_pools(worker_pools_raw_data, account_id)
119
+
120
+ # 3. LOAD - Ingest to Neo4j using data model
121
+ load_worker_pools(
122
+ neo4j_session,
123
+ transformed_worker_pools,
124
+ common_job_parameters["UPDATE_TAG"],
125
+ account_id,
126
+ )
127
+
128
+ # 4. CLEANUP - Remove stale data
129
+ cleanup_worker_pools(neo4j_session, common_job_parameters)
130
+
131
+ logger.info(f"Synced {len(transformed_worker_pools)} Spacelift worker pools")
@@ -0,0 +1,128 @@
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.worker import SpaceliftWorkerSchema
11
+ from cartography.util import timeit
12
+
13
+ logger = logging.getLogger(__name__)
14
+
15
+ # GraphQL query to fetch workers nested under workerPools
16
+ # Note: Workers don't have a top-level query, they're nested under workerPools
17
+ # Note: Worker type doesn't have a 'name' field, only 'id' and 'status'
18
+ GET_WORKERS_QUERY = """
19
+ query {
20
+ workerPools {
21
+ id
22
+ workers {
23
+ id
24
+ status
25
+ }
26
+ }
27
+ }
28
+ """
29
+
30
+
31
+ @timeit
32
+ def get_workers(session: requests.Session, api_endpoint: str) -> list[dict[str, Any]]:
33
+ """
34
+ Fetch all Spacelift workers from the API.
35
+ Workers are nested under workerPools, so we query workerPools and flatten the workers.
36
+ """
37
+ logger.info("Fetching Spacelift workers")
38
+
39
+ response = call_spacelift_api(session, api_endpoint, GET_WORKERS_QUERY)
40
+ worker_pools = response.get("data", {}).get("workerPools", [])
41
+
42
+ # Flatten workers from all pools and add the pool ID to each worker
43
+ all_workers = []
44
+ for pool in worker_pools:
45
+ pool_id = pool["id"]
46
+ for worker in pool.get("workers", []):
47
+ # Add the pool ID to each worker
48
+ worker["workerPool"] = pool_id
49
+ all_workers.append(worker)
50
+
51
+ logger.info(
52
+ f"Retrieved {len(all_workers)} Spacelift workers from {len(worker_pools)} worker pools"
53
+ )
54
+ return all_workers
55
+
56
+
57
+ def transform_workers(
58
+ workers_data: list[dict[str, Any]], account_id: str
59
+ ) -> list[dict[str, Any]]:
60
+ result: list[dict[str, Any]] = []
61
+
62
+ for worker in workers_data:
63
+ worker_id = worker["id"]
64
+
65
+ transformed_worker = {
66
+ "id": worker_id,
67
+ "name": worker_id, # Use ID as name since Worker type doesn't have a name field
68
+ "status": worker.get("status"),
69
+ "worker_pool_id": worker.get("workerPool"),
70
+ "spacelift_account_id": account_id,
71
+ }
72
+
73
+ result.append(transformed_worker)
74
+
75
+ logger.info(f"Transformed {len(result)} workers")
76
+ return result
77
+
78
+
79
+ def load_workers(
80
+ neo4j_session: neo4j.Session,
81
+ workers_data: list[dict[str, Any]],
82
+ update_tag: int,
83
+ account_id: str,
84
+ ) -> None:
85
+
86
+ load(
87
+ neo4j_session,
88
+ SpaceliftWorkerSchema(),
89
+ workers_data,
90
+ lastupdated=update_tag,
91
+ spacelift_account_id=account_id,
92
+ )
93
+
94
+ logger.info(f"Loaded {len(workers_data)} Spacelift workers")
95
+
96
+
97
+ @timeit
98
+ def cleanup_workers(
99
+ neo4j_session: neo4j.Session,
100
+ common_job_parameters: dict[str, Any],
101
+ ) -> None:
102
+
103
+ logger.debug("Running SpaceliftWorker cleanup job")
104
+ GraphJob.from_node_schema(SpaceliftWorkerSchema(), common_job_parameters).run(
105
+ neo4j_session
106
+ )
107
+
108
+
109
+ @timeit
110
+ def sync_workers(
111
+ neo4j_session: neo4j.Session,
112
+ spacelift_session: requests.Session,
113
+ api_endpoint: str,
114
+ account_id: str,
115
+ common_job_parameters: dict[str, Any],
116
+ ) -> None:
117
+
118
+ workers_raw_data = get_workers(spacelift_session, api_endpoint)
119
+ transformed_workers = transform_workers(workers_raw_data, account_id)
120
+ load_workers(
121
+ neo4j_session,
122
+ transformed_workers,
123
+ common_job_parameters["UPDATE_TAG"],
124
+ account_id,
125
+ )
126
+ cleanup_workers(neo4j_session, common_job_parameters)
127
+
128
+ logger.info(f"Synced {len(transformed_workers)} Spacelift workers")
@@ -0,0 +1,272 @@
1
+ import json
2
+ import logging
3
+ from typing import Any
4
+
5
+ import boto3
6
+ from neo4j import Session
7
+
8
+ from cartography.client.aws import list_accounts
9
+ from cartography.client.aws.ecr import get_ecr_images
10
+ from cartography.config import Config
11
+ from cartography.intel.trivy.scanner import cleanup
12
+ from cartography.intel.trivy.scanner import get_json_files_in_dir
13
+ from cartography.intel.trivy.scanner import get_json_files_in_s3
14
+ from cartography.intel.trivy.scanner import sync_single_image
15
+ from cartography.stats import get_stats_client
16
+ from cartography.util import timeit
17
+
18
+ logger = logging.getLogger(__name__)
19
+ stat_handler = get_stats_client("trivy.scanner")
20
+
21
+
22
+ def _get_scan_targets_and_aliases(
23
+ neo4j_session: Session,
24
+ account_ids: list[str] | None = None,
25
+ ) -> tuple[set[str], dict[str, str]]:
26
+ """
27
+ Return tag URIs and a mapping of digest-qualified URIs to tag URIs.
28
+ """
29
+ if not account_ids:
30
+ aws_accounts = list_accounts(neo4j_session)
31
+ else:
32
+ aws_accounts = account_ids
33
+
34
+ image_uris: set[str] = set()
35
+ digest_aliases: dict[str, str] = {}
36
+
37
+ for account_id in aws_accounts:
38
+ for _, _, image_uri, _, digest in get_ecr_images(neo4j_session, account_id):
39
+ if not image_uri:
40
+ continue
41
+ image_uris.add(image_uri)
42
+ if digest:
43
+ # repo URI is everything before the trailing ":" (if present)
44
+ repo_uri = image_uri.rsplit(":", 1)[0]
45
+ digest_uri = f"{repo_uri}@{digest}"
46
+ digest_aliases[digest_uri] = image_uri
47
+
48
+ return image_uris, digest_aliases
49
+
50
+
51
+ @timeit
52
+ def get_scan_targets(
53
+ neo4j_session: Session,
54
+ account_ids: list[str] | None = None,
55
+ ) -> set[str]:
56
+ """
57
+ Return list of ECR images from all accounts in the graph.
58
+ """
59
+ image_uris, _ = _get_scan_targets_and_aliases(neo4j_session, account_ids)
60
+ return image_uris
61
+
62
+
63
+ def _prepare_trivy_data(
64
+ trivy_data: dict[str, Any],
65
+ image_uris: set[str],
66
+ digest_aliases: dict[str, str],
67
+ source: str,
68
+ ) -> tuple[dict[str, Any], str] | None:
69
+ """
70
+ Determine the tag URI that corresponds to this Trivy payload.
71
+
72
+ Returns (trivy_data, display_uri) if the payload can be linked to an image present
73
+ in the graph; otherwise returns None so the caller can skip ingestion.
74
+ """
75
+
76
+ artifact_name = (trivy_data.get("ArtifactName") or "").strip()
77
+ metadata = trivy_data.get("Metadata") or {}
78
+ candidates: list[str] = []
79
+
80
+ if artifact_name:
81
+ candidates.append(artifact_name)
82
+
83
+ repo_tags = metadata.get("RepoTags", [])
84
+ repo_digests = metadata.get("RepoDigests", [])
85
+ stripped_tags_digests = [item.strip() for item in repo_tags + repo_digests]
86
+ candidates.extend(stripped_tags_digests)
87
+
88
+ display_uri: str | None = None
89
+
90
+ for candidate in candidates:
91
+ if not candidate:
92
+ continue
93
+ if candidate in image_uris:
94
+ display_uri = candidate
95
+ break
96
+ alias = digest_aliases.get(candidate)
97
+ if alias:
98
+ display_uri = alias
99
+ break
100
+
101
+ if not display_uri:
102
+ logger.debug(
103
+ "Skipping Trivy results for %s because no matching image URI was found in the graph",
104
+ source,
105
+ )
106
+ return None
107
+
108
+ return trivy_data, display_uri
109
+
110
+
111
+ @timeit
112
+ def sync_trivy_aws_ecr_from_s3(
113
+ neo4j_session: Session,
114
+ trivy_s3_bucket: str,
115
+ trivy_s3_prefix: str,
116
+ update_tag: int,
117
+ common_job_parameters: dict[str, Any],
118
+ boto3_session: boto3.Session,
119
+ ) -> None:
120
+ """
121
+ Sync Trivy scan results from S3 for AWS ECR images.
122
+
123
+ Args:
124
+ neo4j_session: Neo4j session for database operations
125
+ trivy_s3_bucket: S3 bucket containing scan results
126
+ trivy_s3_prefix: S3 prefix path containing scan results
127
+ update_tag: Update tag for tracking
128
+ common_job_parameters: Common job parameters for cleanup
129
+ boto3_session: boto3 session for S3 operations
130
+ """
131
+ logger.info(
132
+ f"Using Trivy scan results from s3://{trivy_s3_bucket}/{trivy_s3_prefix}"
133
+ )
134
+
135
+ image_uris, digest_aliases = _get_scan_targets_and_aliases(neo4j_session)
136
+ json_files: set[str] = get_json_files_in_s3(
137
+ trivy_s3_bucket, trivy_s3_prefix, boto3_session
138
+ )
139
+
140
+ if len(json_files) == 0:
141
+ logger.error(
142
+ f"Trivy sync was configured, but there are no ECR images with S3 json scan results in bucket "
143
+ f"'{trivy_s3_bucket}' with prefix '{trivy_s3_prefix}'. "
144
+ "Skipping Trivy sync to avoid potential data loss. "
145
+ "Please check the S3 bucket and prefix configuration. We expect the json files in s3 to be named "
146
+ f"`<image_uri>.json` and to be in the same bucket and prefix as the scan results. If the prefix is "
147
+ "a folder, it MUST end with a trailing slash '/'. "
148
+ )
149
+ raise ValueError("No ECR images with S3 json scan results found.")
150
+
151
+ logger.info(f"Processing {len(json_files)} Trivy result files from S3")
152
+ s3_client = boto3_session.client("s3")
153
+ for s3_object_key in json_files:
154
+ logger.debug(
155
+ f"Reading scan results from S3: s3://{trivy_s3_bucket}/{s3_object_key}"
156
+ )
157
+ response = s3_client.get_object(Bucket=trivy_s3_bucket, Key=s3_object_key)
158
+ scan_data_json = response["Body"].read().decode("utf-8")
159
+ trivy_data = json.loads(scan_data_json)
160
+
161
+ prepared = _prepare_trivy_data(
162
+ trivy_data,
163
+ image_uris=image_uris,
164
+ digest_aliases=digest_aliases,
165
+ source=f"s3://{trivy_s3_bucket}/{s3_object_key}",
166
+ )
167
+ if prepared is None:
168
+ continue
169
+
170
+ prepared_data, display_uri = prepared
171
+ sync_single_image(
172
+ neo4j_session,
173
+ prepared_data,
174
+ display_uri,
175
+ update_tag,
176
+ )
177
+
178
+ cleanup(neo4j_session, common_job_parameters)
179
+
180
+
181
+ @timeit
182
+ def sync_trivy_aws_ecr_from_dir(
183
+ neo4j_session: Session,
184
+ results_dir: str,
185
+ update_tag: int,
186
+ common_job_parameters: dict[str, Any],
187
+ ) -> None:
188
+ """Sync Trivy scan results from local files for AWS ECR images."""
189
+ logger.info(f"Using Trivy scan results from {results_dir}")
190
+
191
+ image_uris, digest_aliases = _get_scan_targets_and_aliases(neo4j_session)
192
+ json_files: set[str] = get_json_files_in_dir(results_dir)
193
+
194
+ if not json_files:
195
+ logger.error(
196
+ f"Trivy sync was configured, but no json files were found in {results_dir}."
197
+ )
198
+ raise ValueError("No Trivy json results found on disk")
199
+
200
+ logger.info(f"Processing {len(json_files)} local Trivy result files")
201
+
202
+ for file_path in json_files:
203
+ try:
204
+ with open(file_path, encoding="utf-8") as f:
205
+ trivy_data = json.load(f)
206
+ except json.JSONDecodeError as e:
207
+ logger.error(f"Failed to read Trivy data from {file_path}: {e}")
208
+ continue
209
+
210
+ prepared = _prepare_trivy_data(
211
+ trivy_data,
212
+ image_uris=image_uris,
213
+ digest_aliases=digest_aliases,
214
+ source=file_path,
215
+ )
216
+ if prepared is None:
217
+ continue
218
+
219
+ prepared_data, display_uri = prepared
220
+ sync_single_image(
221
+ neo4j_session,
222
+ prepared_data,
223
+ display_uri,
224
+ update_tag,
225
+ )
226
+
227
+ cleanup(neo4j_session, common_job_parameters)
228
+
229
+
230
+ @timeit
231
+ def start_trivy_ingestion(neo4j_session: Session, config: Config) -> None:
232
+ """Start Trivy scan ingestion from S3 or local files.
233
+
234
+ Args:
235
+ neo4j_session: Neo4j session for database operations
236
+ config: Configuration object containing S3 or directory paths
237
+ """
238
+ if not config.trivy_s3_bucket and not config.trivy_results_dir:
239
+ logger.info("Trivy configuration not provided. Skipping Trivy ingestion.")
240
+ return
241
+
242
+ if config.trivy_results_dir:
243
+ common_job_parameters = {
244
+ "UPDATE_TAG": config.update_tag,
245
+ }
246
+ sync_trivy_aws_ecr_from_dir(
247
+ neo4j_session,
248
+ config.trivy_results_dir,
249
+ config.update_tag,
250
+ common_job_parameters,
251
+ )
252
+ return
253
+
254
+ if config.trivy_s3_prefix is None:
255
+ config.trivy_s3_prefix = ""
256
+
257
+ common_job_parameters = {
258
+ "UPDATE_TAG": config.update_tag,
259
+ }
260
+
261
+ boto3_session = boto3.Session()
262
+
263
+ sync_trivy_aws_ecr_from_s3(
264
+ neo4j_session,
265
+ config.trivy_s3_bucket,
266
+ config.trivy_s3_prefix,
267
+ config.update_tag,
268
+ common_job_parameters,
269
+ boto3_session,
270
+ )
271
+
272
+ # Support other Trivy resource types here e.g. if Google Cloud has images.