cartography 0.95.0rc1__py3-none-any.whl → 0.96.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 (44) hide show
  1. cartography/cli.py +15 -0
  2. cartography/client/core/tx.py +1 -1
  3. cartography/config.py +6 -2
  4. cartography/data/indexes.cypher +1 -2
  5. cartography/data/jobs/cleanup/aws_import_identity_center_cleanup.json +16 -0
  6. cartography/data/jobs/cleanup/{github_users_cleanup.json → github_org_and_users_cleanup.json} +5 -0
  7. cartography/data/jobs/cleanup/github_repos_cleanup.json +25 -0
  8. cartography/graph/querybuilder.py +4 -0
  9. cartography/intel/aws/apigateway.py +3 -3
  10. cartography/intel/aws/ec2/auto_scaling_groups.py +147 -185
  11. cartography/intel/aws/ec2/instances.py +2 -0
  12. cartography/intel/aws/ec2/network_acls.py +209 -0
  13. cartography/intel/aws/ec2/subnets.py +2 -0
  14. cartography/intel/aws/iam.py +4 -3
  15. cartography/intel/aws/identitycenter.py +307 -0
  16. cartography/intel/aws/resources.py +4 -0
  17. cartography/intel/cve/__init__.py +1 -1
  18. cartography/intel/cve/feed.py +10 -7
  19. cartography/intel/github/repos.py +176 -27
  20. cartography/intel/github/users.py +156 -39
  21. cartography/intel/okta/users.py +2 -1
  22. cartography/intel/semgrep/__init__.py +9 -2
  23. cartography/intel/semgrep/dependencies.py +233 -0
  24. cartography/intel/semgrep/deployment.py +67 -0
  25. cartography/intel/semgrep/findings.py +22 -53
  26. cartography/models/aws/ec2/auto_scaling_groups.py +204 -0
  27. cartography/models/aws/ec2/launch_configurations.py +55 -0
  28. cartography/models/aws/ec2/network_acl_rules.py +98 -0
  29. cartography/models/aws/ec2/network_acls.py +86 -0
  30. cartography/models/aws/identitycenter/__init__.py +0 -0
  31. cartography/models/aws/identitycenter/awsidentitycenter.py +44 -0
  32. cartography/models/aws/identitycenter/awspermissionset.py +84 -0
  33. cartography/models/aws/identitycenter/awsssouser.py +68 -0
  34. cartography/models/core/common.py +18 -1
  35. cartography/models/github/orgs.py +26 -0
  36. cartography/models/github/users.py +119 -0
  37. cartography/models/semgrep/dependencies.py +90 -0
  38. cartography-0.96.0.dist-info/METADATA +53 -0
  39. {cartography-0.95.0rc1.dist-info → cartography-0.96.0.dist-info}/RECORD +43 -27
  40. {cartography-0.95.0rc1.dist-info → cartography-0.96.0.dist-info}/WHEEL +1 -1
  41. cartography-0.95.0rc1.dist-info/METADATA +0 -53
  42. {cartography-0.95.0rc1.dist-info → cartography-0.96.0.dist-info}/LICENSE +0 -0
  43. {cartography-0.95.0rc1.dist-info → cartography-0.96.0.dist-info}/entry_points.txt +0 -0
  44. {cartography-0.95.0rc1.dist-info → cartography-0.96.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,209 @@
1
+ import logging
2
+ from collections import namedtuple
3
+ from typing import Any
4
+
5
+ import boto3
6
+ import neo4j
7
+
8
+ from .util import get_botocore_config
9
+ from cartography.client.core.tx import load
10
+ from cartography.graph.job import GraphJob
11
+ from cartography.models.aws.ec2.network_acl_rules import EC2NetworkAclEgressRuleSchema
12
+ from cartography.models.aws.ec2.network_acl_rules import EC2NetworkAclInboundRuleSchema
13
+ from cartography.models.aws.ec2.network_acls import EC2NetworkAclSchema
14
+ from cartography.util import aws_handle_regions
15
+ from cartography.util import timeit
16
+
17
+ logger = logging.getLogger(__name__)
18
+
19
+ Ec2AclObjects = namedtuple(
20
+ "Ec2AclObjects", [
21
+ 'network_acls',
22
+ 'inbound_rules',
23
+ 'outbound_rules',
24
+ ],
25
+ )
26
+
27
+
28
+ @timeit
29
+ @aws_handle_regions
30
+ def get_network_acl_data(boto3_session: boto3.session.Session, region: str) -> list[dict[str, Any]]:
31
+ client = boto3_session.client('ec2', region_name=region, config=get_botocore_config())
32
+ paginator = client.get_paginator('describe_network_acls')
33
+ acls = []
34
+ for page in paginator.paginate():
35
+ acls.extend(page['NetworkAcls'])
36
+ return acls
37
+
38
+
39
+ def transform_network_acl_data(
40
+ data_list: list[dict[str, Any]],
41
+ region: str,
42
+ current_aws_account_id: str,
43
+ ) -> Ec2AclObjects:
44
+ network_acls = []
45
+ inbound_rules = []
46
+ outbound_rules = []
47
+
48
+ for network_acl in data_list:
49
+ network_acl_id = network_acl['NetworkAclId']
50
+ base_network_acl = {
51
+ 'Id': network_acl_id,
52
+ 'Arn': f'arn:aws:ec2:{region}:{current_aws_account_id}:network-acl/{network_acl_id}',
53
+ 'IsDefault': network_acl['IsDefault'],
54
+ 'VpcId': network_acl['VpcId'],
55
+ 'OwnerId': network_acl['OwnerId'],
56
+ }
57
+ if network_acl.get('Associations') and network_acl['Associations']:
58
+ # Include subnet associations in the data object if they exist
59
+ for association in network_acl['Associations']:
60
+ base_network_acl['NetworkAclAssociationId'] = association['NetworkAclAssociationId']
61
+ base_network_acl['SubnetId'] = association['SubnetId']
62
+ network_acls.append(base_network_acl)
63
+ else:
64
+ # Otherwise if there's no associations then don't include that in the data object
65
+ network_acls.append(base_network_acl)
66
+
67
+ if network_acl.get("Entries"):
68
+ for rule in network_acl["Entries"]:
69
+ direction = 'egress' if rule['Egress'] else 'inbound'
70
+ transformed_rule = {
71
+ 'Id': f"{network_acl['NetworkAclId']}/{direction}/{rule['RuleNumber']}",
72
+ 'CidrBlock': rule.get('CidrBlock'),
73
+ 'Ipv6CidrBlock': rule.get('Ipv6CidrBlock'),
74
+ 'Egress': rule['Egress'],
75
+ 'Protocol': rule['Protocol'],
76
+ 'RuleAction': rule['RuleAction'],
77
+ 'RuleNumber': rule['RuleNumber'],
78
+ # Add pointer back to the nacl to create an edge
79
+ 'NetworkAclId': network_acl_id,
80
+ 'FromPort': rule.get('PortRange', {}).get('FromPort'),
81
+ 'ToPort': rule.get('PortRange', {}).get('ToPort'),
82
+ }
83
+ if transformed_rule['Egress']:
84
+ outbound_rules.append(transformed_rule)
85
+ else:
86
+ inbound_rules.append(transformed_rule)
87
+ return Ec2AclObjects(
88
+ network_acls=network_acls,
89
+ inbound_rules=inbound_rules,
90
+ outbound_rules=outbound_rules,
91
+ )
92
+
93
+
94
+ @timeit
95
+ def load_all_nacl_data(
96
+ neo4j_session: neo4j.Session,
97
+ ec2_acl_objects: Ec2AclObjects,
98
+ region: str,
99
+ aws_account_id: str,
100
+ update_tag: int,
101
+ ) -> None:
102
+ load_network_acls(
103
+ neo4j_session,
104
+ ec2_acl_objects.network_acls,
105
+ region,
106
+ aws_account_id,
107
+ update_tag,
108
+ )
109
+ load_network_acl_inbound_rules(
110
+ neo4j_session,
111
+ ec2_acl_objects.inbound_rules,
112
+ region,
113
+ aws_account_id,
114
+ update_tag,
115
+ )
116
+ load_network_acl_egress_rules(
117
+ neo4j_session,
118
+ ec2_acl_objects.outbound_rules,
119
+ region,
120
+ aws_account_id,
121
+ update_tag,
122
+ )
123
+
124
+
125
+ @timeit
126
+ def load_network_acls(
127
+ neo4j_session: neo4j.Session,
128
+ data: list[dict[str, Any]],
129
+ region: str,
130
+ aws_account_id: str,
131
+ update_tag: int,
132
+ ) -> None:
133
+ logger.info(f"Loading {len(data)} network acls in {region}.")
134
+ load(
135
+ neo4j_session,
136
+ EC2NetworkAclSchema(),
137
+ data,
138
+ Region=region,
139
+ AWS_ID=aws_account_id,
140
+ lastupdated=update_tag,
141
+ )
142
+
143
+
144
+ @timeit
145
+ def load_network_acl_inbound_rules(
146
+ neo4j_session: neo4j.Session,
147
+ data: list[dict[str, Any]],
148
+ region: str,
149
+ aws_account_id: str,
150
+ update_tag: int,
151
+ ) -> None:
152
+ logger.info(f"Loading {len(data)} network acl inbound rules in {region}.")
153
+ load(
154
+ neo4j_session,
155
+ EC2NetworkAclInboundRuleSchema(),
156
+ data,
157
+ Region=region,
158
+ AWS_ID=aws_account_id,
159
+ lastupdated=update_tag,
160
+ )
161
+
162
+
163
+ @timeit
164
+ def load_network_acl_egress_rules(
165
+ neo4j_session: neo4j.Session,
166
+ data: list[dict[str, Any]],
167
+ region: str,
168
+ aws_account_id: str,
169
+ update_tag: int,
170
+ ) -> None:
171
+ logger.info(f"Loading {len(data)} network acl egress rules in {region}.")
172
+ load(
173
+ neo4j_session,
174
+ EC2NetworkAclEgressRuleSchema(),
175
+ data,
176
+ Region=region,
177
+ AWS_ID=aws_account_id,
178
+ lastupdated=update_tag,
179
+ )
180
+
181
+
182
+ @timeit
183
+ def cleanup_network_acls(neo4j_session: neo4j.Session, common_job_parameters: dict[str, Any]) -> None:
184
+ GraphJob.from_node_schema(EC2NetworkAclSchema(), common_job_parameters).run(neo4j_session)
185
+ GraphJob.from_node_schema(EC2NetworkAclInboundRuleSchema(), common_job_parameters).run(neo4j_session)
186
+ GraphJob.from_node_schema(EC2NetworkAclEgressRuleSchema(), common_job_parameters).run(neo4j_session)
187
+
188
+
189
+ @timeit
190
+ def sync_network_acls(
191
+ neo4j_session: neo4j.Session,
192
+ boto3_session: boto3.session.Session,
193
+ regions: list[str],
194
+ current_aws_account_id: str,
195
+ update_tag: int,
196
+ common_job_parameters: dict[str, Any],
197
+ ) -> None:
198
+ for region in regions:
199
+ logger.info(f"Syncing EC2 network ACLs for region '{region}' in account '{current_aws_account_id}'.")
200
+ data = get_network_acl_data(boto3_session, region)
201
+ ec2_acl_data = transform_network_acl_data(data, region, current_aws_account_id)
202
+ load_all_nacl_data(
203
+ neo4j_session,
204
+ ec2_acl_data,
205
+ region,
206
+ current_aws_account_id,
207
+ update_tag,
208
+ )
209
+ cleanup_network_acls(neo4j_session, common_job_parameters)
@@ -7,6 +7,7 @@ import neo4j
7
7
 
8
8
  from .util import get_botocore_config
9
9
  from cartography.graph.job import GraphJob
10
+ from cartography.models.aws.ec2.auto_scaling_groups import EC2SubnetAutoScalingGroupSchema
10
11
  from cartography.models.aws.ec2.subnet_instance import EC2SubnetInstanceSchema
11
12
  from cartography.util import aws_handle_regions
12
13
  from cartography.util import run_cleanup_job
@@ -79,6 +80,7 @@ def load_subnets(
79
80
  def cleanup_subnets(neo4j_session: neo4j.Session, common_job_parameters: Dict) -> None:
80
81
  run_cleanup_job('aws_ingest_subnets_cleanup.json', neo4j_session, common_job_parameters)
81
82
  GraphJob.from_node_schema(EC2SubnetInstanceSchema(), common_job_parameters).run(neo4j_session)
83
+ GraphJob.from_node_schema(EC2SubnetAutoScalingGroupSchema(), common_job_parameters).run(neo4j_session)
82
84
 
83
85
 
84
86
  @timeit
@@ -539,11 +539,12 @@ def _transform_policy_statements(statements: Any, policy_id: str) -> List[Dict]:
539
539
  if not isinstance(statements, list):
540
540
  statements = [statements]
541
541
  for stmt in statements:
542
- if "Sid" not in stmt:
542
+ if "Sid" in stmt and stmt["Sid"]:
543
+ statement_id = stmt["Sid"]
544
+ else:
543
545
  statement_id = count
544
546
  count += 1
545
- else:
546
- statement_id = stmt["Sid"]
547
+
547
548
  stmt["id"] = f"{policy_id}/statement/{statement_id}"
548
549
  if "Resource" in stmt:
549
550
  stmt["Resource"] = ensure_list(stmt["Resource"])
@@ -0,0 +1,307 @@
1
+ import logging
2
+ from typing import Any
3
+ from typing import Dict
4
+ from typing import List
5
+
6
+ import boto3
7
+ import neo4j
8
+
9
+ from cartography.client.core.tx import load
10
+ from cartography.graph.job import GraphJob
11
+ from cartography.models.aws.identitycenter.awsidentitycenter import AWSIdentityCenterInstanceSchema
12
+ from cartography.models.aws.identitycenter.awspermissionset import AWSPermissionSetSchema
13
+ from cartography.models.aws.identitycenter.awsssouser import AWSSSOUserSchema
14
+ from cartography.util import aws_handle_regions
15
+ from cartography.util import run_cleanup_job
16
+ from cartography.util import timeit
17
+ logger = logging.getLogger(__name__)
18
+
19
+
20
+ @timeit
21
+ @aws_handle_regions
22
+ def get_identity_center_instances(boto3_session: boto3.session.Session, region: str) -> List[Dict]:
23
+ """
24
+ Get all AWS IAM Identity Center instances in the current region
25
+ """
26
+ client = boto3_session.client('sso-admin', region_name=region)
27
+ instances = []
28
+
29
+ paginator = client.get_paginator('list_instances')
30
+ for page in paginator.paginate():
31
+ instances.extend(page.get('Instances', []))
32
+
33
+ return instances
34
+
35
+
36
+ @timeit
37
+ def load_identity_center_instances(
38
+ neo4j_session: neo4j.Session,
39
+ instance_data: List[Dict],
40
+ region: str,
41
+ current_aws_account_id: str,
42
+ aws_update_tag: int,
43
+ ) -> None:
44
+ """
45
+ Load Identity Center instances into the graph
46
+ """
47
+ logger.info(f"Loading {len(instance_data)} Identity Center instances for region {region}")
48
+ load(
49
+ neo4j_session,
50
+ AWSIdentityCenterInstanceSchema(),
51
+ instance_data,
52
+ lastupdated=aws_update_tag,
53
+ Region=region,
54
+ AWS_ID=current_aws_account_id,
55
+ )
56
+
57
+
58
+ @timeit
59
+ def get_permission_sets(boto3_session: boto3.session.Session, instance_arn: str, region: str) -> List[Dict]:
60
+ """
61
+ Get all permission sets for a given Identity Center instance
62
+ """
63
+ client = boto3_session.client('sso-admin', region_name=region)
64
+ permission_sets = []
65
+
66
+ paginator = client.get_paginator('list_permission_sets')
67
+ for page in paginator.paginate(InstanceArn=instance_arn):
68
+ # Get detailed info for each permission set
69
+ for arn in page.get('PermissionSets', []):
70
+ details = client.describe_permission_set(
71
+ InstanceArn=instance_arn,
72
+ PermissionSetArn=arn,
73
+ )
74
+ permission_set = details.get('PermissionSet', {})
75
+ if permission_set:
76
+ permission_set['RoleHint'] = (
77
+ f":role/aws-reserved/sso.amazonaws.com/AWSReservedSSO_{permission_set.get('Name')}"
78
+ )
79
+ permission_sets.append(permission_set)
80
+
81
+ return permission_sets
82
+
83
+
84
+ @timeit
85
+ def get_permission_set_roles(
86
+ boto3_session: boto3.session.Session,
87
+ instance_arn: str,
88
+ permission_set_arn: str,
89
+ region: str,
90
+ ) -> List[Dict]:
91
+ """
92
+ Get all accounts associated with a given permission set
93
+ """
94
+ client = boto3_session.client('sso-admin', region_name=region)
95
+ accounts = []
96
+
97
+ paginator = client.get_paginator('list_accounts_for_provisioned_permission_set')
98
+ for page in paginator.paginate(InstanceArn=instance_arn, PermissionSetArn=permission_set_arn):
99
+ accounts.extend(page.get('AccountIds', []))
100
+
101
+ return accounts
102
+
103
+
104
+ @timeit
105
+ def load_permission_sets(
106
+ neo4j_session: neo4j.Session,
107
+ permission_sets: List[Dict],
108
+ instance_arn: str,
109
+ region: str,
110
+ aws_account_id: str,
111
+ aws_update_tag: int,
112
+ ) -> None:
113
+ """
114
+ Load Identity Center permission sets into the graph
115
+ """
116
+ logger.info(f"Loading {len(permission_sets)} permission sets for instance {instance_arn} in region {region}")
117
+
118
+ load(
119
+ neo4j_session,
120
+ AWSPermissionSetSchema(),
121
+ permission_sets,
122
+ lastupdated=aws_update_tag,
123
+ InstanceArn=instance_arn,
124
+ Region=region,
125
+ AWS_ID=aws_account_id,
126
+ )
127
+
128
+
129
+ @timeit
130
+ def get_sso_users(
131
+ boto3_session: boto3.session.Session,
132
+ identity_store_id: str,
133
+ region: str,
134
+ ) -> List[Dict]:
135
+ """
136
+ Get all SSO users for a given Identity Store
137
+ """
138
+ client = boto3_session.client('identitystore', region_name=region)
139
+ users = []
140
+
141
+ paginator = client.get_paginator('list_users')
142
+ for page in paginator.paginate(IdentityStoreId=identity_store_id):
143
+ user_page = page.get('Users', [])
144
+ for user in user_page:
145
+ if user.get('ExternalIds', None):
146
+ user['ExternalId'] = user.get('ExternalIds')[0].get('Id')
147
+ users.append(user)
148
+
149
+ return users
150
+
151
+
152
+ @timeit
153
+ def load_sso_users(
154
+ neo4j_session: neo4j.Session,
155
+ users: List[Dict],
156
+ identity_store_id: str,
157
+ region: str,
158
+ aws_account_id: str,
159
+ aws_update_tag: int,
160
+ ) -> None:
161
+ """
162
+ Load SSO users into the graph
163
+ """
164
+ logger.info(f"Loading {len(users)} SSO users for identity store {identity_store_id} in region {region}")
165
+
166
+ load(
167
+ neo4j_session,
168
+ AWSSSOUserSchema(),
169
+ users,
170
+ lastupdated=aws_update_tag,
171
+ IdentityStoreId=identity_store_id,
172
+ AWS_ID=aws_account_id,
173
+ Region=region,
174
+ )
175
+
176
+
177
+ @timeit
178
+ def get_role_assignments(
179
+ boto3_session: boto3.session.Session,
180
+ users: List[Dict],
181
+ instance_arn: str,
182
+ region: str,
183
+ ) -> List[Dict]:
184
+ """
185
+ Get role assignments for SSO users
186
+ """
187
+
188
+ logger.info(f"Getting role assignments for {len(users)} users")
189
+ client = boto3_session.client('sso-admin', region_name=region)
190
+ role_assignments = []
191
+
192
+ for user in users:
193
+ user_id = user['UserId']
194
+ paginator = client.get_paginator('list_account_assignments_for_principal')
195
+ for page in paginator.paginate(InstanceArn=instance_arn, PrincipalId=user_id, PrincipalType='USER'):
196
+ for assignment in page.get('AccountAssignments', []):
197
+ role_assignments.append({
198
+ 'UserId': user_id,
199
+ 'PermissionSetArn': assignment.get('PermissionSetArn'),
200
+ 'AccountId': assignment.get('AccountId'),
201
+ })
202
+
203
+ return role_assignments
204
+
205
+
206
+ @timeit
207
+ def load_role_assignments(
208
+ neo4j_session: neo4j.Session,
209
+ role_assignments: List[Dict],
210
+ aws_update_tag: int,
211
+ ) -> None:
212
+ """
213
+ Load role assignments into the graph
214
+ """
215
+ logger.info(f"Loading {len(role_assignments)} role assignments")
216
+ if role_assignments:
217
+ neo4j_session.run(
218
+ """
219
+ UNWIND $role_assignments AS ra
220
+ MATCH (acc:AWSAccount{id:ra.AccountId}) -[:RESOURCE]->
221
+ (role:AWSRole)<-[:ASSIGNED_TO_ROLE]-
222
+ (permset:AWSPermissionSet {id: ra.PermissionSetArn})
223
+ MATCH (sso:AWSSSOUser {id: ra.UserId})
224
+ MERGE (role)-[r:ALLOWED_BY]->(sso)
225
+ SET r.lastupdated = $aws_update_tag,
226
+ r.permission_set_arn = ra.PermissionSetArn
227
+ """,
228
+ role_assignments=role_assignments,
229
+ aws_update_tag=aws_update_tag,
230
+ )
231
+
232
+
233
+ def cleanup(neo4j_session: neo4j.Session, common_job_parameters: Dict[str, Any]) -> None:
234
+ GraphJob.from_node_schema(AWSIdentityCenterInstanceSchema(), common_job_parameters).run(neo4j_session)
235
+ GraphJob.from_node_schema(AWSPermissionSetSchema(), common_job_parameters).run(neo4j_session)
236
+ GraphJob.from_node_schema(AWSSSOUserSchema(), common_job_parameters).run(neo4j_session)
237
+ run_cleanup_job(
238
+ 'aws_import_identity_center_cleanup.json',
239
+ neo4j_session,
240
+ common_job_parameters,
241
+ )
242
+
243
+
244
+ @timeit
245
+ def sync_identity_center_instances(
246
+ neo4j_session: neo4j.Session,
247
+ boto3_session: boto3.session.Session,
248
+ regions: List[str],
249
+ current_aws_account_id: str,
250
+ update_tag: int,
251
+ common_job_parameters: Dict,
252
+ ) -> None:
253
+ """
254
+ Sync Identity Center instances, their permission sets, and SSO users
255
+ """
256
+ logger.info(f"Syncing Identity Center instances for regions {regions}")
257
+ for region in regions:
258
+ logger.info(f"Syncing Identity Center instances for region {region}")
259
+ instances = get_identity_center_instances(boto3_session, region)
260
+ load_identity_center_instances(
261
+ neo4j_session,
262
+ instances,
263
+ region,
264
+ current_aws_account_id,
265
+ update_tag,
266
+ )
267
+
268
+ # For each instance, get and load its permission sets and SSO users
269
+ for instance in instances:
270
+ instance_arn = instance['InstanceArn']
271
+ identity_store_id = instance['IdentityStoreId']
272
+
273
+ permission_sets = get_permission_sets(boto3_session, instance_arn, region)
274
+
275
+ load_permission_sets(
276
+ neo4j_session,
277
+ permission_sets,
278
+ instance_arn,
279
+ region,
280
+ current_aws_account_id,
281
+ update_tag,
282
+ )
283
+
284
+ users = get_sso_users(boto3_session, identity_store_id, region)
285
+ load_sso_users(
286
+ neo4j_session,
287
+ users,
288
+ identity_store_id,
289
+ region,
290
+ current_aws_account_id,
291
+ update_tag,
292
+ )
293
+
294
+ # Get and load role assignments
295
+ role_assignments = get_role_assignments(
296
+ boto3_session,
297
+ users,
298
+ instance_arn,
299
+ region,
300
+ )
301
+ load_role_assignments(
302
+ neo4j_session,
303
+ role_assignments,
304
+ update_tag,
305
+ )
306
+
307
+ cleanup(neo4j_session, common_job_parameters)
@@ -10,6 +10,7 @@ from . import elasticache
10
10
  from . import elasticsearch
11
11
  from . import emr
12
12
  from . import iam
13
+ from . import identitycenter
13
14
  from . import inspector
14
15
  from . import kms
15
16
  from . import lambda_function
@@ -32,6 +33,7 @@ from .ec2.key_pairs import sync_ec2_key_pairs
32
33
  from .ec2.launch_templates import sync_ec2_launch_templates
33
34
  from .ec2.load_balancer_v2s import sync_load_balancer_v2s
34
35
  from .ec2.load_balancers import sync_load_balancers
36
+ from .ec2.network_acls import sync_network_acls
35
37
  from .ec2.network_interfaces import sync_network_interfaces
36
38
  from .ec2.reserved_instances import sync_ec2_reserved_instances
37
39
  from .ec2.security_groups import sync_ec2_security_groupinfo
@@ -55,6 +57,7 @@ RESOURCE_FUNCTIONS: Dict = {
55
57
  'ec2:keypair': sync_ec2_key_pairs,
56
58
  'ec2:load_balancer': sync_load_balancers,
57
59
  'ec2:load_balancer_v2': sync_load_balancer_v2s,
60
+ 'ec2:network_acls': sync_network_acls,
58
61
  'ec2:network_interface': sync_network_interfaces,
59
62
  'ec2:security_group': sync_ec2_security_groupinfo,
60
63
  'ec2:subnet': sync_subnets,
@@ -86,4 +89,5 @@ RESOURCE_FUNCTIONS: Dict = {
86
89
  'ssm': ssm.sync,
87
90
  'inspector': inspector.sync,
88
91
  'config': config.sync,
92
+ 'identitycenter': identitycenter.sync_identity_center_instances,
89
93
  }
@@ -25,7 +25,7 @@ def start_cve_ingestion(
25
25
  """
26
26
  if not config.cve_enabled:
27
27
  return
28
- cve_api_key = config.cve_api_key if config.cve_api_key else None
28
+ cve_api_key: str | None = config.cve_api_key if config.cve_api_key else None
29
29
 
30
30
  # sync CVE year archives, if not yet synced
31
31
  existing_years = feed.get_cve_sync_metadata(neo4j_session)
@@ -22,9 +22,9 @@ from cartography.util import timeit
22
22
 
23
23
  logger = logging.getLogger(__name__)
24
24
 
25
- MAX_RETRIES = 3
26
- # Connect and read timeouts of 60 seconds each; see https://requests.readthedocs.io/en/master/user/advanced/#timeouts
27
- CONNECT_AND_READ_TIMEOUT = (60, 60)
25
+ MAX_RETRIES = 8
26
+ # Connect and read timeouts of 120 seconds each; see https://requests.readthedocs.io/en/master/user/advanced/#timeouts
27
+ CONNECT_AND_READ_TIMEOUT = (30, 120)
28
28
  CVE_FEED_ID = "NIST_NVD"
29
29
  BATCH_SIZE_DAYS = 120
30
30
  RESULTS_PER_PAGE = 2000
@@ -68,7 +68,7 @@ def _map_cve_dict(cve_dict: Dict[Any, Any], data: Dict[Any, Any]) -> None:
68
68
  cve_dict["startIndex"] = data["startIndex"]
69
69
 
70
70
 
71
- def _call_cves_api(url: str, api_key: str, params: Dict[str, Any]) -> Dict[Any, Any]:
71
+ def _call_cves_api(url: str, api_key: str | None, params: Dict[str, Any]) -> Dict[Any, Any]:
72
72
  totalResults = 0
73
73
  sleep_time = DEFAULT_SLEEP_TIME
74
74
  retries = 0
@@ -98,6 +98,9 @@ def _call_cves_api(url: str, api_key: str, params: Dict[str, Any]) -> Dict[Any,
98
98
  retries += 1
99
99
  if retries >= MAX_RETRIES:
100
100
  raise
101
+ # Exponential backoff
102
+ sleep_time *= 2
103
+ time.sleep(sleep_time)
101
104
  continue
102
105
  data = res.json()
103
106
  _map_cve_dict(results, data)
@@ -114,7 +117,7 @@ def get_cves_in_batches(
114
117
  start_date: datetime,
115
118
  end_date: datetime,
116
119
  date_param_names: Dict[str, str],
117
- api_key: str,
120
+ api_key: str | None,
118
121
  ) -> Dict[Any, Any]:
119
122
  cves: Dict[Any, Any] = dict()
120
123
  current_start_date: datetime = start_date
@@ -153,7 +156,7 @@ def get_cves_in_batches(
153
156
 
154
157
 
155
158
  def get_modified_cves(
156
- nist_cve_url: str, last_modified_date: str, api_key: str,
159
+ nist_cve_url: str, last_modified_date: str, api_key: str | None,
157
160
  ) -> Dict[Any, Any]:
158
161
  cves = dict()
159
162
  end_date = datetime.now(tz=timezone.utc)
@@ -171,7 +174,7 @@ def get_modified_cves(
171
174
 
172
175
 
173
176
  def get_published_cves_per_year(
174
- nist_cve_url: str, year: str, api_key: str,
177
+ nist_cve_url: str, year: str, api_key: str | None,
175
178
  ) -> Dict[Any, Any]:
176
179
  cves = {}
177
180
  start_of_year = datetime.strptime(f"{year}-01-01", "%Y-%m-%d")