cartography 0.102.0rc1__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 +327 -0
  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 -44
  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 +97 -0
  208. cartography/models/aws/ec2/route_tables.py +128 -0
  209. cartography/models/aws/ec2/routes.py +85 -0
  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.0rc1.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.0rc1.dist-info → cartography-0.103.0.dist-info}/WHEEL +1 -1
  294. cartography-0.102.0rc1.dist-info/RECORD +0 -377
  295. {cartography-0.102.0rc1.dist-info → cartography-0.103.0.dist-info}/entry_points.txt +0 -0
  296. {cartography-0.102.0rc1.dist-info → cartography-0.103.0.dist-info}/licenses/LICENSE +0 -0
  297. {cartography-0.102.0rc1.dist-info → cartography-0.103.0.dist-info}/top_level.txt +0 -0
@@ -13,7 +13,11 @@ from cartography.models.core.nodes import CartographyNodeSchema
13
13
  from cartography.util import batch
14
14
 
15
15
 
16
- def read_list_of_values_tx(tx: neo4j.Transaction, query: str, **kwargs) -> List[Union[str, int]]:
16
+ def read_list_of_values_tx(
17
+ tx: neo4j.Transaction,
18
+ query: str,
19
+ **kwargs,
20
+ ) -> List[Union[str, int]]:
17
21
  """
18
22
  Runs the given Neo4j query in the given transaction object and returns a list of either str or int. This is intended
19
23
  to be run only with queries that return a list of a single field.
@@ -39,7 +43,11 @@ def read_list_of_values_tx(tx: neo4j.Transaction, query: str, **kwargs) -> List[
39
43
  return values
40
44
 
41
45
 
42
- def read_single_value_tx(tx: neo4j.Transaction, query: str, **kwargs) -> Optional[Union[str, int]]:
46
+ def read_single_value_tx(
47
+ tx: neo4j.Transaction,
48
+ query: str,
49
+ **kwargs,
50
+ ) -> Optional[Union[str, int]]:
43
51
  """
44
52
  Runs the given Neo4j query in the given transaction object and returns a str, int, or None. This is intended to be
45
53
  run only with queries that return a single str, int, or None value.
@@ -70,7 +78,11 @@ def read_single_value_tx(tx: neo4j.Transaction, query: str, **kwargs) -> Optiona
70
78
  return value
71
79
 
72
80
 
73
- def read_list_of_dicts_tx(tx: neo4j.Transaction, query: str, **kwargs) -> List[Dict[str, Any]]:
81
+ def read_list_of_dicts_tx(
82
+ tx: neo4j.Transaction,
83
+ query: str,
84
+ **kwargs,
85
+ ) -> List[Dict[str, Any]]:
74
86
  """
75
87
  Runs the given Neo4j query in the given transaction object and returns the results as a list of dicts.
76
88
 
@@ -92,7 +104,11 @@ def read_list_of_dicts_tx(tx: neo4j.Transaction, query: str, **kwargs) -> List[D
92
104
  return values
93
105
 
94
106
 
95
- def read_list_of_tuples_tx(tx: neo4j.Transaction, query: str, **kwargs) -> List[Tuple[Any, ...]]:
107
+ def read_list_of_tuples_tx(
108
+ tx: neo4j.Transaction,
109
+ query: str,
110
+ **kwargs,
111
+ ) -> List[Tuple[Any, ...]]:
96
112
  """
97
113
  Runs the given Neo4j query in the given transaction object and returns the results as a list of tuples.
98
114
 
@@ -154,9 +170,9 @@ def read_single_dict_tx(tx: neo4j.Transaction, query: str, **kwargs) -> Any:
154
170
 
155
171
 
156
172
  def write_list_of_dicts_tx(
157
- tx: neo4j.Transaction,
158
- query: str,
159
- **kwargs,
173
+ tx: neo4j.Transaction,
174
+ query: str,
175
+ **kwargs,
160
176
  ) -> None:
