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
@@ -26,7 +26,7 @@ def _get_user_roles(api_client: ApiClient, user_id: str, okta_org_id: str) -> st
26
26
  """
27
27
 
28
28
  # https://developer.okta.com/docs/reference/api/roles/#list-roles
29
- response = api_client.get_path(f'/{user_id}/roles')
29
+ response = api_client.get_path(f"/{user_id}/roles")
30
30
  check_rate_limit(response)
31
31
  return response.text
32
32
 
@@ -42,7 +42,7 @@ def _get_group_roles(api_client: ApiClient, group_id: str, okta_org_id: str) ->
42
42
  """
43
43
 
44
44
  # https://developer.okta.com/docs/reference/api/roles/#list-roles-assigned-to-group
45
- response = api_client.get_path(f'/{group_id}/roles')
45
+ response = api_client.get_path(f"/{group_id}/roles")
46
46
  check_rate_limit(response)
47
47
  return response.text
48
48
 
@@ -94,7 +94,12 @@ def transform_group_roles_data(data: str, okta_org_id: str) -> List[Dict]:
94
94
 
95
95
 
96
96
  @timeit
97
- def _load_user_role(neo4j_session: neo4j.Session, user_id: str, roles_data: List[Dict], okta_update_tag: int) -> None:
97
+ def _load_user_role(
98
+ neo4j_session: neo4j.Session,
99
+ user_id: str,
100
+ roles_data: List[Dict],
101
+ okta_update_tag: int,
102
+ ) -> None:
98
103
  ingest = """
99
104
  MATCH (user:OktaUser{id: $USER_ID})<-[:RESOURCE]-(org:OktaOrganization)
100
105
  WITH user,org
@@ -122,7 +127,9 @@ def _load_user_role(neo4j_session: neo4j.Session, user_id: str, roles_data: List
122
127
 
123
128
  @timeit
124
129
  def _load_group_role(
125
- neo4j_session: neo4j.Session, group_id: str, roles_data: List[Dict],
130
+ neo4j_session: neo4j.Session,
131
+ group_id: str,
132
+ roles_data: List[Dict],
126
133
  okta_update_tag: int,
127
134
  ) -> None:
128
135
  ingest = """
