cartography 0.117.0__py3-none-any.whl → 0.119.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 (107) hide show
  1. cartography/_version.py +2 -2
  2. cartography/cli.py +31 -0
  3. cartography/client/core/tx.py +19 -3
  4. cartography/config.py +14 -0
  5. cartography/data/indexes.cypher +0 -6
  6. cartography/graph/job.py +13 -7
  7. cartography/graph/statement.py +4 -0
  8. cartography/intel/aws/__init__.py +22 -9
  9. cartography/intel/aws/apigateway.py +18 -5
  10. cartography/intel/aws/ec2/elastic_ip_addresses.py +3 -1
  11. cartography/intel/aws/ec2/internet_gateways.py +4 -2
  12. cartography/intel/aws/ec2/load_balancer_v2s.py +11 -5
  13. cartography/intel/aws/ec2/network_interfaces.py +4 -0
  14. cartography/intel/aws/ec2/reserved_instances.py +3 -1
  15. cartography/intel/aws/ec2/tgw.py +11 -5
  16. cartography/intel/aws/ec2/volumes.py +1 -1
  17. cartography/intel/aws/ecr.py +209 -26
  18. cartography/intel/aws/ecr_image_layers.py +143 -42
  19. cartography/intel/aws/elasticsearch.py +13 -4
  20. cartography/intel/aws/identitycenter.py +93 -54
  21. cartography/intel/aws/inspector.py +90 -46
  22. cartography/intel/aws/permission_relationships.py +3 -3
  23. cartography/intel/aws/resourcegroupstaggingapi.py +1 -1
  24. cartography/intel/aws/s3.py +26 -13
  25. cartography/intel/aws/ssm.py +3 -5
  26. cartography/intel/azure/compute.py +9 -4
  27. cartography/intel/azure/cosmosdb.py +31 -15
  28. cartography/intel/azure/sql.py +25 -12
  29. cartography/intel/azure/storage.py +19 -9
  30. cartography/intel/azure/subscription.py +3 -1
  31. cartography/intel/crowdstrike/spotlight.py +5 -2
  32. cartography/intel/entra/app_role_assignments.py +9 -2
  33. cartography/intel/gcp/__init__.py +26 -9
  34. cartography/intel/gcp/clients.py +8 -4
  35. cartography/intel/gcp/compute.py +42 -21
  36. cartography/intel/gcp/crm/folders.py +9 -3
  37. cartography/intel/gcp/crm/orgs.py +8 -3
  38. cartography/intel/gcp/crm/projects.py +14 -3
  39. cartography/intel/github/repos.py +23 -5
  40. cartography/intel/gsuite/__init__.py +12 -8
  41. cartography/intel/gsuite/groups.py +291 -0
  42. cartography/intel/gsuite/users.py +142 -0
  43. cartography/intel/jamf/computers.py +7 -1
  44. cartography/intel/oci/iam.py +23 -9
  45. cartography/intel/oci/organizations.py +3 -1
  46. cartography/intel/oci/utils.py +28 -5
  47. cartography/intel/okta/awssaml.py +9 -8
  48. cartography/intel/okta/users.py +1 -1
  49. cartography/intel/ontology/__init__.py +44 -0
  50. cartography/intel/ontology/devices.py +54 -0
  51. cartography/intel/ontology/users.py +54 -0
  52. cartography/intel/ontology/utils.py +121 -0
  53. cartography/intel/pagerduty/escalation_policies.py +13 -6
  54. cartography/intel/pagerduty/schedules.py +9 -4
  55. cartography/intel/pagerduty/services.py +7 -3
  56. cartography/intel/pagerduty/teams.py +5 -2
  57. cartography/intel/pagerduty/users.py +3 -1
  58. cartography/intel/pagerduty/vendors.py +3 -1
  59. cartography/intel/trivy/__init__.py +109 -58
  60. cartography/models/airbyte/user.py +4 -0
  61. cartography/models/anthropic/user.py +4 -0
  62. cartography/models/aws/ec2/networkinterfaces.py +2 -0
  63. cartography/models/aws/ecr/image.py +55 -0
  64. cartography/models/aws/ecr/repository_image.py +1 -1
  65. cartography/models/aws/iam/group_membership.py +3 -2
  66. cartography/models/aws/identitycenter/awsssouser.py +3 -1
  67. cartography/models/bigfix/bigfix_computer.py +1 -1
  68. cartography/models/cloudflare/member.py +4 -0
  69. cartography/models/crowdstrike/hosts.py +1 -1
  70. cartography/models/duo/endpoint.py +1 -1
  71. cartography/models/duo/phone.py +2 -2
  72. cartography/models/duo/user.py +4 -0
  73. cartography/models/entra/user.py +2 -1
  74. cartography/models/github/users.py +4 -0
  75. cartography/models/gsuite/__init__.py +0 -0
  76. cartography/models/gsuite/group.py +218 -0
  77. cartography/models/gsuite/tenant.py +29 -0
  78. cartography/models/gsuite/user.py +107 -0
  79. cartography/models/kandji/device.py +1 -2
  80. cartography/models/keycloak/user.py +4 -0
  81. cartography/models/lastpass/user.py +4 -0
  82. cartography/models/ontology/__init__.py +0 -0
  83. cartography/models/ontology/device.py +125 -0
  84. cartography/models/ontology/mapping/__init__.py +16 -0
  85. cartography/models/ontology/mapping/data/__init__.py +1 -0
  86. cartography/models/ontology/mapping/data/devices.py +160 -0
  87. cartography/models/ontology/mapping/data/users.py +239 -0
  88. cartography/models/ontology/mapping/specs.py +65 -0
  89. cartography/models/ontology/user.py +52 -0
  90. cartography/models/openai/user.py +4 -0
  91. cartography/models/scaleway/iam/user.py +4 -0
  92. cartography/models/snipeit/asset.py +1 -0
  93. cartography/models/snipeit/user.py +4 -0
  94. cartography/models/tailscale/device.py +1 -1
  95. cartography/models/tailscale/user.py +6 -1
  96. cartography/rules/data/frameworks/mitre_attack/requirements/t1098_account_manipulation/__init__.py +176 -89
  97. cartography/sync.py +4 -1
  98. cartography/util.py +49 -18
  99. {cartography-0.117.0.dist-info → cartography-0.119.0.dist-info}/METADATA +3 -3
  100. {cartography-0.117.0.dist-info → cartography-0.119.0.dist-info}/RECORD +104 -89
  101. cartography/data/jobs/cleanup/gsuite_ingest_groups_cleanup.json +0 -23
  102. cartography/data/jobs/cleanup/gsuite_ingest_users_cleanup.json +0 -11
  103. cartography/intel/gsuite/api.py +0 -355
  104. {cartography-0.117.0.dist-info → cartography-0.119.0.dist-info}/WHEEL +0 -0
  105. {cartography-0.117.0.dist-info → cartography-0.119.0.dist-info}/entry_points.txt +0 -0
  106. {cartography-0.117.0.dist-info → cartography-0.119.0.dist-info}/licenses/LICENSE +0 -0
  107. {cartography-0.117.0.dist-info → cartography-0.119.0.dist-info}/top_level.txt +0 -0