161
177
  """
162
178
  Writes a list of dicts to Neo4j.
@@ -192,10 +208,10 @@ def write_list_of_dicts_tx(
192
208
 
193
209
 
194
210
  def load_graph_data(
195
- neo4j_session: neo4j.Session,
196
- query: str,
197
- dict_list: List[Dict[str, Any]],
198
- **kwargs,
211
+ neo4j_session: neo4j.Session,
212
+ query: str,
213
+ dict_list: List[Dict[str, Any]],
214
+ **kwargs,
199
215
  ) -> None:
200
216
  """
201
217
  Writes data to the graph.
@@ -215,7 +231,10 @@ def load_graph_data(
215
231
  )
216
232
 
217
233
 
218
- def ensure_indexes(neo4j_session: neo4j.Session, node_schema: CartographyNodeSchema) -> None:
234
+ def ensure_indexes(
235
+ neo4j_session: neo4j.Session,
236
+ node_schema: CartographyNodeSchema,
237
+ ) -> None:
219
238
  """
220
239
  Creates indexes if they don't exist for the given CartographyNodeSchema object, as well as for all of the
221
240
  relationships defined on its `other_relationships` and `sub_resource_relationship` fields. This operation is
@@ -229,16 +248,18 @@ def ensure_indexes(neo4j_session: neo4j.Session, node_schema: CartographyNodeSch
229
248
  queries = build_create_index_queries(node_schema)
230
249
 
231
250
  for query in queries:
232
- if not query.startswith('CREATE INDEX IF NOT EXISTS'):
233
- raise ValueError('Query provided to `ensure_indexes()` does not start with "CREATE INDEX IF NOT EXISTS".')
251
+ if not query.startswith("CREATE INDEX IF NOT EXISTS"):
252
+ raise ValueError(
253
+ 'Query provided to `ensure_indexes()` does not start with "CREATE INDEX IF NOT EXISTS".',
254
+ )
234
255
  neo4j_session.run(query)
235
256
 
236
257
 
237
258
  def load(
238
- neo4j_session: neo4j.Session,
239
- node_schema: CartographyNodeSchema,
240
- dict_list: List[Dict[str, Any]],
241
- **kwargs,
259
+ neo4j_session: neo4j.Session,
260
+ node_schema: CartographyNodeSchema,
261
+ dict_list: List[Dict[str, Any]],
262
+ **kwargs,
242
263
  ) -> None:
243
264
  """
244
265
  Main entrypoint for intel modules to write data to the graph. Ensures that indexes exist for the datatypes loaded
cartography/config.py CHANGED
@@ -26,6 +26,8 @@ class Config:
26
26
  :type aws_sync_all_profiles: bool
27
27
  :param aws_sync_all_profiles: If True, AWS sync will run for all non-default profiles in the AWS_CONFIG_FILE. If
28
28
  False (default), AWS sync will run using the default credentials only. Optional.
29
+ :type aws_regions: str
30
+ :param aws_regions: Comma-separated list of AWS regions to sync. Optional.
29
31
  :type aws_best_effort_mode: bool
30
32
  :param aws_best_effort_mode: If True, AWS sync will not raise any exceptions, just log. If False (default),
31
33
  exceptions will be raised.
@@ -121,6 +123,18 @@ class Config:
121
123
  :param snipeit_token: Token used to authenticate to the SnipeIT data provider. Optional.
122
124
  :type snipeit_tenant_id: string
123
125
  :param snipeit_tenant_id: Token used to authenticate to the SnipeIT data provider. Optional.
