cartography 0.93.0rc1__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 (822) hide show
  1. cartography/__main__.py +1 -2
  2. cartography/_version.py +34 -0
  3. cartography/cli.py +903 -225
  4. cartography/client/aws/__init__.py +19 -0
  5. cartography/client/aws/ecr.py +51 -0
  6. cartography/client/core/tx.py +400 -27
  7. cartography/config.py +215 -10
  8. cartography/data/azure_permission_relationships.yaml +20 -0
  9. cartography/data/gcp_permission_relationships.yaml +21 -0
  10. cartography/data/indexes.cypher +1 -200
  11. cartography/data/jobs/analysis/aws_ec2_asset_exposure.json +17 -2
  12. cartography/data/jobs/analysis/aws_ec2_keypair_analysis.json +2 -2
  13. cartography/data/jobs/analysis/gcp_compute_asset_inet_exposure.json +1 -1
  14. cartography/data/jobs/analysis/keycloak_inheritance.json +30 -0
  15. cartography/data/jobs/cleanup/crowdstrike_import_cleanup.json +0 -5
  16. cartography/data/jobs/cleanup/gcp_compute_vpc_cleanup.json +0 -12
  17. cartography/data/jobs/cleanup/github_repos_cleanup.json +27 -0
  18. cartography/data/jobs/scoped_analysis/aws_ec2_iaminstanceprofile.json +15 -0
  19. cartography/data/jobs/scoped_analysis/semgrep_sca_risk_analysis.json +13 -13
  20. cartography/driftdetect/__main__.py +1 -2
  21. cartography/driftdetect/add_shortcut.py +10 -2
  22. cartography/driftdetect/cli.py +72 -75
  23. cartography/driftdetect/detect_deviations.py +7 -3
  24. cartography/driftdetect/get_states.py +20 -8
  25. cartography/driftdetect/model.py +5 -5
  26. cartography/driftdetect/serializers.py +8 -6
  27. cartography/driftdetect/storage.py +2 -2
  28. cartography/graph/cleanupbuilder.py +255 -35
  29. cartography/graph/job.py +104 -20
  30. cartography/graph/querybuilder.py +689 -91
  31. cartography/graph/statement.py +49 -36
  32. cartography/intel/airbyte/__init__.py +105 -0
  33. cartography/intel/airbyte/connections.py +120 -0
  34. cartography/intel/airbyte/destinations.py +81 -0
  35. cartography/intel/airbyte/organizations.py +59 -0
  36. cartography/intel/airbyte/sources.py +78 -0
  37. cartography/intel/airbyte/tags.py +64 -0
  38. cartography/intel/airbyte/users.py +106 -0
  39. cartography/intel/airbyte/util.py +122 -0
  40. cartography/intel/airbyte/workspaces.py +63 -0
  41. cartography/intel/analysis.py +4 -1
  42. cartography/intel/anthropic/__init__.py +62 -0
  43. cartography/intel/anthropic/apikeys.py +72 -0
  44. cartography/intel/anthropic/users.py +75 -0
  45. cartography/intel/anthropic/util.py +51 -0
  46. cartography/intel/anthropic/workspaces.py +95 -0
  47. cartography/intel/aws/__init__.py +137 -59
  48. cartography/intel/aws/acm.py +124 -0
  49. cartography/intel/aws/apigateway.py +482 -217
  50. cartography/intel/aws/apigatewayv2.py +116 -0
  51. cartography/intel/aws/cloudtrail.py +105 -0
  52. cartography/intel/aws/cloudtrail_management_events.py +962 -0
  53. cartography/intel/aws/cloudwatch.py +239 -0
  54. cartography/intel/aws/codebuild.py +132 -0
  55. cartography/intel/aws/cognito.py +201 -0
  56. cartography/intel/aws/config.py +63 -23
  57. cartography/intel/aws/dynamodb.py +108 -40
  58. cartography/intel/aws/ec2/__init__.py +2 -2
  59. cartography/intel/aws/ec2/auto_scaling_groups.py +254 -189
  60. cartography/intel/aws/ec2/elastic_ip_addresses.py +44 -14
  61. cartography/intel/aws/ec2/images.py +74 -39
  62. cartography/intel/aws/ec2/instances.py +262 -137
  63. cartography/intel/aws/ec2/internet_gateways.py +44 -13
  64. cartography/intel/aws/ec2/key_pairs.py +72 -39
  65. cartography/intel/aws/ec2/launch_templates.py +143 -66
  66. cartography/intel/aws/ec2/load_balancer_v2s.py +119 -45
  67. cartography/intel/aws/ec2/load_balancers.py +165 -147
  68. cartography/intel/aws/ec2/network_acls.py +233 -0
  69. cartography/intel/aws/ec2/network_interfaces.py +150 -87
  70. cartography/intel/aws/ec2/reserved_instances.py +48 -17
  71. cartography/intel/aws/ec2/route_tables.py +327 -0
  72. cartography/intel/aws/ec2/security_groups.py +189 -121
  73. cartography/intel/aws/ec2/snapshots.py +93 -91
  74. cartography/intel/aws/ec2/subnets.py +70 -58
  75. cartography/intel/aws/ec2/tgw.py +111 -39
  76. cartography/intel/aws/ec2/util.py +1 -1
  77. cartography/intel/aws/ec2/volumes.py +69 -41
  78. cartography/intel/aws/ec2/vpc.py +157 -116
  79. cartography/intel/aws/ec2/vpc_peerings.py +317 -121
  80. cartography/intel/aws/ecr.py +336 -93
  81. cartography/intel/aws/ecr_image_layers.py +923 -0
  82. cartography/intel/aws/ecs.py +310 -403
  83. cartography/intel/aws/efs.py +261 -0
  84. cartography/intel/aws/eks.py +55 -29
  85. cartography/intel/aws/elasticache.py +130 -83
  86. cartography/intel/aws/elasticsearch.py +70 -24
  87. cartography/intel/aws/emr.py +61 -23
  88. cartography/intel/aws/eventbridge.py +164 -0
  89. cartography/intel/aws/glue.py +181 -0
  90. cartography/intel/aws/guardduty.py +443 -0
  91. cartography/intel/aws/iam.py +978 -464
  92. cartography/intel/aws/iam_instance_profiles.py +73 -0
  93. cartography/intel/aws/identitycenter.py +847 -0
  94. cartography/intel/aws/inspector.py +330 -133
  95. cartography/intel/aws/kms.py +235 -209
  96. cartography/intel/aws/lambda_function.py +328 -176
  97. cartography/intel/aws/organizations.py +40 -19
  98. cartography/intel/aws/permission_relationships.py +144 -68
  99. cartography/intel/aws/rds.py +467 -412
  100. cartography/intel/aws/redshift.py +116 -50
  101. cartography/intel/aws/resourcegroupstaggingapi.py +198 -82
  102. cartography/intel/aws/resources.py +80 -42
  103. cartography/intel/aws/route53.py +419 -318
  104. cartography/intel/aws/s3.py +489 -96
  105. cartography/intel/aws/s3accountpublicaccessblock.py +157 -0
  106. cartography/intel/aws/secretsmanager.py +217 -40
  107. cartography/intel/aws/securityhub.py +23 -10
  108. cartography/intel/aws/sns.py +226 -0
  109. cartography/intel/aws/sqs.py +74 -96
  110. cartography/intel/aws/ssm.py +142 -33
  111. cartography/intel/aws/util/arns.py +7 -7
  112. cartography/intel/aws/util/common.py +31 -4
  113. cartography/intel/azure/__init__.py +259 -46
  114. cartography/intel/azure/aks.py +175 -0
  115. cartography/intel/azure/app_service.py +105 -0
  116. cartography/intel/azure/compute.py +141 -120
  117. cartography/intel/azure/container_instances.py +95 -0
  118. cartography/intel/azure/cosmosdb.py +706 -519
  119. cartography/intel/azure/data_factory.py +85 -0
  120. cartography/intel/azure/data_factory_dataset.py +128 -0
  121. cartography/intel/azure/data_factory_linked_service.py +119 -0
  122. cartography/intel/azure/data_factory_pipeline.py +142 -0
  123. cartography/intel/azure/data_lake.py +124 -0
  124. cartography/intel/azure/event_grid.py +94 -0
  125. cartography/intel/azure/functions.py +124 -0
  126. cartography/intel/azure/load_balancers.py +263 -0
  127. cartography/intel/azure/logic_apps.py +101 -0
  128. cartography/intel/azure/monitor.py +105 -0
  129. cartography/intel/azure/network.py +467 -0
  130. cartography/intel/azure/permission_relationships.py +466 -0
  131. cartography/intel/azure/rbac.py +309 -0
  132. cartography/intel/azure/resource_groups.py +82 -0
  133. cartography/intel/azure/security_center.py +106 -0
  134. cartography/intel/azure/sql.py +436 -392
  135. cartography/intel/azure/storage.py +467 -335
  136. cartography/intel/azure/subscription.py +49 -55
  137. cartography/intel/azure/tenant.py +46 -28
  138. cartography/intel/azure/util/common.py +13 -0
  139. cartography/intel/azure/util/credentials.py +58 -143
  140. cartography/intel/azure/util/tag.py +41 -0
  141. cartography/intel/bigfix/__init__.py +2 -2
  142. cartography/intel/bigfix/computers.py +93 -65
  143. cartography/intel/cloudflare/__init__.py +74 -0
  144. cartography/intel/cloudflare/accounts.py +57 -0
  145. cartography/intel/cloudflare/dnsrecords.py +64 -0
  146. cartography/intel/cloudflare/members.py +75 -0
  147. cartography/intel/cloudflare/roles.py +65 -0
  148. cartography/intel/cloudflare/zones.py +64 -0
  149. cartography/intel/create_indexes.py +5 -3
  150. cartography/intel/crowdstrike/__init__.py +26 -12
  151. cartography/intel/crowdstrike/endpoints.py +17 -45
  152. cartography/intel/crowdstrike/spotlight.py +13 -5
  153. cartography/intel/cve/__init__.py +91 -26
  154. cartography/intel/cve/feed.py +77 -56
  155. cartography/intel/digitalocean/__init__.py +22 -13
  156. cartography/intel/digitalocean/compute.py +75 -108
  157. cartography/intel/digitalocean/management.py +44 -80
  158. cartography/intel/digitalocean/platform.py +48 -43
  159. cartography/intel/dns.py +41 -12
  160. cartography/intel/duo/__init__.py +21 -16
  161. cartography/intel/duo/api_host.py +14 -9
  162. cartography/intel/duo/endpoints.py +50 -45
  163. cartography/intel/duo/groups.py +18 -14
  164. cartography/intel/duo/phones.py +37 -34
  165. cartography/intel/duo/tokens.py +26 -23
  166. cartography/intel/duo/users.py +54 -50
  167. cartography/intel/duo/web_authn_credentials.py +30 -25
  168. cartography/intel/entra/__init__.py +160 -0
  169. cartography/intel/entra/app_role_assignments.py +284 -0
  170. cartography/intel/entra/applications.py +182 -0
  171. cartography/intel/entra/federation/__init__.py +0 -0
  172. cartography/intel/entra/federation/aws_identity_center.py +77 -0
  173. cartography/intel/entra/groups.py +198 -0
  174. cartography/intel/entra/ou.py +136 -0
  175. cartography/intel/entra/service_principals.py +217 -0
  176. cartography/intel/entra/users.py +259 -0
  177. cartography/intel/gcp/__init__.py +381 -175
  178. cartography/intel/gcp/bigtable_app_profile.py +101 -0
  179. cartography/intel/gcp/bigtable_backup.py +91 -0
  180. cartography/intel/gcp/bigtable_cluster.py +93 -0
  181. cartography/intel/gcp/bigtable_instance.py +86 -0
  182. cartography/intel/gcp/bigtable_table.py +87 -0
  183. cartography/intel/gcp/cai.py +292 -0
  184. cartography/intel/gcp/clients.py +112 -0
  185. cartography/intel/gcp/compute.py +521 -325
  186. cartography/intel/gcp/crm/__init__.py +0 -0
  187. cartography/intel/gcp/crm/folders.py +114 -0
  188. cartography/intel/gcp/crm/orgs.py +70 -0
  189. cartography/intel/gcp/crm/projects.py +120 -0
  190. cartography/intel/gcp/dns.py +134 -179
  191. cartography/intel/gcp/gke.py +100 -107
  192. cartography/intel/gcp/iam.py +262 -0
  193. cartography/intel/gcp/permission_relationships.py +394 -0
  194. cartography/intel/gcp/policy_bindings.py +225 -0
  195. cartography/intel/gcp/storage.py +103 -158
  196. cartography/intel/github/__init__.py +66 -27
  197. cartography/intel/github/commits.py +423 -0
  198. cartography/intel/github/repos.py +871 -160
  199. cartography/intel/github/teams.py +386 -53
  200. cartography/intel/github/users.py +214 -49
  201. cartography/intel/github/util.py +50 -35
  202. cartography/intel/googleworkspace/__init__.py +193 -0
  203. cartography/intel/googleworkspace/devices.py +254 -0
  204. cartography/intel/googleworkspace/groups.py +568 -0
  205. cartography/intel/googleworkspace/oauth_apps.py +259 -0
  206. cartography/intel/googleworkspace/tenant.py +85 -0
  207. cartography/intel/googleworkspace/users.py +138 -0
  208. cartography/intel/gsuite/__init__.py +101 -42
  209. cartography/intel/gsuite/groups.py +291 -0
  210. cartography/intel/gsuite/users.py +142 -0
  211. cartography/intel/jamf/__init__.py +19 -1
  212. cartography/intel/jamf/computers.py +37 -8
  213. cartography/intel/jamf/util.py +7 -2
  214. cartography/intel/kandji/__init__.py +6 -3
  215. cartography/intel/kandji/devices.py +40 -10
  216. cartography/intel/keycloak/__init__.py +153 -0
  217. cartography/intel/keycloak/authenticationexecutions.py +322 -0
  218. cartography/intel/keycloak/authenticationflows.py +77 -0
  219. cartography/intel/keycloak/clients.py +187 -0
  220. cartography/intel/keycloak/groups.py +126 -0
  221. cartography/intel/keycloak/identityproviders.py +94 -0
  222. cartography/intel/keycloak/organizations.py +163 -0
  223. cartography/intel/keycloak/realms.py +61 -0
  224. cartography/intel/keycloak/roles.py +202 -0
  225. cartography/intel/keycloak/scopes.py +73 -0
  226. cartography/intel/keycloak/users.py +70 -0
  227. cartography/intel/keycloak/util.py +47 -0
  228. cartography/intel/kubernetes/__init__.py +60 -14
  229. cartography/intel/kubernetes/clusters.py +86 -0
  230. cartography/intel/kubernetes/eks.py +402 -0
  231. cartography/intel/kubernetes/namespaces.py +60 -55
  232. cartography/intel/kubernetes/pods.py +171 -75
  233. cartography/intel/kubernetes/rbac.py +597 -0
  234. cartography/intel/kubernetes/secrets.py +95 -45
  235. cartography/intel/kubernetes/services.py +131 -63
  236. cartography/intel/kubernetes/util.py +142 -14
  237. cartography/intel/lastpass/__init__.py +2 -2
  238. cartography/intel/lastpass/users.py +23 -12
  239. cartography/intel/oci/__init__.py +44 -11
  240. cartography/intel/oci/iam.py +157 -47
  241. cartography/intel/oci/organizations.py +16 -7
  242. cartography/intel/oci/utils.py +71 -25
  243. cartography/intel/okta/__init__.py +66 -15
  244. cartography/intel/okta/applications.py +57 -25
  245. cartography/intel/okta/awssaml.py +105 -41
  246. cartography/intel/okta/factors.py +19 -5
  247. cartography/intel/okta/groups.py +61 -31
  248. cartography/intel/okta/organization.py +8 -2
  249. cartography/intel/okta/origins.py +9 -3
  250. cartography/intel/okta/roles.py +20 -7
  251. cartography/intel/okta/users.py +31 -10
  252. cartography/intel/okta/utils.py +6 -4
  253. cartography/intel/ontology/__init__.py +44 -0
  254. cartography/intel/ontology/devices.py +54 -0
  255. cartography/intel/ontology/users.py +54 -0
  256. cartography/intel/ontology/utils.py +176 -0
  257. cartography/intel/openai/__init__.py +86 -0
  258. cartography/intel/openai/adminapikeys.py +89 -0
  259. cartography/intel/openai/apikeys.py +96 -0
  260. cartography/intel/openai/projects.py +97 -0
  261. cartography/intel/openai/serviceaccounts.py +82 -0
  262. cartography/intel/openai/users.py +75 -0
  263. cartography/intel/openai/util.py +45 -0
  264. cartography/intel/pagerduty/__init__.py +8 -7
  265. cartography/intel/pagerduty/escalation_policies.py +31 -12
  266. cartography/intel/pagerduty/schedules.py +21 -8
  267. cartography/intel/pagerduty/services.py +18 -7
  268. cartography/intel/pagerduty/teams.py +13 -5
  269. cartography/intel/pagerduty/users.py +6 -2
  270. cartography/intel/pagerduty/vendors.py +6 -2
  271. cartography/intel/scaleway/__init__.py +127 -0
  272. cartography/intel/scaleway/iam/__init__.py +0 -0
  273. cartography/intel/scaleway/iam/apikeys.py +71 -0
  274. cartography/intel/scaleway/iam/applications.py +71 -0
  275. cartography/intel/scaleway/iam/groups.py +71 -0
  276. cartography/intel/scaleway/iam/users.py +71 -0
  277. cartography/intel/scaleway/instances/__init__.py +0 -0
  278. cartography/intel/scaleway/instances/flexibleips.py +86 -0
  279. cartography/intel/scaleway/instances/instances.py +92 -0
  280. cartography/intel/scaleway/projects.py +79 -0
  281. cartography/intel/scaleway/storage/__init__.py +0 -0
  282. cartography/intel/scaleway/storage/snapshots.py +86 -0
  283. cartography/intel/scaleway/storage/volumes.py +84 -0
  284. cartography/intel/scaleway/utils.py +37 -0
  285. cartography/intel/semgrep/__init__.py +30 -5
  286. cartography/intel/semgrep/dependencies.py +255 -0
  287. cartography/intel/semgrep/deployment.py +69 -0
  288. cartography/intel/semgrep/findings.py +157 -117
  289. cartography/intel/sentinelone/__init__.py +75 -0
  290. cartography/intel/sentinelone/account.py +140 -0
  291. cartography/intel/sentinelone/agent.py +139 -0
  292. cartography/intel/sentinelone/api.py +124 -0
  293. cartography/intel/sentinelone/application.py +248 -0
  294. cartography/intel/sentinelone/cve.py +119 -0
  295. cartography/intel/sentinelone/utils.py +28 -0
  296. cartography/intel/slack/__init__.py +78 -0
  297. cartography/intel/slack/channels.py +80 -0
  298. cartography/intel/slack/groups.py +90 -0
  299. cartography/intel/slack/teams.py +65 -0
  300. cartography/intel/slack/users.py +57 -0
  301. cartography/intel/slack/utils.py +29 -0
  302. cartography/intel/snipeit/__init__.py +44 -0
  303. cartography/intel/snipeit/asset.py +80 -0
  304. cartography/intel/snipeit/user.py +78 -0
  305. cartography/intel/snipeit/util.py +40 -0
  306. cartography/intel/spacelift/__init__.py +161 -0
  307. cartography/intel/spacelift/account.py +73 -0
  308. cartography/intel/spacelift/ec2_ownership.py +280 -0
  309. cartography/intel/spacelift/runs.py +463 -0
  310. cartography/intel/spacelift/spaces.py +112 -0
  311. cartography/intel/spacelift/stacks.py +119 -0
  312. cartography/intel/spacelift/util.py +122 -0
  313. cartography/intel/spacelift/workerpools.py +131 -0
  314. cartography/intel/spacelift/workers.py +128 -0
  315. cartography/intel/tailscale/__init__.py +77 -0
  316. cartography/intel/tailscale/acls.py +146 -0
  317. cartography/intel/tailscale/devices.py +127 -0
  318. cartography/intel/tailscale/postureintegrations.py +81 -0
  319. cartography/intel/tailscale/tailnets.py +76 -0
  320. cartography/intel/tailscale/users.py +80 -0
  321. cartography/intel/tailscale/utils.py +132 -0
  322. cartography/intel/trivy/__init__.py +272 -0
  323. cartography/intel/trivy/scanner.py +386 -0
  324. cartography/models/airbyte/__init__.py +0 -0
  325. cartography/models/airbyte/connection.py +138 -0
  326. cartography/models/airbyte/destination.py +75 -0
  327. cartography/models/airbyte/organization.py +19 -0
  328. cartography/models/airbyte/source.py +75 -0
  329. cartography/models/airbyte/stream.py +74 -0
  330. cartography/models/airbyte/tag.py +69 -0
  331. cartography/models/airbyte/user.py +115 -0
  332. cartography/models/airbyte/workspace.py +46 -0
  333. cartography/models/anthropic/__init__.py +0 -0
  334. cartography/models/anthropic/apikey.py +94 -0
  335. cartography/models/anthropic/organization.py +19 -0
  336. cartography/models/anthropic/user.py +52 -0
  337. cartography/models/anthropic/workspace.py +90 -0
  338. cartography/models/aws/acm/__init__.py +0 -0
  339. cartography/models/aws/acm/certificate.py +75 -0
  340. cartography/models/aws/apigateway/__init__.py +0 -0
  341. cartography/models/aws/apigateway/apigateway.py +51 -0
  342. cartography/models/aws/apigateway/apigatewaycertificate.py +72 -0
  343. cartography/models/aws/apigateway/apigatewaydeployment.py +74 -0
  344. cartography/models/aws/apigateway/apigatewayintegration.py +79 -0
  345. cartography/models/aws/apigateway/apigatewaymethod.py +74 -0
  346. cartography/models/aws/apigateway/apigatewayresource.py +70 -0
  347. cartography/models/aws/apigateway/apigatewaystage.py +75 -0
  348. cartography/models/aws/apigatewayv2/__init__.py +0 -0
  349. cartography/models/aws/apigatewayv2/apigatewayv2.py +53 -0
  350. cartography/models/aws/cloudtrail/__init__.py +0 -0
  351. cartography/models/aws/cloudtrail/management_events.py +153 -0
  352. cartography/models/aws/cloudtrail/trail.py +106 -0
  353. cartography/models/aws/cloudwatch/__init__.py +0 -0
  354. cartography/models/aws/cloudwatch/log_metric_filter.py +79 -0
  355. cartography/models/aws/cloudwatch/loggroup.py +52 -0
  356. cartography/models/aws/cloudwatch/metric_alarm.py +53 -0
  357. cartography/models/aws/codebuild/__init__.py +0 -0
  358. cartography/models/aws/codebuild/project.py +49 -0
  359. cartography/models/aws/cognito/__init__.py +0 -0
  360. cartography/models/aws/cognito/identity_pool.py +70 -0
  361. cartography/models/aws/cognito/user_pool.py +47 -0
  362. cartography/models/aws/dynamodb/gsi.py +30 -22
  363. cartography/models/aws/dynamodb/tables.py +27 -17
  364. cartography/models/aws/ec2/auto_scaling_groups.py +224 -0
  365. cartography/models/aws/ec2/images.py +36 -34
  366. cartography/models/aws/ec2/instances.py +85 -38
  367. cartography/models/aws/ec2/keypair.py +59 -0
  368. cartography/models/aws/ec2/keypair_instance.py +76 -0
  369. cartography/models/aws/ec2/launch_configurations.py +59 -0
  370. cartography/models/aws/ec2/launch_template_versions.py +48 -38
  371. cartography/models/aws/ec2/launch_templates.py +21 -17
  372. cartography/models/aws/ec2/load_balancer_listeners.py +72 -0
  373. cartography/models/aws/ec2/load_balancers.py +112 -0
  374. cartography/models/aws/ec2/network_acl_rules.py +106 -0
  375. cartography/models/aws/ec2/network_acls.py +95 -0
  376. cartography/models/aws/ec2/networkinterface_instance.py +52 -39
  377. cartography/models/aws/ec2/networkinterfaces.py +57 -37
  378. cartography/models/aws/ec2/privateip_networkinterface.py +32 -22
  379. cartography/models/aws/ec2/reservations.py +18 -14
  380. cartography/models/aws/ec2/route_table_associations.py +97 -0
  381. cartography/models/aws/ec2/route_tables.py +128 -0
  382. cartography/models/aws/ec2/routes.py +85 -0
  383. cartography/models/aws/ec2/security_group_rules.py +109 -0
  384. cartography/models/aws/ec2/security_groups.py +90 -0
  385. cartography/models/aws/ec2/securitygroup_instance.py +29 -20
  386. cartography/models/aws/ec2/securitygroup_networkinterface.py +24 -15
  387. cartography/models/aws/ec2/snapshots.py +58 -0
  388. cartography/models/aws/ec2/subnet_instance.py +26 -19
  389. cartography/models/aws/ec2/subnet_networkinterface.py +42 -31
  390. cartography/models/aws/ec2/subnets.py +65 -0
  391. cartography/models/aws/ec2/volumes.py +67 -40
  392. cartography/models/aws/ec2/vpc.py +46 -0
  393. cartography/models/aws/ec2/vpc_cidr.py +102 -0
  394. cartography/models/aws/ec2/vpc_peering.py +157 -0
  395. cartography/models/aws/ecr/__init__.py +0 -0
  396. cartography/models/aws/ecr/image.py +146 -0
  397. cartography/models/aws/ecr/image_layer.py +107 -0
  398. cartography/models/aws/ecr/repository.py +72 -0
  399. cartography/models/aws/ecr/repository_image.py +95 -0
  400. cartography/models/aws/ecs/__init__.py +0 -0
  401. cartography/models/aws/ecs/clusters.py +64 -0
  402. cartography/models/aws/ecs/container_definitions.py +93 -0
  403. cartography/models/aws/ecs/container_instances.py +84 -0
  404. cartography/models/aws/ecs/containers.py +101 -0
  405. cartography/models/aws/ecs/services.py +134 -0
  406. cartography/models/aws/ecs/task_definitions.py +135 -0
  407. cartography/models/aws/ecs/tasks.py +134 -0
  408. cartography/models/aws/efs/__init__.py +0 -0
  409. cartography/models/aws/efs/access_point.py +77 -0
  410. cartography/models/aws/efs/file_system.py +60 -0
  411. cartography/models/aws/efs/mount_target.py +79 -0
  412. cartography/models/aws/eks/clusters.py +23 -21
  413. cartography/models/aws/elasticache/__init__.py +0 -0
  414. cartography/models/aws/elasticache/cluster.py +65 -0
  415. cartography/models/aws/elasticache/topic.py +67 -0
  416. cartography/models/aws/emr.py +32 -30
  417. cartography/models/aws/eventbridge/__init__.py +0 -0
  418. cartography/models/aws/eventbridge/rule.py +77 -0
  419. cartography/models/aws/eventbridge/target.py +71 -0
  420. cartography/models/aws/glue/__init__.py +0 -0
  421. cartography/models/aws/glue/connection.py +51 -0
  422. cartography/models/aws/glue/job.py +69 -0
  423. cartography/models/aws/guardduty/__init__.py +1 -0
  424. cartography/models/aws/guardduty/detectors.py +50 -0
  425. cartography/models/aws/guardduty/findings.py +121 -0
  426. cartography/models/aws/iam/__init__.py +0 -0
  427. cartography/models/aws/iam/access_key.py +103 -0
  428. cartography/models/aws/iam/account_role.py +24 -0
  429. cartography/models/aws/iam/federated_principal.py +60 -0
  430. cartography/models/aws/iam/group.py +60 -0
  431. cartography/models/aws/iam/group_membership.py +27 -0
  432. cartography/models/aws/iam/inline_policy.py +78 -0
  433. cartography/models/aws/iam/instanceprofile.py +76 -0
  434. cartography/models/aws/iam/managed_policy.py +51 -0
  435. cartography/models/aws/iam/policy_statement.py +57 -0
  436. cartography/models/aws/iam/role.py +83 -0
  437. cartography/models/aws/iam/root_principal.py +52 -0
  438. cartography/models/aws/iam/service_principal.py +30 -0
  439. cartography/models/aws/iam/sts_assumerole_allow.py +38 -0
  440. cartography/models/aws/iam/user.py +59 -0
  441. cartography/models/aws/identitycenter/__init__.py +0 -0
  442. cartography/models/aws/identitycenter/awsidentitycenter.py +49 -0
  443. cartography/models/aws/identitycenter/awspermissionset.py +162 -0
  444. cartography/models/aws/identitycenter/awssogroup.py +70 -0
  445. cartography/models/aws/identitycenter/awsssouser.py +110 -0
  446. cartography/models/aws/inspector/findings.py +124 -58
  447. cartography/models/aws/inspector/packages.py +18 -42
  448. cartography/models/aws/kms/__init__.py +0 -0
  449. cartography/models/aws/kms/aliases.py +86 -0
  450. cartography/models/aws/kms/grants.py +65 -0
  451. cartography/models/aws/kms/keys.py +88 -0
  452. cartography/models/aws/lambda_function/__init__.py +0 -0
  453. cartography/models/aws/lambda_function/alias.py +74 -0
  454. cartography/models/aws/lambda_function/event_source_mapping.py +88 -0
  455. cartography/models/aws/lambda_function/lambda_function.py +91 -0
  456. cartography/models/aws/lambda_function/layer.py +72 -0
  457. cartography/models/aws/rds/__init__.py +0 -0
  458. cartography/models/aws/rds/cluster.py +91 -0
  459. cartography/models/aws/rds/event_subscription.py +146 -0
  460. cartography/models/aws/rds/instance.py +156 -0
  461. cartography/models/aws/rds/snapshot.py +108 -0
  462. cartography/models/aws/rds/subnet_group.py +101 -0
  463. cartography/models/aws/route53/__init__.py +0 -0
  464. cartography/models/aws/route53/dnsrecord.py +235 -0
  465. cartography/models/aws/route53/nameserver.py +63 -0
  466. cartography/models/aws/route53/subzone.py +40 -0
  467. cartography/models/aws/route53/zone.py +47 -0
  468. cartography/models/aws/s3/__init__.py +0 -0
  469. cartography/models/aws/s3/account_public_access_block.py +51 -0
  470. cartography/models/aws/s3/notification.py +24 -0
  471. cartography/models/aws/secretsmanager/__init__.py +0 -0
  472. cartography/models/aws/secretsmanager/secret.py +106 -0
  473. cartography/models/aws/secretsmanager/secret_version.py +114 -0
  474. cartography/models/aws/sns/__init__.py +0 -0
  475. cartography/models/aws/sns/topic.py +50 -0
  476. cartography/models/aws/sns/topic_subscription.py +74 -0
  477. cartography/models/aws/sqs/__init__.py +0 -0
  478. cartography/models/aws/sqs/queue.py +89 -0
  479. cartography/models/aws/ssm/instance_information.py +51 -39
  480. cartography/models/aws/ssm/instance_patch.py +32 -26
  481. cartography/models/aws/ssm/parameters.py +84 -0
  482. cartography/models/azure/__init__.py +0 -0
  483. cartography/models/azure/aks_cluster.py +54 -0
  484. cartography/models/azure/aks_nodepool.py +54 -0
  485. cartography/models/azure/app_service.py +59 -0
  486. cartography/models/azure/container_instance.py +57 -0
  487. cartography/models/azure/cosmosdb/__init__.py +0 -0
  488. cartography/models/azure/cosmosdb/account.py +77 -0
  489. cartography/models/azure/cosmosdb/accountfailoverpolicy.py +77 -0
  490. cartography/models/azure/cosmosdb/cassandrakeyspace.py +82 -0
  491. cartography/models/azure/cosmosdb/cassandratable.py +81 -0
  492. cartography/models/azure/cosmosdb/corspolicy.py +74 -0
  493. cartography/models/azure/cosmosdb/dblocation.py +120 -0
  494. cartography/models/azure/cosmosdb/mongodbcollection.py +82 -0
  495. cartography/models/azure/cosmosdb/mongodbdatabase.py +78 -0
  496. cartography/models/azure/cosmosdb/privateendpointconnection.py +81 -0
  497. cartography/models/azure/cosmosdb/sqlcontainer.py +88 -0
  498. cartography/models/azure/cosmosdb/sqldatabase.py +78 -0
  499. cartography/models/azure/cosmosdb/tableresource.py +76 -0
  500. cartography/models/azure/cosmosdb/virtualnetworkrule.py +78 -0
  501. cartography/models/azure/data_factory/__init__.py +0 -0
  502. cartography/models/azure/data_factory/data_factory.py +51 -0
  503. cartography/models/azure/data_factory/data_factory_dataset.py +94 -0
  504. cartography/models/azure/data_factory/data_factory_linked_service.py +78 -0
  505. cartography/models/azure/data_factory/data_factory_pipeline.py +93 -0
  506. cartography/models/azure/data_lake_filesystem.py +51 -0
  507. cartography/models/azure/event_grid_topic.py +57 -0
  508. cartography/models/azure/function_app.py +59 -0
  509. cartography/models/azure/load_balancer/__init__.py +0 -0
  510. cartography/models/azure/load_balancer/load_balancer.py +49 -0
  511. cartography/models/azure/load_balancer/load_balancer_backend_pool.py +73 -0
  512. cartography/models/azure/load_balancer/load_balancer_frontend_ip.py +75 -0
  513. cartography/models/azure/load_balancer/load_balancer_inbound_nat_rule.py +78 -0
  514. cartography/models/azure/load_balancer/load_balancer_rule.py +108 -0
  515. cartography/models/azure/logic_apps.py +56 -0
  516. cartography/models/azure/monitor.py +54 -0
  517. cartography/models/azure/network_interface.py +112 -0
  518. cartography/models/azure/network_security_group.py +50 -0
  519. cartography/models/azure/permission_relationships.py +60 -0
  520. cartography/models/azure/principal.py +41 -0
  521. cartography/models/azure/public_ip_address.py +50 -0
  522. cartography/models/azure/rbac.py +268 -0
  523. cartography/models/azure/resource_groups.py +52 -0
  524. cartography/models/azure/security_center.py +50 -0
  525. cartography/models/azure/sql/__init__.py +0 -0
  526. cartography/models/azure/sql/databasethreatdetectionpolicy.py +85 -0
  527. cartography/models/azure/sql/elasticpool.py +77 -0
  528. cartography/models/azure/sql/failovergroup.py +73 -0
  529. cartography/models/azure/sql/recoverabledatabase.py +75 -0
  530. cartography/models/azure/sql/replicationlink.py +81 -0
  531. cartography/models/azure/sql/restorabledroppeddatabase.py +82 -0
  532. cartography/models/azure/sql/restorepoint.py +74 -0
  533. cartography/models/azure/sql/serveradadministrator.py +74 -0
  534. cartography/models/azure/sql/serverdnsalias.py +71 -0
  535. cartography/models/azure/sql/sqldatabase.py +85 -0
  536. cartography/models/azure/sql/sqlserver.py +50 -0
  537. cartography/models/azure/sql/transparentdataencryption.py +76 -0
  538. cartography/models/azure/storage/__init__.py +0 -0
  539. cartography/models/azure/storage/account.py +59 -0
  540. cartography/models/azure/storage/blobcontainer.py +85 -0
  541. cartography/models/azure/storage/blobservice.py +71 -0
  542. cartography/models/azure/storage/fileservice.py +71 -0
  543. cartography/models/azure/storage/fileshare.py +82 -0
  544. cartography/models/azure/storage/queue.py +71 -0
  545. cartography/models/azure/storage/queueservice.py +73 -0
  546. cartography/models/azure/storage/table.py +72 -0
  547. cartography/models/azure/storage/tableservice.py +73 -0
  548. cartography/models/azure/subnet.py +101 -0
  549. cartography/models/azure/subscription.py +47 -0
  550. cartography/models/azure/tags/__init__.py +0 -0
  551. cartography/models/azure/tags/storage_tag.py +40 -0
  552. cartography/models/azure/tags/tag.py +37 -0
  553. cartography/models/azure/tenant.py +17 -0
  554. cartography/models/azure/virtual_network.py +49 -0
  555. cartography/models/azure/vm/__init__.py +0 -0
  556. cartography/models/azure/vm/datadisk.py +80 -0
  557. cartography/models/azure/vm/disk.py +55 -0
  558. cartography/models/azure/vm/snapshot.py +56 -0
  559. cartography/models/azure/vm/virtualmachine.py +59 -0
  560. cartography/models/bigfix/bigfix_computer.py +42 -38
  561. cartography/models/bigfix/bigfix_root.py +3 -3
  562. cartography/models/cloudflare/__init__.py +0 -0
  563. cartography/models/cloudflare/account.py +25 -0
  564. cartography/models/cloudflare/dnsrecord.py +55 -0
  565. cartography/models/cloudflare/member.py +86 -0
  566. cartography/models/cloudflare/role.py +44 -0
  567. cartography/models/cloudflare/zone.py +59 -0
  568. cartography/models/core/common.py +53 -2
  569. cartography/models/core/nodes.py +20 -4
  570. cartography/models/core/relationships.py +58 -6
  571. cartography/models/crowdstrike/__init__.py +0 -0
  572. cartography/models/crowdstrike/hosts.py +51 -0
  573. cartography/models/cve/cve.py +34 -32
  574. cartography/models/cve/cve_feed.py +6 -6
  575. cartography/models/digitalocean/__init__.py +0 -0
  576. cartography/models/digitalocean/account.py +21 -0
  577. cartography/models/digitalocean/droplet.py +58 -0
  578. cartography/models/digitalocean/project.py +48 -0
  579. cartography/models/duo/api_host.py +3 -3
  580. cartography/models/duo/endpoint.py +43 -41
  581. cartography/models/duo/group.py +14 -14
  582. cartography/models/duo/phone.py +27 -27
  583. cartography/models/duo/token.py +16 -16
  584. cartography/models/duo/user.py +50 -44
  585. cartography/models/duo/web_authn_credential.py +27 -19
  586. cartography/models/entra/__init__.py +0 -0
  587. cartography/models/entra/app_role_assignment.py +115 -0
  588. cartography/models/entra/application.py +49 -0
  589. cartography/models/entra/entra_user_to_aws_sso.py +41 -0
  590. cartography/models/entra/group.py +117 -0
  591. cartography/models/entra/ou.py +48 -0
  592. cartography/models/entra/service_principal.py +104 -0
  593. cartography/models/entra/tenant.py +39 -0
  594. cartography/models/entra/user.py +90 -0
  595. cartography/models/gcp/__init__.py +0 -0
  596. cartography/models/gcp/bigtable/__init__.py +0 -0
  597. cartography/models/gcp/bigtable/app_profile.py +94 -0
  598. cartography/models/gcp/bigtable/backup.py +91 -0
  599. cartography/models/gcp/bigtable/cluster.py +73 -0
  600. cartography/models/gcp/bigtable/instance.py +52 -0
  601. cartography/models/gcp/bigtable/table.py +69 -0
  602. cartography/models/gcp/compute/__init__.py +0 -0
  603. cartography/models/gcp/compute/subnet.py +74 -0
  604. cartography/models/gcp/compute/vpc.py +50 -0
  605. cartography/models/gcp/crm/__init__.py +0 -0
  606. cartography/models/gcp/crm/folders.py +98 -0
  607. cartography/models/gcp/crm/organizations.py +21 -0
  608. cartography/models/gcp/crm/projects.py +100 -0
  609. cartography/models/gcp/dns.py +109 -0
  610. cartography/models/gcp/gke.py +69 -0
  611. cartography/models/gcp/iam.py +73 -0
  612. cartography/models/gcp/permission_relationships.py +61 -0
  613. cartography/models/gcp/policy_bindings.py +93 -0
  614. cartography/models/gcp/storage/__init__.py +0 -0
  615. cartography/models/gcp/storage/bucket.py +119 -0
  616. cartography/models/github/commits.py +63 -0
  617. cartography/models/github/dependencies.py +73 -0
  618. cartography/models/github/manifests.py +49 -0
  619. cartography/models/github/orgs.py +27 -0
  620. cartography/models/github/teams.py +74 -22
  621. cartography/models/github/users.py +149 -0
  622. cartography/models/googleworkspace/__init__.py +0 -0
  623. cartography/models/googleworkspace/device.py +132 -0
  624. cartography/models/googleworkspace/group.py +382 -0
  625. cartography/models/googleworkspace/oauth_app.py +124 -0
  626. cartography/models/googleworkspace/tenant.py +30 -0
  627. cartography/models/googleworkspace/user.py +113 -0
  628. cartography/models/gsuite/__init__.py +0 -0
  629. cartography/models/gsuite/group.py +218 -0
  630. cartography/models/gsuite/tenant.py +29 -0
  631. cartography/models/gsuite/user.py +107 -0
  632. cartography/models/kandji/device.py +22 -17
  633. cartography/models/kandji/tenant.py +6 -4
  634. cartography/models/keycloak/__init__.py +0 -0
  635. cartography/models/keycloak/authenticationexecution.py +160 -0
  636. cartography/models/keycloak/authenticationflow.py +54 -0
  637. cartography/models/keycloak/client.py +179 -0
  638. cartography/models/keycloak/group.py +101 -0
  639. cartography/models/keycloak/identityprovider.py +89 -0
  640. cartography/models/keycloak/organization.py +116 -0
  641. cartography/models/keycloak/organizationdomain.py +73 -0
  642. cartography/models/keycloak/realm.py +173 -0
  643. cartography/models/keycloak/role.py +126 -0
  644. cartography/models/keycloak/scope.py +73 -0
  645. cartography/models/keycloak/user.py +55 -0
  646. cartography/models/kubernetes/__init__.py +0 -0
  647. cartography/models/kubernetes/clusterrolebindings.py +138 -0
  648. cartography/models/kubernetes/clusterroles.py +52 -0
  649. cartography/models/kubernetes/clusters.py +26 -0
  650. cartography/models/kubernetes/containers.py +133 -0
  651. cartography/models/kubernetes/groups.py +107 -0
  652. cartography/models/kubernetes/namespaces.py +51 -0
  653. cartography/models/kubernetes/oidc.py +51 -0
  654. cartography/models/kubernetes/pods.py +80 -0
  655. cartography/models/kubernetes/rolebindings.py +159 -0
  656. cartography/models/kubernetes/roles.py +76 -0
  657. cartography/models/kubernetes/secrets.py +79 -0
  658. cartography/models/kubernetes/serviceaccounts.py +77 -0
  659. cartography/models/kubernetes/services.py +108 -0
  660. cartography/models/kubernetes/users.py +105 -0
  661. cartography/models/lastpass/tenant.py +3 -3
  662. cartography/models/lastpass/user.py +36 -28
  663. cartography/models/ontology/__init__.py +0 -0
  664. cartography/models/ontology/device.py +137 -0
  665. cartography/models/ontology/mapping/__init__.py +76 -0
  666. cartography/models/ontology/mapping/data/__init__.py +0 -0
  667. cartography/models/ontology/mapping/data/apikeys.py +93 -0
  668. cartography/models/ontology/mapping/data/computeinstance.py +95 -0
  669. cartography/models/ontology/mapping/data/containers.py +88 -0
  670. cartography/models/ontology/mapping/data/databases.py +182 -0
  671. cartography/models/ontology/mapping/data/devices.py +194 -0
  672. cartography/models/ontology/mapping/data/thirdpartyapps.py +140 -0
  673. cartography/models/ontology/mapping/data/useraccounts.py +416 -0
  674. cartography/models/ontology/mapping/data/users.py +63 -0
  675. cartography/models/ontology/mapping/specs.py +85 -0
  676. cartography/models/ontology/user.py +51 -0
  677. cartography/models/openai/__init__.py +0 -0
  678. cartography/models/openai/adminapikey.py +94 -0
  679. cartography/models/openai/apikey.py +88 -0
  680. cartography/models/openai/organization.py +17 -0
  681. cartography/models/openai/project.py +89 -0
  682. cartography/models/openai/serviceaccount.py +50 -0
  683. cartography/models/openai/user.py +53 -0
  684. cartography/models/scaleway/__init__.py +0 -0
  685. cartography/models/scaleway/iam/__init__.py +0 -0
  686. cartography/models/scaleway/iam/apikey.py +100 -0
  687. cartography/models/scaleway/iam/application.py +52 -0
  688. cartography/models/scaleway/iam/group.py +95 -0
  689. cartography/models/scaleway/iam/user.py +64 -0
  690. cartography/models/scaleway/instance/__init__.py +0 -0
  691. cartography/models/scaleway/instance/flexibleip.py +52 -0
  692. cartography/models/scaleway/instance/instance.py +120 -0
  693. cartography/models/scaleway/organization.py +19 -0
  694. cartography/models/scaleway/project.py +48 -0
  695. cartography/models/scaleway/storage/__init__.py +0 -0
  696. cartography/models/scaleway/storage/snapshot.py +78 -0
  697. cartography/models/scaleway/storage/volume.py +51 -0
  698. cartography/models/semgrep/dependencies.py +102 -0
  699. cartography/models/semgrep/deployment.py +5 -5
  700. cartography/models/semgrep/findings.py +58 -40
  701. cartography/models/semgrep/locations.py +27 -21
  702. cartography/models/sentinelone/__init__.py +1 -0
  703. cartography/models/sentinelone/account.py +40 -0
  704. cartography/models/sentinelone/agent.py +50 -0
  705. cartography/models/sentinelone/application.py +44 -0
  706. cartography/models/sentinelone/application_version.py +96 -0
  707. cartography/models/sentinelone/cve.py +73 -0
  708. cartography/models/slack/__init__.py +0 -0
  709. cartography/models/slack/channels.py +92 -0
  710. cartography/models/slack/group.py +129 -0
  711. cartography/models/slack/team.py +22 -0
  712. cartography/models/slack/user.py +62 -0
  713. cartography/models/snipeit/__init__.py +0 -0
  714. cartography/models/snipeit/asset.py +92 -0
  715. cartography/models/snipeit/tenant.py +19 -0
  716. cartography/models/snipeit/user.py +60 -0
  717. cartography/models/spacelift/__init__.py +0 -0
  718. cartography/models/spacelift/cloudtrailevent.py +120 -0
  719. cartography/models/spacelift/run.py +162 -0
  720. cartography/models/spacelift/space.py +131 -0
  721. cartography/models/spacelift/spaceliftaccount.py +31 -0
  722. cartography/models/spacelift/spaceliftgitcommit.py +157 -0
  723. cartography/models/spacelift/stack.py +96 -0
  724. cartography/models/spacelift/user.py +63 -0
  725. cartography/models/spacelift/worker.py +97 -0
  726. cartography/models/spacelift/workerpool.py +90 -0
  727. cartography/models/tailscale/__init__.py +0 -0
  728. cartography/models/tailscale/device.py +96 -0
  729. cartography/models/tailscale/group.py +86 -0
  730. cartography/models/tailscale/postureintegration.py +58 -0
  731. cartography/models/tailscale/tag.py +102 -0
  732. cartography/models/tailscale/tailnet.py +29 -0
  733. cartography/models/tailscale/user.py +57 -0
  734. cartography/models/trivy/__init__.py +0 -0
  735. cartography/models/trivy/findings.py +66 -0
  736. cartography/models/trivy/fix.py +66 -0
  737. cartography/models/trivy/package.py +71 -0
  738. cartography/rules/README.md +1 -0
  739. cartography/rules/__init__.py +0 -0
  740. cartography/rules/cli.py +261 -0
  741. cartography/rules/data/__init__.py +0 -0
  742. cartography/rules/data/rules/__init__.py +46 -0
  743. cartography/rules/data/rules/cloud_security_product_deactivated.py +49 -0
  744. cartography/rules/data/rules/compute_instance_exposed.py +51 -0
  745. cartography/rules/data/rules/database_instance_exposed.py +53 -0
  746. cartography/rules/data/rules/delegation_boundary_modifiable.py +90 -0
  747. cartography/rules/data/rules/identity_administration_privileges.py +100 -0
  748. cartography/rules/data/rules/inactive_user_active_accounts.py +48 -0
  749. cartography/rules/data/rules/malicious_npm_dependencies_shai_hulud.py +2222 -0
  750. cartography/rules/data/rules/mfa_missing.py +46 -0
  751. cartography/rules/data/rules/object_storage_public.py +100 -0
  752. cartography/rules/data/rules/policy_administration_privileges.py +104 -0
  753. cartography/rules/data/rules/unmanaged_accounts.py +43 -0
  754. cartography/rules/data/rules/workload_identity_admin_capabilities.py +193 -0
  755. cartography/rules/formatters.py +108 -0
  756. cartography/rules/runners.py +216 -0
  757. cartography/rules/spec/__init__.py +0 -0
  758. cartography/rules/spec/model.py +267 -0
  759. cartography/rules/spec/result.py +38 -0
  760. cartography/stats.py +4 -4
  761. cartography/sync.py +137 -31
  762. cartography/util.py +187 -77
  763. cartography-0.123.0.dist-info/METADATA +230 -0
  764. cartography-0.123.0.dist-info/RECORD +856 -0
  765. {cartography-0.93.0rc1.dist-info → cartography-0.123.0.dist-info}/WHEEL +1 -1
  766. {cartography-0.93.0rc1.dist-info → cartography-0.123.0.dist-info}/entry_points.txt +1 -0
  767. {cartography-0.93.0rc1.dist-info → cartography-0.123.0.dist-info/licenses}/LICENSE +1 -1
  768. cartography/data/jobs/analysis/aws_ec2_iaminstance.json +0 -10
  769. cartography/data/jobs/analysis/aws_ec2_iaminstanceprofile.json +0 -10
  770. cartography/data/jobs/cleanup/aws_apigateway_details.json +0 -10
  771. cartography/data/jobs/cleanup/aws_dns_cleanup.json +0 -65
  772. cartography/data/jobs/cleanup/aws_import_account_access_key_cleanup.json +0 -17
  773. cartography/data/jobs/cleanup/aws_import_apigateway_cleanup.json +0 -45
  774. cartography/data/jobs/cleanup/aws_import_ec2_security_groupinfo_cleanup.json +0 -24
  775. cartography/data/jobs/cleanup/aws_import_groups_cleanup.json +0 -13
  776. cartography/data/jobs/cleanup/aws_import_lambda_cleanup.json +0 -50
  777. cartography/data/jobs/cleanup/aws_import_principals_cleanup.json +0 -30
  778. cartography/data/jobs/cleanup/aws_import_rds_clusters_cleanup.json +0 -23
  779. cartography/data/jobs/cleanup/aws_import_rds_instances_cleanup.json +0 -47
  780. cartography/data/jobs/cleanup/aws_import_rds_snapshots_cleanup.json +0 -23
  781. cartography/data/jobs/cleanup/aws_import_roles_cleanup.json +0 -13
  782. cartography/data/jobs/cleanup/aws_import_secrets_cleanup.json +0 -8
  783. cartography/data/jobs/cleanup/aws_import_snapshots_cleanup.json +0 -30
  784. cartography/data/jobs/cleanup/aws_import_users_cleanup.json +0 -8
  785. cartography/data/jobs/cleanup/aws_import_vpc_cleanup.json +0 -23
  786. cartography/data/jobs/cleanup/aws_import_vpc_peering_cleanup.json +0 -45
  787. cartography/data/jobs/cleanup/aws_kms_details.json +0 -10
  788. cartography/data/jobs/cleanup/azure_cosmosdb_cassandra_keyspace_cleanup.json +0 -25
  789. cartography/data/jobs/cleanup/azure_cosmosdb_cors_details.json +0 -15
  790. cartography/data/jobs/cleanup/azure_cosmosdb_mongodb_database_cleanup.json +0 -25
  791. cartography/data/jobs/cleanup/azure_cosmosdb_sql_database_cleanup.json +0 -25
  792. cartography/data/jobs/cleanup/azure_cosmosdb_table_resources_cleanup.json +0 -15
  793. cartography/data/jobs/cleanup/azure_database_account_cleanup.json +0 -85
  794. cartography/data/jobs/cleanup/azure_import_disks_cleanup.json +0 -15
  795. cartography/data/jobs/cleanup/azure_import_snapshots_cleanup.json +0 -15
  796. cartography/data/jobs/cleanup/azure_import_virtual_machines_cleanup.json +0 -25
  797. cartography/data/jobs/cleanup/azure_sql_server_cleanup.json +0 -125
  798. cartography/data/jobs/cleanup/azure_storage_account_cleanup.json +0 -95
  799. cartography/data/jobs/cleanup/azure_subscriptions_cleanup.json +0 -14
  800. cartography/data/jobs/cleanup/azure_tenant_cleanup.json +0 -9
  801. cartography/data/jobs/cleanup/crxcavator_import_cleanup.json +0 -18
  802. cartography/data/jobs/cleanup/gcp_compute_vpc_subnet_cleanup.json +0 -35
  803. cartography/data/jobs/cleanup/gcp_crm_folder_cleanup.json +0 -23
  804. cartography/data/jobs/cleanup/gcp_crm_organization_cleanup.json +0 -17
  805. cartography/data/jobs/cleanup/gcp_crm_project_cleanup.json +0 -23
  806. cartography/data/jobs/cleanup/gcp_dns_cleanup.json +0 -29
  807. cartography/data/jobs/cleanup/gcp_gke_cluster_cleanup.json +0 -17
  808. cartography/data/jobs/cleanup/gcp_storage_bucket_cleanup.json +0 -29
  809. cartography/data/jobs/cleanup/github_users_cleanup.json +0 -23
  810. cartography/data/jobs/cleanup/gsuite_ingest_groups_cleanup.json +0 -23
  811. cartography/data/jobs/cleanup/gsuite_ingest_users_cleanup.json +0 -11
  812. cartography/data/jobs/cleanup/kubernetes_import_cleanup.json +0 -70
  813. cartography/intel/crxcavator/__init__.py +0 -44
  814. cartography/intel/crxcavator/crxcavator.py +0 -329
  815. cartography/intel/gcp/crm.py +0 -302
  816. cartography/intel/gsuite/api.py +0 -284
  817. cartography/models/aws/ec2/keypairs.py +0 -64
  818. cartography-0.93.0rc1.dist-info/METADATA +0 -55
  819. cartography-0.93.0rc1.dist-info/NOTICE +0 -4
  820. cartography-0.93.0rc1.dist-info/RECORD +0 -341
  821. /cartography/data/jobs/{analysis → scoped_analysis}/aws_s3acl_analysis.json +0 -0
  822. {cartography-0.93.0rc1.dist-info → cartography-0.123.0.dist-info}/top_level.txt +0 -0
