cartography 0.102.0rc2__py3-none-any.whl → 0.103.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.

Potentially problematic release.


This version of cartography might be problematic. Click here for more details.

Files changed (297) hide show
  1. cartography/__main__.py +1 -2
  2. cartography/_version.py +2 -2
  3. cartography/cli.py +376 -249
  4. cartography/client/core/tx.py +39 -18
  5. cartography/config.py +28 -0
  6. cartography/driftdetect/__main__.py +1 -2
  7. cartography/driftdetect/add_shortcut.py +10 -2
  8. cartography/driftdetect/cli.py +71 -75
  9. cartography/driftdetect/detect_deviations.py +7 -3
  10. cartography/driftdetect/get_states.py +20 -8
  11. cartography/driftdetect/model.py +5 -5
  12. cartography/driftdetect/serializers.py +8 -6
  13. cartography/driftdetect/storage.py +2 -2
  14. cartography/graph/cleanupbuilder.py +35 -15
  15. cartography/graph/job.py +46 -17
  16. cartography/graph/querybuilder.py +165 -80
  17. cartography/graph/statement.py +35 -26
  18. cartography/intel/analysis.py +4 -1
  19. cartography/intel/aws/__init__.py +114 -55
  20. cartography/intel/aws/apigateway.py +134 -63
  21. cartography/intel/aws/cloudtrail.py +127 -0
  22. cartography/intel/aws/cloudwatch.py +93 -0
  23. cartography/intel/aws/config.py +56 -20
  24. cartography/intel/aws/dynamodb.py +108 -40
  25. cartography/intel/aws/ec2/__init__.py +2 -2
  26. cartography/intel/aws/ec2/auto_scaling_groups.py +181 -78
  27. cartography/intel/aws/ec2/elastic_ip_addresses.py +41 -13
  28. cartography/intel/aws/ec2/images.py +49 -20
  29. cartography/intel/aws/ec2/instances.py +234 -136
  30. cartography/intel/aws/ec2/internet_gateways.py +40 -11
  31. cartography/intel/aws/ec2/key_pairs.py +44 -20
  32. cartography/intel/aws/ec2/launch_templates.py +101 -59
  33. cartography/intel/aws/ec2/load_balancer_v2s.py +104 -39
  34. cartography/intel/aws/ec2/load_balancers.py +82 -42
  35. cartography/intel/aws/ec2/network_acls.py +89 -65
  36. cartography/intel/aws/ec2/network_interfaces.py +146 -87
  37. cartography/intel/aws/ec2/reserved_instances.py +45 -16
  38. cartography/intel/aws/ec2/route_tables.py +138 -98
  39. cartography/intel/aws/ec2/security_groups.py +71 -21
  40. cartography/intel/aws/ec2/snapshots.py +61 -22
  41. cartography/intel/aws/ec2/subnets.py +54 -18
  42. cartography/intel/aws/ec2/tgw.py +100 -34
  43. cartography/intel/aws/ec2/util.py +1 -1
  44. cartography/intel/aws/ec2/volumes.py +69 -41
  45. cartography/intel/aws/ec2/vpc.py +37 -12
  46. cartography/intel/aws/ec2/vpc_peerings.py +83 -24
  47. cartography/intel/aws/ecr.py +88 -32
  48. cartography/intel/aws/ecs.py +83 -47
  49. cartography/intel/aws/efs.py +93 -0
  50. cartography/intel/aws/eks.py +55 -29
  51. cartography/intel/aws/elasticache.py +42 -18
  52. cartography/intel/aws/elasticsearch.py +57 -20
  53. cartography/intel/aws/emr.py +61 -23
  54. cartography/intel/aws/iam.py +401 -145
  55. cartography/intel/aws/iam_instance_profiles.py +22 -22
  56. cartography/intel/aws/identitycenter.py +71 -37
  57. cartography/intel/aws/inspector.py +159 -89
  58. cartography/intel/aws/kms.py +92 -38
  59. cartography/intel/aws/lambda_function.py +103 -34
  60. cartography/intel/aws/organizations.py +30 -10
  61. cartography/intel/aws/permission_relationships.py +133 -51
  62. cartography/intel/aws/rds.py +249 -85
  63. cartography/intel/aws/redshift.py +107 -46
  64. cartography/intel/aws/resourcegroupstaggingapi.py +120 -66
  65. cartography/intel/aws/resources.py +57 -46
  66. cartography/intel/aws/route53.py +108 -61
  67. cartography/intel/aws/s3.py +168 -83
  68. cartography/intel/aws/s3accountpublicaccessblock.py +157 -0
  69. cartography/intel/aws/secretsmanager.py +24 -12
  70. cartography/intel/aws/securityhub.py +20 -9
  71. cartography/intel/aws/sns.py +166 -0
  72. cartography/intel/aws/sqs.py +60 -28
  73. cartography/intel/aws/ssm.py +70 -30
  74. cartography/intel/aws/util/arns.py +7 -7
  75. cartography/intel/aws/util/common.py +31 -4
  76. cartography/intel/azure/__init__.py +78 -19
  77. cartography/intel/azure/compute.py +101 -27
  78. cartography/intel/azure/cosmosdb.py +496 -170
  79. cartography/intel/azure/sql.py +296 -105
  80. cartography/intel/azure/storage.py +322 -113
  81. cartography/intel/azure/subscription.py +39 -23
  82. cartography/intel/azure/tenant.py +13 -4
  83. cartography/intel/azure/util/credentials.py +95 -55
  84. cartography/intel/bigfix/__init__.py +2 -2
  85. cartography/intel/bigfix/computers.py +93 -65
  86. cartography/intel/cloudflare/__init__.py +74 -0
  87. cartography/intel/cloudflare/accounts.py +57 -0
  88. cartography/intel/cloudflare/dnsrecords.py +64 -0
  89. cartography/intel/cloudflare/members.py +75 -0
  90. cartography/intel/cloudflare/roles.py +65 -0
  91. cartography/intel/cloudflare/zones.py +64 -0
  92. cartography/intel/create_indexes.py +3 -2
  93. cartography/intel/crowdstrike/__init__.py +11 -9
  94. cartography/intel/crowdstrike/endpoints.py +5 -1
  95. cartography/intel/crowdstrike/spotlight.py +8 -3
  96. cartography/intel/cve/__init__.py +46 -13
  97. cartography/intel/cve/feed.py +48 -12
  98. cartography/intel/digitalocean/__init__.py +22 -13
  99. cartography/intel/digitalocean/compute.py +75 -108
  100. cartography/intel/digitalocean/management.py +44 -80
  101. cartography/intel/digitalocean/platform.py +48 -43
  102. cartography/intel/dns.py +36 -10
  103. cartography/intel/duo/__init__.py +21 -16
  104. cartography/intel/duo/api_host.py +14 -9
  105. cartography/intel/duo/endpoints.py +50 -45
  106. cartography/intel/duo/groups.py +18 -14
  107. cartography/intel/duo/phones.py +37 -34
  108. cartography/intel/duo/tokens.py +26 -23
  109. cartography/intel/duo/users.py +54 -50
  110. cartography/intel/duo/web_authn_credentials.py +30 -25
  111. cartography/intel/entra/__init__.py +25 -7
  112. cartography/intel/entra/ou.py +112 -0
  113. cartography/intel/entra/users.py +69 -63
  114. cartography/intel/gcp/__init__.py +185 -49
  115. cartography/intel/gcp/compute.py +418 -231
  116. cartography/intel/gcp/crm.py +96 -43
  117. cartography/intel/gcp/dns.py +60 -19
  118. cartography/intel/gcp/gke.py +72 -38
  119. cartography/intel/gcp/iam.py +61 -41
  120. cartography/intel/gcp/storage.py +84 -55
  121. cartography/intel/github/__init__.py +13 -11
  122. cartography/intel/github/repos.py +270 -137
  123. cartography/intel/github/teams.py +170 -88
  124. cartography/intel/github/users.py +70 -39
  125. cartography/intel/github/util.py +36 -34
  126. cartography/intel/gsuite/__init__.py +47 -26
  127. cartography/intel/gsuite/api.py +73 -30
  128. cartography/intel/jamf/__init__.py +19 -1
  129. cartography/intel/jamf/computers.py +30 -7
  130. cartography/intel/jamf/util.py +7 -2
  131. cartography/intel/kandji/__init__.py +6 -3
  132. cartography/intel/kandji/devices.py +14 -8
  133. cartography/intel/kubernetes/namespaces.py +7 -4
  134. cartography/intel/kubernetes/pods.py +7 -4
  135. cartography/intel/kubernetes/services.py +8 -4
  136. cartography/intel/lastpass/__init__.py +2 -2
  137. cartography/intel/lastpass/users.py +23 -12
  138. cartography/intel/oci/__init__.py +44 -11
  139. cartography/intel/oci/iam.py +134 -38
  140. cartography/intel/oci/organizations.py +13 -6
  141. cartography/intel/oci/utils.py +43 -20
  142. cartography/intel/okta/__init__.py +66 -15
  143. cartography/intel/okta/applications.py +42 -20
  144. cartography/intel/okta/awssaml.py +93 -33
  145. cartography/intel/okta/factors.py +16 -4
  146. cartography/intel/okta/groups.py +56 -29
  147. cartography/intel/okta/organization.py +5 -1
  148. cartography/intel/okta/origins.py +6 -2
  149. cartography/intel/okta/roles.py +15 -5
  150. cartography/intel/okta/users.py +20 -8
  151. cartography/intel/okta/utils.py +6 -4
  152. cartography/intel/openai/__init__.py +86 -0
  153. cartography/intel/openai/adminapikeys.py +90 -0
  154. cartography/intel/openai/apikeys.py +96 -0
  155. cartography/intel/openai/projects.py +94 -0
  156. cartography/intel/openai/serviceaccounts.py +82 -0
  157. cartography/intel/openai/users.py +78 -0
  158. cartography/intel/openai/util.py +29 -0
  159. cartography/intel/pagerduty/__init__.py +8 -7
  160. cartography/intel/pagerduty/escalation_policies.py +18 -6
  161. cartography/intel/pagerduty/schedules.py +12 -4
  162. cartography/intel/pagerduty/services.py +11 -4
  163. cartography/intel/pagerduty/teams.py +8 -3
  164. cartography/intel/pagerduty/users.py +3 -1
  165. cartography/intel/pagerduty/vendors.py +3 -1
  166. cartography/intel/semgrep/__init__.py +24 -6
  167. cartography/intel/semgrep/dependencies.py +50 -28
  168. cartography/intel/semgrep/deployment.py +3 -1
  169. cartography/intel/semgrep/findings.py +42 -18
  170. cartography/intel/snipeit/__init__.py +17 -3
  171. cartography/intel/snipeit/asset.py +12 -6
  172. cartography/intel/snipeit/user.py +8 -5
  173. cartography/intel/snipeit/util.py +9 -4
  174. cartography/intel/tailscale/__init__.py +77 -0
  175. cartography/intel/tailscale/acls.py +146 -0
  176. cartography/intel/tailscale/devices.py +127 -0
  177. cartography/intel/tailscale/postureintegrations.py +81 -0
  178. cartography/intel/tailscale/tailnets.py +76 -0
  179. cartography/intel/tailscale/users.py +80 -0
  180. cartography/intel/tailscale/utils.py +132 -0
  181. cartography/models/aws/apigateway.py +21 -17
  182. cartography/models/aws/apigatewaycertificate.py +28 -22
  183. cartography/models/aws/apigatewayresource.py +28 -20
  184. cartography/models/aws/apigatewaystage.py +33 -25
  185. cartography/models/aws/cloudtrail/__init__.py +0 -0
  186. cartography/models/aws/cloudtrail/trail.py +61 -0
  187. cartography/models/aws/cloudwatch/__init__.py +0 -0
  188. cartography/models/aws/cloudwatch/loggroup.py +52 -0
  189. cartography/models/aws/dynamodb/gsi.py +30 -22
  190. cartography/models/aws/dynamodb/tables.py +25 -17
  191. cartography/models/aws/ec2/auto_scaling_groups.py +102 -82
  192. cartography/models/aws/ec2/images.py +36 -34
  193. cartography/models/aws/ec2/instances.py +51 -45
  194. cartography/models/aws/ec2/keypair.py +21 -16
  195. cartography/models/aws/ec2/keypair_instance.py +28 -21
  196. cartography/models/aws/ec2/launch_configurations.py +30 -26
  197. cartography/models/aws/ec2/launch_template_versions.py +48 -38
  198. cartography/models/aws/ec2/launch_templates.py +21 -17
  199. cartography/models/aws/ec2/load_balancer_listeners.py +27 -23
  200. cartography/models/aws/ec2/load_balancers.py +47 -37
  201. cartography/models/aws/ec2/network_acl_rules.py +38 -30
  202. cartography/models/aws/ec2/network_acls.py +38 -29
  203. cartography/models/aws/ec2/networkinterface_instance.py +52 -39
  204. cartography/models/aws/ec2/networkinterfaces.py +53 -37
  205. cartography/models/aws/ec2/privateip_networkinterface.py +32 -22
  206. cartography/models/aws/ec2/reservations.py +18 -14
  207. cartography/models/aws/ec2/route_table_associations.py +44 -34
  208. cartography/models/aws/ec2/route_tables.py +50 -43
  209. cartography/models/aws/ec2/routes.py +45 -37
  210. cartography/models/aws/ec2/securitygroup_instance.py +29 -20
  211. cartography/models/aws/ec2/securitygroup_networkinterface.py +24 -15
  212. cartography/models/aws/ec2/subnet_instance.py +24 -19
  213. cartography/models/aws/ec2/subnet_networkinterface.py +40 -31
  214. cartography/models/aws/ec2/volumes.py +47 -40
  215. cartography/models/aws/efs/__init__.py +0 -0
  216. cartography/models/aws/efs/mount_target.py +52 -0
  217. cartography/models/aws/eks/clusters.py +23 -21
  218. cartography/models/aws/emr.py +32 -30
  219. cartography/models/aws/iam/instanceprofile.py +33 -24
  220. cartography/models/aws/identitycenter/awsidentitycenter.py +18 -14
  221. cartography/models/aws/identitycenter/awspermissionset.py +37 -29
  222. cartography/models/aws/identitycenter/awsssouser.py +23 -21
  223. cartography/models/aws/inspector/findings.py +77 -65
  224. cartography/models/aws/inspector/packages.py +35 -29
  225. cartography/models/aws/s3/__init__.py +0 -0
  226. cartography/models/aws/s3/account_public_access_block.py +51 -0
  227. cartography/models/aws/sns/__init__.py +0 -0
  228. cartography/models/aws/sns/topic.py +50 -0
  229. cartography/models/aws/ssm/instance_information.py +51 -39
  230. cartography/models/aws/ssm/instance_patch.py +32 -26
  231. cartography/models/bigfix/bigfix_computer.py +42 -38
  232. cartography/models/bigfix/bigfix_root.py +3 -3
  233. cartography/models/cloudflare/__init__.py +0 -0
  234. cartography/models/cloudflare/account.py +25 -0
  235. cartography/models/cloudflare/dnsrecord.py +55 -0
  236. cartography/models/cloudflare/member.py +82 -0
  237. cartography/models/cloudflare/role.py +44 -0
  238. cartography/models/cloudflare/zone.py +59 -0
  239. cartography/models/core/common.py +12 -10
  240. cartography/models/core/nodes.py +5 -2
  241. cartography/models/core/relationships.py +14 -6
  242. cartography/models/crowdstrike/hosts.py +37 -35
  243. cartography/models/cve/cve.py +34 -32
  244. cartography/models/cve/cve_feed.py +6 -6
  245. cartography/models/digitalocean/__init__.py +0 -0
  246. cartography/models/digitalocean/account.py +21 -0
  247. cartography/models/digitalocean/droplet.py +56 -0
  248. cartography/models/digitalocean/project.py +48 -0
  249. cartography/models/duo/api_host.py +3 -3
  250. cartography/models/duo/endpoint.py +43 -41
  251. cartography/models/duo/group.py +14 -14
  252. cartography/models/duo/phone.py +27 -27
  253. cartography/models/duo/token.py +16 -16
  254. cartography/models/duo/user.py +46 -44
  255. cartography/models/duo/web_authn_credential.py +27 -19
  256. cartography/models/entra/ou.py +48 -0
  257. cartography/models/entra/tenant.py +24 -18
  258. cartography/models/entra/user.py +64 -48
  259. cartography/models/gcp/iam.py +23 -23
  260. cartography/models/github/orgs.py +5 -4
  261. cartography/models/github/teams.py +37 -31
  262. cartography/models/github/users.py +34 -23
  263. cartography/models/kandji/device.py +22 -16
  264. cartography/models/kandji/tenant.py +6 -4
  265. cartography/models/lastpass/tenant.py +3 -3
  266. cartography/models/lastpass/user.py +32 -28
  267. cartography/models/openai/__init__.py +0 -0
  268. cartography/models/openai/adminapikey.py +90 -0
  269. cartography/models/openai/apikey.py +84 -0
  270. cartography/models/openai/organization.py +17 -0
  271. cartography/models/openai/project.py +70 -0
  272. cartography/models/openai/serviceaccount.py +50 -0
  273. cartography/models/openai/user.py +49 -0
  274. cartography/models/semgrep/dependencies.py +36 -24
  275. cartography/models/semgrep/deployment.py +5 -5
  276. cartography/models/semgrep/findings.py +58 -42
  277. cartography/models/semgrep/locations.py +27 -21
  278. cartography/models/snipeit/asset.py +30 -21
  279. cartography/models/snipeit/tenant.py +6 -4
  280. cartography/models/snipeit/user.py +19 -12
  281. cartography/models/tailscale/__init__.py +0 -0
  282. cartography/models/tailscale/device.py +95 -0
  283. cartography/models/tailscale/group.py +86 -0
  284. cartography/models/tailscale/postureintegration.py +58 -0
  285. cartography/models/tailscale/tag.py +102 -0
  286. cartography/models/tailscale/tailnet.py +29 -0
  287. cartography/models/tailscale/user.py +52 -0
  288. cartography/stats.py +3 -3
  289. cartography/sync.py +113 -31
  290. cartography/util.py +84 -62
  291. {cartography-0.102.0rc2.dist-info → cartography-0.103.0.dist-info}/METADATA +8 -15
  292. cartography-0.103.0.dist-info/RECORD +442 -0
  293. {cartography-0.102.0rc2.dist-info → cartography-0.103.0.dist-info}/WHEEL +1 -1
  294. cartography-0.102.0rc2.dist-info/RECORD +0 -381
  295. {cartography-0.102.0rc2.dist-info → cartography-0.103.0.dist-info}/entry_points.txt +0 -0
  296. {cartography-0.102.0rc2.dist-info → cartography-0.103.0.dist-info}/licenses/LICENSE +0 -0
  297. {cartography-0.102.0rc2.dist-info → cartography-0.103.0.dist-info}/top_level.txt +0 -0