@@ -8,6 +8,7 @@ import botocore.config
8
8
  import neo4j
9
9
  from policyuniverse.policy import Policy
10
10
 
11
+ from cartography.client.core.tx import run_write_query
11
12
  from cartography.intel.dns import ingest_dns_record_by_fqdn
12
13
  from cartography.util import aws_handle_regions
13
14
  from cartography.util import run_cleanup_job
@@ -95,7 +96,8 @@ def _load_es_domains(
95
96
  for d in domain_list:
96
97
  del d["ServiceSoftwareOptions"]
97
98
 
98
- neo4j_session.run(
99
+ run_write_query(
100
+ neo4j_session,
99
101
  ingest_records,
100
102
  Records=domain_list,
101
103
  AWS_ACCOUNT_ID=aws_account_id,
@@ -179,7 +181,8 @@ def _link_es_domain_vpc(
179
181
  groupList = vpc_data.get("SecurityGroupIds", [])
180
182
 
181
183
  if len(subnetList) > 0:
182
- neo4j_session.run(
184
+ run_write_query(
185
+ neo4j_session,
183
186
  ingest_subnet,
184
187
  DomainId=domain_id,
185
188
  SubnetList=subnetList,
@@ -187,7 +190,8 @@ def _link_es_domain_vpc(
187
190
  )
188
191
 
189
192
  if len(groupList) > 0:
190
- neo4j_session.run(
193
+ run_write_query(
194
+ neo4j_session,
191
195
  ingest_sec_groups,
192
196
  DomainId=domain_id,
193
197
  SecGroupList=groupList,
@@ -220,7 +224,12 @@ def _process_access_policy(
220
224
  if policy.is_internet_accessible():
221
225
  exposed_internet = True
222
226
 
223
- neo4j_session.run(tag_es, DomainId=domain_id, InternetExposed=exposed_internet)
227
+ run_write_query(
228
+ neo4j_session,
229
+ tag_es,
230
+ DomainId=domain_id,
231
+ InternetExposed=exposed_internet,
232
+ )
224
233
 
225
234
 
226
235
  @timeit
@@ -6,10 +6,12 @@ from typing import Optional
6
6
  from typing import Union
7
7
 
8
8
  import boto3
9
+ import botocore.exceptions
9
10
  import neo4j
10
11
 
11
12
  from cartography.client.core.tx import load
12
13
  from cartography.client.core.tx import load_matchlinks
14
+ from cartography.client.core.tx import read_list_of_dicts_tx
13
15
  from cartography.graph.job import GraphJob
14
16
  from cartography.models.aws.identitycenter.awsidentitycenter import (
15
17
  AWSIdentityCenterInstanceSchema,
@@ -31,6 +33,18 @@ from cartography.util import timeit
31
33
  logger = logging.getLogger(__name__)
32
34
 
33
35
 
36
+ def _is_permission_set_sync_unsupported_error(
37
+ error: botocore.exceptions.ClientError,
38
+ ) -> bool:
39
+ """Return True when the Identity Center instance does not support permission sets."""
40
+ error_info = error.response.get("Error", {})
41
+ if error_info.get("Code") != "ValidationException":
42
+ return False
43
+
44
+ message = error_info.get("Message", "").lower()
45
+ return "not supported for this identity center instance" in message
46
+
47
+
34
48
  @timeit
35
49
  @aws_handle_regions
36
50
  def get_identity_center_instances(
@@ -394,8 +408,11 @@ def get_permset_roles(
394
408
  WHERE permset.arn IN $PermSetIds
395
409
  RETURN permset.arn AS PermissionSetArn, role.arn AS RoleArn
396
410
  """
397
- result = neo4j_session.run(query, PermSetIds=permset_ids)
398
- permset_to_role = [record.data() for record in result]
411
+ permset_to_role = neo4j_session.execute_read(
412
+ read_list_of_dicts_tx,
413
+ query,
414
+ PermSetIds=permset_ids,
415
+ )
399
416
 
400
417
  # Create mapping from permission set ARN to role ARN
401
418
  permset_to_role_map = {
@@ -504,32 +521,51 @@ def sync_identity_center_instances(
504
521
  instance_arn = instance["InstanceArn"]
505
522
  identity_store_id = instance["IdentityStoreId"]
506
523
 
507
- permission_sets = get_permission_sets(boto3_session, instance_arn, region)
508
-
509
- load_permission_sets(
510
- neo4j_session,
511
- permission_sets,
512
- instance_arn,
513
- region,
514
- current_aws_account_id,
515
- update_tag,
516
- )
524
+ permission_set_sync_supported = True
525
+ try:
526
+ permission_sets = get_permission_sets(
527
+ boto3_session, instance_arn, region
528
+ )
529
+ except botocore.exceptions.ClientError as error:
530
+ if _is_permission_set_sync_unsupported_error(error):
531
+ permission_set_sync_supported = False
532
+ permission_sets = []
533
+ logger.warning(
534
+ "Skipping permission set sync for Identity Center instance %s in region %s "
535
+ "because the instance does not support permission sets.",
536
+ instance_arn,
537
+ region,
538
+ )
539
+ else:
540
+ raise
541
+
542
+ if permission_set_sync_supported:
543
+ load_permission_sets(
544
+ neo4j_session,
545
+ permission_sets,
546
+ instance_arn,
547
+ region,
548
+ current_aws_account_id,
549
+ update_tag,
550
+ )
517
551
 
518
552
  # Fetch groups first to avoid interleaving between groups and users
519
553
  groups = get_sso_groups(boto3_session, identity_store_id, region)
520
554
 
521
555
  # Get permission set assignments for groups
522
556
  group_permission_sets: Dict[str, List[str]] = {}
523
- group_role_assignments_raw = get_group_role_assignments(
524
- boto3_session,
525
- groups,
526
- instance_arn,
527
- region,
528
- )
529
- for assignment in group_role_assignments_raw:
530
- group_id = assignment["GroupId"]
531
- perm_set = assignment["PermissionSetArn"]
532
- group_permission_sets.setdefault(group_id, []).append(perm_set)
557
+ group_role_assignments_raw: List[Dict[str, Any]] = []
558
+ if permission_set_sync_supported:
559
+ group_role_assignments_raw = get_group_role_assignments(
560
+ boto3_session,
561
+ groups,
562
+ instance_arn,
563
+ region,
564
+ )
565
+ for assignment in group_role_assignments_raw:
566
+ group_id = assignment["GroupId"]
567
+ perm_set = assignment["PermissionSetArn"]
568
+ group_permission_sets.setdefault(group_id, []).append(perm_set)
533
569
 
534
570
  # Transform and load groups with their permission set assignments FIRST
535
571
  # so that user->group membership edges can attach in the same run.
@@ -554,16 +590,18 @@ def sync_identity_center_instances(
554
590
 
555
591
  # Get direct permission set assignments for users
556
592
  user_permission_sets: Dict[str, List[str]] = {}
557
- user_role_assignments_raw = get_role_assignments(
558
- boto3_session,
559
- users,
560
- instance_arn,
561
- region,
562
- )
563
- for assignment in user_role_assignments_raw:
564
- uid = assignment["UserId"]
565
- perm_set = assignment["PermissionSetArn"]
566
- user_permission_sets.setdefault(uid, []).append(perm_set)
593
+ user_role_assignments_raw: List[Dict[str, Any]] = []
594
+ if permission_set_sync_supported:
595
+ user_role_assignments_raw = get_role_assignments(
596
+ boto3_session,
597
+ users,
598
+ instance_arn,
599
+ region,
600
+ )
601
+ for assignment in user_role_assignments_raw:
602
+ uid = assignment["UserId"]
603
+ perm_set = assignment["PermissionSetArn"]
604
+ user_permission_sets.setdefault(uid, []).append(perm_set)
567
605
 
568
606
  # Transform and load users with their group memberships AFTER groups exist
569
607
  transformed_users = transform_sso_users(
@@ -584,28 +622,29 @@ def sync_identity_center_instances(
584
622
  # Note: we do this after groups and users are loaded so that
585
623
  # load_role_assignments calls can MATCH existing AWSSSOUser/AWSSSOGroup
586
624
  # nodes when drawing the ALLOWED_BY edges.
587
- enriched_role_assignments = get_permset_roles(
588
- neo4j_session,
589
- user_role_assignments_raw,
590
- )
591
- load_role_assignments(
592
- neo4j_session,
593
- enriched_role_assignments,
594
- current_aws_account_id,
595
- update_tag,
596
- RoleAssignmentAllowedByMatchLink(),
597
- )
625
+ if permission_set_sync_supported:
626
+ enriched_role_assignments = get_permset_roles(
627
+ neo4j_session,
628
+ user_role_assignments_raw,
629
+ )
630
+ load_role_assignments(
631
+ neo4j_session,
632
+ enriched_role_assignments,
633
+ current_aws_account_id,
634
+ update_tag,
635
+ RoleAssignmentAllowedByMatchLink(),
636
+ )
598
637
 
599
- enriched_group_role_assignments = get_permset_roles(
600
- neo4j_session,
601
- group_role_assignments_raw,
602
- )
603
- load_role_assignments(
604
- neo4j_session,
605
- enriched_group_role_assignments,
606
- current_aws_account_id,
607
- update_tag,
608
- RoleAssignmentAllowedByGroupMatchLink(),
609
- )
638
+ enriched_group_role_assignments = get_permset_roles(
639
+ neo4j_session,
640
+ group_role_assignments_raw,
641
+ )
642
+ load_role_assignments(
643
+ neo4j_session,
644
+ enriched_group_role_assignments,
645
+ current_aws_account_id,
646
+ update_tag,
647
+ RoleAssignmentAllowedByGroupMatchLink(),
648
+ )
610
649
 
611
650
  cleanup(neo4j_session, common_job_parameters)
@@ -7,6 +7,7 @@ from typing import Set
7
7
  from typing import Tuple
8
8
 
9
9
  import boto3
10
+ import botocore
10
11
  import neo4j
11
12
 
12
13
  from cartography.client.core.tx import load
@@ -17,7 +18,9 @@ from cartography.models.aws.inspector.findings import InspectorFindingToPackageM
17
18
  from cartography.models.aws.inspector.packages import AWSInspectorPackageSchema
18
19
  from cartography.util import aws_handle_regions
19
20
  from cartography.util import aws_paginate
21
+ from cartography.util import AWS_REGION_ACCESS_DENIED_ERROR_CODES
20
22
  from cartography.util import batch
23
+ from cartography.util import is_service_control_policy_explicit_deny
21
24
  from cartography.util import timeit
22
25
 
23
26
  logger = logging.getLogger(__name__)
@@ -70,21 +73,25 @@ def get_member_accounts(
70
73
 
71
74
 
72
75
  @timeit
73
- @aws_handle_regions
74
76
  def get_inspector_findings(
75
77
  session: boto3.session.Session,
76
78
  region: str,
77
79
  account_id: str,
80
+ batch_size: int,
78
81
  ) -> Iterator[List[Dict[str, Any]]]:
79
82
  """
80
83
  Query inspector2.list_findings by filtering the request, otherwise the request could timeout.
81
84
  First, we filter by account_id. And since there may be millions of CLOSED findings that may never go away,
82
85
  only fetch those in ACTIVE or SUPPRESSED statuses.
83
- Run the query in batches of 1000 findings and return an iterator to fetch the results.
86
+ Run the query in batches and return an iterator to fetch the results.
84
87
  """
88
+ # Note: We can't use @aws_handle_regions decorator here because this function returns a generator.
89
+ # The decorator would only catch exceptions during function call, not during iteration.
90
+ # Instead, we rely on aws_handle_regions being applied at get_member_accounts level,
91
+ # and the paginate operation itself will raise errors that bubble up naturally.
85
92
  client = session.client("inspector2", region_name=region)
86
93
  logger.info(
87
- f"Getting findings in batches of {BATCH_SIZE} for account {account_id} in region {region}"
94
+ f"Getting findings in batches of {batch_size} for account {account_id} in region {region}"
88
95
  )
89
96
  aws_args: Dict[str, Any] = {
90
97
  "filterCriteria": {
@@ -103,7 +110,7 @@ def get_inspector_findings(
103
110
  }
104
111
  }
105
112
  findings_batches = batch(
106
- aws_paginate(client, "list_findings", "findings", None, **aws_args), BATCH_SIZE
113
+ aws_paginate(client, "list_findings", "findings", None, **aws_args), batch_size
107
114
  )
108
115
  yield from findings_batches
109
116
 
@@ -271,19 +278,25 @@ def load_inspector_finding_to_package_match_links(
271
278
  def cleanup(
272
279
  neo4j_session: neo4j.Session,
273
280
  common_job_parameters: Dict[str, Any],
281
+ batch_size: int = BATCH_SIZE,
274
282
  ) -> None:
275
283
  logger.info("Running AWS Inspector cleanup")
276
- GraphJob.from_node_schema(AWSInspectorFindingSchema(), common_job_parameters).run(
277
- neo4j_session,
278
- )
279
- GraphJob.from_node_schema(AWSInspectorPackageSchema(), common_job_parameters).run(
280
- neo4j_session,
281
- )
282
284
  GraphJob.from_matchlink(
283
285
  InspectorFindingToPackageMatchLink(),
284
286
  "AWSAccount",
285
287
  common_job_parameters["ACCOUNT_ID"],
286
288
  common_job_parameters["UPDATE_TAG"],
289
+ iterationsize=batch_size,
290
+ ).run(
291
+ neo4j_session,
292
+ )
293
+ GraphJob.from_node_schema(
294
+ AWSInspectorPackageSchema(), common_job_parameters, iterationsize=batch_size
295
+ ).run(
296
+ neo4j_session,
297
+ )
298
+ GraphJob.from_node_schema(
299
+ AWSInspectorFindingSchema(), common_job_parameters, iterationsize=batch_size
287
300
  ).run(
288
301
  neo4j_session,
289
302
  )
@@ -296,42 +309,69 @@ def _sync_findings_for_account(
296
309
  account_id: str,
297
310
  update_tag: int,
298
311
  current_aws_account_id: str,
312
+ batch_size: int = BATCH_SIZE,
299
313
  ) -> None:
300
314
  """
301
315
  Syncs the findings for a given account in a given region.
302
316
  """
303
- findings = get_inspector_findings(boto3_session, region, account_id)
304
- if not findings:
305
- logger.info(f"No findings to sync for account {account_id} in region {region}")
306
- return
307
- for f_batch in findings:
308
- finding_data, package_data, finding_to_package_map = (
309
- transform_inspector_findings(f_batch)
310
- )
311
- logger.info(f"Loading {len(finding_data)} findings from account {account_id}")
312
- load_inspector_findings(
313
- neo4j_session,
314
- finding_data,
315
- region,
316
- update_tag,
317
- current_aws_account_id,
318
- )
319
- logger.info(f"Loading {len(package_data)} packages")
320
- load_inspector_packages(
321
- neo4j_session,
322
- package_data,
323
- update_tag,
324
- current_aws_account_id,
325
- )
326
- logger.info(
327
- f"Loading {len(finding_to_package_map)} finding to package relationships"
328
- )
329
- load_inspector_finding_to_package_match_links(
330
- neo4j_session,
331
- finding_to_package_map,
332
- update_tag,
333
- current_aws_account_id,
334
- )
317
+ try:
318
+ findings = get_inspector_findings(boto3_session, region, account_id, batch_size)
319
+ if not findings:
320
+ logger.info(
321
+ f"No findings to sync for account {account_id} in region {region}"
322
+ )
323
+ return
324
+ for f_batch in findings:
325
+ finding_data, package_data, finding_to_package_map = (
326
+ transform_inspector_findings(f_batch)
327
+ )
328
+ logger.info(
329
+ f"Loading {len(finding_data)} findings from account {account_id}"
330
+ )
331
+ load_inspector_findings(
332
+ neo4j_session,
333
+ finding_data,
334
+ region,
335
+ update_tag,
336
+ current_aws_account_id,
337
+ )
338
+ logger.info(f"Loading {len(package_data)} packages")
339
+ load_inspector_packages(
340
+ neo4j_session,
341
+ package_data,
342
+ update_tag,
343
+ current_aws_account_id,
344
+ )
345
+ logger.info(
346
+ f"Loading {len(finding_to_package_map)} finding to package relationships"
347
+ )
348
+ load_inspector_finding_to_package_match_links(
349
+ neo4j_session,
350
+ finding_to_package_map,
351
+ update_tag,
352
+ current_aws_account_id,
353
+ )
354
+ except botocore.exceptions.ClientError as e:
355
+ error_code = e.response.get("Error", {}).get("Code")
356
+ # Handle the same error codes as aws_handle_regions decorator
357
+ if error_code in AWS_REGION_ACCESS_DENIED_ERROR_CODES:
358
+ error_message = e.response.get("Error", {}).get("Message")
359
+ if is_service_control_policy_explicit_deny(e):
360
+ logger.warning(
361
+ "Service control policy denied access to Inspector findings for account %s in region %s: %s",
362
+ account_id,
363
+ region,
364
+ error_message,
365
+ )
366
+ else:
367
+ logger.warning(
368
+ "Access denied to Inspector findings for account %s in region %s. Skipping...",
369
+ account_id,
370
+ region,
371
+ )
372
+ return
373
+ else:
374
+ raise
335
375
 
336
376
 
337
377
  @timeit
@@ -343,6 +383,10 @@ def sync(
343
383
  update_tag: int,
344
384
  common_job_parameters: Dict[str, Any],
345
385
  ) -> None:
386
+ batch_size = common_job_parameters.get(
387
+ "experimental_aws_inspector_batch", BATCH_SIZE
388
+ )
389
+
346
390
  inspector_regions = [
347
391
  region for region in regions if region in AWS_INSPECTOR_REGIONS
348
392
  ]
@@ -363,8 +407,8 @@ def sync(
363
407
  account_id,
364
408
  update_tag,
365
409
  current_aws_account_id,
410
+ batch_size,
366
411
  )
367
- common_job_parameters["ACCOUNT_ID"] = current_aws_account_id
368
- common_job_parameters["UPDATE_TAG"] = update_tag
369
-
370
- cleanup(neo4j_session, common_job_parameters)
412
+ common_job_parameters["ACCOUNT_ID"] = current_aws_account_id
413
+ common_job_parameters["UPDATE_TAG"] = update_tag
414
+ cleanup(neo4j_session, common_job_parameters, batch_size)
@@ -13,6 +13,7 @@ import neo4j
13
13
  import yaml
14
14
 
15
15
  from cartography.client.core.tx import read_list_of_dicts_tx
16
+ from cartography.client.core.tx import read_list_of_values_tx
16
17
  from cartography.client.core.tx import run_write_query
17
18
  from cartography.graph.statement import GraphStatement
18
19
  from cartography.util import timeit
@@ -300,12 +301,11 @@ def get_resource_arns(
300
301
  get_resource_query_template = get_resource_query.safe_substitute(
301
302
  node_label=node_label,
302
303
  )
303
- results = neo4j_session.run(
304
+ return neo4j_session.execute_read(
305
+ read_list_of_values_tx,
304
306
  get_resource_query_template,
305
307
  AccountId=account_id,
306
308
  )
307
- arns = [r["arn"] for r in results]
308
- return arns
309
309
 
310
310
 
311
311
  def load_principal_mappings(
@@ -253,7 +253,7 @@ def _load_tags_tx(
253
253
  UpdateTag=aws_update_tag,
254
254
  Region=region,
255
255
  Account=current_aws_account_id,
256
- )
256
+ ).consume()
257
257
 
258
258
 
259
259
  @timeit
@@ -16,6 +16,7 @@ from botocore.exceptions import ClientError
16
16
  from botocore.exceptions import EndpointConnectionError
17
17
  from policyuniverse.policy import Policy
18
18
 
19
+ from cartography.client.core.tx import run_write_query
19
20
  from cartography.stats import get_stats_client
20
21
  from cartography.util import aws_handle_regions
21
22
  from cartography.util import merge_module_sync_metadata
@@ -334,7 +335,8 @@ def _load_s3_acls(
334
335
  SET r.lastupdated = $UpdateTag
335
336
  """
336
337
 
337
- neo4j_session.run(
338
+ run_write_query(
339
+ neo4j_session,
338
340
  ingest_acls,
339
341
  acls=acls,
340
342
  UpdateTag=update_tag,
@@ -368,7 +370,8 @@ def _load_s3_policies(
368
370
  s.lastupdated = $UpdateTag
369
371
  """
370
372
 
371
- neo4j_session.run(
373
+ run_write_query(
374
+ neo4j_session,
372
375
  ingest_policies,
373
376
  policies=policies,
374
377
  UpdateTag=update_tag,
@@ -401,11 +404,12 @@ def _load_s3_policy_statements(
401
404
  MERGE (bucket)-[r:POLICY_STATEMENT]->(statement)
402
405
  SET r.lastupdated = $UpdateTag
403
406
  """
404
- neo4j_session.run(
407
+ run_write_query(
408
+ neo4j_session,
405
409
  ingest_policy_statement,
406
410
  Statements=statements,
407
411
  UpdateTag=update_tag,
408
- ).consume()
412
+ )
409
413
 
410
414
 
411
415
  @timeit
@@ -427,7 +431,8 @@ def _load_s3_encryption(
427
431
  s.lastupdated = $UpdateTag
428
432
  """
429
433
 
430
- neo4j_session.run(
434
+ run_write_query(
435
+ neo4j_session,
431
436
  ingest_encryption,
432
437
  encryption_configs=encryption_configs,
433
438
  UpdateTag=update_tag,
@@ -451,7 +456,8 @@ def _load_s3_versioning(
451
456
  s.lastupdated = $UpdateTag
452
457
  """
453
458
 
454
- neo4j_session.run(
459
+ run_write_query(
460
+ neo4j_session,
455
461
  ingest_versioning,
456
462
  versioning_configs=versioning_configs,
457
463
  UpdateTag=update_tag,
@@ -477,7 +483,8 @@ def _load_s3_public_access_block(
477
483
  s.lastupdated = $UpdateTag
478
484
  """
479
485
 
480
- neo4j_session.run(
486
+ run_write_query(
487
+ neo4j_session,
481
488
  ingest_public_access_block,
482
489
  public_access_block_configs=public_access_block_configs,
483
490
  UpdateTag=update_tag,
@@ -500,7 +507,8 @@ def _load_bucket_ownership_controls(
500
507
  s.lastupdated = $UpdateTag
501
508
  """
502
509
 
503
- neo4j_session.run(
510
+ run_write_query(
511
+ neo4j_session,
504
512
  ingest_bucket_ownership_controls,
505
513
  bucket_ownership_controls_configs=bucket_ownership_controls_configs,
506
514
  UpdateTag=update_tag,
@@ -524,7 +532,8 @@ def _load_bucket_logging(
524
532
  bucket.logging_target_bucket = bucket_logging.target_bucket,
525
533
  bucket.lastupdated = $update_tag
526
534
  """
527
- neo4j_session.run(
535
+ run_write_query(
536
+ neo4j_session,
528
537
  ingest_bucket_logging,
529
538
  bucket_logging_configs=bucket_logging_configs,
530
539
  update_tag=update_tag,
@@ -536,7 +545,8 @@ def _set_default_values(neo4j_session: neo4j.Session, aws_account_id: str) -> No
536
545
  MATCH (:AWSAccount{id: $AWS_ID})-[:RESOURCE]->(s:S3Bucket) where s.anonymous_actions IS NULL
537
546
  SET s.anonymous_access = false, s.anonymous_actions = []
538
547
  """
539
- neo4j_session.run(
548
+ run_write_query(
549
+ neo4j_session,
540
550
  set_defaults,
541
551
  AWS_ID=aws_account_id,
542
552
  )
@@ -545,7 +555,8 @@ def _set_default_values(neo4j_session: neo4j.Session, aws_account_id: str) -> No
545
555
  MATCH (:AWSAccount{id: $AWS_ID})-[:RESOURCE]->(s:S3Bucket) where s.default_encryption IS NULL
546
556
  SET s.default_encryption = false
547
557
  """
548
- neo4j_session.run(
558
+ run_write_query(
559
+ neo4j_session,
549
560
  set_encryption_defaults,
550
561
  AWS_ID=aws_account_id,
551
562
  )
@@ -993,7 +1004,8 @@ def _load_s3_notifications(
993
1004
  ON CREATE SET r.firstseen = timestamp()
994
1005
  SET r.lastupdated = $UpdateTag
995
1006
  """
996
- neo4j_session.run(
1007
+ run_write_query(
1008
+ neo4j_session,
997
1009
  ingest_notifications,
998
1010
  notifications=notifications,
999
1011
  UpdateTag=update_tag,
@@ -1025,7 +1037,8 @@ def load_s3_buckets(
1025
1037
 
1026
1038
  for bucket in data["Buckets"]:
1027
1039
  arn = "arn:aws:s3:::" + bucket["Name"]
1028
- neo4j_session.run(
1040
+ run_write_query(
1041
+ neo4j_session,
1029
1042
  ingest_bucket,
1030
1043
  BucketName=bucket["Name"],
1031
1044
  BucketRegion=bucket["Region"],
@@ -9,6 +9,7 @@ import boto3
9
9
  import neo4j
10
10
 
11
11
  from cartography.client.core.tx import load
12
+ from cartography.client.core.tx import read_list_of_values_tx
12
13
  from cartography.graph.job import GraphJob
13
14
  from cartography.models.aws.ssm.instance_information import SSMInstanceInformationSchema
14
15
  from cartography.models.aws.ssm.instance_patch import SSMInstancePatchSchema
@@ -31,15 +32,12 @@ def get_instance_ids(
31
32
  WHERE i.region = $Region
32
33
  RETURN i.id
33
34
  """
34
- results = neo4j_session.run(
35
+ return neo4j_session.execute_read(
36
+ read_list_of_values_tx,
35
37
  get_instances_query,
36
38
  AWS_ACCOUNT_ID=current_aws_account_id,
37
39
  Region=region,
38
40
  )
39
- instance_ids = []
40
- for r in results:
41
- instance_ids.append(r["i.id"])
42
- return instance_ids
43
41
 
44
42
 
45
43
  @timeit