@@ -11,14 +11,23 @@ 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
 
20
25
  logger = logging.getLogger(__name__)
21
- InstanceUriPrefix = namedtuple('InstanceUriPrefix', 'zone_name project_id')
26
+ InstanceUriPrefix = namedtuple("InstanceUriPrefix", "zone_name project_id")
27
+
28
+
29
+ # Maximum number of retries for Google API requests
30
+ GOOGLE_API_NUM_RETRIES = 5
22
31
 
23
32
 
24
33
  def _get_error_reason(http_error: HttpError) -> str:
@@ -33,19 +42,23 @@ def _get_error_reason(http_error: HttpError) -> str:
33
42
  :return: The error reason as a string
34
43
  """
35
44
  try:
36
- data = json.loads(http_error.content.decode('utf-8'))
45
+ data = json.loads(http_error.content.decode("utf-8"))
37
46
  if isinstance(data, dict):
38
- reason = data['error']['errors'][0]['reason']
47
+ reason = data["error"]["errors"][0]["reason"]
39
48
  else:
40
- reason = data[0]['error']['errors']['reason']
49
+ reason = data[0]["error"]["errors"]["reason"]
41
50
  except (UnicodeDecodeError, ValueError, KeyError):
42
51
  logger.warning(f"HttpError: {data}")
43
- return ''
52
+ return ""
44
53
  return reason
45
54
 
46
55
 
47
56
  @timeit
48
- def get_zones_in_project(project_id: str, compute: Resource, max_results: Optional[int] = None) -> Optional[List[Dict]]:
57
+ def get_zones_in_project(
58
+ project_id: str,
59
+ compute: Resource,
60
+ max_results: Optional[int] = None,
61
+ ) -> Optional[List[Dict]]:
49
62
  """
