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
@@ -11,9 +11,14 @@ from typing import Optional
11
11
  from typing import Set
12
12
 
13
13
  import neo4j
14
- from googleapiclient.discovery import HttpError
15
14
  from googleapiclient.discovery import Resource
15
+ from googleapiclient.errors import HttpError
16
16
 
17
+ from cartography.client.core.tx import execute_write_with_retry
18
+ from cartography.client.core.tx import load
19
+ from cartography.client.core.tx import run_write_query
20
+ from cartography.graph.job import GraphJob
21
+ from cartography.models.gcp.compute.vpc import GCPVpcSchema
17
22
  from cartography.util import run_cleanup_job
18
23
  from cartography.util import timeit
19
24
 
@@ -21,6 +26,10 @@ logger = logging.getLogger(__name__)
21
26
  InstanceUriPrefix = namedtuple("InstanceUriPrefix", "zone_name project_id")
22
27
 
23
28
 
29
+ # Maximum number of retries for Google API requests
30
+ GOOGLE_API_NUM_RETRIES = 5
31
+
32
+
24
33
  def _get_error_reason(http_error: HttpError) -> str:
25
34
  """
26
35
  Helper function to get an error reason out of the googleapiclient's HttpError object
@@ -63,7 +72,7 @@ def get_zones_in_project(
63
72
  """
64
73
  try:
65
74
  req = compute.zones().list(project=project_id, maxResults=max_results)
66
- res = req.execute()
75
+ res = req.execute(num_retries=GOOGLE_API_NUM_RETRIES)
67
76
  return res["items"]
68
77
  except HttpError as e:
69
78
  reason = _get_error_reason(e)
