cartography 0.102.0rc1__py3-none-any.whl → 0.103.0rc1__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 (251) hide show
  1. cartography/__main__.py +1 -2
  2. cartography/_version.py +2 -2
  3. cartography/cli.py +302 -253
  4. cartography/client/core/tx.py +39 -18
  5. cartography/config.py +4 -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/config.py +56 -20
  23. cartography/intel/aws/dynamodb.py +108 -40
  24. cartography/intel/aws/ec2/__init__.py +2 -2
  25. cartography/intel/aws/ec2/auto_scaling_groups.py +181 -78
  26. cartography/intel/aws/ec2/elastic_ip_addresses.py +41 -13
  27. cartography/intel/aws/ec2/images.py +49 -20
  28. cartography/intel/aws/ec2/instances.py +234 -136
  29. cartography/intel/aws/ec2/internet_gateways.py +40 -11
  30. cartography/intel/aws/ec2/key_pairs.py +44 -20
  31. cartography/intel/aws/ec2/launch_templates.py +101 -59
  32. cartography/intel/aws/ec2/load_balancer_v2s.py +104 -39
  33. cartography/intel/aws/ec2/load_balancers.py +82 -42
  34. cartography/intel/aws/ec2/network_acls.py +89 -65
  35. cartography/intel/aws/ec2/network_interfaces.py +146 -87
  36. cartography/intel/aws/ec2/reserved_instances.py +45 -16
  37. cartography/intel/aws/ec2/route_tables.py +327 -0
  38. cartography/intel/aws/ec2/security_groups.py +71 -21
  39. cartography/intel/aws/ec2/snapshots.py +61 -22
  40. cartography/intel/aws/ec2/subnets.py +54 -18
  41. cartography/intel/aws/ec2/tgw.py +100 -34
  42. cartography/intel/aws/ec2/util.py +1 -1
  43. cartography/intel/aws/ec2/volumes.py +69 -41
  44. cartography/intel/aws/ec2/vpc.py +37 -12
  45. cartography/intel/aws/ec2/vpc_peerings.py +83 -24
  46. cartography/intel/aws/ecr.py +88 -32
  47. cartography/intel/aws/ecs.py +83 -47
  48. cartography/intel/aws/eks.py +55 -29
  49. cartography/intel/aws/elasticache.py +42 -18
  50. cartography/intel/aws/elasticsearch.py +57 -20
  51. cartography/intel/aws/emr.py +61 -23
  52. cartography/intel/aws/iam.py +401 -145
  53. cartography/intel/aws/iam_instance_profiles.py +22 -22
  54. cartography/intel/aws/identitycenter.py +71 -37
  55. cartography/intel/aws/inspector.py +159 -89
  56. cartography/intel/aws/kms.py +92 -38
  57. cartography/intel/aws/lambda_function.py +103 -34
  58. cartography/intel/aws/organizations.py +30 -10
  59. cartography/intel/aws/permission_relationships.py +133 -51
  60. cartography/intel/aws/rds.py +249 -85
  61. cartography/intel/aws/redshift.py +107 -46
  62. cartography/intel/aws/resourcegroupstaggingapi.py +120 -66
  63. cartography/intel/aws/resources.py +53 -44
  64. cartography/intel/aws/route53.py +108 -61
  65. cartography/intel/aws/s3.py +168 -83
  66. cartography/intel/aws/s3accountpublicaccessblock.py +157 -0
  67. cartography/intel/aws/secretsmanager.py +24 -12
  68. cartography/intel/aws/securityhub.py +20 -9
  69. cartography/intel/aws/sns.py +166 -0
  70. cartography/intel/aws/sqs.py +60 -28
  71. cartography/intel/aws/ssm.py +70 -30
  72. cartography/intel/aws/util/arns.py +7 -7
  73. cartography/intel/aws/util/common.py +31 -4
  74. cartography/intel/azure/__init__.py +78 -19
  75. cartography/intel/azure/compute.py +101 -27
  76. cartography/intel/azure/cosmosdb.py +496 -170
  77. cartography/intel/azure/sql.py +296 -105
  78. cartography/intel/azure/storage.py +322 -113
  79. cartography/intel/azure/subscription.py +39 -23
  80. cartography/intel/azure/tenant.py +13 -4
  81. cartography/intel/azure/util/credentials.py +95 -55
  82. cartography/intel/bigfix/__init__.py +2 -2
  83. cartography/intel/bigfix/computers.py +93 -65
  84. cartography/intel/create_indexes.py +3 -2
  85. cartography/intel/crowdstrike/__init__.py +11 -9
  86. cartography/intel/crowdstrike/endpoints.py +5 -1
  87. cartography/intel/crowdstrike/spotlight.py +8 -3
  88. cartography/intel/cve/__init__.py +46 -13
  89. cartography/intel/cve/feed.py +48 -12
  90. cartography/intel/digitalocean/__init__.py +22 -13
  91. cartography/intel/digitalocean/compute.py +75 -108
  92. cartography/intel/digitalocean/management.py +44 -80
  93. cartography/intel/digitalocean/platform.py +48 -43
  94. cartography/intel/dns.py +36 -10
  95. cartography/intel/duo/__init__.py +21 -16
  96. cartography/intel/duo/api_host.py +14 -9
  97. cartography/intel/duo/endpoints.py +50 -45
  98. cartography/intel/duo/groups.py +18 -14
  99. cartography/intel/duo/phones.py +37 -34
  100. cartography/intel/duo/tokens.py +26 -23
  101. cartography/intel/duo/users.py +54 -50
  102. cartography/intel/duo/web_authn_credentials.py +30 -25
  103. cartography/intel/entra/__init__.py +25 -7
  104. cartography/intel/entra/ou.py +112 -0
  105. cartography/intel/entra/users.py +69 -63
  106. cartography/intel/gcp/__init__.py +185 -49
  107. cartography/intel/gcp/compute.py +418 -231
  108. cartography/intel/gcp/crm.py +96 -43
  109. cartography/intel/gcp/dns.py +60 -19
  110. cartography/intel/gcp/gke.py +72 -38
  111. cartography/intel/gcp/iam.py +61 -41
  112. cartography/intel/gcp/storage.py +84 -55
  113. cartography/intel/github/__init__.py +13 -11
  114. cartography/intel/github/repos.py +270 -137
  115. cartography/intel/github/teams.py +170 -88
  116. cartography/intel/github/users.py +70 -39
  117. cartography/intel/github/util.py +36 -34
  118. cartography/intel/gsuite/__init__.py +47 -26
  119. cartography/intel/gsuite/api.py +73 -30
  120. cartography/intel/jamf/__init__.py +19 -1
  121. cartography/intel/jamf/computers.py +30 -7
  122. cartography/intel/jamf/util.py +7 -2
  123. cartography/intel/kandji/__init__.py +6 -3
  124. cartography/intel/kandji/devices.py +14 -8
  125. cartography/intel/kubernetes/namespaces.py +7 -4
  126. cartography/intel/kubernetes/pods.py +7 -4
  127. cartography/intel/kubernetes/services.py +8 -4
  128. cartography/intel/lastpass/__init__.py +2 -2
  129. cartography/intel/lastpass/users.py +23 -12
  130. cartography/intel/oci/__init__.py +44 -11
  131. cartography/intel/oci/iam.py +134 -38
  132. cartography/intel/oci/organizations.py +13 -6
  133. cartography/intel/oci/utils.py +43 -20
  134. cartography/intel/okta/__init__.py +66 -15
  135. cartography/intel/okta/applications.py +42 -20
  136. cartography/intel/okta/awssaml.py +93 -33
  137. cartography/intel/okta/factors.py +16 -4
  138. cartography/intel/okta/groups.py +56 -29
  139. cartography/intel/okta/organization.py +5 -1
  140. cartography/intel/okta/origins.py +6 -2
  141. cartography/intel/okta/roles.py +15 -5
  142. cartography/intel/okta/users.py +20 -8
  143. cartography/intel/okta/utils.py +6 -4
  144. cartography/intel/pagerduty/__init__.py +8 -7
  145. cartography/intel/pagerduty/escalation_policies.py +18 -6
  146. cartography/intel/pagerduty/schedules.py +12 -4
  147. cartography/intel/pagerduty/services.py +11 -4
  148. cartography/intel/pagerduty/teams.py +8 -3
  149. cartography/intel/pagerduty/users.py +3 -1
  150. cartography/intel/pagerduty/vendors.py +3 -1
  151. cartography/intel/semgrep/__init__.py +24 -6
  152. cartography/intel/semgrep/dependencies.py +50 -28
  153. cartography/intel/semgrep/deployment.py +3 -1
  154. cartography/intel/semgrep/findings.py +42 -18
  155. cartography/intel/snipeit/__init__.py +17 -3
  156. cartography/intel/snipeit/asset.py +12 -6
  157. cartography/intel/snipeit/user.py +8 -5
  158. cartography/intel/snipeit/util.py +9 -4
  159. cartography/models/aws/apigateway.py +21 -17
  160. cartography/models/aws/apigatewaycertificate.py +28 -22
  161. cartography/models/aws/apigatewayresource.py +28 -20
  162. cartography/models/aws/apigatewaystage.py +33 -25
  163. cartography/models/aws/cloudtrail/__init__.py +0 -0
  164. cartography/models/aws/cloudtrail/trail.py +61 -0
  165. cartography/models/aws/dynamodb/gsi.py +30 -22
  166. cartography/models/aws/dynamodb/tables.py +25 -17
  167. cartography/models/aws/ec2/auto_scaling_groups.py +102 -82
  168. cartography/models/aws/ec2/images.py +36 -34
  169. cartography/models/aws/ec2/instances.py +51 -45
  170. cartography/models/aws/ec2/keypair.py +21 -16
  171. cartography/models/aws/ec2/keypair_instance.py +28 -21
  172. cartography/models/aws/ec2/launch_configurations.py +30 -26
  173. cartography/models/aws/ec2/launch_template_versions.py +48 -38
  174. cartography/models/aws/ec2/launch_templates.py +21 -17
  175. cartography/models/aws/ec2/load_balancer_listeners.py +27 -23
  176. cartography/models/aws/ec2/load_balancers.py +47 -37
  177. cartography/models/aws/ec2/network_acl_rules.py +38 -30
  178. cartography/models/aws/ec2/network_acls.py +38 -29
  179. cartography/models/aws/ec2/networkinterface_instance.py +52 -39
  180. cartography/models/aws/ec2/networkinterfaces.py +53 -37
  181. cartography/models/aws/ec2/privateip_networkinterface.py +32 -22
  182. cartography/models/aws/ec2/reservations.py +18 -14
  183. cartography/models/aws/ec2/route_table_associations.py +97 -0
  184. cartography/models/aws/ec2/route_tables.py +128 -0
  185. cartography/models/aws/ec2/routes.py +85 -0
  186. cartography/models/aws/ec2/securitygroup_instance.py +29 -20
  187. cartography/models/aws/ec2/securitygroup_networkinterface.py +24 -15
  188. cartography/models/aws/ec2/subnet_instance.py +24 -19
  189. cartography/models/aws/ec2/subnet_networkinterface.py +40 -31
  190. cartography/models/aws/ec2/volumes.py +47 -40
  191. cartography/models/aws/eks/clusters.py +23 -21
  192. cartography/models/aws/emr.py +32 -30
  193. cartography/models/aws/iam/instanceprofile.py +33 -24
  194. cartography/models/aws/identitycenter/awsidentitycenter.py +18 -14
  195. cartography/models/aws/identitycenter/awspermissionset.py +37 -29
  196. cartography/models/aws/identitycenter/awsssouser.py +23 -21
  197. cartography/models/aws/inspector/findings.py +77 -65
  198. cartography/models/aws/inspector/packages.py +35 -29
  199. cartography/models/aws/s3/__init__.py +0 -0
  200. cartography/models/aws/s3/account_public_access_block.py +51 -0
  201. cartography/models/aws/sns/__init__.py +0 -0
  202. cartography/models/aws/sns/topic.py +50 -0
  203. cartography/models/aws/ssm/instance_information.py +51 -39
  204. cartography/models/aws/ssm/instance_patch.py +32 -26
  205. cartography/models/bigfix/bigfix_computer.py +42 -38
  206. cartography/models/bigfix/bigfix_root.py +3 -3
  207. cartography/models/core/common.py +12 -10
  208. cartography/models/core/nodes.py +5 -2
  209. cartography/models/core/relationships.py +14 -6
  210. cartography/models/crowdstrike/hosts.py +37 -35
  211. cartography/models/cve/cve.py +34 -32
  212. cartography/models/cve/cve_feed.py +6 -6
  213. cartography/models/digitalocean/__init__.py +0 -0
  214. cartography/models/digitalocean/account.py +21 -0
  215. cartography/models/digitalocean/droplet.py +56 -0
  216. cartography/models/digitalocean/project.py +48 -0
  217. cartography/models/duo/api_host.py +3 -3
  218. cartography/models/duo/endpoint.py +43 -41
  219. cartography/models/duo/group.py +14 -14
  220. cartography/models/duo/phone.py +27 -27
  221. cartography/models/duo/token.py +16 -16
  222. cartography/models/duo/user.py +46 -44
  223. cartography/models/duo/web_authn_credential.py +27 -19
  224. cartography/models/entra/ou.py +48 -0
  225. cartography/models/entra/tenant.py +24 -18
  226. cartography/models/entra/user.py +64 -48
  227. cartography/models/gcp/iam.py +23 -23
  228. cartography/models/github/orgs.py +5 -4
  229. cartography/models/github/teams.py +37 -31
  230. cartography/models/github/users.py +34 -23
  231. cartography/models/kandji/device.py +22 -16
  232. cartography/models/kandji/tenant.py +6 -4
  233. cartography/models/lastpass/tenant.py +3 -3
  234. cartography/models/lastpass/user.py +32 -28
  235. cartography/models/semgrep/dependencies.py +36 -24
  236. cartography/models/semgrep/deployment.py +5 -5
  237. cartography/models/semgrep/findings.py +58 -42
  238. cartography/models/semgrep/locations.py +27 -21
  239. cartography/models/snipeit/asset.py +30 -21
  240. cartography/models/snipeit/tenant.py +6 -4
  241. cartography/models/snipeit/user.py +19 -12
  242. cartography/stats.py +3 -3
  243. cartography/sync.py +107 -31
  244. cartography/util.py +84 -62
  245. {cartography-0.102.0rc1.dist-info → cartography-0.103.0rc1.dist-info}/METADATA +3 -14
  246. cartography-0.103.0rc1.dist-info/RECORD +396 -0
  247. {cartography-0.102.0rc1.dist-info → cartography-0.103.0rc1.dist-info}/WHEEL +1 -1
  248. cartography-0.102.0rc1.dist-info/RECORD +0 -377
  249. {cartography-0.102.0rc1.dist-info → cartography-0.103.0rc1.dist-info}/entry_points.txt +0 -0
  250. {cartography-0.102.0rc1.dist-info → cartography-0.103.0rc1.dist-info}/licenses/LICENSE +0 -0
  251. {cartography-0.102.0rc1.dist-info → cartography-0.103.0rc1.dist-info}/top_level.txt +0 -0