@@ -51,7 +51,10 @@ def get_last_modified_cve_date(neo4j_session: neo4j.Session) -> str:
51
51
  ORDER BY last_modified DESC
52
52
  LIMIT 1
53
53
  """
54
- result = cast(neo4j.time.DateTime, read_single_value_tx(neo4j_session, query)).to_native()
54
+ result = cast(
55
+ neo4j.time.DateTime,
56
+ read_single_value_tx(neo4j_session, query),
57
+ ).to_native()
55
58
  return result.strftime("%Y-%m-%dT%H:%M:%S")
56
59
 
57
60
 
@@ -61,13 +64,19 @@ def _map_cve_dict(cve_dict: Dict[Any, Any], data: Dict[Any, Any]) -> None:
61
64
  cve_dict["timestamp"] = data["timestamp"]
62
65
  cve_dict["totalResults"] = data["totalResults"]
63
66
  cve_dict["vulnerabilities"] = cve_dict.get("vulnerabilities", []) + data.get(
64
- "vulnerabilities", [],
67
+ "vulnerabilities",
68
+ [],
65
69
  )
66
70
  cve_dict["resultsPerPage"] = data["resultsPerPage"]
67
71
  cve_dict["startIndex"] = data["startIndex"]
68
72
 
69
73
 
70
- def _call_cves_api(http_session: Session, url: str, api_key: str | None, params: Dict[str, Any]) -> Dict[Any, Any]:
74
+ def _call_cves_api(
75
+ http_session: Session,
76
+ url: str,
77
+ api_key: str | None,
78
+ params: Dict[str, Any],
79
+ ) -> Dict[Any, Any]:
71
80
  total_results = 0
72
81
  params["startIndex"] = 0
73
82
  params["resultsPerPage"] = RESULTS_PER_PAGE
@@ -84,7 +93,12 @@ def _call_cves_api(http_session: Session, url: str, api_key: str | None, params:
84
93
 
85
94
  while params["resultsPerPage"] > 0 or params["startIndex"] < total_results:
86
95
  logger.info(f"Calling NIST NVD API at {url} with params {params}")
87
- res = http_session.get(url, params=params, headers=headers, timeout=CONNECT_AND_READ_TIMEOUT)
96
+ res = http_session.get(
97
+ url,
98
+ params=params,
99
+ headers=headers,
100
+ timeout=CONNECT_AND_READ_TIMEOUT,
101
+ )
88
102
  res.raise_for_status()
89
103
  data = res.json()
90
104
  _map_cve_dict(results, data)
@@ -140,7 +154,10 @@ def get_cves_in_batches(
140
154
 
141
155
 
142
156
  def get_modified_cves(
143
- http_session: Session, nist_cve_url: str, last_modified_date: str, api_key: str | None,
157
+ http_session: Session,
158
+ nist_cve_url: str,
159
+ last_modified_date: str,
160
+ api_key: str | None,
144
161
  ) -> Dict[Any, Any]:
145
162
  end_date = datetime.now(tz=timezone.utc)
146
163
  start_date = datetime.strptime(last_modified_date, "%Y-%m-%dT%H:%M:%S").replace(
@@ -151,13 +168,21 @@ def get_modified_cves(
151
168
  "end": "lastModEndDate",
152
169
  }
153
170
  cves = get_cves_in_batches(
154
- http_session, nist_cve_url, start_date, end_date, date_param_names, api_key,
171
+ http_session,
172
+ nist_cve_url,
173
+ start_date,
174
+ end_date,
175
+ date_param_names,
176
+ api_key,
155
177
  )
156
178
  return cves
157
179
 
158
180
 
159
181
  def get_published_cves_per_year(
160
- http_session: Session, nist_cve_url: str, year: str, api_key: str | None,
182
+ http_session: Session,
183
+ nist_cve_url: str,
184
+ year: str,
185
+ api_key: str | None,
161
186
  ) -> Dict[Any, Any]:
162
187
  start_of_year = datetime.strptime(f"{year}-01-01", "%Y-%m-%d")
163
188
  next_year = int(year) + 1
@@ -167,7 +192,12 @@ def get_published_cves_per_year(
167
192
  "end": "pubEndDate",
168
193
  }
169
194
  cves = get_cves_in_batches(
170
- http_session, nist_cve_url, start_of_year, end_of_next_year, date_param_names, api_key,
195
+ http_session,
196
+ nist_cve_url,
197
+ start_of_year,
198
+ end_of_next_year,
199
+ date_param_names,
200
+ api_key,
171
201
  )
172
202
  return cves
173
203
 
@@ -198,9 +228,13 @@ def transform_cves(cve_json: Dict[Any, Any]) -> List[Dict[Any, Any]]:
198
228
  ]
199
229
  cve["references_urls"] = [url["url"] for url in cve["references"]]
200
230
  if cve.get("weaknesses"):
201
- weakness_descriptions = [weakness["description"] for weakness in cve["weaknesses"]]
231
+ weakness_descriptions = [
232
+ weakness["description"] for weakness in cve["weaknesses"]
233
+ ]
202
234
  weakness_descriptions = reduce(
203
- lambda x, y: x + y, weakness_descriptions, [],
235
+ lambda x, y: x + y,
236
+ weakness_descriptions,
237
+ [],
204
238
  )
205
239
  cve["weaknesses"] = [
206
240
  description["value"]
@@ -226,7 +260,7 @@ def transform_cves(cve_json: Dict[Any, Any]) -> List[Dict[Any, Any]]:
226
260
  cve["exploitabilityScore"] = cvss31.get("exploitabilityScore")
227
261
  cve["impactScore"] = cvss31.get("impactScore")
228
262
  except Exception:
229
- logger.error("Failed to transform CVE data {data}")
263
+ logger.error(f"Failed to transform CVE data {data}")
230
264
  raise
231
265
  cves.append(cve)
232
266
  return cves
@@ -265,7 +299,9 @@ def load_cves(
265
299
 
266
300
 
267
301
  def load_cve_feed(
268
- neo4j_session: neo4j.Session, data: List[Dict[str, Any]], update_tag: int,
302
+ neo4j_session: neo4j.Session,
303
+ data: List[Dict[str, Any]],
304
+ update_tag: int,
269
305
  ) -> None:
270
306
  """