126
+ :type tailscale_token: str
127
+ :param tailscale_token: Tailscale API token. Optional.
128
+ :type tailscale_org: str
129
+ :param tailscale_org: Tailscale organization name. Optional.
130
+ :type tailscale_base_url: str
131
+ :param tailscale_base_url: Tailscale API base URL. Optional.
132
+ :type cloudflare_token: string
133
+ :param cloudflare_token: Cloudflare API key. Optional.
134
+ :type openai_apikey: string
135
+ :param openai_apikey: OpenAI API key. Optional.
136
+ :type openai_org_id: string
137
+ :param openai_org_id: OpenAI organization id. Optional.
124
138
  """
125
139
 
126
140
  def __init__(
@@ -133,6 +147,7 @@ class Config:
133
147
  selected_modules=None,
134
148
  update_tag=None,
135
149
  aws_sync_all_profiles=False,
150
+ aws_regions=None,
136
151
  aws_best_effort_mode=False,
137
152
  azure_sync_all_subscriptions=False,
138
153
  azure_sp_auth=None,
@@ -185,6 +200,12 @@ class Config:
185
200
  snipeit_base_uri=None,
186
201
  snipeit_token=None,
187
202
  snipeit_tenant_id=None,
203
+ tailscale_token=None,
204
+ tailscale_org=None,
205
+ tailscale_base_url=None,
206
+ cloudflare_token=None,
207
+ openai_apikey=None,
208
+ openai_org_id=None,
188
209
  ):
189
210
  self.neo4j_uri = neo4j_uri
190
211
  self.neo4j_user = neo4j_user
@@ -194,6 +215,7 @@ class Config:
194
215
  self.selected_modules = selected_modules
195
216
  self.update_tag = update_tag
196
217
  self.aws_sync_all_profiles = aws_sync_all_profiles
218
+ self.aws_regions = aws_regions
197
219
  self.aws_best_effort_mode = aws_best_effort_mode
198
220
  self.azure_sync_all_subscriptions = azure_sync_all_subscriptions
199
221
  self.azure_sp_auth = azure_sp_auth
@@ -246,3 +268,9 @@ class Config:
246
268
  self.snipeit_base_uri = snipeit_base_uri
247
269
  self.snipeit_token = snipeit_token
248
270
  self.snipeit_tenant_id = snipeit_tenant_id
271
+ self.tailscale_token = tailscale_token
272
+ self.tailscale_org = tailscale_org
273
+ self.tailscale_base_url = tailscale_base_url
274
+ self.cloudflare_token = cloudflare_token
275
+ self.openai_apikey = openai_apikey
276
+ self.openai_org_id = openai_org_id
@@ -2,6 +2,5 @@ import sys
2
2
 
3
3
  import cartography.driftdetect.cli
4
4
 
5
-
6
- if __name__ == '__main__':
5
+ if __name__ == "__main__":
7
6
  sys.exit(cartography.driftdetect.cli.main())
@@ -22,7 +22,13 @@ def run_add_shortcut(config):
22
22
  logger.error("Invalid Drift Detection Directory")
23
23
  return
24
24
  try:
25
- add_shortcut(FileSystem, ShortcutSchema(), config.query_directory, config.shortcut, config.filename)
25
+ add_shortcut(
26
+ FileSystem,
27
+ ShortcutSchema(),
28
+ config.query_directory,
29
+ config.shortcut,
30
+ config.filename,
31
+ )
26
32
  except ValidationError as err:
27
33
  msg = "Could not load shortcut file from json file {} in query directory {}.".format(
28
34
  err.messages,
@@ -48,7 +54,9 @@ def add_shortcut(storage, shortcut_serializer, query_directory, alias, filename)
48
54
  :return:
49
55
  """
50
56
  if storage.has_file(os.path.join(query_directory, alias)):
51
- logger.error(f"Shortcut {alias} is the name of another File in directory {query_directory}.")
57
+ logger.error(
58
+ f"Shortcut {alias} is the name of another File in directory {query_directory}.",
59
+ )
52
60
  return
53
61
  shortcut_path = os.path.join(query_directory, "shortcut.json")
54
62
  shortcut_data = storage.load(shortcut_path)
@@ -8,7 +8,6 @@ from cartography.driftdetect.add_shortcut import run_add_shortcut
8
8
  from cartography.driftdetect.detect_deviations import run_drift_detection
9
9
  from cartography.driftdetect.get_states import run_get_states
10
10
 
11
-
12
11
  logger = logging.getLogger(__name__)
13
12
 
14
13
 
@@ -26,138 +25,130 @@ class CLI:
26
25
  parser = argparse.ArgumentParser(
27
26
  prog=self.prog,
28
27
  description=(
29
- 'drift-detection takes database queries along with their expected states in the cartography-generated '
30
- 'graph database and reports the deviations.'
28
+ "drift-detection takes database queries along with their expected states in the cartography-generated "
29
+ "graph database and reports the deviations."
31
30
  ),
32
- epilog='For more documentation please visit: '
33
- 'https://cartography-cncf.github.io/cartography/usage/drift-detect.html',
31
+ epilog="For more documentation please visit: "
32
+ "https://cartography-cncf.github.io/cartography/usage/drift-detect.html",
34
33
  )