@@ -117,22 +126,53 @@ def get_gcp_instance_responses(
117
126
  response_objects: List[Resource] = []
118
127
  for zone in zones:
119
128
  req = compute.instances().list(project=project_id, zone=zone["name"])
120
- res = req.execute()
121
- response_objects.append(res)
129
+ try:
130
+ res = req.execute(num_retries=GOOGLE_API_NUM_RETRIES)
131
+ response_objects.append(res)
132
+ except HttpError as e:
133
+ reason = _get_error_reason(e)
134
+ if reason in {"backendError", "rateLimitExceeded", "internalError"}:
135
+ logger.warning(
136
+ "Transient error listing instances for project %s zone %s: %s; skipping this zone.",
137
+ project_id,
138
+ zone.get("name"),
139
+ e,
140
+ )
141
+ continue
142
+ raise
122
143
  return response_objects
123
144
 
124
145
 
125
146
  @timeit
126
- def get_gcp_subnets(projectid: str, region: str, compute: Resource) -> Resource:
147
+ def get_gcp_subnets(projectid: str, region: str, compute: Resource) -> Dict:
127
148
  """
128
- Return list of all subnets in the given projectid and region
129
- :param projectid: THe projectid
149
+ Return list of all subnets in the given projectid and region. If the API
150
+ call times out mid-pagination, return any subnets gathered so far rather than
151
+ bubbling the error up to the caller.
152
+ :param projectid: The project ID
130
153
  :param region: The region to pull subnets from
131
154
  :param compute: The compute resource object created by googleapiclient.discovery.build()
132
155
  :return: Response object containing data on all GCP subnets for a given project
133
156
  """
134
157
  req = compute.subnetworks().list(project=projectid, region=region)
135
- return req.execute()
158
+ items: List[Dict] = []
159
+ response_id = f"projects/{projectid}/regions/{region}/subnetworks"
160
+ while req is not None:
161
+ try:
162
+ res = req.execute(num_retries=GOOGLE_API_NUM_RETRIES)
163
+ except TimeoutError:
164
+ logger.warning(
165
+ "GCP: subnetworks.list for project %s region %s timed out; continuing with partial data.",
166
+ projectid,
167
+ region,
168
+ )
169
+ break
170
+ items.extend(res.get("items", []))
171
+ response_id = res.get("id", response_id)
172
+ req = compute.subnetworks().list_next(
173
+ previous_request=req, previous_response=res
174
+ )
175
+ return {"id": response_id, "items": items}
136
176
 
137
177
 
138
178
  @timeit
@@ -144,7 +184,7 @@ def get_gcp_vpcs(projectid: str, compute: Resource) -> Resource:
144
184
  :return: VPC response object
145
185
  """
146
186
  req = compute.networks().list(project=projectid)
147
- return req.execute()
187
+ return req.execute(num_retries=GOOGLE_API_NUM_RETRIES)
148
188
 
149
189
 
150
190
  @timeit
@@ -161,7 +201,7 @@ def get_gcp_regional_forwarding_rules(
161
201
  :return: Response object containing data on all GCP forwarding rules for a given project
162
202
  """
163
203
  req = compute.forwardingRules().list(project=project_id, region=region)
164
- return req.execute()
204
+ return req.execute(num_retries=GOOGLE_API_NUM_RETRIES)
165
205
 
166
206
 
167
207
  @timeit
@@ -173,7 +213,7 @@ def get_gcp_global_forwarding_rules(project_id: str, compute: Resource) -> Resou
173
213
  :return: Response object containing data on all GCP forwarding rules for a given project
174
214
  """
175
215
  req = compute.globalForwardingRules().list(project=project_id)
176
- return req.execute()
216
+ return req.execute(num_retries=GOOGLE_API_NUM_RETRIES)
177
217
 
178
218
 
179
219
  @timeit
@@ -185,7 +225,7 @@ def get_gcp_firewall_ingress_rules(project_id: str, compute: Resource) -> Resour
185
225
  :return: Firewall response object
186
226
  """
187
227
  req = compute.firewalls().list(project=project_id, filter='(direction="INGRESS")')
188
- return req.execute()
228
+ return req.execute(num_retries=GOOGLE_API_NUM_RETRIES)
189
229
 
190
230
 
191
231
  @timeit
@@ -581,7 +621,8 @@ def load_gcp_instances(
581
621
  SET r.lastupdated = $gcp_update_tag
582
622
  """
583
623
  for instance in data:
584
- neo4j_session.run(
624
+ run_write_query(
625
+ neo4j_session,
585
626
  query,
586
627
  ProjectId=instance["project_id"],
587
628
  PartialUri=instance["partial_uri"],
@@ -600,48 +641,17 @@ def load_gcp_instances(
600
641
  @timeit
601
642
  def load_gcp_vpcs(
602
643
  neo4j_session: neo4j.Session,
603
- vpcs: List[Dict],
644
+ vpcs: list[dict[str, Any]],
604
645
  gcp_update_tag: int,
646
+ project_id: str,
605
647
  ) -> None:
606
- """
607
- Ingest VPCs to Neo4j
608
- :param neo4j_session: The Neo4j session object
609
- :param vpcs: List of VPCs to ingest
610
- :param gcp_update_tag: The timestamp value to set our new Neo4j nodes with
611
- :return: Nothing
612
- """
613
- query = """
614
- MERGE(p:GCPProject{id:$ProjectId})
615
- ON CREATE SET p.firstseen = timestamp()
616
- SET p.lastupdated = $gcp_update_tag
617
-
618
- MERGE(vpc:GCPVpc{id:$PartialUri})
619
- ON CREATE SET vpc.firstseen = timestamp(),
620
- vpc.partial_uri = $PartialUri
621
- SET vpc.self_link = $SelfLink,
622
- vpc.name = $VpcName,
623
- vpc.project_id = $ProjectId,
624
- vpc.auto_create_subnetworks = $AutoCreateSubnetworks,
625
- vpc.routing_config_routing_mode = $RoutingMode,
626
- vpc.description = $Description,
627
- vpc.lastupdated = $gcp_update_tag
628
-
629
- MERGE (p)-[r:RESOURCE]->(vpc)
630
- ON CREATE SET r.firstseen = timestamp()
631
- SET r.lastupdated = $gcp_update_tag
632
- """
633
- for vpc in vpcs:
634
- neo4j_session.run(
635
- query,
636
- ProjectId=vpc["project_id"],
637
- PartialUri=vpc["partial_uri"],
638
- SelfLink=vpc["self_link"],
639
- VpcName=vpc["name"],
640
- AutoCreateSubnetworks=vpc["auto_create_subnetworks"],
641
- RoutingMode=vpc["routing_config_routing_mode"],
642
- Description=vpc["description"],
643
- gcp_update_tag=gcp_update_tag,
644
- )
648
+ load(
649
+ neo4j_session,
650
+ GCPVpcSchema(),
651
+ vpcs,
652
+ PROJECT_ID=project_id,
653
+ LASTUPDATED=gcp_update_tag,
654
+ )
645
655
 
646
656
 
647
657
  @timeit
@@ -649,51 +659,25 @@ def load_gcp_subnets(
649
659
  neo4j_session: neo4j.Session,
650
660
  subnets: List[Dict],
651
661
  gcp_update_tag: int,
662
+ project_id: str,
652
663
  ) -> None:
653
664
  """
654
- Ingest GCP subnet data to Neo4j
665
+ Ingest GCP subnet data to Neo4j using the data model
655
666
  :param neo4j_session: The Neo4j session
656
667
  :param subnets: List of the subnets
657
668
  :param gcp_update_tag: The timestamp to set these Neo4j nodes with
669
+ :param project_id: The project ID
658
670
  :return: Nothing
659
671
  """
660
- query = """
661
- MERGE(vpc:GCPVpc{id:$VpcPartialUri})
662
- ON CREATE SET vpc.firstseen = timestamp(),
663
- vpc.partial_uri = $VpcPartialUri
672
+ from cartography.models.gcp.compute.subnet import GCPSubnetSchema
664
673
 
665
- MERGE(subnet:GCPSubnet{id:$PartialUri})
666
- ON CREATE SET subnet.firstseen = timestamp(),
667
- subnet.partial_uri = $PartialUri
668
- SET subnet.self_link = $SubnetSelfLink,
669
- subnet.project_id = $ProjectId,
670
- subnet.name = $SubnetName,
671
- subnet.region = $Region,
672
- subnet.gateway_address = $GatewayAddress,
673
- subnet.ip_cidr_range = $IpCidrRange,
674
- subnet.private_ip_google_access = $PrivateIpGoogleAccess,
675
- subnet.vpc_partial_uri = $VpcPartialUri,
676
- subnet.lastupdated = $gcp_update_tag
677
-
678
- MERGE (vpc)-[r:RESOURCE]->(subnet)
679
- ON CREATE SET r.firstseen = timestamp()
680
- SET r.lastupdated = $gcp_update_tag
681
- """
682
- for s in subnets:
683
- neo4j_session.run(
684
- query,
685
- VpcPartialUri=s["vpc_partial_uri"],
686
- VpcSelfLink=s["vpc_self_link"],
687
- PartialUri=s["partial_uri"],
688
- SubnetSelfLink=s["self_link"],
689
- ProjectId=s["project_id"],
690
- SubnetName=s["name"],
691
- Region=s["region"],
692
- GatewayAddress=s["gateway_address"],
693
- IpCidrRange=s["ip_cidr_range"],
694
- PrivateIpGoogleAccess=s["private_ip_google_access"],
695
- gcp_update_tag=gcp_update_tag,
696
- )
674
+ load(
675
+ neo4j_session,
676
+ GCPSubnetSchema(),
677
+ subnets,
678
+ lastupdated=gcp_update_tag,
679
+ PROJECT_ID=project_id,
680
+ )
697
681
 
698
682
 
699
683
  @timeit
@@ -733,7 +717,8 @@ def load_gcp_forwarding_rules(
733
717
  network = fwd.get("network", None)
734
718
  subnetwork = fwd.get("subnetwork", None)
735
719
 
736
- neo4j_session.run(
720
+ run_write_query(
721
+ neo4j_session,
737
722
  query,
738
723
  PartialUri=fwd["partial_uri"],
739
724
  IPAddress=fwd["ip_address"],
@@ -779,7 +764,8 @@ def _attach_fwd_rule_to_subnet(
779
764
  SET p.lastupdated = $gcp_update_tag
780
765
  """
781
766
 
782
- neo4j_session.run(
767
+ run_write_query(
768
+ neo4j_session,
783
769
  query,
784
770
  PartialUri=fwd["partial_uri"],
785
771
  SubNetworkPartialUri=fwd.get("subnetwork_partial_uri", None),
@@ -806,7 +792,8 @@ def _attach_fwd_rule_to_vpc(
806
792
  SET r.lastupdated = $gcp_update_tag
807
793
  """
808
794
 
809
- neo4j_session.run(
795
+ run_write_query(
796
+ neo4j_session,
810
797
  query,
811
798
  PartialUri=fwd["partial_uri"],
812
799
  NetworkPartialUri=fwd.get("network_partial_uri", None),
@@ -850,7 +837,8 @@ def _attach_instance_tags(
850
837
  for tag in instance.get("tags", {}).get("items", []):
851
838
  for nic in instance.get("networkInterfaces", []):
852
839
  tag_id = _create_gcp_network_tag_id(nic["vpc_partial_uri"], tag)
853
- neo4j_session.run(
840
+ run_write_query(
841
+ neo4j_session,
854
842
  query,
855
843
  InstanceId=instance["partial_uri"],
856
844
  TagId=tag_id,
@@ -899,7 +887,8 @@ def _attach_gcp_nics(
899
887
  for nic in instance.get("networkInterfaces", []):
900
888
  # Make an ID for GCPNetworkInterface nodes because GCP doesn't define one but we need to uniquely identify them
901
889
  nic_id = f"{instance['partial_uri']}/networkinterfaces/{nic['name']}"
902
- neo4j_session.run(
890
+ run_write_query(
891
+ neo4j_session,
903
892
  query,
904
893
  InstanceId=instance["partial_uri"],
905
894
  NicId=nic_id,
@@ -945,7 +934,8 @@ def _attach_gcp_nic_access_configs(
945
934
  for ac in nic.get("accessConfigs", []):
946
935
  # Make an ID for GCPNicAccessConfig nodes because GCP doesn't define one but we need to uniquely identify them
947
936
  access_config_id = f"{nic_id}/accessconfigs/{ac['type']}"
948
- neo4j_session.run(
937
+ run_write_query(
938
+ neo4j_session,
949
939
  query,
950
940
  NicId=nic_id,
951
941
  AccessConfigId=access_config_id,
@@ -974,12 +964,13 @@ def _attach_gcp_vpc(
974
964
  """
975
965
  query = """
976
966
  MATCH (i:GCPInstance{id:$InstanceId})-[:NETWORK_INTERFACE]->(nic:GCPNetworkInterface)
977
- -[p:PART_OF_SUBNET]->(sn:GCPSubnet)<-[r:RESOURCE]-(vpc:GCPVpc)
967
+ -[p:PART_OF_SUBNET]->(sn:GCPSubnet)<-[r:HAS]-(vpc:GCPVpc)
978
968
  MERGE (i)-[m:MEMBER_OF_GCP_VPC]->(vpc)
979
969
  ON CREATE SET m.firstseen = timestamp()
980
970
  SET m.lastupdated = $gcp_update_tag
981
971
  """
982
- neo4j_session.run(
972
+ run_write_query(
973
+ neo4j_session,
983
974
  query,
984
975
  InstanceId=instance_id,
985
976
  gcp_update_tag=gcp_update_tag,
@@ -993,10 +984,23 @@ def load_gcp_ingress_firewalls(
993
984
  gcp_update_tag: int,
994
985
  ) -> None:
995
986
  """
996
- Load the firewall list to Neo4j
987
+ Load the firewall list to Neo4j with retry logic for transient errors.
997
988
  :param fw_list: The transformed list of firewalls
998
989
  :return: Nothing
999
990
  """
991
+ execute_write_with_retry(
992
+ neo4j_session,
993
+ _load_gcp_ingress_firewalls_tx,
994
+ fw_list,
995
+ gcp_update_tag,
996
+ )
997
+
998
+
999
+ def _load_gcp_ingress_firewalls_tx(
1000
+ tx: neo4j.Transaction,
1001
+ fw_list: List[Resource],
1002
+ gcp_update_tag: int,
1003
+ ) -> None:
1000
1004
  query = """
1001
1005
  MERGE (fw:GCPFirewall{id:$FwPartialUri})
1002
1006
  ON CREATE SET fw.firstseen = timestamp(),
@@ -1019,7 +1023,7 @@ def load_gcp_ingress_firewalls(
1019
1023
  SET r.lastupdated = $gcp_update_tag
1020
1024
  """
1021
1025
  for fw in fw_list:
1022
- neo4j_session.run(
1026
+ tx.run(
1023
1027
  query,
1024
1028
  FwPartialUri=fw["id"],
1025
1029
  Direction=fw["direction"],
@@ -1030,20 +1034,20 @@ def load_gcp_ingress_firewalls(
1030
1034
  VpcPartialUri=fw["vpc_partial_uri"],
1031
1035
  HasTargetServiceAccounts=fw["has_target_service_accounts"],
1032
1036
  gcp_update_tag=gcp_update_tag,
1033
- )
1034
- _attach_firewall_rules(neo4j_session, fw, gcp_update_tag)
1035
- _attach_target_tags(neo4j_session, fw, gcp_update_tag)
1037
+ ).consume()
1038
+ _attach_firewall_rules(tx, fw, gcp_update_tag)
1039
+ _attach_target_tags(tx, fw, gcp_update_tag)
1036
1040
 
1037
1041
 
1038
1042
  @timeit
1039
1043
  def _attach_firewall_rules(
1040
- neo4j_session: neo4j.Session,
1044
+ tx: neo4j.Transaction,
1041
1045
  fw: Resource,
1042
1046
  gcp_update_tag: int,
1043
1047
  ) -> None:
1044
1048
  """
1045
1049
  Attach the allow_rules to the Firewall object
1046
- :param neo4j_session: The Neo4j session
1050
+ :param tx: The Neo4j transaction
1047
1051
  :param fw: The Firewall object
1048
1052
  :param gcp_update_tag: The timestamp
1049
1053
  :return: Nothing
@@ -1084,7 +1088,7 @@ def _attach_firewall_rules(
1084
1088
  # If sourceRanges is not specified then the rule must specify sourceTags.
1085
1089
  # Since an IP range cannot have a tag applied to it, it is ok if we don't ingest this rule.
1086
1090
  for ip_range in fw.get("sourceRanges", []):
1087
- neo4j_session.run(
1091
+ tx.run(
1088
1092
  template.safe_substitute(fw_rule_relationship_label=label),
1089
1093
  FwPartialUri=fw["id"],
1090
1094
  RuleId=rule["ruleid"],
@@ -1093,18 +1097,18 @@ def _attach_firewall_rules(
1093
1097
  ToPort=rule.get("toport"),
1094
1098
  Range=ip_range,
1095
1099
  gcp_update_tag=gcp_update_tag,
1096
- )
1100
+ ).consume()
1097
1101
 
1098
1102
 
1099
1103
  @timeit
1100
1104
  def _attach_target_tags(
1101
- neo4j_session: neo4j.Session,
1105
+ tx: neo4j.Transaction,
1102
1106
  fw: Resource,
1103
1107
  gcp_update_tag: int,
1104
1108
  ) -> None:
1105
1109
  """
1106
1110
  Attach target tags to the firewall object
1107
- :param neo4j_session: The neo4j session
1111
+ :param tx: The neo4j transaction
1108
1112
  :param fw: The firewall object
1109
1113
  :param gcp_update_tag: The timestamp
1110
1114
  :return: Nothing
@@ -1124,13 +1128,13 @@ def _attach_target_tags(
1124
1128
  """
1125
1129
  for tag in fw.get("targetTags", []):
1126
1130
  tag_id = _create_gcp_network_tag_id(fw["vpc_partial_uri"], tag)
1127
- neo4j_session.run(
1131
+ tx.run(
1128
1132
  query,
1129
1133
  FwPartialUri=fw["id"],
1130
1134
  TagId=tag_id,
1131
1135
  TagValue=tag,
1132
1136
  gcp_update_tag=gcp_update_tag,
1133
- )
1137
+ ).consume()
1134
1138
 
1135
1139
 
1136
1140
  @timeit
@@ -1159,6 +1163,12 @@ def cleanup_gcp_vpcs(neo4j_session: neo4j.Session, common_job_parameters: Dict)
1159
1163
  :param common_job_parameters: dict of other job parameters to pass to Neo4j
1160
1164
  :return: Nothing
1161
1165
  """
1166
+ GraphJob.from_node_schema(
1167
+ GCPVpcSchema(),
1168
+ common_job_parameters,
1169
+ ).run(neo4j_session)
1170
+
1171
+ # TODO: remove this once we refactor GCP instances and add the instance to vpc rel as an object
1162
1172
  run_cleanup_job(
1163
1173
  "gcp_compute_vpc_cleanup.json",
1164
1174
  neo4j_session,
@@ -1172,15 +1182,15 @@ def cleanup_gcp_subnets(
1172
1182
  common_job_parameters: Dict,
1173
1183
  ) -> None:
1174
1184
  """
1175
- Delete out-of-date GCP VPC subnet nodes and relationships
1185
+ Delete out-of-date GCP VPC subnet nodes and relationships using data model
1176
1186
  :param neo4j_session: The Neo4j session
1177
1187
  :param common_job_parameters: dict of other job parameters to pass to Neo4j
1178
1188
  :return: Nothing
1179
1189
  """
1180
- run_cleanup_job(
1181
- "gcp_compute_vpc_subnet_cleanup.json",
1182
- neo4j_session,
1183
- common_job_parameters,
1190
+ from cartography.models.gcp.compute.subnet import GCPSubnetSchema
1191
+
1192
+ GraphJob.from_node_schema(GCPSubnetSchema(), common_job_parameters).run(
1193
+ neo4j_session
1184
1194
  )
1185
1195
 
1186
1196
 
@@ -1267,8 +1277,7 @@ def sync_gcp_vpcs(
1267
1277
  """
1268
1278
  vpc_res = get_gcp_vpcs(project_id, compute)
1269
1279
  vpcs = transform_gcp_vpcs(vpc_res)
1270
- load_gcp_vpcs(neo4j_session, vpcs, gcp_update_tag)
1271
- # TODO scope the cleanup to the current project - https://github.com/cartography-cncf/cartography/issues/381
1280
+ load_gcp_vpcs(neo4j_session, vpcs, gcp_update_tag, project_id)
1272
1281
  cleanup_gcp_vpcs(neo4j_session, common_job_parameters)
1273
1282
 
1274
1283
 
@@ -1284,7 +1293,7 @@ def sync_gcp_subnets(
1284
1293
  for r in regions:
1285
1294
  subnet_res = get_gcp_subnets(project_id, r, compute)
1286
1295
  subnets = transform_gcp_subnets(subnet_res)
1287
- load_gcp_subnets(neo4j_session, subnets, gcp_update_tag)
1296
+ load_gcp_subnets(neo4j_session, subnets, gcp_update_tag, project_id)
1288
1297
  # TODO scope the cleanup to the current project - https://github.com/cartography-cncf/cartography/issues/381
1289
1298
  cleanup_gcp_subnets(neo4j_session, common_job_parameters)
1290
1299
 
File without changes
@@ -0,0 +1,114 @@
1
+ import logging
2
+ from typing import Dict
3
+ from typing import List
4
+ from typing import Optional
5
+
6
+ import neo4j
7
+ from google.auth.credentials import Credentials as GoogleCredentials
8
+ from google.cloud import resourcemanager_v3
9
+
10
+ from cartography.client.core.tx import load
11
+ from cartography.models.gcp.crm.folders import GCPFolderSchema
12
+ from cartography.util import timeit
13
+
14
+ logger = logging.getLogger(__name__)
15
+
16
+
17
+ @timeit
18
+ def get_gcp_folders(
19
+ org_resource_name: str,
20
+ credentials: Optional[GoogleCredentials] = None,
21
+ ) -> List[Dict]:
22
+ """
23
+ Return a list of all descendant GCP folders under the specified organization by traversing the folder tree.
24
+
25
+ :param org_resource_name: Full organization resource name (e.g., "organizations/123456789012")
26
+ :return: List of folder dicts with 'name' field containing full resource names (e.g., "folders/123456")
27
+ """
28
+ results: List[Dict] = []
29
+ client = resourcemanager_v3.FoldersClient(credentials=credentials)
30
+ # BFS over folders starting at the org root
31
+ queue: List[str] = [org_resource_name]
32
+ seen: set[str] = set()
33
+ while queue:
34
+ parent = queue.pop(0)
35
+ if parent in seen:
36
+ continue
37
+ seen.add(parent)
38
+
39
+ for folder in client.list_folders(parent=parent):
40
+ results.append(
41
+ {
42
+ "name": folder.name,
43
+ "parent": parent,
44
+ "displayName": folder.display_name,
45
+ "lifecycleState": folder.state.name,
46
+ }
47
+ )
48
+ if folder.name:
49
+ queue.append(folder.name)
50
+ return results
51
+
52
+
53
+ @timeit
54
+ def transform_gcp_folders(data: List[Dict]) -> List[Dict]:
55
+ """
56
+ Transform GCP folder data to add parent_org or parent_folder fields based on parent type.
57
+
58
+ :param data: List of folder dicts
59
+ :return: List of transformed folder dicts with parent_org and parent_folder fields
60
+ """
61
+ for folder in data:
62
+ folder["parent_org"] = None
63
+ folder["parent_folder"] = None
64
+
65
+ if folder["parent"].startswith("organizations"):
66
+ folder["parent_org"] = folder["parent"]
67
+ elif folder["parent"].startswith("folders"):
68
+ folder["parent_folder"] = folder["parent"]
69
+ else:
70
+ logger.warning(
71
+ f"Folder {folder['name']} has unexpected parent type: {folder['parent']}"
72
+ )
73
+
74
+ return data
75
+
76
+
77
+ @timeit
78
+ def load_gcp_folders(
79
+ neo4j_session: neo4j.Session,
80
+ data: List[Dict],
81
+ gcp_update_tag: int,
82
+ org_resource_name: str,
83
+ ) -> None:
84
+ """
85
+ Load GCP folders into the graph.
86
+ :param org_resource_name: Full organization resource name (e.g., "organizations/123456789012")
87
+ """
88
+ transformed_data = transform_gcp_folders(data)
89
+ load(
90
+ neo4j_session,
91
+ GCPFolderSchema(),
92
+ transformed_data,
93
+ lastupdated=gcp_update_tag,
94
+ ORG_RESOURCE_NAME=org_resource_name,
95
+ )
96
+
97
+
98
+ @timeit
99
+ def sync_gcp_folders(
100
+ neo4j_session: neo4j.Session,
101
+ gcp_update_tag: int,
102
+ common_job_parameters: Dict,
103
+ org_resource_name: str,
104
+ credentials: Optional[GoogleCredentials] = None,
105
+ ) -> List[Dict]:
106
+ """
107
+ Get GCP folder data using the CRM v2 resource object and load the data to Neo4j.
108
+ :param org_resource_name: Full organization resource name (e.g., "organizations/123456789012")
109
+ :return: List of folders synced
110
+ """
111
+ logger.debug("Syncing GCP folders")
112
+ folders = get_gcp_folders(org_resource_name, credentials=credentials)
113
+ load_gcp_folders(neo4j_session, folders, gcp_update_tag, org_resource_name)
114
+ return folders
@@ -0,0 +1,70 @@
1
+ import logging
2
+ from typing import Dict
3
+ from typing import List
4
+ from typing import Optional
5
+
6
+ import neo4j
7
+ from google.auth.credentials import Credentials as GoogleCredentials
8
+ from google.cloud import resourcemanager_v3
9
+
10
+ from cartography.client.core.tx import load
11
+ from cartography.models.gcp.crm.organizations import GCPOrganizationSchema
12
+ from cartography.util import timeit
13
+
14
+ logger = logging.getLogger(__name__)
15
+
16
+
17
+ @timeit
18
+ def get_gcp_organizations(
19
+ credentials: Optional[GoogleCredentials] = None,
20
+ ) -> List[Dict]:
21
+ """
22
+ Return list of GCP organizations that the authenticated principal can access using the high-level client.
23
+ Returns empty list on error.
24
+ :return: List of org dicts with keys: name, displayName, lifecycleState.
25
+ """
26
+ client = resourcemanager_v3.OrganizationsClient(credentials=credentials)
27
+ orgs = []
28
+ for org in client.search_organizations():
29
+ orgs.append(
30
+ {
31
+ "name": org.name,
32
+ "displayName": org.display_name,
33
+ "lifecycleState": org.state.name,
34
+ }
35
+ )
36
+ return orgs
37
+
38
+
39
+ @timeit
40
+ def load_gcp_organizations(
41
+ neo4j_session: neo4j.Session,
42
+ data: List[Dict],
43
+ gcp_update_tag: int,
44
+ ) -> None:
45
+ for org in data:
46
+ org["id"] = org["name"]
47
+
48
+ load(
49
+ neo4j_session,
50
+ GCPOrganizationSchema(),
51
+ data,
52
+ lastupdated=gcp_update_tag,
53
+ )
54
+
55
+
56
+ @timeit
57
+ def sync_gcp_organizations(
58
+ neo4j_session: neo4j.Session,
59
+ gcp_update_tag: int,
60
+ common_job_parameters: Dict,
61
+ credentials: Optional[GoogleCredentials] = None,
62
+ ) -> List[Dict]:
63
+ """
64
+ Get GCP organization data using the CRM v1 resource object and load the data to Neo4j.
65
+ Returns the list of organizations synced.
66
+ """
67
+ logger.debug("Syncing GCP organizations")
68
+ data = get_gcp_organizations(credentials=credentials)
69
+ load_gcp_organizations(neo4j_session, data, gcp_update_tag)
70
+ return data