50
63
  Return the zones where the Compute Engine API is enabled for the given project_id.
51
64
  See https://cloud.google.com/compute/docs/reference/rest/v1/zones and
@@ -59,11 +72,11 @@ def get_zones_in_project(project_id: str, compute: Resource, max_results: Option
59
72
  """
60
73
  try:
61
74
  req = compute.zones().list(project=project_id, maxResults=max_results)
62
- res = req.execute()
63
- return res['items']
75
+ res = req.execute(num_retries=GOOGLE_API_NUM_RETRIES)
76
+ return res["items"]
64
77
  except HttpError as e:
65
78
  reason = _get_error_reason(e)
66
- if reason == 'accessNotConfigured':
79
+ if reason == "accessNotConfigured":
67
80
  logger.info(
68
81
  (
69
82
  "Google Compute Engine API access is not configured for project %s; skipping. "
@@ -73,17 +86,14 @@ def get_zones_in_project(project_id: str, compute: Resource, max_results: Option
73
86
  e,
74
87
  )
75
88
  return None
76
- elif reason == 'notFound':
89
+ elif reason == "notFound":
77
90
  logger.info(
78
- (
79
- "Project %s returned a 404 not found error. "
80
- "Full details: %s"
81
- ),
91
+ ("Project %s returned a 404 not found error. " "Full details: %s"),
82
92
  project_id,
83
93
  e,
84
94
  )
85
95
  return None
86
- elif reason == 'forbidden':
96
+ elif reason == "forbidden":
87
97
  logger.info(
88
98
  (
89
99
  "Your GCP identity does not have the compute.zones.list permission for project %s; skipping "
@@ -98,7 +108,11 @@ def get_zones_in_project(project_id: str, compute: Resource, max_results: Option
98
108
 
99
109
 
100
110
  @timeit
101
- def get_gcp_instance_responses(project_id: str, zones: Optional[List[Dict]], compute: Resource) -> List[Resource]:
111
+ def get_gcp_instance_responses(
112
+ project_id: str,
113
+ zones: Optional[List[Dict]],
114
+ compute: Resource,
115
+ ) -> List[Resource]:
102
116
  """
103
117
  Return list of GCP instance response objects for a given project and list of zones
104
118
  :param project_id: The project ID
@@ -111,23 +125,54 @@ def get_gcp_instance_responses(project_id: str, zones: Optional[List[Dict]], com
111
125
  return []
112
126
  response_objects: List[Resource] = []
113
127
  for zone in zones:
114
- req = compute.instances().list(project=project_id, zone=zone['name'])
115
- res = req.execute()
116
- response_objects.append(res)
128
+ req = compute.instances().list(project=project_id, zone=zone["name"])
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
117
143
  return response_objects
118
144
 
119
145
 
120
146
  @timeit
121
- def get_gcp_subnets(projectid: str, region: str, compute: Resource) -> Resource:
147
+ def get_gcp_subnets(projectid: str, region: str, compute: Resource) -> Dict:
122
148
  """
123
- Return list of all subnets in the given projectid and region
124
- :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
125
153
  :param region: The region to pull subnets from
126
154
  :param compute: The compute resource object created by googleapiclient.discovery.build()
127
155
  :return: Response object containing data on all GCP subnets for a given project
128
156
  """
129
157
  req = compute.subnetworks().list(project=projectid, region=region)
130
- 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}
131
176
 
132
177
 
133
178
  @timeit
@@ -139,11 +184,15 @@ def get_gcp_vpcs(projectid: str, compute: Resource) -> Resource:
139
184
  :return: VPC response object
140
185
  """
141
186
  req = compute.networks().list(project=projectid)
142
- return req.execute()
187
+ return req.execute(num_retries=GOOGLE_API_NUM_RETRIES)
143
188
 
144
189
 
145
190
  @timeit
146
- def get_gcp_regional_forwarding_rules(project_id: str, region: str, compute: Resource) -> Resource:
191
+ def get_gcp_regional_forwarding_rules(
192
+ project_id: str,
193
+ region: str,
194
+ compute: Resource,
195
+ ) -> Resource:
147
196
  """
148
197
  Return list of all regional forwarding rules in the given project_id and region
149
198
  :param project_id: The project ID
@@ -152,7 +201,7 @@ def get_gcp_regional_forwarding_rules(project_id: str, region: str, compute: Res
152
201
  :return: Response object containing data on all GCP forwarding rules for a given project
153
202
  """
154
203
  req = compute.forwardingRules().list(project=project_id, region=region)
155
- return req.execute()
204
+ return req.execute(num_retries=GOOGLE_API_NUM_RETRIES)
156
205
 
157
206
 
158
207
  @timeit
@@ -164,7 +213,7 @@ def get_gcp_global_forwarding_rules(project_id: str, compute: Resource) -> Resou
164
213
  :return: Response object containing data on all GCP forwarding rules for a given project
165
214
  """
166
215
  req = compute.globalForwardingRules().list(project=project_id)
167
- return req.execute()
216
+ return req.execute(num_retries=GOOGLE_API_NUM_RETRIES)
168
217
 
169
218
 
170
219
  @timeit
@@ -176,7 +225,7 @@ def get_gcp_firewall_ingress_rules(project_id: str, compute: Resource) -> Resour
176
225
  :return: Firewall response object
177
226
  """
178
227
  req = compute.firewalls().list(project=project_id, filter='(direction="INGRESS")')
179
- return req.execute()
228
+ return req.execute(num_retries=GOOGLE_API_NUM_RETRIES)
180
229
 
181
230
 
182
231
  @timeit
@@ -189,17 +238,21 @@ def transform_gcp_instances(response_objects: List[Dict]) -> List[Dict]:
189
238
  """
190
239
  instance_list = []
191
240
  for res in response_objects:
192
- prefix = res['id']
241
+ prefix = res["id"]
193
242
  prefix_fields = _parse_instance_uri_prefix(prefix)
194
243
 
195
- for instance in res.get('items', []):
196
- instance['partial_uri'] = f"{prefix}/{instance['name']}"
197
- instance['project_id'] = prefix_fields.project_id
198
- instance['zone_name'] = prefix_fields.zone_name
244
+ for instance in res.get("items", []):
245
+ instance["partial_uri"] = f"{prefix}/{instance['name']}"
246
+ instance["project_id"] = prefix_fields.project_id
247
+ instance["zone_name"] = prefix_fields.zone_name
199
248
 
200
- for nic in instance.get('networkInterfaces', []):
201
- nic['subnet_partial_uri'] = _parse_compute_full_uri_to_partial_uri(nic['subnetwork'])
202
- nic['vpc_partial_uri'] = _parse_compute_full_uri_to_partial_uri(nic['network'])
249
+ for nic in instance.get("networkInterfaces", []):
250
+ nic["subnet_partial_uri"] = _parse_compute_full_uri_to_partial_uri(
251
+ nic["subnetwork"],
252
+ )
253
+ nic["vpc_partial_uri"] = _parse_compute_full_uri_to_partial_uri(
254
+ nic["network"],
255
+ )
203
256
 
204
257
  instance_list.append(instance)
205
258
  return instance_list
@@ -211,7 +264,7 @@ def _parse_instance_uri_prefix(prefix: str) -> InstanceUriPrefix:
211
264
  :param prefix: String of the form `projects/{project}/zones/{zone}/instances`
212
265
  :return: namedtuple with fields project_id and zone_name
213
266
  """
214
- split_list = prefix.split('/')
267
+ split_list = prefix.split("/")
215
268
 
216
269
  return InstanceUriPrefix(
217
270
  project_id=split_list[1],
@@ -219,7 +272,7 @@ def _parse_instance_uri_prefix(prefix: str) -> InstanceUriPrefix:
219
272
  )
220
273
 
221
274
 
222
- def _parse_compute_full_uri_to_partial_uri(full_uri: str, version: str = 'v1') -> str:
275
+ def _parse_compute_full_uri_to_partial_uri(full_uri: str, version: str = "v1") -> str:
223
276
  """
224
277
  Take a GCP Compute object's self_link of the form
225
278
  `https://www.googleapis.com/compute/{version}/projects/{project}/{location specifier}/{subtype}/{resource name}`
@@ -229,7 +282,7 @@ def _parse_compute_full_uri_to_partial_uri(full_uri: str, version: str = 'v1') -
229
282
  :param version: The version number; default to v1 since at the time of this writing v1 is the only Compute API.
230
283
  :return: Partial URI `{project}/{location specifier}/{subtype}/{resource name}`
231
284
  """
232
- return full_uri.split(f'compute/{version}/')[1]
285
+ return full_uri.split(f"compute/{version}/")[1]
233
286
 
234
287
 
235
288
  def _create_gcp_network_tag_id(vpc_partial_uri: str, tag: str) -> str:
@@ -251,19 +304,22 @@ def transform_gcp_vpcs(vpc_res: Dict) -> List[Dict]:
251
304
  vpc_list = []
252
305
 
253
306
  # prefix has the form `projects/{project ID}/global/networks`
254
- prefix = vpc_res['id']
255
- projectid = prefix.split('/')[1]
256
- for v in vpc_res.get('items', []):
307
+ prefix = vpc_res["id"]
308
+ projectid = prefix.split("/")[1]
309
+ for v in vpc_res.get("items", []):
257
310
  vpc = {}
258
311
  partial_uri = f"{prefix}/{v['name']}"
259
312
 
260
- vpc['partial_uri'] = partial_uri
261
- vpc['name'] = v['name']
262
- vpc['self_link'] = v['selfLink']
263
- vpc['project_id'] = projectid
264
- vpc['auto_create_subnetworks'] = v.get('autoCreateSubnetworks', None)
265
- vpc['description'] = v.get('description', None)
266
- vpc['routing_config_routing_mode'] = v.get('routingConfig', {}).get('routingMode', None)
313
+ vpc["partial_uri"] = partial_uri
314
+ vpc["name"] = v["name"]
315
+ vpc["self_link"] = v["selfLink"]
316
+ vpc["project_id"] = projectid
317
+ vpc["auto_create_subnetworks"] = v.get("autoCreateSubnetworks", None)
318
+ vpc["description"] = v.get("description", None)
319
+ vpc["routing_config_routing_mode"] = v.get("routingConfig", {}).get(
320
+ "routingMode",
321
+ None,
322
+ )
267
323
 
268
324
  vpc_list.append(vpc)
269
325
  return vpc_list
@@ -278,29 +334,29 @@ def transform_gcp_subnets(subnet_res: Dict) -> List[Dict]:
278
334
  """
279
335
  # The `id` in the response object has the form `projects/{project}/regions/{region}/subnetworks`.
280
336
  # We can include this in each subnet object in the list to form the partial_uri later on.
281
- prefix = subnet_res['id']
282
- projectid = prefix.split('/')[1]
337
+ prefix = subnet_res["id"]
338
+ projectid = prefix.split("/")[1]
283
339
  subnet_list: List[Dict] = []
284
- for s in subnet_res.get('items', []):
340
+ for s in subnet_res.get("items", []):
285
341
  subnet = {}
286
342
 
287
343
  # Has the form `projects/{project}/regions/{region}/subnetworks/{subnet_name}`
288
344
  partial_uri = f"{prefix}/{s['name']}"
289
- subnet['id'] = partial_uri
290
- subnet['partial_uri'] = partial_uri
345
+ subnet["id"] = partial_uri
346
+ subnet["partial_uri"] = partial_uri
291
347
 
292
348
  # Let's maintain an on-node reference to the VPC that this subnet belongs to.
293
- subnet['vpc_self_link'] = s['network']
294
- subnet['vpc_partial_uri'] = _parse_compute_full_uri_to_partial_uri(s['network'])
349
+ subnet["vpc_self_link"] = s["network"]
350
+ subnet["vpc_partial_uri"] = _parse_compute_full_uri_to_partial_uri(s["network"])
295
351
 
296
- subnet['name'] = s['name']
297
- subnet['project_id'] = projectid
352
+ subnet["name"] = s["name"]
353
+ subnet["project_id"] = projectid
298
354
  # Region looks like "https://www.googleapis.com/compute/v1/projects/{project}/regions/{region name}"
299
- subnet['region'] = s['region'].split('/')[-1]
300
- subnet['gateway_address'] = s.get('gatewayAddress', None)
301
- subnet['ip_cidr_range'] = s.get('ipCidrRange', None)
302
- subnet['self_link'] = s['selfLink']
303
- subnet['private_ip_google_access'] = s.get('privateIpGoogleAccess', None)
355
+ subnet["region"] = s["region"].split("/")[-1]
356
+ subnet["gateway_address"] = s.get("gatewayAddress", None)
357
+ subnet["ip_cidr_range"] = s.get("ipCidrRange", None)
358
+ subnet["self_link"] = s["selfLink"]
359
+ subnet["private_ip_google_access"] = s.get("privateIpGoogleAccess", None)
304
360
 
305
361
  subnet_list.append(subnet)
306
362
  return subnet_list
@@ -314,43 +370,47 @@ def transform_gcp_forwarding_rules(fwd_response: Resource) -> List[Dict]:
314
370
  :return: A transformed fwd_response
315
371
  """
316
372
  fwd_list: List[Dict] = []
317
- prefix = fwd_response['id']
318
- project_id = prefix.split('/')[1]
319
- for fwd in fwd_response.get('items', []):
373
+ prefix = fwd_response["id"]
374
+ project_id = prefix.split("/")[1]
375
+ for fwd in fwd_response.get("items", []):
320
376
  forwarding_rule: Dict[str, Any] = {}
321
377
 
322
378
  fwd_partial_uri = f"{prefix}/{fwd['name']}"
323
- forwarding_rule['id'] = fwd_partial_uri
324
- forwarding_rule['partial_uri'] = fwd_partial_uri
379
+ forwarding_rule["id"] = fwd_partial_uri
380
+ forwarding_rule["partial_uri"] = fwd_partial_uri
325
381
 
326
- forwarding_rule['project_id'] = project_id
382
+ forwarding_rule["project_id"] = project_id
327
383
  # Region looks like "https://www.googleapis.com/compute/v1/projects/{project}/regions/{region name}"
328
- region = fwd.get('region', None)
329
- forwarding_rule['region'] = region.split('/')[-1] if region else None
330
- forwarding_rule['ip_address'] = fwd.get('IPAddress', None)
331
- forwarding_rule['ip_protocol'] = fwd.get('IPProtocol', None)
332
- forwarding_rule['allow_global_access'] = fwd.get('allowGlobalAccess', None)
333
-
334
- forwarding_rule['load_balancing_scheme'] = fwd.get('loadBalancingScheme', None)
335
- forwarding_rule['name'] = fwd.get('name', None)
336
- forwarding_rule['port_range'] = fwd.get('portRange', None)
337
- forwarding_rule['ports'] = fwd.get('ports', None)
338
- forwarding_rule['self_link'] = fwd.get('selfLink', None)
339
- target = fwd.get('target', None)
384
+ region = fwd.get("region", None)
385
+ forwarding_rule["region"] = region.split("/")[-1] if region else None
386
+ forwarding_rule["ip_address"] = fwd.get("IPAddress", None)
387
+ forwarding_rule["ip_protocol"] = fwd.get("IPProtocol", None)
388
+ forwarding_rule["allow_global_access"] = fwd.get("allowGlobalAccess", None)
389
+
390
+ forwarding_rule["load_balancing_scheme"] = fwd.get("loadBalancingScheme", None)
391
+ forwarding_rule["name"] = fwd.get("name", None)
392
+ forwarding_rule["port_range"] = fwd.get("portRange", None)
393
+ forwarding_rule["ports"] = fwd.get("ports", None)
394
+ forwarding_rule["self_link"] = fwd.get("selfLink", None)
395
+ target = fwd.get("target", None)
340
396
  if target:
341
- forwarding_rule['target'] = _parse_compute_full_uri_to_partial_uri(target)
397
+ forwarding_rule["target"] = _parse_compute_full_uri_to_partial_uri(target)
342
398
  else:
343
- forwarding_rule['target'] = None
399
+ forwarding_rule["target"] = None
344
400
 
345
- network = fwd.get('network', None)
401
+ network = fwd.get("network", None)
346
402
  if network:
347
- forwarding_rule['network'] = network
348
- forwarding_rule['network_partial_uri'] = _parse_compute_full_uri_to_partial_uri(network)
403
+ forwarding_rule["network"] = network
404
+ forwarding_rule["network_partial_uri"] = (
405
+ _parse_compute_full_uri_to_partial_uri(network)
406
+ )
349
407
 
350
- subnetwork = fwd.get('subnetwork', None)
408
+ subnetwork = fwd.get("subnetwork", None)
351
409
  if subnetwork:
352
- forwarding_rule['subnetwork'] = subnetwork
353
- forwarding_rule['subnetwork_partial_uri'] = _parse_compute_full_uri_to_partial_uri(subnetwork)
410
+ forwarding_rule["subnetwork"] = subnetwork
411
+ forwarding_rule["subnetwork_partial_uri"] = (
412
+ _parse_compute_full_uri_to_partial_uri(subnetwork)
413
+ )
354
414
 
355
415
  fwd_list.append(forwarding_rule)
356
416
  return fwd_list
@@ -365,31 +425,45 @@ def transform_gcp_firewall(fw_response: Resource) -> List[Dict]:
365
425
  :return: List of transformed firewall rule objects.
366
426
  """
367
427
  fw_list: List[Dict] = []
368
- prefix = fw_response['id']
369
- for fw in fw_response.get('items', []):
428
+ prefix = fw_response["id"]
429
+ for fw in fw_response.get("items", []):
370
430
  fw_partial_uri = f"{prefix}/{fw['name']}"
371
- fw['id'] = fw_partial_uri
372
- fw['vpc_partial_uri'] = _parse_compute_full_uri_to_partial_uri(fw['network'])
431
+ fw["id"] = fw_partial_uri
432
+ fw["vpc_partial_uri"] = _parse_compute_full_uri_to_partial_uri(fw["network"])
373
433
 
374
- fw['transformed_allow_list'] = []
375
- fw['transformed_deny_list'] = []
434
+ fw["transformed_allow_list"] = []
435
+ fw["transformed_deny_list"] = []
376
436
  # Mark whether this FW is defined on a target service account.
377
437
  # In future we will need to ingest GCP IAM objects but for now we simply mark the presence of svc accounts here.
378
- fw['has_target_service_accounts'] = True if 'targetServiceAccounts' in fw else False
438
+ fw["has_target_service_accounts"] = (
439
+ True if "targetServiceAccounts" in fw else False
440
+ )
379
441
 
380
- for allow_rule in fw.get('allowed', []):
381
- transformed_allow_rules = _transform_fw_entry(allow_rule, fw_partial_uri, is_allow_rule=True)
382
- fw['transformed_allow_list'].extend(transformed_allow_rules)
442
+ for allow_rule in fw.get("allowed", []):
443
+ transformed_allow_rules = _transform_fw_entry(
444
+ allow_rule,
445
+ fw_partial_uri,
446
+ is_allow_rule=True,
447
+ )
448
+ fw["transformed_allow_list"].extend(transformed_allow_rules)
383
449
 
384
- for deny_rule in fw.get('denied', []):
385
- transformed_deny_rules = _transform_fw_entry(deny_rule, fw_partial_uri, is_allow_rule=False)
386
- fw['transformed_deny_list'].extend(transformed_deny_rules)
450
+ for deny_rule in fw.get("denied", []):
451
+ transformed_deny_rules = _transform_fw_entry(
452
+ deny_rule,
453
+ fw_partial_uri,
454
+ is_allow_rule=False,
455
+ )
456
+ fw["transformed_deny_list"].extend(transformed_deny_rules)
387
457
 
388
458
  fw_list.append(fw)
389
459
  return fw_list
390
460
 
391
461
 
392
- def _transform_fw_entry(rule: Dict, fw_partial_uri: str, is_allow_rule: bool) -> List[Dict]:
462
+ def _transform_fw_entry(
463
+ rule: Dict,
464
+ fw_partial_uri: str,
465
+ is_allow_rule: bool,
466
+ ) -> List[Dict]:
393
467
  """
394
468
  Takes a rule entry from a GCP firewall object's allow or deny list and converts it to a list of one or more
395
469
  dicts representing a firewall rule for each port and port range. This format is easier to load into Neo4j.
@@ -415,21 +489,31 @@ def _transform_fw_entry(rule: Dict, fw_partial_uri: str, is_allow_rule: bool) ->
415
489
  """
416
490
  result: List[Dict] = []
417
491
  # rule['ruleid'] = f"{fw_partial_uri}/"
418
- protocol = rule['IPProtocol']
492
+ protocol = rule["IPProtocol"]
419
493
 
420
494
  # If the protocol covered is TCP or UDP then we need to handle ports
421
- if protocol == 'tcp' or protocol == 'udp':
495
+ if protocol == "tcp" or protocol == "udp":
422
496
 
423
497
  # If ports are specified then create rules for each port and range
424
- if 'ports' in rule:
425
- for port in rule['ports']:
426
- rule = _parse_port_string_to_rule(port, protocol, fw_partial_uri, is_allow_rule)
498
+ if "ports" in rule:
499
+ for port in rule["ports"]:
500
+ rule = _parse_port_string_to_rule(
501
+ port,
502
+ protocol,
503
+ fw_partial_uri,
504
+ is_allow_rule,
505
+ )
427
506
  result.append(rule)
428
507
  return result
429
508
 
430
509
  # If ports are not specified then the rule applies to every port
431
510
  else:
432
- rule = _parse_port_string_to_rule('0-65535', protocol, fw_partial_uri, is_allow_rule)
511
+ rule = _parse_port_string_to_rule(
512
+ "0-65535",
513
+ protocol,
514
+ fw_partial_uri,
515
+ is_allow_rule,
516
+ )
433
517
  result.append(rule)
434
518
  return result
435
519
 
@@ -440,7 +524,12 @@ def _transform_fw_entry(rule: Dict, fw_partial_uri: str, is_allow_rule: bool) ->
440
524
  return result
441
525
 
442
526
 
443
- def _parse_port_string_to_rule(port: Optional[str], protocol: str, fw_partial_uri: str, is_allow_rule: bool) -> Dict:
527
+ def _parse_port_string_to_rule(
528
+ port: Optional[str],
529
+ protocol: str,
530
+ fw_partial_uri: str,
531
+ is_allow_rule: bool,
532
+ ) -> Dict:
444
533
  """
445
534
  Takes a string argument representing a GCP firewall rule port or port range and returns a dict that is easier to
446
535
  load into Neo4j.
@@ -467,13 +556,13 @@ def _parse_port_string_to_rule(port: Optional[str], protocol: str, fw_partial_ur
467
556
 
468
557
  if port is None:
469
558
  # Keep the port range as the empty string
470
- port_range_str = ''
559
+ port_range_str = ""
471
560
  fromport = None
472
561
  toport = None
473
562
  else:
474
563
  # Case 1 - port range: '12345-12349'.split('-') => ['12345','12349'].
475
564
  # Case 2 - single port: '22'.split('-') => ['22'].
476
- port_split = port.split('-')
565
+ port_split = port.split("-")
477
566
 
478
567
  # Port range
479
568
  if len(port_split) == 2:
@@ -486,18 +575,22 @@ def _parse_port_string_to_rule(port: Optional[str], protocol: str, fw_partial_ur
486
575
  fromport = int(port_split[0])
487
576
  toport = int(port_split[0])
488
577
 
489
- rule_type = 'allow' if is_allow_rule else 'deny'
578
+ rule_type = "allow" if is_allow_rule else "deny"
490
579
 
491
580
  return {
492
- 'ruleid': f"{fw_partial_uri}/{rule_type}/{port_range_str}{protocol}",
493
- 'fromport': fromport,
494
- 'toport': toport,
495
- 'protocol': protocol,
581
+ "ruleid": f"{fw_partial_uri}/{rule_type}/{port_range_str}{protocol}",
582
+ "fromport": fromport,
583
+ "toport": toport,
584
+ "protocol": protocol,
496
585
  }
497
586
 
498
587
 
499
588
  @timeit
500
- def load_gcp_instances(neo4j_session: neo4j.Session, data: List[Dict], gcp_update_tag: int) -> None:
589
+ def load_gcp_instances(
590
+ neo4j_session: neo4j.Session,
591
+ data: List[Dict],
592
+ gcp_update_tag: int,
593
+ ) -> None:
501
594
  """
502
595
  Ingest GCP instance objects to Neo4j
503
596
  :param neo4j_session: The Neo4j session object
@@ -528,115 +621,71 @@ def load_gcp_instances(neo4j_session: neo4j.Session, data: List[Dict], gcp_updat
528
621
  SET r.lastupdated = $gcp_update_tag
529
622
  """
530
623
  for instance in data:
531
- neo4j_session.run(
624
+ run_write_query(
625
+ neo4j_session,
532
626
  query,
533
- ProjectId=instance['project_id'],
534
- PartialUri=instance['partial_uri'],
535
- SelfLink=instance['selfLink'],
536
- InstanceName=instance['name'],
537
- ZoneName=instance['zone_name'],
538
- Hostname=instance.get('hostname', None),
539
- Status=instance['status'],
627
+ ProjectId=instance["project_id"],
628
+ PartialUri=instance["partial_uri"],
629
+ SelfLink=instance["selfLink"],
630
+ InstanceName=instance["name"],
631
+ ZoneName=instance["zone_name"],
632
+ Hostname=instance.get("hostname", None),
633
+ Status=instance["status"],
540
634
  gcp_update_tag=gcp_update_tag,
541
635
  )
542
636
  _attach_instance_tags(neo4j_session, instance, gcp_update_tag)
543
637
  _attach_gcp_nics(neo4j_session, instance, gcp_update_tag)
544
- _attach_gcp_vpc(neo4j_session, instance['partial_uri'], gcp_update_tag)
638
+ _attach_gcp_vpc(neo4j_session, instance["partial_uri"], gcp_update_tag)
545
639
 
546
640
 
547
641
  @timeit
548
- def load_gcp_vpcs(neo4j_session: neo4j.Session, vpcs: List[Dict], gcp_update_tag: int) -> None:
549
- """
550
- Ingest VPCs to Neo4j
551
- :param neo4j_session: The Neo4j session object
552
- :param vpcs: List of VPCs to ingest
553
- :param gcp_update_tag: The timestamp value to set our new Neo4j nodes with
554
- :return: Nothing
555
- """
556
- query = """
557
- MERGE(p:GCPProject{id:$ProjectId})
558
- ON CREATE SET p.firstseen = timestamp()
559
- SET p.lastupdated = $gcp_update_tag
560
-
561
- MERGE(vpc:GCPVpc{id:$PartialUri})
562
- ON CREATE SET vpc.firstseen = timestamp(),
563
- vpc.partial_uri = $PartialUri
564
- SET vpc.self_link = $SelfLink,
565
- vpc.name = $VpcName,
566
- vpc.project_id = $ProjectId,
567
- vpc.auto_create_subnetworks = $AutoCreateSubnetworks,
568
- vpc.routing_config_routing_mode = $RoutingMode,
569
- vpc.description = $Description,
570
- vpc.lastupdated = $gcp_update_tag
571
-
572
- MERGE (p)-[r:RESOURCE]->(vpc)
573
- ON CREATE SET r.firstseen = timestamp()
574
- SET r.lastupdated = $gcp_update_tag
575
- """
576
- for vpc in vpcs:
577
- neo4j_session.run(
578
- query,
579
- ProjectId=vpc['project_id'],
580
- PartialUri=vpc['partial_uri'],
581
- SelfLink=vpc['self_link'],
582
- VpcName=vpc['name'],
583
- AutoCreateSubnetworks=vpc['auto_create_subnetworks'],
584
- RoutingMode=vpc['routing_config_routing_mode'],
585
- Description=vpc['description'],
586
- gcp_update_tag=gcp_update_tag,
587
- )
642
+ def load_gcp_vpcs(
643
+ neo4j_session: neo4j.Session,
644
+ vpcs: list[dict[str, Any]],
645
+ gcp_update_tag: int,
646
+ project_id: str,
647
+ ) -> None:
648
+ load(
649
+ neo4j_session,
650
+ GCPVpcSchema(),
651
+ vpcs,
652
+ PROJECT_ID=project_id,
653
+ LASTUPDATED=gcp_update_tag,
654
+ )
588
655
 
589
656
 
590
657
  @timeit
591
- def load_gcp_subnets(neo4j_session: neo4j.Session, subnets: List[Dict], gcp_update_tag: int) -> None:
658
+ def load_gcp_subnets(
659
+ neo4j_session: neo4j.Session,
660
+ subnets: List[Dict],
661
+ gcp_update_tag: int,
662
+ project_id: str,
663
+ ) -> None:
592
664
  """
593
- Ingest GCP subnet data to Neo4j
665
+ Ingest GCP subnet data to Neo4j using the data model
594
666
  :param neo4j_session: The Neo4j session
595
667
  :param subnets: List of the subnets
596
668
  :param gcp_update_tag: The timestamp to set these Neo4j nodes with
669
+ :param project_id: The project ID
597
670
  :return: Nothing
598
671
  """
599
- query = """
600
- MERGE(vpc:GCPVpc{id:$VpcPartialUri})
601
- ON CREATE SET vpc.firstseen = timestamp(),
602
- vpc.partial_uri = $VpcPartialUri
672
+ from cartography.models.gcp.compute.subnet import GCPSubnetSchema
603
673
 
604
- MERGE(subnet:GCPSubnet{id:$PartialUri})
605
- ON CREATE SET subnet.firstseen = timestamp(),
606
- subnet.partial_uri = $PartialUri
607
- SET subnet.self_link = $SubnetSelfLink,
608
- subnet.project_id = $ProjectId,
609
- subnet.name = $SubnetName,
610
- subnet.region = $Region,
611
- subnet.gateway_address = $GatewayAddress,
612
- subnet.ip_cidr_range = $IpCidrRange,
613
- subnet.private_ip_google_access = $PrivateIpGoogleAccess,
614
- subnet.vpc_partial_uri = $VpcPartialUri,
615
- subnet.lastupdated = $gcp_update_tag
616
-
617
- MERGE (vpc)-[r:RESOURCE]->(subnet)
618
- ON CREATE SET r.firstseen = timestamp()
619
- SET r.lastupdated = $gcp_update_tag
620
- """
621
- for s in subnets:
622
- neo4j_session.run(
623
- query,
624
- VpcPartialUri=s['vpc_partial_uri'],
625
- VpcSelfLink=s['vpc_self_link'],
626
- PartialUri=s['partial_uri'],
627
- SubnetSelfLink=s['self_link'],
628
- ProjectId=s['project_id'],
629
- SubnetName=s['name'],
630
- Region=s['region'],
631
- GatewayAddress=s['gateway_address'],
632
- IpCidrRange=s['ip_cidr_range'],
633
- PrivateIpGoogleAccess=s['private_ip_google_access'],
634
- gcp_update_tag=gcp_update_tag,
635
- )
674
+ load(
675
+ neo4j_session,
676
+ GCPSubnetSchema(),
677
+ subnets,
678
+ lastupdated=gcp_update_tag,
679
+ PROJECT_ID=project_id,
680
+ )
636
681
 
637
682
 
638
683
  @timeit
639
- def load_gcp_forwarding_rules(neo4j_session: neo4j.Session, fwd_rules: List[Dict], gcp_update_tag: int) -> None:
684
+ def load_gcp_forwarding_rules(
685
+ neo4j_session: neo4j.Session,
686
+ fwd_rules: List[Dict],
687
+ gcp_update_tag: int,
688
+ ) -> None:
640
689
  """
641
690
  Ingest GCP forwarding rules data to Neo4j
642
691
  :param neo4j_session: The Neo4j session
@@ -665,26 +714,27 @@ def load_gcp_forwarding_rules(neo4j_session: neo4j.Session, fwd_rules: List[Dict
665
714
  """
666
715
 
667
716
  for fwd in fwd_rules:
668
- network = fwd.get('network', None)
669
- subnetwork = fwd.get('subnetwork', None)
717
+ network = fwd.get("network", None)
718
+ subnetwork = fwd.get("subnetwork", None)
670
719
 
671
- neo4j_session.run(
720
+ run_write_query(
721
+ neo4j_session,
672
722
  query,
673
- PartialUri=fwd['partial_uri'],
674
- IPAddress=fwd['ip_address'],
675
- IPProtocol=fwd['ip_protocol'],
676
- LoadBalancingScheme=fwd['load_balancing_scheme'],
677
- Name=fwd['name'],
723
+ PartialUri=fwd["partial_uri"],
724
+ IPAddress=fwd["ip_address"],
725
+ IPProtocol=fwd["ip_protocol"],
726
+ LoadBalancingScheme=fwd["load_balancing_scheme"],
727
+ Name=fwd["name"],
678
728
  Network=network,
679
- NetworkPartialUri=fwd.get('network_partial_uri', None),
680
- PortRange=fwd.get('port_range', None),
681
- Ports=fwd.get('ports', None),
682
- ProjectId=fwd['project_id'],
683
- Region=fwd.get('region', None),
684
- SelfLink=fwd['self_link'],
729
+ NetworkPartialUri=fwd.get("network_partial_uri", None),
730
+ PortRange=fwd.get("port_range", None),
731
+ Ports=fwd.get("ports", None),
732
+ ProjectId=fwd["project_id"],
733
+ Region=fwd.get("region", None),
734
+ SelfLink=fwd["self_link"],
685
735
  SubNetwork=subnetwork,
686
- SubNetworkPartialUri=fwd.get('subnetwork_partial_uri', None),
687
- TargetPartialUri=fwd['target'],
736
+ SubNetworkPartialUri=fwd.get("subnetwork_partial_uri", None),
737
+ TargetPartialUri=fwd["target"],
688
738
  gcp_update_tag=gcp_update_tag,
689
739
  )
690
740
 
@@ -695,7 +745,11 @@ def load_gcp_forwarding_rules(neo4j_session: neo4j.Session, fwd_rules: List[Dict
695
745
 
696
746
 
697
747
  @timeit
698
- def _attach_fwd_rule_to_subnet(neo4j_session: neo4j.Session, fwd: Dict, gcp_update_tag: int) -> None:
748
+ def _attach_fwd_rule_to_subnet(
749
+ neo4j_session: neo4j.Session,
750
+ fwd: Dict,
751
+ gcp_update_tag: int,
752
+ ) -> None:
699
753
  query = """
700
754
  MERGE(subnet:GCPSubnet{id:$SubNetworkPartialUri})
701
755
  ON CREATE SET subnet.firstseen = timestamp(),
@@ -710,16 +764,21 @@ def _attach_fwd_rule_to_subnet(neo4j_session: neo4j.Session, fwd: Dict, gcp_upda
710
764
  SET p.lastupdated = $gcp_update_tag
711
765
  """
712
766
 
713
- neo4j_session.run(
767
+ run_write_query(
768
+ neo4j_session,
714
769
  query,
715
- PartialUri=fwd['partial_uri'],
716
- SubNetworkPartialUri=fwd.get('subnetwork_partial_uri', None),
770
+ PartialUri=fwd["partial_uri"],
771
+ SubNetworkPartialUri=fwd.get("subnetwork_partial_uri", None),
717
772
  gcp_update_tag=gcp_update_tag,
718
773
  )
719
774
 
720
775
 
721
776
  @timeit
722
- def _attach_fwd_rule_to_vpc(neo4j_session: neo4j.Session, fwd: Dict, gcp_update_tag: int) -> None:
777
+ def _attach_fwd_rule_to_vpc(
778
+ neo4j_session: neo4j.Session,
779
+ fwd: Dict,
780
+ gcp_update_tag: int,
781
+ ) -> None:
723
782
  query = """
724
783
  MERGE (vpc:GCPVpc{id:$NetworkPartialUri})
725
784
  ON CREATE SET vpc.firstseen = timestamp(),
@@ -733,16 +792,21 @@ def _attach_fwd_rule_to_vpc(neo4j_session: neo4j.Session, fwd: Dict, gcp_update_
733
792
  SET r.lastupdated = $gcp_update_tag
734
793
  """
735
794
 
736
- neo4j_session.run(
795
+ run_write_query(
796
+ neo4j_session,
737
797
  query,
738
- PartialUri=fwd['partial_uri'],
739
- NetworkPartialUri=fwd.get('network_partial_uri', None),
798
+ PartialUri=fwd["partial_uri"],
799
+ NetworkPartialUri=fwd.get("network_partial_uri", None),
740
800
  gcp_update_tag=gcp_update_tag,
741
801
  )
742
802
 
743
803
 
744
804
  @timeit
745
- def _attach_instance_tags(neo4j_session: neo4j.Session, instance: Resource, gcp_update_tag: int) -> None:
805
+ def _attach_instance_tags(
806
+ neo4j_session: neo4j.Session,
807
+ instance: Resource,
808
+ gcp_update_tag: int,
809
+ ) -> None:
746
810
  """
747
811
  Attach tags to GCP instance and to the VPCs that they are defined in.
748
812
  :param neo4j_session: The session
@@ -770,21 +834,26 @@ def _attach_instance_tags(neo4j_session: neo4j.Session, instance: Resource, gcp_
770
834
  ON CREATE SET d.firstseen = timestamp()
771
835
  SET d.lastupdated = $gcp_update_tag
772
836
  """
773
- for tag in instance.get('tags', {}).get('items', []):
774
- for nic in instance.get('networkInterfaces', []):
775
- tag_id = _create_gcp_network_tag_id(nic['vpc_partial_uri'], tag)
776
- neo4j_session.run(
837
+ for tag in instance.get("tags", {}).get("items", []):
838
+ for nic in instance.get("networkInterfaces", []):
839
+ tag_id = _create_gcp_network_tag_id(nic["vpc_partial_uri"], tag)
840
+ run_write_query(
841
+ neo4j_session,
777
842
  query,
778
- InstanceId=instance['partial_uri'],
843
+ InstanceId=instance["partial_uri"],
779
844
  TagId=tag_id,
780
845
  TagValue=tag,
781
- VpcPartialUri=nic['vpc_partial_uri'],
846
+ VpcPartialUri=nic["vpc_partial_uri"],
782
847
  gcp_update_tag=gcp_update_tag,
783
848
  )
784
849
 
785
850
 
786
851
  @timeit
787
- def _attach_gcp_nics(neo4j_session: neo4j.Session, instance: Resource, gcp_update_tag: int) -> None:
852
+ def _attach_gcp_nics(
853
+ neo4j_session: neo4j.Session,
854
+ instance: Resource,
855
+ gcp_update_tag: int,
856
+ ) -> None:
788
857
  """
789
858
  Attach GCP Network Interfaces to GCP Instances and GCP Subnets.
790
859
  Then, attach GCP Instances directly to VPCs.
@@ -815,24 +884,28 @@ def _attach_gcp_nics(neo4j_session: neo4j.Session, instance: Resource, gcp_updat
815
884
  ON CREATE SET p.firstseen = timestamp()
816
885
  SET p.lastupdated = $gcp_update_tag
817
886
  """
818
- for nic in instance.get('networkInterfaces', []):
887
+ for nic in instance.get("networkInterfaces", []):
819
888
  # Make an ID for GCPNetworkInterface nodes because GCP doesn't define one but we need to uniquely identify them
820
889
  nic_id = f"{instance['partial_uri']}/networkinterfaces/{nic['name']}"
821
- neo4j_session.run(
890
+ run_write_query(
891
+ neo4j_session,
822
892
  query,
823
- InstanceId=instance['partial_uri'],
893
+ InstanceId=instance["partial_uri"],
824
894
  NicId=nic_id,
825
- NetworkIP=nic.get('networkIP'),
826
- NicName=nic['name'],
895
+ NetworkIP=nic.get("networkIP"),
896
+ NicName=nic["name"],
827
897
  gcp_update_tag=gcp_update_tag,
828
- SubnetPartialUri=nic['subnet_partial_uri'],
898
+ SubnetPartialUri=nic["subnet_partial_uri"],
829
899
  )
830
900
  _attach_gcp_nic_access_configs(neo4j_session, nic_id, nic, gcp_update_tag)
831
901
 
832
902
 
833
903
  @timeit
834
904
  def _attach_gcp_nic_access_configs(
835
- neo4j_session: neo4j.Session, nic_id: str, nic: Resource, gcp_update_tag: int,
905
+ neo4j_session: neo4j.Session,
906
+ nic_id: str,
907
+ nic: Resource,
908
+ gcp_update_tag: int,
836
909
  ) -> None:
837
910
  """
838
911
  Attach an access configuration to the GCP NIC.
@@ -858,25 +931,30 @@ def _attach_gcp_nic_access_configs(
858
931
  ON CREATE SET r.firstseen = timestamp()
859
932
  SET r.lastupdated = $gcp_update_tag
860
933
  """
861
- for ac in nic.get('accessConfigs', []):
934
+ for ac in nic.get("accessConfigs", []):
862
935
  # Make an ID for GCPNicAccessConfig nodes because GCP doesn't define one but we need to uniquely identify them
863
936
  access_config_id = f"{nic_id}/accessconfigs/{ac['type']}"
864
- neo4j_session.run(
937
+ run_write_query(
938
+ neo4j_session,
865
939
  query,
866
940
  NicId=nic_id,
867
941
  AccessConfigId=access_config_id,
868
- Type=ac['type'],
869
- Name=ac['name'],
870
- NatIP=ac.get('natIP', None),
871
- SetPublicPtr=ac.get('setPublicPtr', None),
872
- PublicPtrDomainName=ac.get('publicPtrDomainName', None),
873
- NetworkTier=ac.get('networkTier', None),
942
+ Type=ac["type"],
943
+ Name=ac["name"],
944
+ NatIP=ac.get("natIP", None),
945
+ SetPublicPtr=ac.get("setPublicPtr", None),
946
+ PublicPtrDomainName=ac.get("publicPtrDomainName", None),
947
+ NetworkTier=ac.get("networkTier", None),
874
948
  gcp_update_tag=gcp_update_tag,
875
949
  )
876
950
 
877
951
 
878
952
  @timeit
879
- def _attach_gcp_vpc(neo4j_session: neo4j.Session, instance_id: str, gcp_update_tag: int) -> None:
953
+ def _attach_gcp_vpc(
954
+ neo4j_session: neo4j.Session,
955
+ instance_id: str,
956
+ gcp_update_tag: int,
957
+ ) -> None:
880
958
  """
881
959
  Attach a GCP instance directly to a VPC
882
960
  :param neo4j_session: neo4j_session
@@ -886,12 +964,13 @@ def _attach_gcp_vpc(neo4j_session: neo4j.Session, instance_id: str, gcp_update_t
886
964
  """
887
965
  query = """
888
966
  MATCH (i:GCPInstance{id:$InstanceId})-[:NETWORK_INTERFACE]->(nic:GCPNetworkInterface)
889
- -[p:PART_OF_SUBNET]->(sn:GCPSubnet)<-[r:RESOURCE]-(vpc:GCPVpc)
967
+ -[p:PART_OF_SUBNET]->(sn:GCPSubnet)<-[r:HAS]-(vpc:GCPVpc)
890
968
  MERGE (i)-[m:MEMBER_OF_GCP_VPC]->(vpc)
891
969
  ON CREATE SET m.firstseen = timestamp()
892
970
  SET m.lastupdated = $gcp_update_tag
893
971
  """
894
- neo4j_session.run(
972
+ run_write_query(
973
+ neo4j_session,
895
974
  query,
896
975
  InstanceId=instance_id,
897
976
  gcp_update_tag=gcp_update_tag,
@@ -899,12 +978,29 @@ def _attach_gcp_vpc(neo4j_session: neo4j.Session, instance_id: str, gcp_update_t
899
978
 
900
979
 
901
980
  @timeit
902
- def load_gcp_ingress_firewalls(neo4j_session: neo4j.Session, fw_list: List[Resource], gcp_update_tag: int) -> None:
981
+ def load_gcp_ingress_firewalls(
982
+ neo4j_session: neo4j.Session,
983
+ fw_list: List[Resource],
984
+ gcp_update_tag: int,
985
+ ) -> None:
903
986
  """
904
- Load the firewall list to Neo4j
987
+ Load the firewall list to Neo4j with retry logic for transient errors.
905
988
  :param fw_list: The transformed list of firewalls
906
989
  :return: Nothing
907
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:
908
1004
  query = """
909
1005
  MERGE (fw:GCPFirewall{id:$FwPartialUri})
910
1006
  ON CREATE SET fw.firstseen = timestamp(),
@@ -927,32 +1023,37 @@ def load_gcp_ingress_firewalls(neo4j_session: neo4j.Session, fw_list: List[Resou
927
1023
  SET r.lastupdated = $gcp_update_tag
928
1024
  """
929
1025
  for fw in fw_list:
930
- neo4j_session.run(
1026
+ tx.run(
931
1027
  query,
932
- FwPartialUri=fw['id'],
933
- Direction=fw['direction'],
934
- Disabled=fw['disabled'],
935
- Name=fw['name'],
936
- Priority=fw['priority'],
937
- SelfLink=fw['selfLink'],
938
- VpcPartialUri=fw['vpc_partial_uri'],
939
- HasTargetServiceAccounts=fw['has_target_service_accounts'],
1028
+ FwPartialUri=fw["id"],
1029
+ Direction=fw["direction"],
1030
+ Disabled=fw["disabled"],
1031
+ Name=fw["name"],
1032
+ Priority=fw["priority"],
1033
+ SelfLink=fw["selfLink"],
1034
+ VpcPartialUri=fw["vpc_partial_uri"],
1035
+ HasTargetServiceAccounts=fw["has_target_service_accounts"],
940
1036
  gcp_update_tag=gcp_update_tag,
941
- )
942
- _attach_firewall_rules(neo4j_session, fw, gcp_update_tag)
943
- _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)
944
1040
 
945
1041
 
946
1042
  @timeit
947
- def _attach_firewall_rules(neo4j_session: neo4j.Session, fw: Resource, gcp_update_tag: int) -> None:
1043
+ def _attach_firewall_rules(
1044
+ tx: neo4j.Transaction,
1045
+ fw: Resource,
1046
+ gcp_update_tag: int,
1047
+ ) -> None:
948
1048
  """
949
1049
  Attach the allow_rules to the Firewall object
950
- :param neo4j_session: The Neo4j session
1050
+ :param tx: The Neo4j transaction
951
1051
  :param fw: The Firewall object
952
1052
  :param gcp_update_tag: The timestamp
953
1053
  :return: Nothing
954
1054
  """
955
- template = Template("""
1055
+ template = Template(
1056
+ """
956
1057
  MATCH (fw:GCPFirewall{id:$FwPartialUri})
957
1058
 
958
1059
  MERGE (rule:IpRule:IpPermissionInbound:GCPIpRule{id:$RuleId})
@@ -975,9 +1076,10 @@ def _attach_firewall_rules(neo4j_session: neo4j.Session, fw: Resource, gcp_updat
975
1076
  MERGE (fw)<-[r:$fw_rule_relationship_label]-(rule)
976
1077
  ON CREATE SET r.firstseen = timestamp()
977
1078
  SET r.lastupdated = $gcp_update_tag
978
- """)
979
- for list_type in 'transformed_allow_list', 'transformed_deny_list':
980
- if list_type == 'transformed_allow_list':
1079
+ """,
1080
+ )
1081
+ for list_type in "transformed_allow_list", "transformed_deny_list":
1082
+ if list_type == "transformed_allow_list":
981
1083
  label = "ALLOWED_BY"
982
1084
  else:
983
1085
  label = "DENIED_BY"
@@ -985,24 +1087,28 @@ def _attach_firewall_rules(neo4j_session: neo4j.Session, fw: Resource, gcp_updat
985
1087
  # It is possible for sourceRanges to not be specified for this rule
986
1088
  # If sourceRanges is not specified then the rule must specify sourceTags.
987
1089
  # Since an IP range cannot have a tag applied to it, it is ok if we don't ingest this rule.
988
- for ip_range in fw.get('sourceRanges', []):
989
- neo4j_session.run(
1090
+ for ip_range in fw.get("sourceRanges", []):
1091
+ tx.run(
990
1092
  template.safe_substitute(fw_rule_relationship_label=label),
991
- FwPartialUri=fw['id'],
992
- RuleId=rule['ruleid'],
993
- Protocol=rule['protocol'],
994
- FromPort=rule.get('fromport'),
995
- ToPort=rule.get('toport'),
1093
+ FwPartialUri=fw["id"],
1094
+ RuleId=rule["ruleid"],
1095
+ Protocol=rule["protocol"],
1096
+ FromPort=rule.get("fromport"),
1097
+ ToPort=rule.get("toport"),
996
1098
  Range=ip_range,
997
1099
  gcp_update_tag=gcp_update_tag,
998
- )
1100
+ ).consume()
999
1101
 
1000
1102
 
1001
1103
  @timeit
1002
- def _attach_target_tags(neo4j_session: neo4j.Session, fw: Resource, gcp_update_tag: int) -> None:
1104
+ def _attach_target_tags(
1105
+ tx: neo4j.Transaction,
1106
+ fw: Resource,
1107
+ gcp_update_tag: int,
1108
+ ) -> None:
1003
1109
  """
1004
1110
  Attach target tags to the firewall object
1005
- :param neo4j_session: The neo4j session
1111
+ :param tx: The neo4j transaction
1006
1112
  :param fw: The firewall object
1007
1113
  :param gcp_update_tag: The timestamp
1008
1114
  :return: Nothing
@@ -1020,26 +1126,33 @@ def _attach_target_tags(neo4j_session: neo4j.Session, fw: Resource, gcp_update_t
1020
1126
  ON CREATE SET h.firstseen = timestamp()
1021
1127
  SET h.lastupdated = $gcp_update_tag
1022
1128
  """
1023
- for tag in fw.get('targetTags', []):
1024
- tag_id = _create_gcp_network_tag_id(fw['vpc_partial_uri'], tag)
1025
- neo4j_session.run(
1129
+ for tag in fw.get("targetTags", []):
1130
+ tag_id = _create_gcp_network_tag_id(fw["vpc_partial_uri"], tag)
1131
+ tx.run(
1026
1132
  query,
1027
- FwPartialUri=fw['id'],
1133
+ FwPartialUri=fw["id"],
1028
1134
  TagId=tag_id,
1029
1135
  TagValue=tag,
1030
1136
  gcp_update_tag=gcp_update_tag,
1031
- )
1137
+ ).consume()
1032
1138
 
1033
1139
 
1034
1140
  @timeit
1035
- def cleanup_gcp_instances(neo4j_session: neo4j.Session, common_job_parameters: Dict) -> None:
1141
+ def cleanup_gcp_instances(
1142
+ neo4j_session: neo4j.Session,
1143
+ common_job_parameters: Dict,
1144
+ ) -> None:
1036
1145
  """
1037
1146
  Delete out-of-date GCP instance nodes and relationships
1038
1147
  :param neo4j_session: The Neo4j session
1039
1148
  :param common_job_parameters: dict of other job parameters to pass to Neo4j
1040
1149
  :return: Nothing
1041
1150
  """
1042
- run_cleanup_job('gcp_compute_instance_cleanup.json', neo4j_session, common_job_parameters)
1151
+ run_cleanup_job(
1152
+ "gcp_compute_instance_cleanup.json",
1153
+ neo4j_session,
1154
+ common_job_parameters,
1155
+ )
1043
1156
 
1044
1157
 
1045
1158
  @timeit
@@ -1050,46 +1163,81 @@ def cleanup_gcp_vpcs(neo4j_session: neo4j.Session, common_job_parameters: Dict)
1050
1163
  :param common_job_parameters: dict of other job parameters to pass to Neo4j
1051
1164
  :return: Nothing
1052
1165
  """
1053
- run_cleanup_job('gcp_compute_vpc_cleanup.json', neo4j_session, common_job_parameters)
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
1172
+ run_cleanup_job(
1173
+ "gcp_compute_vpc_cleanup.json",
1174
+ neo4j_session,
1175
+ common_job_parameters,
1176
+ )
1054
1177
 
1055
1178
 
1056
1179
  @timeit
1057
- def cleanup_gcp_subnets(neo4j_session: neo4j.Session, common_job_parameters: Dict) -> None:
1180
+ def cleanup_gcp_subnets(
1181
+ neo4j_session: neo4j.Session,
1182
+ common_job_parameters: Dict,
1183
+ ) -> None:
1058
1184
  """
1059
- Delete out-of-date GCP VPC subnet nodes and relationships
1185
+ Delete out-of-date GCP VPC subnet nodes and relationships using data model
1060
1186
  :param neo4j_session: The Neo4j session
1061
1187
  :param common_job_parameters: dict of other job parameters to pass to Neo4j
1062
1188
  :return: Nothing
1063
1189
  """
1064
- run_cleanup_job('gcp_compute_vpc_subnet_cleanup.json', neo4j_session, 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
1194
+ )
1065
1195
 
1066
1196
 
1067
1197
  @timeit
1068
- def cleanup_gcp_forwarding_rules(neo4j_session: neo4j.Session, common_job_parameters: Dict) -> None:
1198
+ def cleanup_gcp_forwarding_rules(
1199
+ neo4j_session: neo4j.Session,
1200
+ common_job_parameters: Dict,
1201
+ ) -> None:
1069
1202
  """
1070
1203
  Delete out-of-date GCP forwarding rules and relationships
1071
1204
  :param neo4j_session: The Neo4j session
1072
1205
  :param common_job_parameters: dict of other job parameters to pass to Neo4j
1073
1206
  :return: Nothing
1074
1207
  """
1075
- run_cleanup_job('gcp_compute_forwarding_rules_cleanup.json', neo4j_session, common_job_parameters)
1208
+ run_cleanup_job(
1209
+ "gcp_compute_forwarding_rules_cleanup.json",
1210
+ neo4j_session,
1211
+ common_job_parameters,
1212
+ )
1076
1213
 
1077
1214
 
1078
1215
  @timeit
1079
- def cleanup_gcp_firewall_rules(neo4j_session: neo4j.Session, common_job_parameters: Dict) -> None:
1216
+ def cleanup_gcp_firewall_rules(
1217
+ neo4j_session: neo4j.Session,
1218
+ common_job_parameters: Dict,
1219
+ ) -> None:
1080
1220
  """
1081
1221
  Delete out of date GCP firewalls and their relationships
1082
1222
  :param neo4j_session: The Neo4j session
1083
1223
  :param common_job_parameters: dict of other job parameters to pass to Neo4j
1084
1224
  :return: Nothing
1085
1225
  """
1086
- run_cleanup_job('gcp_compute_firewall_cleanup.json', neo4j_session, common_job_parameters)
1226
+ run_cleanup_job(
1227
+ "gcp_compute_firewall_cleanup.json",
1228
+ neo4j_session,
1229
+ common_job_parameters,
1230
+ )
1087
1231
 
1088
1232
 
1089
1233
  @timeit
1090
1234
  def sync_gcp_instances(
1091
- neo4j_session: neo4j.Session, compute: Resource, project_id: str, zones: Optional[List[Dict]],
1092
- gcp_update_tag: int, common_job_parameters: Dict,
1235
+ neo4j_session: neo4j.Session,
1236
+ compute: Resource,
1237
+ project_id: str,
1238
+ zones: Optional[List[Dict]],
1239
+ gcp_update_tag: int,
1240
+ common_job_parameters: Dict,
1093
1241
  ) -> None:
1094
1242
  """
1095
1243
  Get GCP instances using the Compute resource object, ingest to Neo4j, and clean up old data.
@@ -1106,13 +1254,16 @@ def sync_gcp_instances(
1106
1254
  instance_responses = get_gcp_instance_responses(project_id, zones, compute)
1107
1255
  instance_list = transform_gcp_instances(instance_responses)
1108
1256
  load_gcp_instances(neo4j_session, instance_list, gcp_update_tag)
1109
- # TODO scope the cleanup to the current project - https://github.com/lyft/cartography/issues/381
1257
+ # TODO scope the cleanup to the current project - https://github.com/cartography-cncf/cartography/issues/381
1110
1258
  cleanup_gcp_instances(neo4j_session, common_job_parameters)
1111
1259
 
1112
1260
 
1113
1261
  @timeit
1114
1262
  def sync_gcp_vpcs(
1115
- neo4j_session: neo4j.Session, compute: Resource, project_id: str, gcp_update_tag: int,
1263
+ neo4j_session: neo4j.Session,
1264
+ compute: Resource,
1265
+ project_id: str,
1266
+ gcp_update_tag: int,
1116
1267
  common_job_parameters: Dict,
1117
1268
  ) -> None:
1118
1269
  """
@@ -1126,28 +1277,34 @@ def sync_gcp_vpcs(
1126
1277
  """
1127
1278
  vpc_res = get_gcp_vpcs(project_id, compute)
1128
1279
  vpcs = transform_gcp_vpcs(vpc_res)
1129
- load_gcp_vpcs(neo4j_session, vpcs, gcp_update_tag)
1130
- # TODO scope the cleanup to the current project - https://github.com/lyft/cartography/issues/381
1280
+ load_gcp_vpcs(neo4j_session, vpcs, gcp_update_tag, project_id)
1131
1281
  cleanup_gcp_vpcs(neo4j_session, common_job_parameters)
1132
1282
 
1133
1283
 
1134
1284
  @timeit
1135
1285
  def sync_gcp_subnets(
1136
- neo4j_session: neo4j.Session, compute: Resource, project_id: str, regions: List[str], gcp_update_tag: int,
1286
+ neo4j_session: neo4j.Session,
1287
+ compute: Resource,
1288
+ project_id: str,
1289
+ regions: List[str],
1290
+ gcp_update_tag: int,
1137
1291
  common_job_parameters: Dict,
1138
1292
  ) -> None:
1139
1293
  for r in regions:
1140
1294
  subnet_res = get_gcp_subnets(project_id, r, compute)
1141
1295
  subnets = transform_gcp_subnets(subnet_res)
1142
- load_gcp_subnets(neo4j_session, subnets, gcp_update_tag)
1143
- # TODO scope the cleanup to the current project - https://github.com/lyft/cartography/issues/381
1296
+ load_gcp_subnets(neo4j_session, subnets, gcp_update_tag, project_id)
1297
+ # TODO scope the cleanup to the current project - https://github.com/cartography-cncf/cartography/issues/381
1144
1298
  cleanup_gcp_subnets(neo4j_session, common_job_parameters)
1145
1299
 
1146
1300
 
1147
1301
  @timeit
1148
1302
  def sync_gcp_forwarding_rules(
1149
- neo4j_session: neo4j.Session, compute: Resource, project_id: str, regions: List[str], gcp_update_tag: int,
1150
-
1303
+ neo4j_session: neo4j.Session,
1304
+ compute: Resource,
1305
+ project_id: str,
1306
+ regions: List[str],
1307
+ gcp_update_tag: int,
1151
1308
  common_job_parameters: Dict,
1152
1309
  ) -> None:
1153
1310
  """
@@ -1163,20 +1320,23 @@ def sync_gcp_forwarding_rules(
1163
1320
  global_fwd_response = get_gcp_global_forwarding_rules(project_id, compute)
1164
1321
  forwarding_rules = transform_gcp_forwarding_rules(global_fwd_response)
1165
1322
  load_gcp_forwarding_rules(neo4j_session, forwarding_rules, gcp_update_tag)
1166
- # TODO scope the cleanup to the current project - https://github.com/lyft/cartography/issues/381
1323
+ # TODO scope the cleanup to the current project - https://github.com/cartography-cncf/cartography/issues/381
1167
1324
  cleanup_gcp_forwarding_rules(neo4j_session, common_job_parameters)
1168
1325
 
1169
1326
  for r in regions:
1170
1327
  fwd_response = get_gcp_regional_forwarding_rules(project_id, r, compute)
1171
1328
  forwarding_rules = transform_gcp_forwarding_rules(fwd_response)
1172
1329
  load_gcp_forwarding_rules(neo4j_session, forwarding_rules, gcp_update_tag)
1173
- # TODO scope the cleanup to the current project - https://github.com/lyft/cartography/issues/381
1330
+ # TODO scope the cleanup to the current project - https://github.com/cartography-cncf/cartography/issues/381
1174
1331
  cleanup_gcp_forwarding_rules(neo4j_session, common_job_parameters)
1175
1332
 
1176
1333
 
1177
1334
  @timeit
1178
1335
  def sync_gcp_firewall_rules(
1179
- neo4j_session: neo4j.Session, compute: Resource, project_id: str, gcp_update_tag: int,
1336
+ neo4j_session: neo4j.Session,
1337
+ compute: Resource,
1338
+ project_id: str,
1339
+ gcp_update_tag: int,
1180
1340
  common_job_parameters: Dict,
1181
1341
  ) -> None:
1182
1342
  """
@@ -1190,7 +1350,7 @@ def sync_gcp_firewall_rules(
1190
1350
  fw_response = get_gcp_firewall_ingress_rules(project_id, compute)
1191
1351
  fw_list = transform_gcp_firewall(fw_response)
1192
1352
  load_gcp_ingress_firewalls(neo4j_session, fw_list, gcp_update_tag)
1193
- # TODO scope the cleanup to the current project - https://github.com/lyft/cartography/issues/381
1353
+ # TODO scope the cleanup to the current project - https://github.com/cartography-cncf/cartography/issues/381
1194
1354
  cleanup_gcp_firewall_rules(neo4j_session, common_job_parameters)
1195
1355
 
1196
1356
 
@@ -1203,13 +1363,16 @@ def _zones_to_regions(zones: List[str]) -> List[Set]:
1203
1363
  regions = set()
1204
1364
  for zone in zones:
1205
1365
  # Chop off the last 2 chars to turn the zone to a region
1206
- region = zone['name'][:-2] # type: ignore
1366
+ region = zone["name"][:-2] # type: ignore
1207
1367
  regions.add(region)
1208
- return list(regions) # type: ignore
1368
+ return list(regions) # type: ignore
1209
1369
 
1210
1370
 
1211
1371
  def sync(
1212
- neo4j_session: neo4j.Session, compute: Resource, project_id: str, gcp_update_tag: int,
1372
+ neo4j_session: neo4j.Session,
1373
+ compute: Resource,
1374
+ project_id: str,
1375
+ gcp_update_tag: int,
1213
1376
  common_job_parameters: dict,
1214
1377
  ) -> None:
1215
1378
  """
@@ -1230,8 +1393,41 @@ def sync(
1230
1393
  return
1231
1394
  else:
1232
1395
  regions = _zones_to_regions(zones)
1233
- sync_gcp_vpcs(neo4j_session, compute, project_id, gcp_update_tag, common_job_parameters)
1234
- sync_gcp_firewall_rules(neo4j_session, compute, project_id, gcp_update_tag, common_job_parameters)
1235
- sync_gcp_subnets(neo4j_session, compute, project_id, regions, gcp_update_tag, common_job_parameters)
1236
- sync_gcp_instances(neo4j_session, compute, project_id, zones, gcp_update_tag, common_job_parameters)
1237
- sync_gcp_forwarding_rules(neo4j_session, compute, project_id, regions, gcp_update_tag, common_job_parameters)
1396
+ sync_gcp_vpcs(
1397
+ neo4j_session,
1398
+ compute,
1399
+ project_id,
1400
+ gcp_update_tag,
1401
+ common_job_parameters,
1402
+ )
1403
+ sync_gcp_firewall_rules(
1404
+ neo4j_session,
1405
+ compute,
1406
+ project_id,
1407
+ gcp_update_tag,
1408
+ common_job_parameters,
1409
+ )
1410
+ sync_gcp_subnets(
1411
+ neo4j_session,
1412
+ compute,
1413
+ project_id,
1414
+ regions,
1415
+ gcp_update_tag,
1416
+ common_job_parameters,
1417
+ )
1418
+ sync_gcp_instances(
1419
+ neo4j_session,
1420
+ compute,
1421
+ project_id,
1422
+ zones,
1423
+ gcp_update_tag,
1424
+ common_job_parameters,
1425
+ )
1426
+ sync_gcp_forwarding_rules(
1427
+ neo4j_session,
1428
+ compute,
1429
+ project_id,
1430
+ regions,
1431
+ gcp_update_tag,
1432
+ common_job_parameters,
1433
+ )