35
34
  parser.add_argument(
36
- '-v',
37
- '--verbose',
38
- action='store_true',
39
- help='Enable verbose logging for drift-detection.',
35
+ "-v",
36
+ "--verbose",
37
+ action="store_true",
38
+ help="Enable verbose logging for drift-detection.",
40
39
  )
41
40
  parser.add_argument(
42
- '-q',
43
- '--quiet',
44
- action='store_true',
45
- help='Restrict drift-detection logging to warnings and errors only.',
41
+ "-q",
42
+ "--quiet",
43
+ action="store_true",
44
+ help="Restrict drift-detection logging to warnings and errors only.",
46
45
  )
47
46
  subparsers = parser.add_subparsers(
48
- dest='command',
49
- help='To get the current drift state, use the command `cartography-detectdrift get-state --neo4j_uri <your '
50
- 'neo4j_uri> --drift-detection-directory <your drift detection directory>` \n'
51
- 'To get drift between two state files, use the command `cartography-detectdrift get-drift'
52
- '--query-directory <path to query directory> --start-state <beginning drift state file> --end-state '
53
- '<final drift state file>'
54
- 'To add a shortcut between two state files, use the command `cartography-detectdrift add-shortcut'
55
- '--query-directory <path to query directory> --shortcut <shortcut name> --file <driftstate filename>',
47
+ dest="command",
48
+ help="To get the current drift state, use the command `cartography-detectdrift get-state --neo4j_uri <your "
49
+ "neo4j_uri> --drift-detection-directory <your drift detection directory>` \n"
50
+ "To get drift between two state files, use the command `cartography-detectdrift get-drift"
51
+ "--query-directory <path to query directory> --start-state <beginning drift state file> --end-state "
52
+ "<final drift state file>"
53
+ "To add a shortcut between two state files, use the command `cartography-detectdrift add-shortcut"
54
+ "--query-directory <path to query directory> --shortcut <shortcut name> --file <driftstate filename>",
56
55
  )
57
56
  parser_get_state = subparsers.add_parser(
58
- name='get-state',
59
- help='generates new drift state for each query with the current status of the neo4j database',
57
+ name="get-state",
58
+ help="generates new drift state for each query with the current status of the neo4j database",
60
59
  )
61
60
  parser_get_state.add_argument(
62
- '--neo4j-uri',
61
+ "--neo4j-uri",
63
62
  type=str,
64
- default='bolt://localhost:7687',
63
+ default="bolt://localhost:7687",
65
64
  help=(
66
- 'A valid Neo4j URI to sync against. See '
67
- 'https://neo4j.com/docs/api/python-driver/current/driver.html#uri for complete documentation on the '
68
- 'structure of a Neo4j URI.'
65
+ "A valid Neo4j URI to sync against. See "
66
+ "https://neo4j.com/docs/api/python-driver/current/driver.html#uri for complete documentation on the "
67
+ "structure of a Neo4j URI."
69
68
  ),
70
69
  )
71
70
  parser_get_state.add_argument(
72
- '--neo4j-user',
71
+ "--neo4j-user",
73
72
  type=str,
74
73
  default=None,
75
- help='A username with which to authenticate to Neo4j.',
74
+ help="A username with which to authenticate to Neo4j.",
76
75
  )
77
76
  parser_get_state.add_argument(
78
- '--neo4j-password-env-var',
77
+ "--neo4j-password-env-var",
79
78
  type=str,
80
79
  default=None,
81
- help='The name of an environment variable containing a password with which to authenticate to Neo4j.',
80
+ help="The name of an environment variable containing a password with which to authenticate to Neo4j.",
82
81
  )
