cartography 0.114.0__py3-none-any.whl → 0.115.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 (39) hide show
  1. cartography/_version.py +2 -2
  2. cartography/cli.py +2 -2
  3. cartography/client/core/tx.py +11 -0
  4. cartography/intel/aws/config.py +7 -3
  5. cartography/intel/aws/ecr.py +9 -9
  6. cartography/intel/aws/identitycenter.py +240 -13
  7. cartography/intel/aws/lambda_function.py +69 -2
  8. cartography/intel/aws/organizations.py +3 -1
  9. cartography/intel/aws/permission_relationships.py +3 -1
  10. cartography/intel/aws/redshift.py +9 -4
  11. cartography/intel/aws/route53.py +53 -3
  12. cartography/intel/aws/securityhub.py +3 -1
  13. cartography/intel/azure/__init__.py +8 -0
  14. cartography/intel/azure/logic_apps.py +101 -0
  15. cartography/intel/create_indexes.py +2 -1
  16. cartography/intel/dns.py +5 -2
  17. cartography/intel/gcp/dns.py +2 -1
  18. cartography/intel/github/repos.py +3 -6
  19. cartography/intel/gsuite/api.py +17 -4
  20. cartography/intel/okta/applications.py +9 -4
  21. cartography/intel/okta/awssaml.py +5 -2
  22. cartography/intel/okta/factors.py +3 -1
  23. cartography/intel/okta/groups.py +5 -2
  24. cartography/intel/okta/organization.py +3 -1
  25. cartography/intel/okta/origins.py +3 -1
  26. cartography/intel/okta/roles.py +5 -2
  27. cartography/intel/okta/users.py +3 -1
  28. cartography/models/aws/identitycenter/awspermissionset.py +24 -1
  29. cartography/models/aws/identitycenter/awssogroup.py +70 -0
  30. cartography/models/aws/identitycenter/awsssouser.py +37 -1
  31. cartography/models/aws/lambda_function/lambda_function.py +2 -0
  32. cartography/models/azure/logic_apps.py +56 -0
  33. cartography/models/entra/user.py +18 -0
  34. {cartography-0.114.0.dist-info → cartography-0.115.0.dist-info}/METADATA +3 -2
  35. {cartography-0.114.0.dist-info → cartography-0.115.0.dist-info}/RECORD +39 -36
  36. {cartography-0.114.0.dist-info → cartography-0.115.0.dist-info}/WHEEL +0 -0
  37. {cartography-0.114.0.dist-info → cartography-0.115.0.dist-info}/entry_points.txt +0 -0
  38. {cartography-0.114.0.dist-info → cartography-0.115.0.dist-info}/licenses/LICENSE +0 -0
  39. {cartography-0.114.0.dist-info → cartography-0.115.0.dist-info}/top_level.txt +0 -0
cartography/_version.py CHANGED
@@ -28,7 +28,7 @@ version_tuple: VERSION_TUPLE
28
28
  commit_id: COMMIT_ID
29
29
  __commit_id__: COMMIT_ID
30
30
 
31
- __version__ = version = '0.114.0'
32
- __version_tuple__ = version_tuple = (0, 114, 0)
31
+ __version__ = version = '0.115.0'
32
+ __version_tuple__ = version_tuple = (0, 115, 0)
33
33
 
34
34
  __commit_id__ = commit_id = None
cartography/cli.py CHANGED
@@ -967,8 +967,8 @@ class CLI:
967
967
  logger.warning("A Kandji base URI was provided but a token was not.")
968
968
  config.kandji_token = None
969
969
  else:
970
- logger.warning("A Kandji base URI was not provided.")
971
970
  config.kandji_base_uri = None
971
+ config.kandji_token = None
972
972
 
973
973
  if config.statsd_enabled:
974
974
  logger.debug(
@@ -1096,8 +1096,8 @@ class CLI:
1096
1096
  logger.warning("A SnipeIT base URI was provided but a token was not.")
1097
1097
  config.snipeit_token = None
1098
1098
  else:
1099
- logger.warning("A SnipeIT base URI was not provided.")
1100
1099
  config.snipeit_base_uri = None
1100
+ config.snipeit_token = None
1101
1101
 
1102
1102
  # Tailscale config
1103
1103
  if config.tailscale_token_env_var:
@@ -19,6 +19,17 @@ from cartography.util import batch
19
19
  logger = logging.getLogger(__name__)
20
20
 
21
21
 
22
+ def run_write_query(
23
+ neo4j_session: neo4j.Session, query: str, **parameters: Any
24
+ ) -> None:
25
+ """Execute a write query inside a managed transaction."""
26
+
27
+ def _run_query_tx(tx: neo4j.Transaction) -> None:
28
+ tx.run(query, **parameters).consume()
29
+
30
+ neo4j_session.execute_write(_run_query_tx)
31
+
32
+
22
33
  def read_list_of_values_tx(
23
34
  tx: neo4j.Transaction,
24
35
  query: str,
@@ -5,6 +5,7 @@ from typing import List
5
5
  import boto3
6
6
  import neo4j
7
7
 
8
+ from cartography.client.core.tx import run_write_query
8
9
  from cartography.util import aws_handle_regions
9
10
  from cartography.util import run_cleanup_job
10
11
  from cartography.util import timeit
@@ -80,7 +81,8 @@ def load_configuration_recorders(
80
81
  for recorder in data:
81
82
  recorder["_id"] = f'{recorder["name"]}:{current_aws_account_id}:{region}'
82
83
 
83
- neo4j_session.run(
84
+ run_write_query(
85
+ neo4j_session,
84
86
  ingest_configuration_recorders,
85
87
  Recorders=data,
86
88
  Region=region,
@@ -120,7 +122,8 @@ def load_delivery_channels(
120
122
  for channel in data:
121
123
  channel["_id"] = f'{channel["name"]}:{current_aws_account_id}:{region}'
122
124
 
123
- neo4j_session.run(
125
+ run_write_query(
126
+ neo4j_session,
124
127
  ingest_delivery_channels,
125
128
  Channels=data,
126
129
  Region=region,
@@ -167,7 +170,8 @@ def load_config_rules(
167
170
  for detail in rule["Source"]["SourceDetails"]:
168
171
  details.append(f"{detail}")
169
172
  rule["_source_details"] = details
170
- neo4j_session.run(
173
+ run_write_query(
174
+ neo4j_session,
171
175
  ingest_config_rules,
172
176
  Rules=data,
173
177
  Region=region,
@@ -57,15 +57,15 @@ def get_ecr_repository_images(
57
57
  )
58
58
  for response in describe_response:
59
59
  image_details = response["imageDetails"]
60
- image_details = [
61
- (
62
- {**detail, "imageTag": detail["imageTags"][0]}
63
- if detail.get("imageTags")
64
- else detail
65
- )
66
- for detail in image_details
67
- ]
68
- ecr_repository_images.extend(image_details)
60
+ for detail in image_details:
61
+ tags = detail.get("imageTags") or []
62
+ if tags:
63
+ for tag in tags:
64
+ image_detail = {**detail, "imageTag": tag}
65
+ image_detail.pop("imageTags", None)
66
+ ecr_repository_images.append(image_detail)
67
+ else:
68
+ ecr_repository_images.append({**detail})
69
69
  return ecr_repository_images
70
70
 
71
71
 
@@ -2,6 +2,8 @@ import logging
2
2
  from typing import Any
3
3
  from typing import Dict
4
4
  from typing import List
5
+ from typing import Optional
6
+ from typing import Union
5
7
 
6
8
  import boto3
7
9
  import neo4j
@@ -15,9 +17,13 @@ from cartography.models.aws.identitycenter.awsidentitycenter import (
15
17
  from cartography.models.aws.identitycenter.awspermissionset import (
16
18
  AWSPermissionSetSchema,
17
19
  )
20
+ from cartography.models.aws.identitycenter.awspermissionset import (
21
+ RoleAssignmentAllowedByGroupMatchLink,
22
+ )
18
23
  from cartography.models.aws.identitycenter.awspermissionset import (
19
24
  RoleAssignmentAllowedByMatchLink,
20
25
  )
26
+ from cartography.models.aws.identitycenter.awssogroup import AWSSSOGroupSchema
21
27
  from cartography.models.aws.identitycenter.awsssouser import AWSSSOUserSchema
22
28
  from cartography.util import aws_handle_regions
23
29
  from cartography.util import timeit
@@ -150,18 +156,72 @@ def get_sso_users(
150
156
  return users
151
157
 
152
158
 
153
- def transform_sso_users(users: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
159
+ @timeit
160
+ @aws_handle_regions
161
+ def get_sso_groups(
162
+ boto3_session: boto3.session.Session,
163
+ identity_store_id: str,
164
+ region: str,
165
+ ) -> List[Dict]:
166
+ """
167
+ Get all SSO groups for a given Identity Store
168
+ """
169
+ client = boto3_session.client("identitystore", region_name=region)
170
+ groups: List[Dict[str, Any]] = []
171
+
172
+ paginator = client.get_paginator("list_groups")
173
+ for page in paginator.paginate(IdentityStoreId=identity_store_id):
174
+ group_page = page.get("Groups", [])
175
+ for group in group_page:
176
+ groups.append(group)
177
+
178
+ return groups
179
+
180
+
181
+ def transform_sso_users(
182
+ users: List[Dict[str, Any]],
183
+ user_group_memberships: Optional[Dict[str, List[str]]] = None,
184
+ user_permission_sets: Optional[Dict[str, List[str]]] = None,
185
+ ) -> List[Dict[str, Any]]:
154
186
  """
155
- Transform SSO users to match the expected schema
187
+ Transform SSO users to match the expected schema, optionally including group memberships
156
188
  """
157
189
  transformed_users = []
158
190
  for user in users:
159
- if user.get("ExternalIds") is not None:
191
+ if user.get("ExternalIds"):
160
192
  user["ExternalId"] = user["ExternalIds"][0].get("Id")
193
+ # Add group memberships if provided
194
+ if user_group_memberships:
195
+ user["MemberOfGroups"] = user_group_memberships.get(user["UserId"], [])
196
+ # Add direct permission set assignments if provided
197
+ if user_permission_sets:
198
+ user["AssignedPermissionSets"] = user_permission_sets.get(
199
+ user["UserId"], []
200
+ )
161
201
  transformed_users.append(user)
162
202
  return transformed_users
163
203
 
164
204
 
205
+ def transform_sso_groups(
206
+ groups: List[Dict[str, Any]],
207
+ group_permission_sets: Optional[Dict[str, List[str]]] = None,
208
+ ) -> List[Dict[str, Any]]:
209
+ """
210
+ Transform SSO groups to match the expected schema, optionally including permission set assignments
211
+ """
212
+ transformed_groups: List[Dict[str, Any]] = []
213
+ for group in groups:
214
+ if group.get("ExternalIds"):
215
+ group["ExternalId"] = group["ExternalIds"][0].get("Id")
216
+ # Add permission set assignments if provided
217
+ if group_permission_sets:
218
+ group["AssignedPermissionSets"] = group_permission_sets.get(
219
+ group["GroupId"], []
220
+ )
221
+ transformed_groups.append(group)
222
+ return transformed_groups
223
+
224
+
165
225
  @timeit
166
226
  def load_sso_users(
167
227
  neo4j_session: neo4j.Session,
@@ -189,6 +249,33 @@ def load_sso_users(
189
249
  )
190
250
 
191
251
 
252
+ @timeit
253
+ def load_sso_groups(
254
+ neo4j_session: neo4j.Session,
255
+ groups: List[Dict],
256
+ identity_store_id: str,
257
+ region: str,
258
+ aws_account_id: str,
259
+ aws_update_tag: int,
260
+ ) -> None:
261
+ """
262
+ Load SSO groups into the graph
263
+ """
264
+ logger.info(
265
+ f"Loading {len(groups)} SSO groups for identity store {identity_store_id} in region {region}",
266
+ )
267
+
268
+ load(
269
+ neo4j_session,
270
+ AWSSSOGroupSchema(),
271
+ groups,
272
+ lastupdated=aws_update_tag,
273
+ IdentityStoreId=identity_store_id,
274
+ AWS_ID=aws_account_id,
275
+ Region=region,
276
+ )
277
+
278
+
192
279
  @timeit
193
280
  @aws_handle_regions
194
281
  def get_role_assignments(
@@ -225,6 +312,71 @@ def get_role_assignments(
225
312
  return role_assignments
226
313
 
227
314
 
315
+ @timeit
316
+ @aws_handle_regions
317
+ def get_group_role_assignments(
318
+ boto3_session: boto3.session.Session,
319
+ groups: List[Dict],
320
+ instance_arn: str,
321
+ region: str,
322
+ ) -> List[Dict]:
323
+ """
324
+ Get role assignments for SSO groups
325
+ """
326
+
327
+ logger.info(f"Getting role assignments for {len(groups)} groups")
328
+ client = boto3_session.client("sso-admin", region_name=region)
329
+ role_assignments: List[Dict[str, Any]] = []
330
+
331
+ for group in groups:
332
+ group_id = group["GroupId"]
333
+ paginator = client.get_paginator("list_account_assignments_for_principal")
334
+ for page in paginator.paginate(
335
+ InstanceArn=instance_arn,
336
+ PrincipalId=group_id,
337
+ PrincipalType="GROUP",
338
+ ):
339
+ for assignment in page.get("AccountAssignments", []):
340
+ role_assignments.append(
341
+ {
342
+ "GroupId": group_id,
343
+ "PermissionSetArn": assignment.get("PermissionSetArn"),
344
+ "AccountId": assignment.get("AccountId"),
345
+ }
346
+ )
347
+
348
+ return role_assignments
349
+
350
+
351
+ @timeit
352
+ @aws_handle_regions
353
+ def get_user_group_memberships(
354
+ boto3_session: boto3.session.Session,
355
+ identity_store_id: str,
356
+ groups: List[Dict],
357
+ region: str,
358
+ ) -> Dict[str, List[str]]:
359
+ """
360
+ Return a mapping of UserId -> [GroupIds] for all group memberships in the identity store.
361
+ """
362
+ client = boto3_session.client("identitystore", region_name=region)
363
+ user_groups: Dict[str, List[str]] = {}
364
+
365
+ for group in groups:
366
+ group_id = group["GroupId"]
367
+ paginator = client.get_paginator("list_group_memberships")
368
+ for page in paginator.paginate(
369
+ IdentityStoreId=identity_store_id, GroupId=group_id
370
+ ):
371
+ for membership in page.get("GroupMemberships", []):
372
+ member = membership.get("MemberId", {})
373
+ user_id = member.get("UserId")
374
+ if user_id:
375
+ user_groups.setdefault(user_id, []).append(group_id)
376
+
377
+ return user_groups
378
+
379
+
228
380
  @timeit
229
381
  def get_permset_roles(
230
382
  neo4j_session: neo4j.Session,
@@ -270,14 +422,18 @@ def load_role_assignments(
270
422
  role_assignments: List[Dict],
271
423
  aws_account_id: str,
272
424
  aws_update_tag: int,
425
+ matchlink_schema: Union[
426
+ RoleAssignmentAllowedByMatchLink,
427
+ RoleAssignmentAllowedByGroupMatchLink,
428
+ ],
273
429
  ) -> None:
274
430
  """
275
- Load role assignments into the graph using MatchLink schema
431
+ Load role assignments into the graph using the provided MatchLink schema
276
432
  """
277
433
  logger.info(f"Loading {len(role_assignments)} role assignments")
278
434
  load_matchlinks(
279
435
  neo4j_session,
280
- RoleAssignmentAllowedByMatchLink(),
436
+ matchlink_schema,
281
437
  role_assignments,
282
438
  lastupdated=aws_update_tag,
283
439
  _sub_resource_label="AWSAccount",
@@ -300,6 +456,9 @@ def cleanup(
300
456
  GraphJob.from_node_schema(AWSSSOUserSchema(), common_job_parameters).run(
301
457
  neo4j_session,
302
458
  )
459
+ GraphJob.from_node_schema(AWSSSOGroupSchema(), common_job_parameters).run(
460
+ neo4j_session,
461
+ )
303
462
 
304
463
  # Clean up role assignment MatchLinks
305
464
  GraphJob.from_matchlink(
@@ -308,6 +467,12 @@ def cleanup(
308
467
  common_job_parameters["AWS_ID"],
309
468
  common_job_parameters["UPDATE_TAG"],
310
469
  ).run(neo4j_session)
470
+ GraphJob.from_matchlink(
471
+ RoleAssignmentAllowedByGroupMatchLink(),
472
+ "AWSAccount",
473
+ common_job_parameters["AWS_ID"],
474
+ common_job_parameters["UPDATE_TAG"],
475
+ ).run(neo4j_session)
311
476
 
312
477
 
313
478
  @timeit
@@ -350,35 +515,97 @@ def sync_identity_center_instances(
350
515
  update_tag,
351
516
  )
352
517
 
353
- users = get_sso_users(boto3_session, identity_store_id, region)
354
- transformed_users = transform_sso_users(users)
355
- load_sso_users(
518
+ # Fetch groups first to avoid interleaving between groups and users
519
+ groups = get_sso_groups(boto3_session, identity_store_id, region)
520
+
521
+ # Get permission set assignments for groups
522
+ 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)
533
+
534
+ # Transform and load groups with their permission set assignments FIRST
535
+ # so that user->group membership edges can attach in the same run.
536
+ transformed_groups = transform_sso_groups(groups, group_permission_sets)
537
+ load_sso_groups(
356
538
  neo4j_session,
357
- transformed_users,
539
+ transformed_groups,
358
540
  identity_store_id,
359
541
  region,
360
542
  current_aws_account_id,
361
543
  update_tag,
362
544
  )
363
545
 
364
- # Get and load role assignments
365
- role_assignments = get_role_assignments(
546
+ # Handle users AFTER groups exist
547
+ users = get_sso_users(boto3_session, identity_store_id, region)
548
+ user_group_memberships = get_user_group_memberships(
549
+ boto3_session,
550
+ identity_store_id,
551
+ groups,
552
+ region,
553
+ )
554
+
555
+ # Get direct permission set assignments for users
556
+ user_permission_sets: Dict[str, List[str]] = {}
557
+ user_role_assignments_raw = get_role_assignments(
366
558
  boto3_session,
367
559
  users,
368
560
  instance_arn,
369
561
  region,
370
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)
371
567
 
372
- # Enrich role assignments with exact role ARNs using permission set relationships
568
+ # Transform and load users with their group memberships AFTER groups exist
569
+ transformed_users = transform_sso_users(
570
+ users,
571
+ user_group_memberships,
572
+ user_permission_sets,
573
+ )
574
+ load_sso_users(
575
+ neo4j_session,
576
+ transformed_users,
577
+ identity_store_id,
578
+ region,
579
+ current_aws_account_id,
580
+ update_tag,
581
+ )
582
+
583
+ # Enrich role assignments with exact role ARNs using permission set relationships.
584
+ # Note: we do this after groups and users are loaded so that
585
+ # load_role_assignments calls can MATCH existing AWSSSOUser/AWSSSOGroup
586
+ # nodes when drawing the ALLOWED_BY edges.
373
587
  enriched_role_assignments = get_permset_roles(
374
588
  neo4j_session,
375
- role_assignments,
589
+ user_role_assignments_raw,
376
590
  )
377
591
  load_role_assignments(
378
592
  neo4j_session,
379
593
  enriched_role_assignments,
380
594
  current_aws_account_id,
381
595
  update_tag,
596
+ RoleAssignmentAllowedByMatchLink(),
597
+ )
598
+
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(),
382
609
  )
383
610
 
384
611
  cleanup(neo4j_session, common_job_parameters)
@@ -1,3 +1,4 @@
1
+ import json
1
2
  import logging
2
3
  from typing import Any
3
4
  from typing import Dict
@@ -6,6 +7,7 @@ from typing import List
6
7
  import boto3
7
8
  import botocore
8
9
  import neo4j
10
+ from policyuniverse.policy import Policy
9
11
 
10
12
  from cartography.client.core.tx import load
11
13
  from cartography.graph.job import GraphJob
@@ -36,7 +38,11 @@ def get_lambda_data(boto3_session: boto3.session.Session, region: str) -> List[D
36
38
  return lambda_functions
37
39
 
38
40
 
39
- def transform_lambda_functions(lambda_functions: List[Dict], region: str) -> List[Dict]:
41
+ def transform_lambda_functions(
42
+ lambda_functions: List[Dict],
43
+ permissions_by_arn: Dict[str, Dict[str, Any]],
44
+ region: str,
45
+ ) -> List[Dict]:
40
46
  transformed_functions = []
41
47
 
42
48
  for function_data in lambda_functions:
@@ -48,6 +54,11 @@ def transform_lambda_functions(lambda_functions: List[Dict], region: str) -> Lis
48
54
 
49
55
  transformed_function["Region"] = region
50
56
 
57
+ function_arn = function_data["FunctionArn"]
58
+ permission_data = permissions_by_arn[function_arn]
59
+ transformed_function["AnonymousAccess"] = permission_data["AnonymousAccess"]
60
+ transformed_function["AnonymousActions"] = permission_data["AnonymousActions"]
61
+
51
62
  transformed_functions.append(transformed_function)
52
63
 
53
64
  return transformed_functions
@@ -126,6 +137,61 @@ def get_event_source_mappings(
126
137
  return event_source_mappings
127
138
 
128
139
 
140
+ @timeit
141
+ @aws_handle_regions
142
+ def get_lambda_permissions(
143
+ lambda_functions: List[Dict],
144
+ boto3_session: boto3.Session,
145
+ region: str,
146
+ ) -> Dict[str, Dict[str, Any]]:
147
+ """
148
+ Get Lambda permissions for the given functions in the specified region.
149
+ """
150
+ client = boto3_session.client("lambda", region_name=region)
151
+ all_permissions = {}
152
+ for function in lambda_functions:
153
+ function_name = function["FunctionName"]
154
+ function_arn = function["FunctionArn"]
155
+
156
+ all_permissions[function_arn] = {
157
+ "AnonymousAccess": None,
158
+ "AnonymousActions": None,
159
+ }
160
+
161
+ try:
162
+ response = client.get_policy(FunctionName=function_name)
163
+ policy = response.get("Policy")
164
+
165
+ if policy:
166
+ parsed_policy = parse_policy(function_arn, policy)
167
+ all_permissions[function_arn] = {
168
+ "AnonymousAccess": parsed_policy.get("AnonymousAccess"),
169
+ "AnonymousActions": parsed_policy.get("AnonymousActions"),
170
+ }
171
+ except client.exceptions.ResourceNotFoundException:
172
+ logger.debug(f"No policy found for Lambda function {function_name}")
173
+ pass
174
+ except Exception as e:
175
+ logger.warning(
176
+ f"Error getting policy for Lambda function {function_name}: {e}"
177
+ )
178
+
179
+ return all_permissions
180
+
181
+
182
+ def parse_policy(function_arn: str, policy: str) -> Dict[str, Any]:
183
+ """
184
+ Parse the Lambda permission policy to extract anonymous access and actions.
185
+ """
186
+ policy_obj = Policy(json.loads(policy))
187
+ inet_actions = policy_obj.internet_accessible_actions()
188
+
189
+ return {
190
+ "AnonymousAccess": policy_obj.is_internet_accessible(),
191
+ "AnonymousActions": list(inet_actions) if inet_actions else [],
192
+ }
193
+
194
+
129
195
  @timeit
130
196
  def load_lambda_function_aliases(
131
197
  neo4j_session: neo4j.Session,
@@ -306,7 +372,8 @@ def sync(
306
372
 
307
373
  # Get and load core lambda functions
308
374
  data = get_lambda_data(boto3_session, region)
309
- transformed_data = transform_lambda_functions(data, region)
375
+ permissions_by_arn = get_lambda_permissions(data, boto3_session, region)
376
+ transformed_data = transform_lambda_functions(data, permissions_by_arn, region)
310
377
  load_lambda_functions(
311
378
  neo4j_session,
312
379
  transformed_data,
@@ -5,6 +5,7 @@ import boto3
5
5
  import botocore.exceptions
6
6
  import neo4j
7
7
 
8
+ from cartography.client.core.tx import run_write_query
8
9
  from cartography.intel.aws.iam import sync_root_principal
9
10
  from cartography.util import timeit
10
11
 
@@ -114,7 +115,8 @@ def load_aws_accounts(
114
115
  """
115
116
  for account_name, account_id in aws_accounts.items():
116
117
  root_arn = f"arn:aws:iam::{account_id}:root"
117
- neo4j_session.run(
118
+ run_write_query(
119
+ neo4j_session,
118
120
  query,
119
121
  ACCOUNT_ID=account_id,
120
122
  ACCOUNT_NAME=account_name,
@@ -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 run_write_query
16
17
  from cartography.graph.statement import GraphStatement
17
18
  from cartography.util import timeit
18
19
 
@@ -329,7 +330,8 @@ def load_principal_mappings(
329
330
  node_label=node_label,
330
331
  relationship_name=relationship_name,
331
332
  )
332
- neo4j_session.run(
333
+ run_write_query(
334
+ neo4j_session,
333
335
  map_policy_query_template,
334
336
  Mapping=principal_mappings,
335
337
  aws_update_tag=update_tag,
@@ -5,6 +5,7 @@ from typing import List
5
5
  import boto3
6
6
  import neo4j
7
7
 
8
+ from cartography.client.core.tx import run_write_query
8
9
  from cartography.util import aws_handle_regions
9
10
  from cartography.util import run_cleanup_job
10
11
  from cartography.util import timeit
@@ -88,7 +89,8 @@ def load_redshift_cluster_data(
88
89
  SET r.lastupdated = $aws_update_tag
89
90
  """
90
91
  for cluster in clusters:
91
- neo4j_session.run(
92
+ run_write_query(
93
+ neo4j_session,
92
94
  ingest_cluster,
93
95
  Arn=cluster["arn"],
94
96
  AZ=cluster["AvailabilityZone"],
@@ -128,7 +130,8 @@ def _attach_ec2_security_groups(
128
130
  SET m.lastupdated = $aws_update_tag
129
131
  """
130
132
  for group in cluster.get("VpcSecurityGroups", []):
131
- neo4j_session.run(
133
+ run_write_query(
134
+ neo4j_session,
132
135
  attach_cluster_to_group,
133
136
  ClusterArn=cluster["arn"],
134
137
  GroupId=group["VpcSecurityGroupId"],
@@ -150,7 +153,8 @@ def _attach_iam_roles(
150
153
  SET s.lastupdated = $aws_update_tag
151
154
  """
152
155
  for role in cluster.get("IamRoles", []):
153
- neo4j_session.run(
156
+ run_write_query(
157
+ neo4j_session,
154
158
  attach_cluster_to_role,
155
159
  ClusterArn=cluster["arn"],
156
160
  RoleArn=role["IamRoleArn"],
@@ -172,7 +176,8 @@ def _attach_aws_vpc(
172
176
  SET m.lastupdated = $aws_update_tag
173
177
  """
174
178
  if cluster.get("VpcId"):
175
- neo4j_session.run(
179
+ run_write_query(
180
+ neo4j_session,
176
181
  attach_cluster_to_vpc,
177
182
  ClusterArn=cluster["arn"],
178
183
  VpcId=cluster["VpcId"],