cartography 0.108.0rc2__py3-none-any.whl → 0.109.0rc2__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 (63) hide show
  1. cartography/_version.py +2 -2
  2. cartography/data/indexes.cypher +0 -17
  3. cartography/data/jobs/cleanup/gcp_compute_vpc_cleanup.json +0 -12
  4. cartography/intel/aws/cloudtrail_management_events.py +36 -3
  5. cartography/intel/aws/ecr.py +55 -80
  6. cartography/intel/aws/glue.py +117 -0
  7. cartography/intel/aws/identitycenter.py +71 -23
  8. cartography/intel/aws/kms.py +160 -200
  9. cartography/intel/aws/lambda_function.py +206 -190
  10. cartography/intel/aws/rds.py +243 -458
  11. cartography/intel/aws/resourcegroupstaggingapi.py +77 -18
  12. cartography/intel/aws/resources.py +2 -0
  13. cartography/intel/aws/route53.py +334 -332
  14. cartography/intel/aws/secretsmanager.py +62 -44
  15. cartography/intel/entra/groups.py +29 -1
  16. cartography/intel/gcp/__init__.py +10 -0
  17. cartography/intel/gcp/compute.py +19 -42
  18. cartography/models/aws/ecr/__init__.py +0 -0
  19. cartography/models/aws/ecr/image.py +41 -0
  20. cartography/models/aws/ecr/repository.py +72 -0
  21. cartography/models/aws/ecr/repository_image.py +95 -0
  22. cartography/models/aws/glue/__init__.py +0 -0
  23. cartography/models/aws/glue/connection.py +51 -0
  24. cartography/models/aws/identitycenter/awspermissionset.py +44 -0
  25. cartography/models/aws/kms/__init__.py +0 -0
  26. cartography/models/aws/kms/aliases.py +86 -0
  27. cartography/models/aws/kms/grants.py +65 -0
  28. cartography/models/aws/kms/keys.py +88 -0
  29. cartography/models/aws/lambda_function/__init__.py +0 -0
  30. cartography/models/aws/lambda_function/alias.py +74 -0
  31. cartography/models/aws/lambda_function/event_source_mapping.py +88 -0
  32. cartography/models/aws/lambda_function/lambda_function.py +89 -0
  33. cartography/models/aws/lambda_function/layer.py +72 -0
  34. cartography/models/aws/rds/__init__.py +0 -0
  35. cartography/models/aws/rds/cluster.py +89 -0
  36. cartography/models/aws/rds/instance.py +154 -0
  37. cartography/models/aws/rds/snapshot.py +108 -0
  38. cartography/models/aws/rds/subnet_group.py +101 -0
  39. cartography/models/aws/route53/__init__.py +0 -0
  40. cartography/models/aws/route53/dnsrecord.py +214 -0
  41. cartography/models/aws/route53/nameserver.py +63 -0
  42. cartography/models/aws/route53/subzone.py +40 -0
  43. cartography/models/aws/route53/zone.py +47 -0
  44. cartography/models/aws/secretsmanager/secret.py +106 -0
  45. cartography/models/entra/group.py +26 -0
  46. cartography/models/entra/user.py +6 -0
  47. cartography/models/gcp/compute/__init__.py +0 -0
  48. cartography/models/gcp/compute/vpc.py +50 -0
  49. cartography/util.py +8 -1
  50. {cartography-0.108.0rc2.dist-info → cartography-0.109.0rc2.dist-info}/METADATA +2 -2
  51. {cartography-0.108.0rc2.dist-info → cartography-0.109.0rc2.dist-info}/RECORD +55 -34
  52. cartography/data/jobs/cleanup/aws_dns_cleanup.json +0 -65
  53. cartography/data/jobs/cleanup/aws_import_identity_center_cleanup.json +0 -16
  54. cartography/data/jobs/cleanup/aws_import_lambda_cleanup.json +0 -50
  55. cartography/data/jobs/cleanup/aws_import_rds_clusters_cleanup.json +0 -23
  56. cartography/data/jobs/cleanup/aws_import_rds_instances_cleanup.json +0 -47
  57. cartography/data/jobs/cleanup/aws_import_rds_snapshots_cleanup.json +0 -23
  58. cartography/data/jobs/cleanup/aws_import_secrets_cleanup.json +0 -8
  59. cartography/data/jobs/cleanup/aws_kms_details.json +0 -10
  60. {cartography-0.108.0rc2.dist-info → cartography-0.109.0rc2.dist-info}/WHEEL +0 -0
  61. {cartography-0.108.0rc2.dist-info → cartography-0.109.0rc2.dist-info}/entry_points.txt +0 -0
  62. {cartography-0.108.0rc2.dist-info → cartography-0.109.0rc2.dist-info}/licenses/LICENSE +0 -0
  63. {cartography-0.108.0rc2.dist-info → cartography-0.109.0rc2.dist-info}/top_level.txt +0 -0