83
82
  parser_get_state.add_argument(
84
- '--neo4j-password-prompt',
85
- action='store_true',
83
+ "--neo4j-password-prompt",
84
+ action="store_true",
86
85
  help=(
87
- 'Present an interactive prompt for a password with which to authenticate to Neo4j. This parameter '
88
- 'supersedes other methods of supplying a Neo4j password.'
86
+ "Present an interactive prompt for a password with which to authenticate to Neo4j. This parameter "
87
+ "supersedes other methods of supplying a Neo4j password."
89
88
  ),
90
89
  )
91
90
  parser_get_state.add_argument(
92
- '--drift-detection-directory',
91
+ "--drift-detection-directory",
93
92
  type=str,
94
93
  default=None,
95
94
  help=(
96
- 'A path to a directory containing drift-states to build. Drift-detection will discover all JSON'
97
- 'files in the given directory (and its subdirectories) and construct detectors from'
98
- 'them. Drift-detection does not guarantee the order in which the detector jobs are executed.'
95
+ "A path to a directory containing drift-states to build. Drift-detection will discover all JSON"
96
+ "files in the given directory (and its subdirectories) and construct detectors from"
97
+ "them. Drift-detection does not guarantee the order in which the detector jobs are executed."
99
98
  ),
100
99
  )
101
100
  parser_get_drift = subparsers.add_parser(
102
- name='get-drift',
101
+ name="get-drift",
103
102
  help=(
104
- 'gets drift between two drift states. Must be between the same detection directory'
103
+ "gets drift between two drift states. Must be between the same detection directory"
105
104
  ),
106
105
  )
107
106
  parser_get_drift.add_argument(
108
- '--query-directory',
107
+ "--query-directory",
109
108
  type=str,
110
109
  default=None,
111
110
  help=(
112
- 'A path to a directory containing drift-states for a specific query. Drift-detection will read in the'
113
- 'report_info file and specified drift-states from the directory to report the differences between files'
111
+ "A path to a directory containing drift-states for a specific query. Drift-detection will read in the"
112
+ "report_info file and specified drift-states from the directory to report the differences between files"
114
113
  ),
115
114
  )
116
115
  parser_get_drift.add_argument(
117
- '--start-state',
116
+ "--start-state",
118
117
  type=str,
119
118
  default=None,
120
119
  help=(
121
- 'The filename of the earlier state chronologically to be compared to.'
120
+ "The filename of the earlier state chronologically to be compared to."
122
121
  ),
123
122
  )
124
123
  parser_get_drift.add_argument(
125
- '--end-state',
124
+ "--end-state",
126
125
  type=str,
127
126
  default=None,
128
- help=(
129
- 'The filename of the later state chronologically to be compared to.'
130
- ),
127
+ help=("The filename of the later state chronologically to be compared to."),
131
128
  )
132
129
  parser_add_shortcut = subparsers.add_parser(
133
- name='add-shortcut',
134
- help=(
135
- 'Adds a shortcut to a specific file in a query directory.'
136
- ),
130
+ name="add-shortcut",
131
+ help=("Adds a shortcut to a specific file in a query directory."),
137
132
  )
138
133
  parser_add_shortcut.add_argument(
139
- '--query-directory',
134
+ "--query-directory",
140
135
  type=str,
141
136
  default=None,
142
137
  help=(
143
- 'A path to a directory containing drift-states for a specific query.'
138
+ "A path to a directory containing drift-states for a specific query."
144
139
  ),
145
140
  )
146
141
  parser_add_shortcut.add_argument(
147
- '--shortcut',
142
+ "--shortcut",
148
143
  type=str,
149
144
  default=None,
150
- help=(
151
- 'The desired alias for the filename.'
152
- ),
145
+ help=("The desired alias for the filename."),
153
146
  )
154
147
  parser_add_shortcut.add_argument(
155
- '--filename',
148
+ "--filename",
156
149
  type=str,
157
150
  default=None,
158
- help=(
159
- 'The desired name of the file to be replaced.'
160
- ),
151
+ help=("The desired name of the file to be replaced."),
161
152
  )
162
153
  return parser
163
154
 
@@ -171,13 +162,13 @@ class CLI:
171
162
  # TODO support parameter lookup in environment variables if not present on command line
172
163
  config = self.parser.parse_args(argv)