@@ -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
+ )
@@ -1,67 +1,72 @@
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 Account
8
+ from digitalocean import Manager
5
9
 
10
+ from cartography.client.core.tx import load
11
+ from cartography.graph.job import GraphJob
12
+ from cartography.models.digitalocean.account import DOAccountSchema
6
13
  from cartography.util import timeit
7
14
 
8
-
9
15
  logger = logging.getLogger(__name__)
10
16
 
11
17
 
12
18
  @timeit
13
19
  def sync(
14
- neo4j_session: neo4j.Session,
15
- account: Account,
16
- digitalocean_update_tag: int,
17
- common_job_parameters: dict,
18
- ) -> None:
19
- sync_account(neo4j_session, account, digitalocean_update_tag, common_job_parameters)
20
+ neo4j_session: neo4j.Session,
21
+ manager: Manager,
22
+ update_tag: int,
23
+ common_job_parameters: dict,
24
+ ) -> str:
25
+ logger.info("Syncing Account")
26
+ account = get_account(manager)
27
+ account_transformed = transform_account(account)
28
+ load_account(
29
+ neo4j_session,
30
+ [
31
+ account_transformed,
32
+ ],
33
+ update_tag,
34
+ )
35
+ cleanup(neo4j_session, common_job_parameters)
36
+
37
+ return account_transformed["id"]
20
38
 