@@ -152,7 +159,10 @@ def _load_group_role(
152
159
 
153
160
  @timeit
154
161
  def sync_roles(
155
- neo4j_session: str, okta_org_id: str, okta_update_tag: int, okta_api_key: str,
162
+ neo4j_session: str,
163
+ okta_org_id: str,
164
+ okta_update_tag: int,
165
+ okta_api_key: str,
156
166
  sync_state: OktaSyncState,
157
167
  ) -> None:
158
168
  """
@@ -12,7 +12,6 @@ from cartography.intel.okta.sync_state import OktaSyncState
12
12
  from cartography.intel.okta.utils import check_rate_limit
13
13
  from cartography.util import timeit
14
14
 
15
-
16
15
  logger = logging.getLogger(__name__)
17
16
 
18
17
 
@@ -56,7 +55,9 @@ def _get_okta_users(user_client: UsersClient) -> List[Dict]:
56
55
 
57
56
 
58
57
  @timeit
59
- def transform_okta_user_list(okta_user_list: List[User]) -> Tuple[List[Dict], List[str]]:
58
+ def transform_okta_user_list(
59
+ okta_user_list: List[User],
60
+ ) -> Tuple[List[Dict], List[str]]:
60
61
  users: List[Dict] = []
61
62
  user_ids: List[str] = []
62
63
 
@@ -91,7 +92,9 @@ def transform_okta_user(okta_user: User) -> Dict:
91
92
  user_props["activated"] = None
92
93
 
93
94
  if okta_user.statusChanged:
94
- user_props["status_changed"] = okta_user.statusChanged.strftime("%m/%d/%Y, %H:%M:%S")
95
+ user_props["status_changed"] = okta_user.statusChanged.strftime(
96
+ "%m/%d/%Y, %H:%M:%S",
97
+ )
95
98
  else:
96
99
  user_props["status_changed"] = None
97
100
 
@@ -101,12 +104,16 @@ def transform_okta_user(okta_user: User) -> Dict:
101
104
  user_props["last_login"] = None
102
105
 
103
106
  if okta_user.lastUpdated:
104
- user_props["okta_last_updated"] = okta_user.lastUpdated.strftime("%m/%d/%Y, %H:%M:%S")
107
+ user_props["okta_last_updated"] = okta_user.lastUpdated.strftime(
108
+ "%m/%d/%Y, %H:%M:%S",
109
+ )
105
110
  else:
106
111
  user_props["okta_last_updated"] = None
107
112
 
108
113
  if okta_user.passwordChanged:
109
- user_props["password_changed"] = okta_user.passwordChanged.strftime("%m/%d/%Y, %H:%M:%S")
114
+ user_props["password_changed"] = okta_user.passwordChanged.strftime(
115
+ "%m/%d/%Y, %H:%M:%S",
116
+ )
110
117
  else:
111
118
  user_props["password_changed"] = None
112
119
 
@@ -120,7 +127,9 @@ def transform_okta_user(okta_user: User) -> Dict:
120
127
 
121
128
  @timeit
122
129
  def _load_okta_users(
123
- neo4j_session: neo4j.Session, okta_org_id: str, user_list: List[Dict],
130
+ neo4j_session: neo4j.Session,
131
+ okta_org_id: str,
132
+ user_list: List[Dict],
124
133
  okta_update_tag: int,
125
134
  ) -> None:
126
135
  """
@@ -175,8 +184,11 @@ def _load_okta_users(
175
184
 
176
185
  @timeit
177
186
  def sync_okta_users(
178
- neo4j_session: neo4j.Session, okta_org_id: str, okta_update_tag: int,
179
- okta_api_key: str, sync_state: OktaSyncState,
187
+ neo4j_session: neo4j.Session,
188
+ okta_org_id: str,
189
+ okta_update_tag: int,
190
+ okta_api_key: str,
191
+ sync_state: OktaSyncState,
180
192
  ) -> None:
181
193
  """
182
194
  Sync okta users
@@ -43,9 +43,9 @@ def check_rate_limit(response: Response) -> None:
43
43
  """
44
44
  rate_limit_threshold = 0.1
45
45
 
46
- remaining = response.headers.get('x-rate-limit-remaining')
47
- limit = response.headers.get('x-rate-limit-limit')
48
- reset_time = response.headers.get('x-rate-limit-reset')
46
+ remaining = response.headers.get("x-rate-limit-remaining")
47
+ limit = response.headers.get("x-rate-limit-limit")
48
+ reset_time = response.headers.get("x-rate-limit-reset")
49
49
 
50
50
  if remaining and limit and reset_time:
51
51
  if (int(remaining) / int(limit)) < rate_limit_threshold:
@@ -58,5 +58,7 @@ def check_rate_limit(response: Response) -> None:
58
58
  f"Okta API limit exceeded. Sleep time of {sleep_time_seconds} would exceed one minute. Crashing "
59
59
  f"Okta sync to avoid blocking.",
60
60
  )
61
- logger.warning(f"Okta rate limit threshold reached. Waiting {sleep_time_seconds} seconds.")
61
+ logger.warning(
62
+ f"Okta rate limit threshold reached. Waiting {sleep_time_seconds} seconds.",
63
+ )
62
64
  time.sleep(sleep_time_seconds)
@@ -4,9 +4,7 @@ import neo4j
4
4
  from pdpyras import APISession
5
5
 
6
6
  from cartography.config import Config
7
- from cartography.intel.pagerduty.escalation_policies import (
8
- sync_escalation_policies,
9
- )
7
+ from cartography.intel.pagerduty.escalation_policies import sync_escalation_policies
10
8
  from cartography.intel.pagerduty.schedules import sync_schedules
11
9
  from cartography.intel.pagerduty.services import sync_services
12
10
  from cartography.intel.pagerduty.teams import sync_teams
@@ -23,7 +21,8 @@ stat_handler = get_stats_client(__name__)
23
21
 
24
22
  @timeit
25
23
  def start_pagerduty_ingestion(
26
- neo4j_session: neo4j.Session, config: Config,
24
+ neo4j_session: neo4j.Session,
25
+ config: Config,
27
26
  ) -> None:
28
27
  """
29
28
  Perform ingestion of pagerduty data.
@@ -35,7 +34,9 @@ def start_pagerduty_ingestion(
35
34
  "UPDATE_TAG": config.update_tag,
36
35
  }
37
36
  if not config.pagerduty_api_key:
38
- logger.info('PagerDuty import is not configured - skipping this module. See docs to configure.')
37
+ logger.info(
38
+ "PagerDuty import is not configured - skipping this module. See docs to configure.",
39
+ )
39
40
  return
40
41
  session = APISession(config.pagerduty_api_key)
41
42
  if config.pagerduty_request_timeout is not None:
@@ -54,8 +55,8 @@ def start_pagerduty_ingestion(
54
55
 
55
56
  merge_module_sync_metadata(
56
57
  neo4j_session,
57
- group_type='pagerduty',
58
- group_id='module',
58
+ group_type="pagerduty",
59
+ group_id="module",
59
60
  synced_type="pagerduty",
60
61
  update_tag=config.update_tag,
61
62
  stat_handler=stat_handler,
@@ -31,7 +31,9 @@ def get_escalation_policies(pd_session: APISession) -> List[Dict[str, Any]]:
31
31
 
32
32
 
33
33
  def load_escalation_policy_data(
34
- neo4j_session: neo4j.Session, data: List[Dict], update_tag: int,
34
+ neo4j_session: neo4j.Session,
35
+ data: List[Dict],
36
+ update_tag: int,
35
37
  ) -> None:
36
38
  """
37
39
  Transform and load escalation_policy information
@@ -82,7 +84,9 @@ def load_escalation_policy_data(
82
84
 
83
85
 
84
86
  def _attach_rules(
85
- neo4j_session: neo4j.Session, data: List[Dict], update_tag: int,
87
+ neo4j_session: neo4j.Session,
88
+ data: List[Dict],
89
+ update_tag: int,
86
90
  ) -> None:
87
91
  """
88
92
  Add escalation policy rules, and attach them to targets.
@@ -122,7 +126,9 @@ def _attach_rules(
122
126
 
123
127
 
124
128
  def _attach_user_targets(
125
- neo4j_session: neo4j.Session, data: List[Dict], update_tag: int,
129
+ neo4j_session: neo4j.Session,
130
+ data: List[Dict],
131
+ update_tag: int,
126
132
  ) -> None:
127
133
  """
128
134
  Add relationship between escalation policy and services.
@@ -142,7 +148,9 @@ def _attach_user_targets(
142
148
 
143
149
 
144
150
  def _attach_schedule_targets(
145
- neo4j_session: neo4j.Session, data: List[Dict], update_tag: int,
151
+ neo4j_session: neo4j.Session,
152
+ data: List[Dict],
153
+ update_tag: int,
146
154
  ) -> None:
147
155
  """
148
156
  Add relationship between escalation policy and services.
@@ -162,7 +170,9 @@ def _attach_schedule_targets(
162
170
 
163
171
 
164
172
  def _attach_services(
165
- neo4j_session: neo4j.Session, data: List[Dict], update_tag: int,
173
+ neo4j_session: neo4j.Session,
174
+ data: List[Dict],
175
+ update_tag: int,
166
176
  ) -> None:
167
177
  """
168
178
  Add relationship between escalation policy and services.
@@ -182,7 +192,9 @@ def _attach_services(
182
192
 
183
193
 
184
194
  def _attach_teams(
185
- neo4j_session: neo4j.Session, data: List[Dict], update_tag: int,
195
+ neo4j_session: neo4j.Session,
196
+ data: List[Dict],
197
+ update_tag: int,
186
198
  ) -> None:
187
199
  """
188
200
  Add relationship between escalation policy and teams.
@@ -32,7 +32,9 @@ def get_schedules(pd_session: APISession) -> List[Dict[str, Any]]:
32
32
 
33
33
 
34
34
  def load_schedule_data(
35
- neo4j_session: neo4j.Session, data: List[Dict], update_tag: int,
35
+ neo4j_session: neo4j.Session,
36
+ data: List[Dict],
37
+ update_tag: int,
36
38
  ) -> None:
37
39
  """
38
40
  Transform and load schedule information
@@ -72,7 +74,9 @@ def load_schedule_data(
72
74
 
73
75
 
74
76
  def _attach_users(
75
- neo4j_session: neo4j.Session, data: List[Dict], update_tag: int,
77
+ neo4j_session: neo4j.Session,
78
+ data: List[Dict],
79
+ update_tag: int,
76
80
  ) -> None:
77
81
  """
78
82
  Add relationship between schedule and users.
@@ -91,7 +95,9 @@ def _attach_users(
91
95
 
92
96
 
93
97
  def _attach_layers(
94
- neo4j_session: neo4j.Session, data: List[Dict], update_tag: int,
98
+ neo4j_session: neo4j.Session,
99
+ data: List[Dict],
100
+ update_tag: int,
95
101
  ) -> None:
96
102
  """
97
103
  Create layers for a schedule and attach them together
@@ -133,7 +139,9 @@ def _attach_layers(
133
139
 
134
140
 
135
141
  def _attach_layer_users(
136
- neo4j_session: neo4j.Session, data: List[Dict], update_tag: int,
142
+ neo4j_session: neo4j.Session,
143
+ data: List[Dict],
144
+ update_tag: int,
137
145
  ) -> None:
138
146
  """
139
147
  Add relationship between schedule layers and users.
@@ -34,7 +34,8 @@ def get_services(pd_session: APISession) -> List[Dict[str, Any]]:
34
34
 
35
35
  @timeit
36
36
  def get_integrations(
37
- pd_session: APISession, services: List[Dict[str, Any]],
37
+ pd_session: APISession,
38
+ services: List[Dict[str, Any]],
38
39
  ) -> List[Dict[str, Any]]:
39
40
  """
40
41
  Get integrations from services.
@@ -51,7 +52,9 @@ def get_integrations(
51
52
 
52
53
 
53
54
  def load_service_data(
54
- neo4j_session: neo4j.Session, data: List[Dict], update_tag: int,
55
+ neo4j_session: neo4j.Session,
56
+ data: List[Dict],
57
+ update_tag: int,
55
58
  ) -> None:
56
59
  """
57
60
  Transform and load service information
@@ -104,7 +107,9 @@ def load_service_data(
104
107
 
105
108
 
106
109
  def _attach_teams(
107
- neo4j_session: neo4j.Session, data: List[Dict], update_tag: int,
110
+ neo4j_session: neo4j.Session,
111
+ data: List[Dict],
112
+ update_tag: int,
108
113
  ) -> None:
109
114
  """
110
115
  Add relationship between teams and services.
@@ -123,7 +128,9 @@ def _attach_teams(
123
128
 
124
129
 
125
130
  def load_integration_data(
126
- neo4j_session: neo4j.Session, data: List[Dict], update_tag: int,
131
+ neo4j_session: neo4j.Session,
132
+ data: List[Dict],
133
+ update_tag: int,
127
134
  ) -> None:
128
135
  """
129
136
  Transform and load integration information
@@ -33,7 +33,8 @@ def get_teams(pd_session: APISession) -> List[Dict[str, Any]]:
33
33
 
34
34
  @timeit
35
35
  def get_team_members(
36
- pd_session: APISession, teams: List[Dict[str, Any]],
36
+ pd_session: APISession,
37
+ teams: List[Dict[str, Any]],
37
38
  ) -> List[Dict[str, str]]:
38
39
  relations: List[Dict[str, str]] = []
39
40
  for team in teams:
@@ -46,7 +47,9 @@ def get_team_members(
46
47
 
47
48
 
48
49
  def load_team_data(
49
- neo4j_session: neo4j.Session, data: List[Dict], update_tag: int,
50
+ neo4j_session: neo4j.Session,
51
+ data: List[Dict],
52
+ update_tag: int,
50
53
  ) -> None:
51
54
  """
52
55
  Transform and load teamuser information
@@ -73,7 +76,9 @@ def load_team_data(
73
76
 
74
77
 
75
78
  def load_team_relations(
76
- neo4j_session: neo4j.Session, data: List[Dict], update_tag: int,
79
+ neo4j_session: neo4j.Session,
80
+ data: List[Dict],
81
+ update_tag: int,
77
82
  ) -> None:
78
83
  """
79
84
  Attach users to their teams
@@ -30,7 +30,9 @@ def get_users(pd_session: APISession) -> List[Dict[str, Any]]:
30
30
 
31
31
 
32
32
  def load_user_data(
33
- neo4j_session: neo4j.Session, data: List[Dict], update_tag: int,
33
+ neo4j_session: neo4j.Session,
34
+ data: List[Dict],
35
+ update_tag: int,
34
36
  ) -> None:
35
37
  """
36
38
  Transform and load user information
@@ -30,7 +30,9 @@ def get_vendors(pd_session: APISession) -> List[Dict[str, Any]]:
30
30
 
31
31
 
32
32
  def load_vendor_data(
33
- neo4j_session: neo4j.Session, data: List[Dict], update_tag: int,
33
+ neo4j_session: neo4j.Session,
34
+ data: List[Dict],
35
+ update_tag: int,
34
36
  ) -> None:
35
37
  """
36
38
  Transform and load vendor information
@@ -8,23 +8,41 @@ from cartography.intel.semgrep.deployment import sync_deployment
8
8
  from cartography.intel.semgrep.findings import sync_findings
9
9
  from cartography.util import timeit
10
10
 
11
-
12
11
  logger = logging.getLogger(__name__)
13
12
 
14
13
 
15
14
  @timeit
16
15
  def start_semgrep_ingestion(
17
- neo4j_session: neo4j.Session, config: Config,
16
+ neo4j_session: neo4j.Session,
17
+ config: Config,
18
18
  ) -> None:
19
19
  common_job_parameters = {
20
20
  "UPDATE_TAG": config.update_tag,
21
21
  }
22
22
  if not config.semgrep_app_token:
23
- logger.info('Semgrep import is not configured - skipping this module. See docs to configure.')
23
+ logger.info(
24
+ "Semgrep import is not configured - skipping this module. See docs to configure.",
25
+ )
24
26
  return
25
27
 
26
28
  # sync_deployment must be called first since it populates common_job_parameters
27
29
  # with the deployment ID and slug, which are required by the other sync functions
28
- sync_deployment(neo4j_session, config.semgrep_app_token, config.update_tag, common_job_parameters)
29
- sync_dependencies(neo4j_session, config.semgrep_app_token, config.semgrep_dependency_ecosystems, config.update_tag, common_job_parameters) # noqa: E501
30
- sync_findings(neo4j_session, config.semgrep_app_token, config.update_tag, common_job_parameters)
30
+ sync_deployment(
31
+ neo4j_session,
32
+ config.semgrep_app_token,
33
+ config.update_tag,
34
+ common_job_parameters,
35
+ )
36
+ sync_dependencies(
37
+ neo4j_session,
38
+ config.semgrep_app_token,
39
+ config.semgrep_dependency_ecosystems,
40
+ config.update_tag,
41
+ common_job_parameters,
42
+ ) # noqa: E501
43
+ sync_findings(
44
+ neo4j_session,
45
+ config.semgrep_app_token,
46
+ config.update_tag,
47
+ common_job_parameters,
48
+ )
@@ -26,30 +26,34 @@ _MAX_RETRIES = 3
26
26
  # The keys in this dictionary must be in Semgrep's list of supported ecosystems, defined here:
27
27
  # https://semgrep.dev/api/v1/docs/#tag/SupplyChainService/operation/semgrep_app.products.sca.handlers.dependency.list_dependencies_conexxion
28
28
  ECOSYSTEM_TO_SCHEMA: Dict = {
29
- 'gomod': SemgrepGoLibrarySchema,
30
- 'npm': SemgrepNpmLibrarySchema,
29
+ "gomod": SemgrepGoLibrarySchema,
30
+ "npm": SemgrepNpmLibrarySchema,
31
31
  }
32
32
 
33
33
 
34
34
  def parse_and_validate_semgrep_ecosystems(ecosystems: str) -> List[str]:
35
35
  validated_ecosystems: List[str] = []
36
- for ecosystem in ecosystems.split(','):
36
+ for ecosystem in ecosystems.split(","):
37
37
  ecosystem = ecosystem.strip().lower()
38
38
 
39
39
  if ecosystem in ECOSYSTEM_TO_SCHEMA:
40
40
  validated_ecosystems.append(ecosystem)
41
41
  else:
42
- valid_ecosystems: str = ','.join(ECOSYSTEM_TO_SCHEMA.keys())
42
+ valid_ecosystems: str = ",".join(ECOSYSTEM_TO_SCHEMA.keys())
43
43
  raise ValueError(
44
44
  f'Error parsing `semgrep-dependency-ecosystems`. You specified "{ecosystems}". '
45
45
  f'Please check that your input is formatted as comma-separated values, e.g. "gomod,npm". '
46
- f'Full list of supported ecosystems: {valid_ecosystems}.',
46
+ f"Full list of supported ecosystems: {valid_ecosystems}.",
47
47
  )
48
48
  return validated_ecosystems
49
49
 
50
50
 
51
51
  @timeit
52
- def get_dependencies(semgrep_app_token: str, deployment_id: str, ecosystem: str) -> List[Dict[str, Any]]:
52
+ def get_dependencies(
53
+ semgrep_app_token: str,
54
+ deployment_id: str,
55
+ ecosystem: str,
56
+ ) -> List[Dict[str, Any]]:
53
57
  """
54
58
  Gets all dependencies for the given ecosystem within the given Semgrep deployment ID.
55
59
  param: semgrep_app_token: The Semgrep App token to use for authentication.
@@ -73,14 +77,23 @@ def get_dependencies(semgrep_app_token: str, deployment_id: str, ecosystem: str)
73
77
  },
74
78
  }
75
79
 
76
- logger.info(f"Retrieving Semgrep {ecosystem} dependencies for deployment '{deployment_id}'.")
80
+ logger.info(
81
+ f"Retrieving Semgrep {ecosystem} dependencies for deployment '{deployment_id}'.",
82
+ )
77
83
  while has_more:
78
84
  try:
79
- response = requests.post(deps_url, json=request_data, headers=headers, timeout=_TIMEOUT)
85
+ response = requests.post(
86
+ deps_url,
87
+ json=request_data,
88
+ headers=headers,
89
+ timeout=_TIMEOUT,
90
+ )
80
91
  response.raise_for_status()
81
92
  data = response.json()
82
93
  except (ReadTimeout, HTTPError):
83
- logger.warning(f"Failed to retrieve Semgrep {ecosystem} dependencies for page {page}. Retrying...")
94
+ logger.warning(
95
+ f"Failed to retrieve Semgrep {ecosystem} dependencies for page {page}. Retrying...",
96
+ )
84
97
  retries += 1
85
98
  if retries >= _MAX_RETRIES:
86
99
  raise
@@ -93,7 +106,9 @@ def get_dependencies(semgrep_app_token: str, deployment_id: str, ecosystem: str)
93
106
  page += 1
94
107
  request_data["cursor"] = data.get("cursor")
95
108
 
96
- logger.info(f"Retrieved {len(all_deps)} Semgrep {ecosystem} dependencies in {page} pages.")
109
+ logger.info(
110
+ f"Retrieved {len(all_deps)} Semgrep {ecosystem} dependencies in {page} pages.",
111
+ )
97
112
  return all_deps
98
113
 
99
114
 
@@ -142,19 +157,20 @@ def transform_dependencies(raw_deps: List[Dict[str, Any]]) -> List[Dict[str, Any
142
157
  # If Semgrep eventually supports version specifiers, update this line accordingly.
143
158
  specifier = f"=={version}"
144
159
 
145
- deps.append({
146
- # existing dependency properties:
147
- "id": id,
148
- "name": name,
149
- "specifier": specifier,
150
- "version": version,
151
- "repo_url": repo_url,
152
-
153
- # Semgrep-specific properties:
154
- "ecosystem": raw_dep["ecosystem"],
155
- "transitivity": raw_dep["transitivity"].lower(),
156
- "url": raw_dep["definedAt"]["url"],
157
- })
160
+ deps.append(
161
+ {
162
+ # existing dependency properties:
163
+ "id": id,
164
+ "name": name,
165
+ "specifier": specifier,
166
+ "version": version,
167
+ "repo_url": repo_url,
168
+ # Semgrep-specific properties:
169
+ "ecosystem": raw_dep["ecosystem"],
170
+ "transitivity": raw_dep["transitivity"].lower(),
171
+ "url": raw_dep["definedAt"]["url"],
172
+ },
173
+ )
158
174
 
159
175
  return deps
160
176
 
@@ -167,7 +183,9 @@ def load_dependencies(
167
183
  deployment_id: str,
168
184
  update_tag: int,
169
185
  ) -> None:
170
- logger.info(f"Loading {len(dependencies)} {dependency_schema().label} objects into the graph.")
186
+ logger.info(
187
+ f"Loading {len(dependencies)} {dependency_schema().label} objects into the graph.",
188
+ )
171
189
  load(
172
190
  neo4j_session,
173
191
  dependency_schema(),
@@ -183,8 +201,12 @@ def cleanup(
183
201
  dependency_schema: Callable,
184
202
  common_job_parameters: Dict[str, Any],
185
203
  ) -> None:
186
- logger.info(f"Running Semgrep Dependencies cleanup job for {dependency_schema().label}.")
187
- GraphJob.from_node_schema(dependency_schema(), common_job_parameters).run(neo4j_session)
204
+ logger.info(
205
+ f"Running Semgrep Dependencies cleanup job for {dependency_schema().label}.",
206
+ )
207
+ GraphJob.from_node_schema(dependency_schema(), common_job_parameters).run(
208
+ neo4j_session,
209
+ )
188
210
 
189
211
 
190
212
  @timeit
@@ -225,9 +247,9 @@ def sync_dependencies(
225
247
 
226
248
  merge_module_sync_metadata(
227
249
  neo4j_session=neo4j_session,
228
- group_type='Semgrep',
250
+ group_type="Semgrep",
229
251
  group_id=deployment_id,
230
- synced_type='SemgrepDependency',
252
+ synced_type="SemgrepDependency",
231
253
  update_tag=update_tag,
232
254
  stat_handler=stat_handler,
233
255
  )
@@ -40,7 +40,9 @@ def get_deployment(semgrep_app_token: str) -> Dict[str, Any]:
40
40
 
41
41
  @timeit
42
42
  def load_semgrep_deployment(
43
- neo4j_session: neo4j.Session, deployment: Dict[str, Any], update_tag: int,
43
+ neo4j_session: neo4j.Session,
44
+ deployment: Dict[str, Any],
45
+ update_tag: int,
44
46
  ) -> None:
45
47
  logger.info(f"Loading SemgrepDeployment {deployment} into the graph.")
46
48
  load(