173
164
  if config.verbose:
174
- logging.getLogger('driftdetect').setLevel(logging.DEBUG)
165
+ logging.getLogger("driftdetect").setLevel(logging.DEBUG)
175
166
  elif config.quiet:
176
- logging.getLogger('driftdetect').setLevel(logging.WARNING)
167
+ logging.getLogger("driftdetect").setLevel(logging.WARNING)
177
168
  else:
178
- logging.getLogger('driftdetect').setLevel(logging.INFO)
169
+ logging.getLogger("driftdetect").setLevel(logging.INFO)
179
170
  logger.debug("Launching driftdetect with CLI configuration: %r", vars(config))
180
- if config.command == 'get-state':
171
+ if config.command == "get-state":
181
172
  config = configure_get_state_neo4j(config)
182
173
  return config
183
174
 
@@ -190,11 +181,11 @@ class CLI:
190
181
  """
191
182
  config = self.configure(argv)
192
183
  try:
193
- if config.command == 'get-state':
184
+ if config.command == "get-state":
194
185
  run_get_states(config)
195
- elif config.command == 'get-drift':
186
+ elif config.command == "get-drift":
196
187
  run_drift_detection(config)
197
- elif config.command == 'add-shortcut':
188
+ elif config.command == "add-shortcut":
198
189
  run_add_shortcut(config)
199
190
  else:
200
191
  msg = "No command detected. Try --help."
@@ -215,7 +206,10 @@ def configure_get_state_neo4j(config):
215
206
  if config.neo4j_user:
216
207
  config.neo4j_password = None
217
208
  if config.neo4j_password_prompt:
218
- logger.info("Reading password for Neo4j user '%s' interactively.", config.neo4j_user)
209
+ logger.info(
210
+ "Reading password for Neo4j user '%s' interactively.",
211
+ config.neo4j_user,
212
+ )
219
213
  config.neo4j_password = getpass.getpass()
220
214
  elif config.neo4j_password_env_var:
221
215
  logger.debug(
@@ -225,7 +219,9 @@ def configure_get_state_neo4j(config):
225
219
  )
226
220
  config.neo4j_password = os.environ.get(config.neo4j_password_env_var)
227
221
  if not config.neo4j_password:
228
- logger.warning("Neo4j username was provided but a password could not be found.")
222
+ logger.warning(
223
+ "Neo4j username was provided but a password could not be found.",
224
+ )
229
225
  else:
230
226
  config.neo4j_password = None
231
227
  return config
@@ -239,6 +235,6 @@ def main(argv=None):
239
235
  :return: The return code.
240
236
  """
241
237
  logging.basicConfig(level=logging.INFO)
242
- logging.getLogger('neo4j').setLevel(logging.WARNING)
238
+ logging.getLogger("neo4j").setLevel(logging.WARNING)
243
239
  argv = argv if argv is not None else sys.argv[1:]
244
240
  return CLI(prog="cartography-detectdrift").main(argv)
@@ -23,11 +23,14 @@ def run_drift_detection(config: GetDriftConfig) -> None:
23
23
  return
24
24
  state_serializer = StateSchema()
25
25
  shortcut_serializer = ShortcutSchema()
26
- shortcut_data = FileSystem.load(os.path.join(config.query_directory, "shortcut.json"))
26
+ shortcut_data = FileSystem.load(
27
+ os.path.join(config.query_directory, "shortcut.json"),
28
+ )
27
29
  shortcut = shortcut_serializer.load(shortcut_data)