271
307
  Load CVE feed information
@@ -9,7 +9,6 @@ from cartography.intel.digitalocean import management
9
9
  from cartography.intel.digitalocean import platform
10
10
  from cartography.util import timeit
11
11
 
12
-
13
12
  logger = logging.getLogger(__name__)
14
13
 
15
14
 
@@ -23,7 +22,9 @@ def start_digitalocean_ingestion(neo4j_session: neo4j.Session, config: Config) -
23
22
  """
24
23
 
25
24
  if not config.digitalocean_token:
26
- logger.info('DigitalOcean import is not configured - skipping this module. See docs to configure.')
25
+ logger.info(
26
+ "DigitalOcean import is not configured - skipping this module. See docs to configure.",
27
+ )
27
28
  return
28
29
 
29
30
  common_job_parameters = {
@@ -31,14 +32,22 @@ def start_digitalocean_ingestion(neo4j_session: neo4j.Session, config: Config) -
31
32
  }
32
33
  manager = Manager(token=config.digitalocean_token)
33
34
 
34
- """
35
- Get Account ID related to this credentials and pass it along in `common_job_parameters` to avoid cleaning up other
36
- accounts resources
37
- """
38
- account = manager.get_account()
39
- common_job_parameters["DO_ACCOUNT_ID"] = account.uuid
40
-
41
- platform.sync(neo4j_session, account, config.update_tag, common_job_parameters)
42
- project_resources = management.sync(neo4j_session, manager, config.update_tag, common_job_parameters)
43
- compute.sync(neo4j_session, manager, project_resources, config.update_tag, common_job_parameters)
44
- return
35
+ account_id = platform.sync(
36
+ neo4j_session, manager, config.update_tag, common_job_parameters
37
+ )
38
+ common_job_parameters["ACCOUNT_ID"] = str(account_id)
39
+ projects_resources = management.sync(
40
+ neo4j_session,
41
+ manager,
42
+ account_id,
43
+ config.update_tag,
44
+ common_job_parameters,
45
+ )
46
+ compute.sync(
47
+ neo4j_session,
48
+ manager,
49
+ account_id,
50
+ projects_resources,
51
+ config.update_tag,
52
+ common_job_parameters,
53
+ )
@@ -1,10 +1,15 @@
1
1
  import logging
2
+ from typing import Any
3
+ from typing import Dict
4
+ from typing import List
2
5
  from typing import Optional
3
6
 
4
7
  import neo4j
5
8
  from digitalocean import Manager
6
9
 
7
- from cartography.util import run_cleanup_job
10
+ from cartography.client.core.tx import load
11
+ from cartography.graph.job import GraphJob
12
+ from cartography.models.digitalocean.droplet import DODropletSchema
8
13
  from cartography.util import timeit
9
14
 
10
15
  logger = logging.getLogger(__name__)
@@ -12,29 +17,20 @@ logger = logging.getLogger(__name__)
12
17
 
13
18
  @timeit
14
19
  def sync(
15
- neo4j_session: neo4j.Session,
16
- manager: Manager,
17
- projects_resources: dict,
18
- digitalocean_update_tag: int,
19
- common_job_parameters: dict,
20
- ) -> None:
21
- sync_droplets(neo4j_session, manager, projects_resources, digitalocean_update_tag, common_job_parameters)
22
-
23
-
24
- @timeit
25
- def sync_droplets(
26
- neo4j_session: neo4j.Session,
27
- manager: Manager,
28
- projects_resources: dict,
29
- digitalocean_update_tag: int,
30
- common_job_parameters: dict,
20
+ neo4j_session: neo4j.Session,
21
+ manager: Manager,
22
+ account_id: str,
23
+ projects_resources: dict,
24
+ update_tag: int,
25
+ common_job_parameters: dict,
31
26
  ) -> None:
32
27
  logger.info("Syncing Droplets")
33
- account_id = common_job_parameters['DO_ACCOUNT_ID']
34
28
  droplets_res = get_droplets(manager)
35
- droplets = transform_droplets(droplets_res, account_id, projects_resources)
36
- load_droplets(neo4j_session, droplets, digitalocean_update_tag)
37
- cleanup_droplets(neo4j_session, common_job_parameters)
29
+ droplets_by_project = transform_droplets(
30
+ droplets_res, account_id, projects_resources
31
+ )
32
+ load_droplets(neo4j_session, account_id, droplets_by_project, update_tag)
33
+ cleanup(neo4j_session, list(droplets_by_project.keys()), common_job_parameters)
38
34
 
39
35
 
40
36
  @timeit
@@ -43,35 +39,45 @@ def get_droplets(manager: Manager) -> list:
43
39
 
44
40
 
45
41
  @timeit
46
- def transform_droplets(droplets_res: list, account_id: str, projects_resources: dict) -> list:
47
- droplets = list()
42
+ def transform_droplets(
43
+ droplets_res: list,
44
+ account_id: str,
45
+ projects_resources: dict,
46
+ ) -> Dict[str, List[Dict[str, Any]]]:
47
+ droplets_by_project: Dict[str, List[Dict[str, Any]]] = {}
48
48
  for d in droplets_res:
49
+ project_id = str(_get_project_id_for_droplet(d.id, projects_resources))
50
+ if project_id not in droplets_by_project:
51
+ droplets_by_project[project_id] = []
49
52
  droplet = {
50
- 'id': d.id,
51
- 'name': d.name,
52
- 'locked': d.locked,
53
- 'status': d.status,
54
- 'features': d.features,
55
- 'region': d.region['slug'],
56
- 'created_at': d.created_at,
57
- 'image': d.image['slug'],
58
- 'size': d.size_slug,
59
- 'kernel': d.kernel,
60
- 'tags': d.tags,
61
- 'volumes': d.volume_ids,
62
- 'vpc_uuid': d.vpc_uuid,
63
- 'ip_address': d.ip_address,
64
- 'private_ip_address': d.private_ip_address,
65
- 'ip_v6_address': d.ip_v6_address,
66
- 'account_id': account_id,
67
- 'project_id': _get_project_id_for_droplet(d.id, projects_resources),
53
+ "id": d.id,
54
+ "name": d.name,
55
+ "locked": d.locked,
56
+ "status": d.status,
57
+ "features": d.features,
58
+ "region": d.region["slug"],
59
+ "created_at": d.created_at,
60
+ "image": d.image["slug"],
61
+ "size": d.size_slug,
62
+ "kernel": d.kernel,
63
+ "tags": d.tags,
64
+ "volumes": d.volume_ids,
65
+ "vpc_uuid": d.vpc_uuid,
66
+ "ip_address": d.ip_address,
67
+ "private_ip_address": d.private_ip_address,
68
+ "ip_v6_address": d.ip_v6_address,
69
+ "account_id": account_id,
70
+ "project_id": _get_project_id_for_droplet(d.id, projects_resources),
68
71
  }
69
- droplets.append(droplet)
70
- return droplets
72
+ droplets_by_project[project_id].append(droplet)
73
+ return droplets_by_project
71
74
 
72
75
 
73
76
  @timeit
74
- def _get_project_id_for_droplet(droplet_id: int, project_resources: dict) -> Optional[str]:
77
+ def _get_project_id_for_droplet(
78
+ droplet_id: int,
79
+ project_resources: dict,
80
+ ) -> Optional[str]:
75
81
  for project_id, resource_list in project_resources.items():
76
82
  droplet_resource_name = "do:droplet:" + str(droplet_id)
77
83
  if droplet_resource_name in resource_list:
@@ -80,71 +86,32 @@ def _get_project_id_for_droplet(droplet_id: int, project_resources: dict) -> Opt
80
86
 
81
87
 
82
88
  @timeit
83
- def load_droplets(neo4j_session: neo4j.Session, data: list, digitalocean_update_tag: int) -> None:
84
- query = """
85
- MERGE (p:DOProject{id:$ProjectId})
86
- ON CREATE SET p.firstseen = timestamp()
87
- SET p.lastupdated = $digitalocean_update_tag
88
-
89
- MERGE (d:DODroplet{id:$DropletId})
90
- ON CREATE SET d.firstseen = timestamp()
91
- SET d.account_id = $AccountId,
92
- d.name = $Name,
93
- d.locked = $Locked,
94
- d.status = $Status,
95
- d.features = $Features,
96
- d.region = $RegionSlug,
97
- d.created_at = $CreatedAt,
98
- d.image = $ImageSlug,
99
- d.size = $SizeSlug,
100
- d.kernel = $Kernel,
101
- d.ip_address = $IpAddress,
102
- d.private_ip_address = $PrivateIpAddress,
103
- d.project_id = $ProjectId,
104
- d.ip_v6_address = $IpV6Address,
105
- d.tags = $Tags,
106
- d.volumes = $Volumes,
107
- d.vpc_uuid = $VpcUuid,
108
- d.lastupdated = $digitalocean_update_tag
109
- WITH d, p
110
-
111
- MERGE (p)-[r:RESOURCE]->(d)
112
- ON CREATE SET r.firstseen = timestamp()
113
- SET r.lastupdated = $digitalocean_update_tag
114
- """
115
- for droplet in data:
116
- neo4j_session.run(
117
- query,
118
- AccountId=droplet['account_id'],
119
- DropletId=droplet['id'],
120
- Name=droplet['name'],
121
- Locked=droplet['locked'],
122
- Status=droplet['status'],
123
- Features=droplet['features'],
124
- RegionSlug=droplet['region'],
125
- CreatedAt=droplet['created_at'],
126
- ImageSlug=droplet['image'],
127
- SizeSlug=droplet['size'],
128
- IpAddress=droplet['ip_address'],
129
- PrivateIpAddress=droplet['private_ip_address'],
130
- ProjectId=droplet['project_id'],
131
- IpV6Address=droplet['ip_v6_address'],
132
- Kernel=droplet['kernel'],
133
- Tags=droplet['tags'],
134
- Volumes=droplet['volumes'],
135
- VpcUuid=droplet['vpc_uuid'],
136
- digitalocean_update_tag=digitalocean_update_tag,
89
+ def load_droplets(
90
+ neo4j_session: neo4j.Session,
91
+ account_id: str,
92
+ data: Dict[str, List[Dict[str, Any]]],
93
+ update_tag: int,
94
+ ) -> None:
95
+ for project_id, droplets in data.items():
96
+ load(
97
+ neo4j_session,
98
+ DODropletSchema(),
99
+ droplets,
100
+ lastupdated=update_tag,
101
+ PROJECT_ID=str(project_id),
102
+ ACCOUNT_ID=str(account_id),
137
103
  )
138
- return
139
104
 
140
105
 
141
106
  @timeit
142
- def cleanup_droplets(neo4j_session: neo4j.Session, common_job_parameters: dict) -> None:
143
- """
144
- Delete out-of-date DigitalOcean droplets and relationships
145
- :param neo4j_session: The Neo4j session
146
- :param common_job_parameters: dict of other job parameters to pass to Neo4j
147
- :return: Nothing
148
- """
149
- run_cleanup_job('digitalocean_droplet_cleanup.json', neo4j_session, common_job_parameters)
150
- return
107
+ def cleanup(
108
+ neo4j_session: neo4j.Session,
109
+ projects_ids: List[str],
110
+ common_job_parameters: Dict[str, Any],
111
+ ) -> None:
112
+ for project_id in projects_ids:
113
+ parameters = common_job_parameters.copy()
114
+ parameters["PROJECT_ID"] = str(project_id)
115
+ GraphJob.from_node_schema(DODropletSchema(), parameters).run(
116
+ neo4j_session,
117
+ )
@@ -1,9 +1,14 @@
1
1
  import logging
2
+ from typing import Any
3
+ from typing import Dict
4
+ from typing import List
2
5
 
3
6
  import neo4j
4
7
  from digitalocean import Manager
5
8
 
6
- from cartography.util import run_cleanup_job
9
+ from cartography.client.core.tx import load
10
+ from cartography.graph.job import GraphJob
11
+ from cartography.models.digitalocean.project import DOProjectSchema
7
12
  from cartography.util import timeit
8
13
 
9
14
  logger = logging.getLogger(__name__)
@@ -11,31 +16,19 @@ logger = logging.getLogger(__name__)
11
16
 
12
17
  @timeit
13
18
  def sync(
14
- neo4j_session: neo4j.Session,
15
- manager: Manager,
16
- digitalocean_update_tag: int,
17
- common_job_parameters: dict,
18
- ) -> dict:
19
- projects_resources = sync_projects(neo4j_session, manager, digitalocean_update_tag, common_job_parameters)
20
- return projects_resources
21
-
22
-
23
- @timeit
24
- def sync_projects(
25
- neo4j_session: neo4j.Session,
26
- manager: Manager,
27
- digitalocean_update_tag: int,
28
- common_job_parameters: dict,
19
+ neo4j_session: neo4j.Session,
20
+ manager: Manager,
21
+ account_id: str,
22
+ update_tag: int,
23
+ common_job_parameters: dict,
29
24
  ) -> dict:
30
25
  logger.info("Syncing Projects")
31
- account_id = common_job_parameters['DO_ACCOUNT_ID']
32
26
  projects_res = get_projects(manager)
33
- projects_resources = get_projects_resources(projects_res)
34
- projects = transform_projects(projects_res, account_id)
35
- load_projects(neo4j_session, projects, digitalocean_update_tag)
36
- cleanup_projects(neo4j_session, common_job_parameters)
27
+ projects = transform_projects(projects_res)
28
+ load_projects(neo4j_session, projects, account_id, update_tag)
29
+ cleanup(neo4j_session, common_job_parameters)
37
30
 
38
- return projects_resources
31
+ return get_projects_resources(projects_res)
39
32
 
40
33
 
41
34
  @timeit
@@ -49,77 +42,48 @@ def get_projects_resources(projects_res: list) -> dict:
49
42
  for p in projects_res:
50
43
  resources = p.get_all_resources()
51
44
  result[p.id] = resources
52
-
53
45
  return result
54
46
 
55
47
 
56
48
  @timeit
57
- def transform_projects(project_res: list, account_id: str) -> list:
49
+ def transform_projects(project_res: list) -> list:
58
50
  result = list()
59
51
  for p in project_res:
60
52
  project = {
61
- 'id': p.id,
62
- 'name': p.name,
63
- 'owner_uuid': p.owner_uuid,
64
- 'description': p.description,
65
- 'environment': p.environment,
66
- 'is_default': p.is_default,
67
- 'created_at': p.created_at,
68
- 'updated_at': p.updated_at,
69
- 'account_id': account_id,
53
+ "id": p.id,
54
+ "name": p.name,
55
+ "owner_uuid": p.owner_uuid,
56
+ "description": p.description,
57
+ "environment": p.environment,
58
+ "is_default": p.is_default,
59
+ "created_at": p.created_at,
60
+ "updated_at": p.updated_at,
70
61
  }
71
62
  result.append(project)
72
63
  return result
73
64
 
74
65
 
75
66
  @timeit
76
- def load_projects(neo4j_session: neo4j.Session, data: list, digitalocean_update_tag: int) -> None:
77
- query = """
78
- MERGE (a:DOAccount{id:$AccountId})
79
- ON CREATE SET a.firstseen = timestamp()
80
- SET a.lastupdated = $digitalocean_update_tag
81
-
82
- MERGE (p:DOProject{id:$ProjectId})
83
- ON CREATE SET p.firstseen = timestamp()
84
- SET p.account_id = $AccountId,
85
- p.name = $Name,
86
- p.owner_uuid = $OwnerUuid,
87
- p.description = $Description,
88
- p.environment = $Environment,
89
- p.is_default = $IsDefault,
90
- p.created_at = $CreatedAt,
91
- p.updated_at = $UpdatedAt,
92
- p.lastupdated = $digitalocean_update_tag
93
- WITH p, a
94
-
95
- MERGE (a)-[r:RESOURCE]->(p)
96
- ON CREATE SET r.firstseen = timestamp()
97
- SET r.lastupdated = $digitalocean_update_tag
98
- """
99
- for project in data:
100
- neo4j_session.run(
101
- query,
102
- AccountId=project['account_id'],
103
- ProjectId=project['id'],
104
- Name=project['name'],
105
- OwnerUuid=project['owner_uuid'],
106
- Description=project['description'],
107
- Environment=project['environment'],
108
- IsDefault=project['is_default'],
109
- CreatedAt=project['created_at'],
110
- UpdatedAt=project['updated_at'],
111
- digitalocean_update_tag=digitalocean_update_tag,
112
- )
113
- return
67
+ def load_projects(
68
+ neo4j_session: neo4j.Session,
69
+ data: List[Dict[str, Any]],
70
+ account_id: str,
71
+ update_tag: int,
72
+ ) -> None:
73
+ load(
74
+ neo4j_session,
75
+ DOProjectSchema(),
76
+ data,
77
+ lastupdated=update_tag,
78
+ ACCOUNT_ID=str(account_id),
79
+ )
114
80
 
115
81
 
116
82
  @timeit
117
- def cleanup_projects(neo4j_session: neo4j.Session, common_job_parameters: dict) -> None:
118
- """
119
- Delete out-of-date DigitalOcean projects and relationships
120
- :param neo4j_session: The Neo4j session
121
- :param common_job_parameters: dict of other job parameters to pass to Neo4j
122
- :return: Nothing
123
- """
124
- run_cleanup_job('digitalocean_project_cleanup.json', neo4j_session, common_job_parameters)
125
- return
83
+ def cleanup(
84
+ neo4j_session: neo4j.Session,
85
+ common_job_parameters: Dict[str, Any],
86
+ ) -> None:
87
+ GraphJob.from_node_schema(DOProjectSchema(), common_job_parameters).run(
88
+ neo4j_session,
89
+ )