21
39
 
22
40
  @timeit
23
- def sync_account(
24
- neo4j_session: neo4j.Session,
25
- account: Account,
26
- digitalocean_update_tag: int,
27
- common_job_parameters: dict,
28
- ) -> None:
29
- logger.info("Syncing Account")
30
- account_transformed = transform_account(account)
31
- load_account(neo4j_session, account_transformed, digitalocean_update_tag)
32
- return
41
+ def get_account(manager: Manager) -> Account:
42
+ return manager.get_account()
33
43
 
34
44
 
35
45
  @timeit
36
46
  def transform_account(account_res: Account) -> dict:
37
- account = {
38
- 'id': account_res.uuid,
39
- 'uuid': account_res.uuid,
40
- 'droplet_limit': account_res.droplet_limit,
41
- 'floating_ip_limit': account_res.floating_ip_limit,
42
- 'status': account_res.status,
47
+ return {
48
+ "id": account_res.uuid,
49
+ "uuid": account_res.uuid,
50
+ "droplet_limit": account_res.droplet_limit,
51
+ "floating_ip_limit": account_res.floating_ip_limit,
52
+ "status": account_res.status,
43
53
  }
44
- return account
45
54
 
46
55
 
47
56
  @timeit
48
- def load_account(neo4j_session: neo4j.Session, account: dict, digitalocean_update_tag: int) -> None:
49
- query = """
50
- MERGE (a:DOAccount{id:$AccountId})
51
- ON CREATE SET a.firstseen = timestamp()
52
- SET a.uuid = $Uuid,
53
- a.droplet_limit = $DropletLimit,
54
- a.floating_ip_limit = $FloatingIpLimit,
55
- a.status = $Status,
56
- a.lastupdated = $digitalocean_update_tag
57
- """
58
- neo4j_session.run(
59
- query,
60
- AccountId=account['id'],
61
- Uuid=account['uuid'],
62
- DropletLimit=account['droplet_limit'],
63
- FloatingIpLimit=account['floating_ip_limit'],
64
- Status=account['status'],
65
- digitalocean_update_tag=digitalocean_update_tag,
57
+ def load_account(
58
+ neo4j_session: neo4j.Session,
59
+ data: List[Dict[str, Any]],
60
+ update_tag: int,
61
+ ) -> None:
62
+ load(neo4j_session, DOAccountSchema(), data, lastupdated=update_tag)
63
+
64
+
65
+ @timeit
66
+ def cleanup(
67
+ neo4j_session: neo4j.Session,
68
+ common_job_parameters: Dict[str, Any],
69
+ ) -> None:
70
+ GraphJob.from_node_schema(DOAccountSchema(), common_job_parameters).run(
71
+ neo4j_session,
66
72
  )
67
- return
cartography/intel/dns.py CHANGED
@@ -15,7 +15,11 @@ logger = logging.getLogger(__name__)
15
15
 
16
16
  @timeit
17
17
  def ingest_dns_record_by_fqdn(
18
- neo4j_session: neo4j.Session, update_tag: int, fqdn: str, points_to_record: str, record_label: str,
18
+ neo4j_session: neo4j.Session,
19
+ update_tag: int,
20
+ fqdn: str,
21
+ points_to_record: str,
22
+ record_label: str,
19
23
  dns_node_additional_label: Optional[str] = None,
20
24
  ) -> None:
21
25
  """
@@ -43,7 +47,7 @@ def ingest_dns_record_by_fqdn(
43
47
  fqdn_data = get_dns_resolution_by_fqdn(fqdn)
44
48
  record_type = get_dns_record_type(fqdn_data)
45
49
 
46
- if record_type == 'A':
50
+ if record_type == "A":
47
51
  ip_list = []
48
52
  for result in fqdn_data:
49
53
  ip = str(result)
@@ -51,8 +55,14 @@ def ingest_dns_record_by_fqdn(
51
55
 
52
56
  value = ",".join(ip_list)
53
57
  record_id = ingest_dns_record(
54
- neo4j_session, fqdn, value, record_type, update_tag, points_to_record,
55
- record_label, dns_node_additional_label, # type: ignore
58
+ neo4j_session,
59
+ fqdn,
60
+ value,
61
+ record_type,
62
+ update_tag,
63
+ points_to_record,
64
+ record_label,
65
+ dns_node_additional_label, # type: ignore
56
66
  )
57
67
  _link_ip_to_A_record(neo4j_session, update_tag, ip_list, record_id)
58
68
 
@@ -67,7 +77,12 @@ def ingest_dns_record_by_fqdn(
67
77
 
68
78
 
69
79
  @timeit
70
- def _link_ip_to_A_record(neo4j_session: neo4j.Session, update_tag: int, ip_list: List[str], parent_record: str) -> None:
80
+ def _link_ip_to_A_record(
81
+ neo4j_session: neo4j.Session,
82
+ update_tag: int,
83
+ ip_list: List[str],
84
+ parent_record: str,
85
+ ) -> None:
71
86
  """
72
87
  Link A record to to its IP
73
88
 
@@ -99,8 +114,14 @@ def _link_ip_to_A_record(neo4j_session: neo4j.Session, update_tag: int, ip_list:
99
114
 
100
115
  @timeit
101
116
  def ingest_dns_record(
102
- neo4j_session: neo4j.Session, name: neo4j.Session, value: str, type: str, update_tag: int, points_to_record: str,
103
- record_label: str, dns_node_additional_label: str,
117
+ neo4j_session: neo4j.Session,
118
+ name: neo4j.Session,
119
+ value: str,
120
+ type: str,
121
+ update_tag: int,
122
+ points_to_record: str,
123
+ record_label: str,
124
+ dns_node_additional_label: str,
104
125
  ) -> str:
105
126
  """
106
127
  Ingest a new DNS record
@@ -115,7 +136,8 @@ def ingest_dns_record(
115
136
  :param dns_node_additional_label: The specific label of the DNSRecord, e.g. AWSDNSRecord.
116
137
  :return: the intel graph node id for the new/merged record
117
138
  """
118
- template = Template("""
139
+ template = Template(
140
+ """
119
141
  MERGE (record:DNSRecord:$dns_node_additional_label{id: $Id})
120
142
  ON CREATE SET record.firstseen = timestamp(), record.name = $Name, record.type = $Type
121
143
  SET record.lastupdated = $update_tag, record.value = $Value
@@ -124,12 +146,16 @@ def ingest_dns_record(
124
146
  MERGE (record)-[r:DNS_POINTS_TO]->(n)
125
147
  ON CREATE SET r.firstseen = timestamp()
126
148
  SET r.lastupdated = $update_tag
127
- """)
149
+ """,
150
+ )
128
151
 
129
152
  record_id = f"{name}+{type}"
130
153
 
131
154
  neo4j_session.run(
132
- template.safe_substitute(record_label=record_label, dns_node_additional_label=dns_node_additional_label),
155
+ template.safe_substitute(
156
+ record_label=record_label,
157
+ dns_node_additional_label=dns_node_additional_label,
158
+ ),
133
159
  Id=record_id,
134
160
  Name=name,
135
161
  Type=type,
@@ -13,31 +13,34 @@ from cartography.intel.duo.groups import sync_duo_groups
13
13
  from cartography.intel.duo.phones import sync as sync_duo_phones
14
14
  from cartography.intel.duo.tokens import sync as sync_duo_tokens
15
15
  from cartography.intel.duo.users import sync_duo_users
16
- from cartography.intel.duo.web_authn_credentials import sync as sync_duo_web_authn_credentials
16
+ from cartography.intel.duo.web_authn_credentials import (
17
+ sync as sync_duo_web_authn_credentials,
18
+ )
17
19
  from cartography.util import timeit
18
20
 
19
-
20
21
  logger = logging.getLogger(__name__)
21
22
 
22
23
 
23
24
  @timeit
24
25
  def get_client(config: Config) -> duo_client.Admin:
25
- '''
26
+ """
26
27
  Return a duo Admin client with the creds in the config object
27
- '''
28
+ """
28
29
  client = duo_client.Admin(
29
30
  ikey=config.duo_api_key,
30
31
  skey=config.duo_api_secret,
31
32
  host=config.duo_api_hostname,
32
33
  )
33
34
  # Duo's library does not automatically respect the HTTP_PROXY env variable
34
- proxy_url = os.environ.get('HTTP_PROXY')
35
+ proxy_url = os.environ.get("HTTP_PROXY")
35
36
  if proxy_url:
36
37
  proxy_config = urlparse(proxy_url)
37
38
  headers = {}
38
39
  if proxy_config.username:
39
- proxy_auth_token = b64encode(f"{proxy_config.username}:{proxy_config.password}".encode()).decode('ascii')
40
- headers['Proxy-Authorization'] = f'Basic {proxy_auth_token}'
40
+ proxy_auth_token = b64encode(
41
+ f"{proxy_config.username}:{proxy_config.password}".encode(),
42
+ ).decode("ascii")
43
+ headers["Proxy-Authorization"] = f"Basic {proxy_auth_token}"
41
44
  client.set_proxy(
42
45
  host=proxy_config.hostname,
43
46
  port=proxy_config.port,
@@ -48,20 +51,22 @@ def get_client(config: Config) -> duo_client.Admin:
48
51
 
49
52
  @timeit
50
53
  def start_duo_ingestion(neo4j_session: neo4j.Session, config: Config) -> None:
51
- '''
54
+ """
52
55
  If this module is configured, perform ingestion of duo data. Otherwise warn and exit
53
56
  :param neo4j_session: Neo4J session for database interface
54
57
  :param config: A cartography.config object
55
58
  :return: None
56
- '''
57
- if not all([
58
- config.duo_api_key,
59
- config.duo_api_secret,
60
- config.duo_api_hostname,
61
- ]):
59
+ """
60
+ if not all(
61
+ [
62
+ config.duo_api_key,
63
+ config.duo_api_secret,
64
+ config.duo_api_hostname,
65
+ ],
66
+ ):
62
67
  logger.info(
63
- 'Duo import is not configured - skipping this module. '
64
- 'See docs to configure.',
68
+ "Duo import is not configured - skipping this module. "
69
+ "See docs to configure.",
65
70
  )
66
71
  return
67
72
 
@@ -8,31 +8,36 @@ from cartography.client.core.tx import load
8
8
  from cartography.models.duo.api_host import DuoApiHostSchema
9
9
  from cartography.util import timeit
10
10
 
11
-
12
11
  logger = logging.getLogger(__name__)
13
12
 
14
13
 
15
14
  @timeit
16
- def sync_duo_api_host(neo4j_session: neo4j.Session, common_job_parameters: Dict[str, Any]) -> None:
17
- '''
15
+ def sync_duo_api_host(
16
+ neo4j_session: neo4j.Session,
17
+ common_job_parameters: Dict[str, Any],
18
+ ) -> None:
19
+ """
18
20
  Add the DuoApiHost subresource
19
- '''
21
+ """
20
22
  _load_api_host(neo4j_session, common_job_parameters)
21
23
 
22
24
 
23
25
  @timeit
24
- def _load_api_host(neo4j_session: neo4j.Session, common_job_parameters: Dict[str, Any]) -> None:
25
- '''
26
+ def _load_api_host(
27
+ neo4j_session: neo4j.Session,
28
+ common_job_parameters: Dict[str, Any],
29
+ ) -> None:
30
+ """
26
31
  Load the host node into the graph
27
- '''
32
+ """
28
33
  data = [
29
34
  {
30
- 'id': common_job_parameters['DUO_API_HOSTNAME'],
35
+ "id": common_job_parameters["DUO_API_HOSTNAME"],
31
36
  },
32
37
  ]
33
38
  load(
34
39
  neo4j_session,
35
40
  DuoApiHostSchema(),
36
41
  data,
37
- lastupdated=common_job_parameters['UPDATE_TAG'],
42
+ lastupdated=common_job_parameters["UPDATE_TAG"],
38
43
  )
@@ -21,9 +21,9 @@ def sync_duo_endpoints(
21
21
  neo4j_session: neo4j.Session,
22
22
  common_job_parameters: Dict[str, Any],
23
23
  ) -> None:
24
- '''
24
+ """
25
25
  Sync Duo Endpoints
26
- '''
26
+ """
27
27
  endpoints = _get_endpoints(client)
28
28
  transformed_endpoints = _transform_endpoints(endpoints)
29
29
  _load_endpoints(neo4j_session, transformed_endpoints, common_job_parameters)
@@ -32,52 +32,52 @@ def sync_duo_endpoints(
32
32
 
33
33
  @timeit
34
34
  def _get_endpoints(client: duo_client.Admin) -> List[Dict[str, Any]]:
35
- '''
35
+ """
36
36
  Fetch all endpoint data
37
37
  https://duo.com/docs/adminapi#endpoints
38
- '''
38
+ """
39
39
  logger.info("Fetching Duo endpoints")
40
40
  return client.get_endpoints()
41
41
 
42
42
 
43
43
  @timeit
44
44
  def _transform_endpoints(endpoints: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
45
- '''
45
+ """
46
46
  Reformat the data before loading
47
- '''
48
- logger.info(f'Transforming {len(endpoints)} duo endpoints')
47
+ """
48
+ logger.info(f"Transforming {len(endpoints)} duo endpoints")
49
49
  transformed_endpoints = []
50
50
  for endpoint in endpoints:
51
51
  transformed_endpoint = {
52
- 'browsers': [dumps(browser) for browser in endpoint['browsers']],
53
- 'computer_sid': endpoint['computer_sid'],
54
- 'cpu_id': endpoint['cpu_id'],
55
- 'device_id': endpoint['device_id'],
56
- 'device_identifier': endpoint['device_identifier'],
57
- 'device_identifier_type': endpoint['device_identifier_type'],
58
- 'device_name': endpoint['device_name'],
59
- 'device_udid': endpoint['device_udid'],
60
- 'device_username': endpoint['device_username'],
61
- 'device_username_type': endpoint['device_username_type'],
62
- 'disk_encryption_status': endpoint['disk_encryption_status'],
63
- 'domain_sid': endpoint['domain_sid'],
64
- 'email': endpoint['email'],
65
- 'epkey': endpoint['epkey'],
66
- 'firewall_status': endpoint['firewall_status'],
67
- 'hardware_uuid': endpoint['hardware_uuid'],
68
- 'health_app_client_version': endpoint['health_app_client_version'],
69
- 'health_data_last_collected': endpoint['health_data_last_collected'],
70
- 'last_updated': endpoint['last_updated'],
71
- 'machine_guid': endpoint['machine_guid'],
72
- 'model': endpoint['model'],
73
- 'os_build': endpoint['os_build'],
74
- 'os_family': endpoint['os_family'],
75
- 'os_version': endpoint['os_version'],
76
- 'password_status': endpoint['password_status'],
77
- 'security_agents': [dumps(agent) for agent in endpoint['security_agents']],
78
- 'trusted_endpoint': endpoint['trusted_endpoint'],
79
- 'type': endpoint['type'],
80
- 'username': endpoint['username'],
52
+ "browsers": [dumps(browser) for browser in endpoint["browsers"]],
53
+ "computer_sid": endpoint["computer_sid"],
54
+ "cpu_id": endpoint["cpu_id"],
55
+ "device_id": endpoint["device_id"],
56
+ "device_identifier": endpoint["device_identifier"],
57
+ "device_identifier_type": endpoint["device_identifier_type"],
58
+ "device_name": endpoint["device_name"],
59
+ "device_udid": endpoint["device_udid"],
60
+ "device_username": endpoint["device_username"],
61
+ "device_username_type": endpoint["device_username_type"],
62
+ "disk_encryption_status": endpoint["disk_encryption_status"],
63
+ "domain_sid": endpoint["domain_sid"],
64
+ "email": endpoint["email"],
65
+ "epkey": endpoint["epkey"],
66
+ "firewall_status": endpoint["firewall_status"],
67
+ "hardware_uuid": endpoint["hardware_uuid"],
68
+ "health_app_client_version": endpoint["health_app_client_version"],
69
+ "health_data_last_collected": endpoint["health_data_last_collected"],
70
+ "last_updated": endpoint["last_updated"],
71
+ "machine_guid": endpoint["machine_guid"],
72
+ "model": endpoint["model"],
73
+ "os_build": endpoint["os_build"],
74
+ "os_family": endpoint["os_family"],
75
+ "os_version": endpoint["os_version"],
76
+ "password_status": endpoint["password_status"],
77
+ "security_agents": [dumps(agent) for agent in endpoint["security_agents"]],
78
+ "trusted_endpoint": endpoint["trusted_endpoint"],
79
+ "type": endpoint["type"],
80
+ "username": endpoint["username"],
81
81
  }
82
82
  transformed_endpoints.append(transformed_endpoint)
83
83
  return transformed_endpoints
@@ -89,22 +89,27 @@ def _load_endpoints(
89
89
  endpoints: List[Dict[str, Any]],
90
90
  common_job_parameters: Dict[str, Any],
91
91
  ) -> None:
92
- '''
92
+ """
93
93
  Load the endpoints into the database
94
- '''
95
- logger.info(f'Loading {len(endpoints)} duo endpoints')
94
+ """
95
+ logger.info(f"Loading {len(endpoints)} duo endpoints")
96
96
  load(
97
97
  neo4j_session,
98
98
  DuoEndpointSchema(),
99
99
  endpoints,
100
- DUO_API_HOSTNAME=common_job_parameters['DUO_API_HOSTNAME'],
101
- lastupdated=common_job_parameters['UPDATE_TAG'],
100
+ DUO_API_HOSTNAME=common_job_parameters["DUO_API_HOSTNAME"],
101
+ lastupdated=common_job_parameters["UPDATE_TAG"],
102
102
  )
103
103
 
104
104
 
105
105
  @timeit
106
- def _cleanup_endpoints(neo4j_session: neo4j.Session, common_job_parameters: Dict[str, Any]) -> None:
107
- '''
106
+ def _cleanup_endpoints(
107
+ neo4j_session: neo4j.Session,
108
+ common_job_parameters: Dict[str, Any],
109
+ ) -> None:
110
+ """
108
111
  Cleanup endpoints
109
- '''
110
- GraphJob.from_node_schema(DuoEndpointSchema(), common_job_parameters).run(neo4j_session)
112
+ """
113
+ GraphJob.from_node_schema(DuoEndpointSchema(), common_job_parameters).run(
114
+ neo4j_session,
115
+ )