28
30
  start_state_data = FileSystem.load(
29
31
  os.path.join(
30
- config.query_directory, shortcut.shortcuts.get(
32
+ config.query_directory,
33
+ shortcut.shortcuts.get(
31
34
  config.start_state,
32
35
  config.start_state,
33
36
  ),
@@ -36,7 +39,8 @@ def run_drift_detection(config: GetDriftConfig) -> None:
36
39
  start_state = state_serializer.load(start_state_data)
37
40
  end_state_data = FileSystem.load(
38
41
  os.path.join(
39
- config.query_directory, shortcut.shortcuts.get(
42
+ config.query_directory,
43
+ shortcut.shortcuts.get(
40
44
  config.end_state,
41
45
  config.end_state,
42
46
  ),
@@ -74,13 +74,25 @@ def run_get_states(config: UpdateConfig) -> None:
74
74
  return
75
75
 
76
76
  with neo4j_driver.session() as session:
77
- filename = '.'.join([str(i) for i in time.gmtime()] + ["json"])
77
+ filename = ".".join([str(i) for i in time.gmtime()] + ["json"])
78
78
  state_serializer = StateSchema()
79
79
  shortcut_serializer = ShortcutSchema()
80
80
  for query_directory in FileSystem.walk(config.drift_detection_directory):
81
81
  try:
82
- get_query_state(session, query_directory, state_serializer, FileSystem, filename)
83
- add_shortcut(FileSystem, shortcut_serializer, query_directory, 'most-recent', filename)
82
+ get_query_state(
83
+ session,
84
+ query_directory,
85
+ state_serializer,
86
+ FileSystem,
87
+ filename,
88
+ )
89
+ add_shortcut(
90
+ FileSystem,
91
+ shortcut_serializer,
92
+ query_directory,
93
+ "most-recent",
94
+ filename,
95
+ )
84
96
  except ValidationError as err:
85
97
  msg = "Unable to create State for directory {}, with data \n{}".format(
86
98
  query_directory,
@@ -97,11 +109,11 @@ def run_get_states(config: UpdateConfig) -> None:
97
109
 
98
110
 
99
111
  def get_query_state(
100
- session: neo4j.Session,
101
- query_directory: str,
102
- state_serializer: StateSchema,
103
- storage,
104
- filename: str,
112
+ session: neo4j.Session,
113
+ query_directory: str,
114
+ state_serializer: StateSchema,
115
+ storage,
116
+ filename: str,
105
117
  ) -> State:
106
118
  """
107
119
  Gets the most recent state of a query.
@@ -19,11 +19,11 @@ class State:
19
19
  """
20
20
 
21
21
  def __init__(
22
- self,
23
- name: str,
24
- validation_query: str,
25
- properties: List[str],
26
- results: List[List[str]],
22
+ self,
23
+ name: str,
24
+ validation_query: str,
25
+ properties: List[str],
26
+ results: List[List[str]],
27
27
  ):
28
28
 
29
29
  self.name: str = name
@@ -10,6 +10,7 @@ class StateSchema(Schema):
10
10
  """
11
11
  Schema to serialize and deserialize DriftStates from JSON.
12
12
  """
13
+
13
14
  name = fields.Str()
14
15
  validation_query = fields.Str()
15
16
  properties = fields.List(fields.Str())
@@ -18,10 +19,10 @@ class StateSchema(Schema):
18
19
  @post_load
19
20
  def make_state(self, data, **kwargs):
20
21
  return State(
21
- data['name'],
22
- data['validation_query'],
23
- data['properties'],
24
- data['results'],
22
+ data["name"],
23
+ data["validation_query"],
24
+ data["properties"],
25
+ data["results"],
25
26
  )
26
27
 
27
28
 
@@ -29,12 +30,13 @@ class ShortcutSchema(Schema):
29
30
  """
30
31
  Schema to serialize and deserialize Shortcuts from JSON.
31
32
  """
33
+
32
34
  name = fields.Str()
33
35
  shortcuts = fields.Dict(keys=fields.Str(), values=fields.Str())
34
36
 
35
37
  @post_load
36
38
  def make_misc(self, data, **kwargs):
37
39
  return Shortcut(
38
- data['name'],
39
- data['shortcuts'],
40
+ data["name"],
41
+ data["shortcuts"],
40
42
  )
@@ -25,9 +25,9 @@ class FileSystem:
25
25
  :param file_path: Filepath to be written to.
26
26
  :return:
27
27
  """
28
- with open(file_path, 'w') as json_file:
28
+ with open(file_path, "w") as json_file:
29
29
  json.dump(data, json_file, sort_keys=True, indent=4)
30
- json_file.write('\n')
30
+ json_file.write("\n")
31
31
 
32
32
  @classmethod
33
33
  def walk(cls, drift_detection_directory):