@@ -2,14 +2,20 @@ import logging
2
2
  from typing import Any
3
3
  from typing import Dict
4
4
  from typing import List
5
- from typing import Tuple
6
5
 
7
6
  import boto3
8
7
  import botocore
9
8
  import neo4j
10
9
 
10
+ from cartography.client.core.tx import load
11
+ from cartography.graph.job import GraphJob
12
+ from cartography.models.aws.lambda_function.alias import AWSLambdaFunctionAliasSchema
13
+ from cartography.models.aws.lambda_function.event_source_mapping import (
14
+ AWSLambdaEventSourceMappingSchema,
15
+ )
16
+ from cartography.models.aws.lambda_function.lambda_function import AWSLambdaSchema
17
+ from cartography.models.aws.lambda_function.layer import AWSLambdaLayerSchema
11
18
  from cartography.util import aws_handle_regions
12
- from cartography.util import run_cleanup_job
13
19
  from cartography.util import timeit
14
20
 
15
21
  logger = logging.getLogger(__name__)
@@ -30,6 +36,47 @@ def get_lambda_data(boto3_session: boto3.session.Session, region: str) -> List[D
30
36
  return lambda_functions
31
37
 
32
38
 
39
+ def transform_lambda_functions(lambda_functions: List[Dict], region: str) -> List[Dict]:
40
+ transformed_functions = []
41
+
42
+ for function_data in lambda_functions:
43
+ transformed_function = function_data.copy()
44
+
45
+ # In API response, TracingConfig is a nested object so flatten it for use in the data model
46
+ tracing_config = function_data.get("TracingConfig", {})
47
+ transformed_function["TracingConfigMode"] = tracing_config.get("Mode")
48
+
49
+ transformed_function["Region"] = region
50
+
51
+ transformed_functions.append(transformed_function)
52
+
53
+ return transformed_functions
54
+
55
+
56
+ def transform_lambda_aliases(aliases: List[Dict], function_arn: str) -> List[Dict]:
57
+ """
58
+ Transform lambda function aliases by adding the parent function ARN.
59
+ """
60
+ transformed_aliases = []
61
+ for alias in aliases:
62
+ transformed_alias = alias.copy()
63
+ transformed_alias["FunctionArn"] = function_arn
64
+ transformed_aliases.append(transformed_alias)
65
+ return transformed_aliases
66
+
67
+
68
+ def transform_lambda_layers(layers: List[Dict], function_arn: str) -> List[Dict]:
69
+ """
70
+ Transform lambda layers by adding the parent function ARN.
71
+ """
72
+ transformed_layers = []
73
+ for layer in layers:
74
+ transformed_layer = layer.copy()
75
+ transformed_layer["FunctionArn"] = function_arn
76
+ transformed_layers.append(transformed_layer)
77
+ return transformed_layers
78
+
79
+
33
80
  @timeit
34
81
  def load_lambda_functions(
35
82
  neo4j_session: neo4j.Session,
@@ -38,53 +85,16 @@ def load_lambda_functions(
38
85
  current_aws_account_id: str,
39
86
  aws_update_tag: int,
40
87
  ) -> None:
41
- ingest_lambda_functions = """
42
- UNWIND $lambda_functions_list AS lf
43
- MERGE (lambda:AWSLambda{id: lf.FunctionArn})
44
- ON CREATE SET lambda.firstseen = timestamp()
45
- SET lambda.name = lf.FunctionName,
46
- lambda.modifieddate = lf.LastModified,
47
- lambda.runtime = lf.Runtime,
48
- lambda.description = lf.Description,
49
- lambda.timeout = lf.Timeout,
50
- lambda.memory = lf.MemorySize,
51
- lambda.codesize = lf.CodeSize,
52
- lambda.handler = lf.Handler,
53
- lambda.version = lf.Version,
54
- lambda.tracingconfigmode = lf.TracingConfig.Mode,
55
- lambda.revisionid = lf.RevisionId,
56
- lambda.state = lf.State,
57
- lambda.statereason = lf.StateReason,
58
- lambda.statereasoncode = lf.StateReasonCode,
59
- lambda.lastupdatestatus = lf.LastUpdateStatus,
60
- lambda.lastupdatestatusreason = lf.LastUpdateStatusReason,
61
- lambda.lastupdatestatusreasoncode = lf.LastUpdateStatusReasonCode,
62
- lambda.packagetype = lf.PackageType,
63
- lambda.signingprofileversionarn = lf.SigningProfileVersionArn,
64
- lambda.signingjobarn = lf.SigningJobArn,
65
- lambda.codesha256 = lf.CodeSha256,
66
- lambda.architectures = lf.Architectures,
67
- lambda.masterarn = lf.MasterArn,
68
- lambda.kmskeyarn = lf.KMSKeyArn,
69
- lambda.lastupdated = $aws_update_tag
70
- WITH lambda, lf
71
- MATCH (owner:AWSAccount{id: $AWS_ACCOUNT_ID})
72
- MERGE (owner)-[r:RESOURCE]->(lambda)
73
- ON CREATE SET r.firstseen = timestamp()
74
- SET r.lastupdated = $aws_update_tag
75
- WITH lambda, lf
76
- MATCH (role:AWSPrincipal{arn: lf.Role})
77
- MERGE (lambda)-[r:STS_ASSUMEROLE_ALLOW]->(role)
78
- ON CREATE SET r.firstseen = timestamp()
79
- SET r.lastupdated = $aws_update_tag
80
88
  """
81
-
82
- neo4j_session.run(
83
- ingest_lambda_functions,
84
- lambda_functions_list=data,
89
+ Load AWS Lambda functions using the data model
90
+ """
91
+ load(
92
+ neo4j_session,
93
+ AWSLambdaSchema(),
94
+ data,
95
+ AWS_ID=current_aws_account_id,
85
96
  Region=region,
86
- AWS_ACCOUNT_ID=current_aws_account_id,
87
- aws_update_tag=aws_update_tag,
97
+ lastupdated=aws_update_tag,
88
98
  )
89
99
 
90
100
 
@@ -117,170 +127,174 @@ def get_event_source_mappings(
117
127
 
118
128
 
119
129
  @timeit
120
- @aws_handle_regions
121
- def get_lambda_function_details(
122
- boto3_session: boto3.session.Session,
123
- data: List[Dict],
130
+ def load_lambda_function_aliases(
131
+ neo4j_session: neo4j.Session,
132
+ lambda_aliases: List[Dict],
124
133
  region: str,
125
- ) -> List[Tuple[str, List[Any], List[Any], List[Any]]]:
126
- client = boto3_session.client("lambda", region_name=region)
127
- details = []
128
- for lambda_function in data:
129
- function_aliases = get_function_aliases(lambda_function, client)
130
- event_source_mappings = get_event_source_mappings(lambda_function, client)
131
- layers = lambda_function.get("Layers", [])
132
- details.append(
133
- (
134
- lambda_function["FunctionArn"],
135
- function_aliases,
136
- event_source_mappings,
137
- layers,
138
- ),
139
- )
140
- return details
134
+ current_aws_account_id: str,
135
+ update_tag: int,
136
+ ) -> None:
137
+ """
138
+ Load AWS Lambda function aliases using the data model
139
+ """
140
+ load(
141
+ neo4j_session,
142
+ AWSLambdaFunctionAliasSchema(),
143
+ lambda_aliases,
144
+ AWS_ID=current_aws_account_id,
145
+ Region=region,
146
+ lastupdated=update_tag,
147
+ )
141
148
 
142
149
 
143
150
  @timeit
144
- def load_lambda_function_details(
151
+ def load_lambda_event_source_mappings(
145
152
  neo4j_session: neo4j.Session,
146
- lambda_function_details: List[Tuple[str, List[Dict], List[Dict], List[Dict]]],
153
+ lambda_event_source_mappings: List[Dict],
154
+ current_aws_account_id: str,
147
155
  update_tag: int,
148
156
  ) -> None:
149
- lambda_aliases: List[Dict] = []
150
- lambda_event_source_mappings: List[Dict] = []
151
- lambda_layers: List[Dict] = []
152
- for function_arn, aliases, event_source_mappings, layers in lambda_function_details:
153
- if len(aliases) > 0:
154
- for alias in aliases:
155
- alias["FunctionArn"] = function_arn
156
- lambda_aliases.extend(aliases)
157
- if len(event_source_mappings) > 0:
158
- lambda_event_source_mappings.extend(event_source_mappings)
159
- if len(layers) > 0:
160
- for layer in layers:
161
- layer["FunctionArn"] = function_arn
162
- lambda_layers.extend(layers)
163
-
164
- _load_lambda_function_aliases(neo4j_session, lambda_aliases, update_tag)
165
- _load_lambda_event_source_mappings(
157
+ """
158
+ Load AWS Lambda event source mappings using the data model approach.
159
+ """
160
+ load(
166
161
  neo4j_session,
162
+ AWSLambdaEventSourceMappingSchema(),
167
163
  lambda_event_source_mappings,
168
- update_tag,
164
+ AWS_ID=current_aws_account_id,
165
+ lastupdated=update_tag,
169
166
  )
170
- _load_lambda_layers(neo4j_session, lambda_layers, update_tag)
171
167
 
172
168
 
173
169
  @timeit
174
- def _load_lambda_function_aliases(
170
+ def load_lambda_layers(
175
171
  neo4j_session: neo4j.Session,
176
- lambda_aliases: List[Dict],
172
+ lambda_layers: List[Dict],
173
+ current_aws_account_id: str,
177
174
  update_tag: int,
178
175
  ) -> None:
179
- ingest_aliases = """
180
- UNWIND $aliases_list AS alias
181
- MERGE (a:AWSLambdaFunctionAlias{id: alias.AliasArn})
182
- ON CREATE SET a.firstseen = timestamp()
183
- SET a.aliasname = alias.Name,
184
- a.functionversion = alias.FunctionVersion,
185
- a.description = alias.Description,
186
- a.revisionid = alias.RevisionId,
187
- a.lastupdated = $aws_update_tag
188
- WITH a, alias
189
- MATCH (lambda:AWSLambda{id: alias.FunctionArn})
190
- MERGE (lambda)-[r:KNOWN_AS]->(a)
191
- ON CREATE SET r.firstseen = timestamp()
192
- SET r.lastupdated = $aws_update_tag
193
176
  """
177
+ Load AWS Lambda layers using the data model approach.
178
+ """
179
+ load(
180
+ neo4j_session,
181
+ AWSLambdaLayerSchema(),
182
+ lambda_layers,
183
+ AWS_ID=current_aws_account_id,
184
+ lastupdated=update_tag,
185
+ )
186
+
194
187
 
195
- neo4j_session.run(
196
- ingest_aliases,
197
- aliases_list=lambda_aliases,
198
- aws_update_tag=update_tag,
188
+ @timeit
189
+ def cleanup_lambda(neo4j_session: neo4j.Session, common_job_parameters: Dict) -> None:
190
+ """
191
+ Clean up Lambda resources
192
+ """
193
+ logger.info("Running Lambda cleanup")
194
+
195
+ # Clean up child entities first
196
+ GraphJob.from_node_schema(
197
+ AWSLambdaFunctionAliasSchema(), common_job_parameters
198
+ ).run(neo4j_session)
199
+ GraphJob.from_node_schema(
200
+ AWSLambdaEventSourceMappingSchema(), common_job_parameters
201
+ ).run(neo4j_session)
202
+ GraphJob.from_node_schema(AWSLambdaLayerSchema(), common_job_parameters).run(
203
+ neo4j_session
204
+ )
205
+
206
+ # Clean up parent Lambda nodes last
207
+ GraphJob.from_node_schema(AWSLambdaSchema(), common_job_parameters).run(
208
+ neo4j_session
199
209
  )
200
210
 
201
211
 
202
212
  @timeit
203
- def _load_lambda_event_source_mappings(
213
+ def sync_aliases(
204
214
  neo4j_session: neo4j.Session,
205
- lambda_event_source_mappings: List[Dict],
215
+ lambda_functions: List[Dict],
216
+ client: Any,
217
+ region: str,
218
+ current_aws_account_id: str,
206
219
  update_tag: int,
207
220
  ) -> None:
208
- ingest_esms = """
209
- UNWIND $esm_list AS esm
210
- MERGE (e:AWSLambdaEventSourceMapping{id: esm.UUID})
211
- ON CREATE SET e.firstseen = timestamp()
212
- SET e.batchsize = esm.BatchSize,
213
- e.startingposition = esm.StartingPosition,
214
- e.startingpositiontimestamp = esm.StartingPositionTimestamp,
215
- e.parallelizationfactor = esm.ParallelizationFactor,
216
- e.maximumbatchingwindowinseconds = esm.MaximumBatchingWindowInSeconds,
217
- e.eventsourcearn = esm.EventSourceArn,
218
- e.lastmodified = esm.LastModified,
219
- e.lastprocessingresult = esm.LastProcessingResult,
220
- e.state = esm.State,
221
- e.maximumrecordage = esm.MaximumRecordAgeInSeconds,
222
- e.bisectbatchonfunctionerror = esm.BisectBatchOnFunctionError,
223
- e.maximumretryattempts = esm.MaximumRetryAttempts,
224
- e.tumblingwindowinseconds = esm.TumblingWindowInSeconds,
225
- e.lastupdated = $aws_update_tag
226
- WITH e, esm
227
- MATCH (lambda:AWSLambda{id: esm.FunctionArn})
228
- MERGE (lambda)-[r:RESOURCE]->(e)
229
- ON CREATE SET r.firstseen = timestamp()
230
- SET r.lastupdated = $aws_update_tag
231
221
  """
222
+ Sync Lambda function aliases for all functions in the region.
223
+ """
224
+ all_aliases = []
225
+
226
+ for lambda_function in lambda_functions:
227
+ function_arn = lambda_function["FunctionArn"]
232
228
 
233
- neo4j_session.run(
234
- ingest_esms,
235
- esm_list=lambda_event_source_mappings,
236
- aws_update_tag=update_tag,
229
+ # Get, transform, and collect aliases
230
+ aliases = get_function_aliases(lambda_function, client)
231
+ if aliases:
232
+ transformed_aliases = transform_lambda_aliases(aliases, function_arn)
233
+ all_aliases.extend(transformed_aliases)
234
+
235
+ # Load all aliases
236
+ load_lambda_function_aliases(
237
+ neo4j_session, all_aliases, region, current_aws_account_id, update_tag
237
238
  )
238
239
 
239
240
 
240
241
  @timeit
241
- def _load_lambda_layers(
242
+ def sync_event_source_mappings(
242
243
  neo4j_session: neo4j.Session,
243
- lambda_layers: List[Dict],
244
+ lambda_functions: List[Dict],
245
+ client: Any,
246
+ current_aws_account_id: str,
244
247
  update_tag: int,
245
248
  ) -> None:
246
- ingest_layers = """
247
- UNWIND $layers_list AS layer
248
- MERGE (l:AWSLambdaLayer{id: layer.Arn})
249
- ON CREATE SET l.firstseen = timestamp()
250
- SET l.codesize = layer.CodeSize,
251
- l.signingprofileversionarn = layer.SigningProfileVersionArn,
252
- l.signingjobarn = layer.SigningJobArn,
253
- l.lastupdated = $aws_update_tag
254
- WITH l, layer
255
- MATCH (lambda:AWSLambda{id: layer.FunctionArn})
256
- MERGE (lambda)-[r:HAS]->(l)
257
- ON CREATE SET r.firstseen = timestamp()
258
- SET r.lastupdated = $aws_update_tag
259
249
  """
250
+ Sync Lambda event source mappings for all functions in the region.
251
+ """
252
+ all_esms = []
253
+
254
+ for lambda_function in lambda_functions:
255
+ # Get and collect event source mappings (no transformation needed)
256
+ esms = get_event_source_mappings(lambda_function, client)
257
+ if esms:
258
+ all_esms.extend(esms)
260
259
 
261
- neo4j_session.run(
262
- ingest_layers,
263
- layers_list=lambda_layers,
264
- aws_update_tag=update_tag,
260
+ # Load all event source mappings
261
+ load_lambda_event_source_mappings(
262
+ neo4j_session, all_esms, current_aws_account_id, update_tag
265
263
  )
266
264
 
267
265
 
268
266
  @timeit
269
- def cleanup_lambda(neo4j_session: neo4j.Session, common_job_parameters: Dict) -> None:
270
- run_cleanup_job(
271
- "aws_import_lambda_cleanup.json",
272
- neo4j_session,
273
- common_job_parameters,
274
- )
267
+ def sync_lambda_layers(
268
+ neo4j_session: neo4j.Session,
269
+ lambda_functions: List[Dict],
270
+ current_aws_account_id: str,
271
+ update_tag: int,
272
+ ) -> None:
273
+ """
274
+ Sync Lambda layers for all functions in the region.
275
+ """
276
+ all_layers = []
277
+
278
+ for lambda_function in lambda_functions:
279
+ function_arn = lambda_function["FunctionArn"]
280
+
281
+ # Get, transform, and collect layers (from function data)
282
+ layers = lambda_function.get("Layers", [])
283
+ if layers:
284
+ transformed_layers = transform_lambda_layers(layers, function_arn)
285
+ all_layers.extend(transformed_layers)
286
+
287
+ # Load all layers
288
+ load_lambda_layers(neo4j_session, all_layers, current_aws_account_id, update_tag)
275
289
 
276
290
 
277
291
  @timeit
278
- def sync_lambda_functions(
292
+ def sync(
279
293
  neo4j_session: neo4j.Session,
280
294
  boto3_session: boto3.session.Session,
281
295
  regions: List[str],
282
296
  current_aws_account_id: str,
283
- aws_update_tag: int,
297
+ update_tag: int,
284
298
  common_job_parameters: Dict,
285
299
  ) -> None:
286
300
  for region in regions:
@@ -289,42 +303,44 @@ def sync_lambda_functions(
289
303
  region,
290
304
  current_aws_account_id,
291
305
  )
306
+
307
+ # Get and load core lambda functions
292
308
  data = get_lambda_data(boto3_session, region)
309
+ transformed_data = transform_lambda_functions(data, region)
293
310
  load_lambda_functions(
294
311
  neo4j_session,
295
- data,
312
+ transformed_data,
296
313
  region,
297
314
  current_aws_account_id,
298
- aws_update_tag,
315
+ update_tag,
299
316
  )
300
- lambda_function_details = get_lambda_function_details(
301
- boto3_session,
317
+
318
+ # Create Lambda client for sub-entity requests
319
+ client = boto3_session.client("lambda", region_name=region)
320
+
321
+ # Sync all sub-entities
322
+ sync_aliases(
323
+ neo4j_session,
302
324
  data,
325
+ client,
303
326
  region,
327
+ current_aws_account_id,
328
+ update_tag,
304
329
  )
305
- load_lambda_function_details(
330
+
331
+ sync_event_source_mappings(
306
332
  neo4j_session,
307
- lambda_function_details,
308
- aws_update_tag,
333
+ data,
334
+ client,
335
+ current_aws_account_id,
336
+ update_tag,
309
337
  )
310
338
 
311
- cleanup_lambda(neo4j_session, common_job_parameters)
312
-
339
+ sync_lambda_layers(
340
+ neo4j_session,
341
+ data,
342
+ current_aws_account_id,
343
+ update_tag,
344
+ )
313
345
 
314
- @timeit
315
- def sync(
316
- neo4j_session: neo4j.Session,
317
- boto3_session: boto3.session.Session,
318
- regions: List[str],
319
- current_aws_account_id: str,
320
- update_tag: int,
321
- common_job_parameters: Dict,
322
- ) -> None:
323
- sync_lambda_functions(
324
- neo4j_session,
325
- boto3_session,
326
- regions,
327
- current_aws_account_id,
328
- update_tag,
329
- common_job_parameters,
330
- )
346
+ cleanup_lambda(neo4j_session, common_job_parameters)