cartography 0.108.0rc1__py3-none-any.whl → 0.109.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 (81) hide show
  1. cartography/_version.py +2 -2
  2. cartography/cli.py +14 -0
  3. cartography/config.py +4 -0
  4. cartography/data/indexes.cypher +0 -17
  5. cartography/data/jobs/cleanup/gcp_compute_vpc_cleanup.json +0 -12
  6. cartography/intel/aws/cloudtrail.py +17 -4
  7. cartography/intel/aws/cloudtrail_management_events.py +614 -16
  8. cartography/intel/aws/cloudwatch.py +73 -4
  9. cartography/intel/aws/ec2/subnets.py +37 -63
  10. cartography/intel/aws/ecr.py +55 -80
  11. cartography/intel/aws/elasticache.py +102 -79
  12. cartography/intel/aws/eventbridge.py +91 -0
  13. cartography/intel/aws/glue.py +117 -0
  14. cartography/intel/aws/identitycenter.py +71 -23
  15. cartography/intel/aws/kms.py +160 -200
  16. cartography/intel/aws/lambda_function.py +206 -190
  17. cartography/intel/aws/rds.py +243 -458
  18. cartography/intel/aws/resourcegroupstaggingapi.py +77 -18
  19. cartography/intel/aws/resources.py +4 -0
  20. cartography/intel/aws/route53.py +334 -332
  21. cartography/intel/aws/secretsmanager.py +62 -44
  22. cartography/intel/entra/groups.py +29 -1
  23. cartography/intel/gcp/__init__.py +10 -0
  24. cartography/intel/gcp/compute.py +19 -42
  25. cartography/intel/trivy/__init__.py +73 -13
  26. cartography/intel/trivy/scanner.py +115 -92
  27. cartography/models/aws/cloudtrail/management_events.py +95 -6
  28. cartography/models/aws/cloudtrail/trail.py +21 -0
  29. cartography/models/aws/cloudwatch/metric_alarm.py +53 -0
  30. cartography/models/aws/ec2/subnets.py +65 -0
  31. cartography/models/aws/ecr/__init__.py +0 -0
  32. cartography/models/aws/ecr/image.py +41 -0
  33. cartography/models/aws/ecr/repository.py +72 -0
  34. cartography/models/aws/ecr/repository_image.py +95 -0
  35. cartography/models/aws/elasticache/__init__.py +0 -0
  36. cartography/models/aws/elasticache/cluster.py +65 -0
  37. cartography/models/aws/elasticache/topic.py +67 -0
  38. cartography/models/aws/eventbridge/__init__.py +0 -0
  39. cartography/models/aws/eventbridge/rule.py +77 -0
  40. cartography/models/aws/glue/__init__.py +0 -0
  41. cartography/models/aws/glue/connection.py +51 -0
  42. cartography/models/aws/identitycenter/awspermissionset.py +44 -0
  43. cartography/models/aws/kms/__init__.py +0 -0
  44. cartography/models/aws/kms/aliases.py +86 -0
  45. cartography/models/aws/kms/grants.py +65 -0
  46. cartography/models/aws/kms/keys.py +88 -0
  47. cartography/models/aws/lambda_function/__init__.py +0 -0
  48. cartography/models/aws/lambda_function/alias.py +74 -0
  49. cartography/models/aws/lambda_function/event_source_mapping.py +88 -0
  50. cartography/models/aws/lambda_function/lambda_function.py +89 -0
  51. cartography/models/aws/lambda_function/layer.py +72 -0
  52. cartography/models/aws/rds/__init__.py +0 -0
  53. cartography/models/aws/rds/cluster.py +89 -0
  54. cartography/models/aws/rds/instance.py +154 -0
  55. cartography/models/aws/rds/snapshot.py +108 -0
  56. cartography/models/aws/rds/subnet_group.py +101 -0
  57. cartography/models/aws/route53/__init__.py +0 -0
  58. cartography/models/aws/route53/dnsrecord.py +214 -0
  59. cartography/models/aws/route53/nameserver.py +63 -0
  60. cartography/models/aws/route53/subzone.py +40 -0
  61. cartography/models/aws/route53/zone.py +47 -0
  62. cartography/models/aws/secretsmanager/secret.py +106 -0
  63. cartography/models/entra/group.py +26 -0
  64. cartography/models/entra/user.py +6 -0
  65. cartography/models/gcp/compute/__init__.py +0 -0
  66. cartography/models/gcp/compute/vpc.py +50 -0
  67. cartography/util.py +8 -1
  68. {cartography-0.108.0rc1.dist-info → cartography-0.109.0.dist-info}/METADATA +2 -2
  69. {cartography-0.108.0rc1.dist-info → cartography-0.109.0.dist-info}/RECORD +73 -44
  70. cartography/data/jobs/cleanup/aws_dns_cleanup.json +0 -65
  71. cartography/data/jobs/cleanup/aws_import_identity_center_cleanup.json +0 -16
  72. cartography/data/jobs/cleanup/aws_import_lambda_cleanup.json +0 -50
  73. cartography/data/jobs/cleanup/aws_import_rds_clusters_cleanup.json +0 -23
  74. cartography/data/jobs/cleanup/aws_import_rds_instances_cleanup.json +0 -47
  75. cartography/data/jobs/cleanup/aws_import_rds_snapshots_cleanup.json +0 -23
  76. cartography/data/jobs/cleanup/aws_import_secrets_cleanup.json +0 -8
  77. cartography/data/jobs/cleanup/aws_kms_details.json +0 -10
  78. {cartography-0.108.0rc1.dist-info → cartography-0.109.0.dist-info}/WHEEL +0 -0
  79. {cartography-0.108.0rc1.dist-info → cartography-0.109.0.dist-info}/entry_points.txt +0 -0
  80. {cartography-0.108.0rc1.dist-info → cartography-0.109.0.dist-info}/licenses/LICENSE +0 -0
  81. {cartography-0.108.0rc1.dist-info → cartography-0.109.0.dist-info}/top_level.txt +0 -0
@@ -1,5 +1,6 @@
1
1
  import json
2
2
  import logging
3
+ import os
3
4
  from typing import Any
4
5
 
5
6
  import boto3
@@ -127,6 +128,90 @@ def transform_scan_results(
127
128
  return findings_list, packages_list, fixes_list
128
129
 
129
130
 
131
+ def _parse_trivy_data(
132
+ trivy_data: dict, source: str
133
+ ) -> tuple[str | None, list[dict], str]:
134
+ """
135
+ Parse Trivy scan data and extract common fields.
136
+
137
+ Args:
138
+ trivy_data: Raw JSON Trivy data
139
+ source: Source identifier for error messages (file path or S3 URI)
140
+
141
+ Returns:
142
+ Tuple of (artifact_name, results, image_digest)
143
+ """
144
+ # Extract artifact name if present (only for file-based)
145
+ artifact_name = trivy_data.get("ArtifactName")
146
+
147
+ if "Results" not in trivy_data:
148
+ logger.error(
149
+ f"Scan data did not contain a `Results` key for {source}. This indicates a malformed scan result."
150
+ )
151
+ raise ValueError(f"Missing 'Results' key in scan data for {source}")
152
+
153
+ results = trivy_data["Results"]
154
+ if not results:
155
+ stat_handler.incr("image_scan_no_results_count")
156
+ logger.info(f"No vulnerabilities found for {source}")
157
+
158
+ if "Metadata" not in trivy_data or not trivy_data["Metadata"]:
159
+ raise ValueError(f"Missing 'Metadata' in scan data for {source}")
160
+
161
+ repo_digests = trivy_data["Metadata"].get("RepoDigests", [])
162
+ if not repo_digests:
163
+ raise ValueError(f"Missing 'RepoDigests' in scan metadata for {source}")
164
+
165
+ repo_digest = repo_digests[0]
166
+ if "@" not in repo_digest:
167
+ raise ValueError(f"Invalid repo digest format in {source}: {repo_digest}")
168
+
169
+ image_digest = repo_digest.split("@")[1]
170
+ if not image_digest:
171
+ raise ValueError(f"Empty image digest for {source}")
172
+
173
+ return artifact_name, results, image_digest
174
+
175
+
176
+ @timeit
177
+ def sync_single_image(
178
+ neo4j_session: Session,
179
+ trivy_data: dict,
180
+ source: str,
181
+ update_tag: int,
182
+ ) -> None:
183
+ """
184
+ Sync a single image's Trivy scan results to Neo4j.
185
+
186
+ Args:
187
+ neo4j_session: Neo4j session for database operations
188
+ trivy_data: Raw Trivy JSON data
189
+ source: Source identifier for logging (file path or image URI)
190
+ update_tag: Update tag for tracking
191
+ """
192
+ try:
193
+ _, results, image_digest = _parse_trivy_data(trivy_data, source)
194
+
195
+ # Transform all data in one pass
196
+ findings_list, packages_list, fixes_list = transform_scan_results(
197
+ results,
198
+ image_digest,
199
+ )
200
+
201
+ num_findings = len(findings_list)
202
+ stat_handler.incr("image_scan_cve_count", num_findings)
203
+
204
+ # Load the transformed data
205
+ load_scan_vulns(neo4j_session, findings_list, update_tag=update_tag)
206
+ load_scan_packages(neo4j_session, packages_list, update_tag=update_tag)
207
+ load_scan_fixes(neo4j_session, fixes_list, update_tag=update_tag)
208
+ stat_handler.incr("images_processed_count")
209
+
210
+ except Exception as e:
211
+ logger.error(f"Failed to process scan results for {source}: {e}")
212
+ raise
213
+
214
+
130
215
  @timeit
131
216
  def get_json_files_in_s3(
132
217
  s3_bucket: str, s3_prefix: str, boto3_session: boto3.Session
@@ -177,6 +262,18 @@ def get_json_files_in_s3(
177
262
  return results
178
263
 
179
264
 
265
+ @timeit
266
+ def get_json_files_in_dir(results_dir: str) -> set[str]:
267
+ """Return set of JSON file paths under a directory."""
268
+ results = set()
269
+ for root, _dirs, files in os.walk(results_dir):
270
+ for filename in files:
271
+ if filename.endswith(".json"):
272
+ results.add(os.path.join(root, filename))
273
+ logger.info(f"Found {len(results)} json files in {results_dir}")
274
+ return results
275
+
276
+
180
277
  @timeit
181
278
  def cleanup(neo4j_session: Session, common_job_parameters: dict[str, Any]) -> None:
182
279
  """
@@ -245,58 +342,6 @@ def load_scan_fixes(
245
342
  )
246
343
 
247
344
 
248
- @timeit
249
- def read_scan_results_from_s3(
250
- boto3_session: boto3.Session,
251
- s3_bucket: str,
252
- s3_object_key: str,
253
- image_uri: str,
254
- ) -> tuple[list[dict], str | None]:
255
- """
256
- Read and parse Trivy scan results from S3.
257
-
258
- Args:
259
- boto3_session: boto3 session for S3 operations
260
- s3_bucket: S3 bucket containing scan results
261
- s3_object_key: S3 object key for the scan results
262
- image_uri: ECR image URI (for logging purposes)
263
-
264
- Returns:
265
- Tuple of (list of scan result dictionaries from the "Results" key, image digest)
266
- """
267
- s3_client = boto3_session.client("s3")
268
-
269
- # Read JSON scan results from S3
270
- logger.debug(f"Reading scan results from S3: s3://{s3_bucket}/{s3_object_key}")
271
- response = s3_client.get_object(Bucket=s3_bucket, Key=s3_object_key)
272
- scan_data_json = response["Body"].read().decode("utf-8")
273
-
274
- # Parse JSON data
275
- trivy_data = json.loads(scan_data_json)
276
-
277
- # Extract results using the same logic as binary scanning
278
- if "Results" in trivy_data and trivy_data["Results"]:
279
- results = trivy_data["Results"]
280
- else:
281
- stat_handler.incr("image_scan_no_results_count")
282
- logger.warning(
283
- f"S3 scan data did not contain a `Results` key for URI = {image_uri}; continuing."
284
- )
285
- results = []
286
-
287
- image_digest = None
288
- if "Metadata" in trivy_data and trivy_data["Metadata"]:
289
- repo_digests = trivy_data["Metadata"].get("RepoDigests", [])
290
- if repo_digests:
291
- # Sample input: 000000000000.dkr.ecr.us-east-1.amazonaws.com/test-repository@sha256:88016
292
- # Sample output: sha256:88016
293
- repo_digest = repo_digests[0]
294
- if "@" in repo_digest:
295
- image_digest = repo_digest.split("@")[1]
296
-
297
- return results, image_digest
298
-
299
-
300
345
  @timeit
301
346
  def sync_single_image_from_s3(
302
347
  neo4j_session: Session,
@@ -317,47 +362,25 @@ def sync_single_image_from_s3(
317
362
  s3_object_key: S3 object key for this image's scan results
318
363
  boto3_session: boto3 session for S3 operations
319
364
  """
320
- try:
321
- # Read and parse scan results from S3
322
- results, image_digest = read_scan_results_from_s3(
323
- boto3_session,
324
- s3_bucket,
325
- s3_object_key,
326
- image_uri,
327
- )
328
- if not image_digest:
329
- logger.warning(f"No image digest found for {image_uri}; skipping over.")
330
- return
365
+ s3_client = boto3_session.client("s3")
331
366
 
332
- # Transform all data in one pass using existing function
333
- findings_list, packages_list, fixes_list = transform_scan_results(
334
- results,
335
- image_digest,
336
- )
367
+ logger.debug(f"Reading scan results from S3: s3://{s3_bucket}/{s3_object_key}")
368
+ response = s3_client.get_object(Bucket=s3_bucket, Key=s3_object_key)
369
+ scan_data_json = response["Body"].read().decode("utf-8")
337
370
 
338
- num_findings = len(findings_list)
339
- stat_handler.incr("image_scan_cve_count", num_findings)
371
+ trivy_data = json.loads(scan_data_json)
372
+ sync_single_image(neo4j_session, trivy_data, image_uri, update_tag)
340
373
 
341
- # Load the transformed data using existing functions
342
- load_scan_vulns(
343
- neo4j_session,
344
- findings_list,
345
- update_tag=update_tag,
346
- )
347
- load_scan_packages(
348
- neo4j_session,
349
- packages_list,
350
- update_tag=update_tag,
351
- )
352
- load_scan_fixes(
353
- neo4j_session,
354
- fixes_list,
355
- update_tag=update_tag,
356
- )
357
- stat_handler.incr("images_processed_count")
358
374
 
359
- except Exception as e:
360
- logger.error(
361
- f"Failed to process S3 scan results for {image_uri} from {s3_object_key}: {e}"
362
- )
363
- raise
375
+ @timeit
376
+ def sync_single_image_from_file(
377
+ neo4j_session: Session,
378
+ file_path: str,
379
+ update_tag: int,
380
+ ) -> None:
381
+ """Read a Trivy JSON file from disk and sync to Neo4j."""
382
+ logger.debug(f"Reading scan results from file: {file_path}")
383
+ with open(file_path, encoding="utf-8") as f:
384
+ trivy_data = json.load(f)
385
+
386
+ sync_single_image(neo4j_session, trivy_data, file_path, update_tag)
@@ -29,12 +29,6 @@ class AssumedRoleRelProperties(CartographyRelProperties):
29
29
  times_used: PropertyRef = PropertyRef("times_used")
30
30
  first_seen_in_time_window: PropertyRef = PropertyRef("first_seen_in_time_window")
31
31
 
32
- # Event type tracking properties
33
- event_types: PropertyRef = PropertyRef("event_types")
34
- assume_role_count: PropertyRef = PropertyRef("assume_role_count")
35
- saml_count: PropertyRef = PropertyRef("saml_count")
36
- web_identity_count: PropertyRef = PropertyRef("web_identity_count")
37
-
38
32
 
39
33
  @dataclass(frozen=True)
40
34
  class AssumedRoleMatchLink(CartographyRelSchema):
@@ -62,3 +56,98 @@ class AssumedRoleMatchLink(CartographyRelSchema):
62
56
  direction: LinkDirection = LinkDirection.OUTWARD
63
57
  rel_label: str = "ASSUMED_ROLE"
64
58
  properties: AssumedRoleRelProperties = AssumedRoleRelProperties()
59
+
60
+
61
+ @dataclass(frozen=True)
62
+ class AssumedRoleWithSAMLRelProperties(CartographyRelProperties):
63
+ """
64
+ Properties for the ASSUMED_ROLE_WITH_SAML relationship representing SAML-based role assumption events.
65
+ Focuses specifically on SAML federated identity role assumptions.
66
+ """
67
+
68
+ # Mandatory fields for MatchLinks
69
+ lastupdated: PropertyRef = PropertyRef("lastupdated", set_in_kwargs=True)
70
+ _sub_resource_label: PropertyRef = PropertyRef(
71
+ "_sub_resource_label", set_in_kwargs=True
72
+ )
73
+ _sub_resource_id: PropertyRef = PropertyRef("_sub_resource_id", set_in_kwargs=True)
74
+
75
+ # CloudTrail-specific relationship properties
76
+ last_used: PropertyRef = PropertyRef("last_used")
77
+ times_used: PropertyRef = PropertyRef("times_used")
78
+ first_seen_in_time_window: PropertyRef = PropertyRef("first_seen_in_time_window")
79
+
80
+
81
+ @dataclass(frozen=True)
82
+ class AssumedRoleWithSAMLMatchLink(CartographyRelSchema):
83
+ """
84
+ MatchLink schema for ASSUMED_ROLE_WITH_SAML relationships from CloudTrail SAML events.
85
+ Creates relationships like: (AWSRole)-[:ASSUMED_ROLE_WITH_SAML]->(AWSRole)
86
+
87
+ This MatchLink handles SAML-based role assumption relationships discovered via CloudTrail
88
+ AssumeRoleWithSAML events. It creates separate relationships from regular AssumeRole events
89
+ to preserve visibility into authentication methods used.
90
+ """
91
+
92
+ # MatchLink-specific fields
93
+ source_node_label: str = "AWSSSOUser" # Match against AWS SSO User nodes
94
+ source_node_matcher: SourceNodeMatcher = make_source_node_matcher(
95
+ {"user_name": PropertyRef("source_principal_arn")},
96
+ )
97
+
98
+ # Standard CartographyRelSchema fields
99
+ target_node_label: str = "AWSRole"
100
+ target_node_matcher: TargetNodeMatcher = make_target_node_matcher(
101
+ {"arn": PropertyRef("destination_principal_arn")},
102
+ )
103
+ direction: LinkDirection = LinkDirection.OUTWARD
104
+ rel_label: str = "ASSUMED_ROLE_WITH_SAML"
105
+ properties: AssumedRoleWithSAMLRelProperties = AssumedRoleWithSAMLRelProperties()
106
+
107
+
108
+ @dataclass(frozen=True)
109
+ class AssumeRoleWithWebIdentityRelProperties(CartographyRelProperties):
110
+ """
111
+ Properties for the ASSUMED_ROLE_WITH_WEB_IDENTITY relationship representing web identity-based role assumption events.
112
+ Focuses specifically on web identity federation role assumptions (Google, Amazon, Facebook, etc.).
113
+ """
114
+
115
+ # Mandatory fields for MatchLinks
116
+ lastupdated: PropertyRef = PropertyRef("lastupdated", set_in_kwargs=True)
117
+ _sub_resource_label: PropertyRef = PropertyRef(
118
+ "_sub_resource_label", set_in_kwargs=True
119
+ )
120
+ _sub_resource_id: PropertyRef = PropertyRef("_sub_resource_id", set_in_kwargs=True)
121
+
122
+ # CloudTrail-specific relationship properties
123
+ last_used: PropertyRef = PropertyRef("last_used")
124
+ times_used: PropertyRef = PropertyRef("times_used")
125
+ first_seen_in_time_window: PropertyRef = PropertyRef("first_seen_in_time_window")
126
+
127
+
128
+ @dataclass(frozen=True)
129
+ class GitHubRepoAssumeRoleWithWebIdentityMatchLink(CartographyRelSchema):
130
+ """
131
+ MatchLink schema for ASSUMED_ROLE_WITH_WEB_IDENTITY relationships from GitHub Actions to AWS roles.
132
+ Creates relationships like: (GitHubRepository)-[:ASSUMED_ROLE_WITH_WEB_IDENTITY]->(AWSRole)
133
+
134
+ This MatchLink provides granular visibility into which specific GitHub repositories are assuming
135
+ AWS roles via GitHub Actions OIDC, rather than just showing provider-level relationships.
136
+ """
137
+
138
+ # MatchLink-specific fields for GitHub repositories
139
+ source_node_label: str = "GitHubRepository"
140
+ source_node_matcher: SourceNodeMatcher = make_source_node_matcher(
141
+ {"fullname": PropertyRef("source_repo_fullname")},
142
+ )
143
+
144
+ # Standard CartographyRelSchema fields
145
+ target_node_label: str = "AWSRole"
146
+ target_node_matcher: TargetNodeMatcher = make_target_node_matcher(
147
+ {"arn": PropertyRef("destination_principal_arn")},
148
+ )
149
+ direction: LinkDirection = LinkDirection.OUTWARD
150
+ rel_label: str = "ASSUMED_ROLE_WITH_WEB_IDENTITY"
151
+ properties: AssumeRoleWithWebIdentityRelProperties = (
152
+ AssumeRoleWithWebIdentityRelProperties()
153
+ )
@@ -73,6 +73,26 @@ class CloudTrailTrailToS3BucketRel(CartographyRelSchema):
73
73
  )
74
74
 
75
75
 
76
+ @dataclass(frozen=True)
77
+ class CloudTrailTrailToCloudWatchLogGroupRelProperties(CartographyRelProperties):
78
+ lastupdated: PropertyRef = PropertyRef("lastupdated", set_in_kwargs=True)
79
+
80
+
81
+ @dataclass(frozen=True)
82
+ class CloudTrailTrailToCloudWatchLogGroupRel(CartographyRelSchema):
83
+ target_node_label: str = "CloudWatchLogGroup"
84
+ target_node_matcher: TargetNodeMatcher = make_target_node_matcher(
85
+ {
86
+ "id": PropertyRef("CloudWatchLogsLogGroupArn"),
87
+ }
88
+ )
89
+ direction: LinkDirection = LinkDirection.OUTWARD
90
+ rel_label: str = "SENDS_LOGS_TO_CLOUDWATCH"
91
+ properties: CloudTrailTrailToCloudWatchLogGroupRelProperties = (
92
+ CloudTrailTrailToCloudWatchLogGroupRelProperties()
93
+ )
94
+
95
+
76
96
  @dataclass(frozen=True)
77
97
  class CloudTrailTrailSchema(CartographyNodeSchema):
78
98
  label: str = "CloudTrailTrail"
@@ -81,5 +101,6 @@ class CloudTrailTrailSchema(CartographyNodeSchema):
81
101
  other_relationships: OtherRelationships = OtherRelationships(
82
102
  [
83
103
  CloudTrailTrailToS3BucketRel(),
104
+ CloudTrailTrailToCloudWatchLogGroupRel(),
84
105
  ]
85
106
  )
@@ -0,0 +1,53 @@
1
+ from dataclasses import dataclass
2
+
3
+ from cartography.models.core.common import PropertyRef
4
+ from cartography.models.core.nodes import CartographyNodeProperties
5
+ from cartography.models.core.nodes import CartographyNodeSchema
6
+ from cartography.models.core.relationships import CartographyRelProperties
7
+ from cartography.models.core.relationships import CartographyRelSchema
8
+ from cartography.models.core.relationships import LinkDirection
9
+ from cartography.models.core.relationships import make_target_node_matcher
10
+ from cartography.models.core.relationships import TargetNodeMatcher
11
+
12
+
13
+ @dataclass(frozen=True)
14
+ class CloudWatchMetricAlarmNodeProperties(CartographyNodeProperties):
15
+ id: PropertyRef = PropertyRef("AlarmArn")
16
+ arn: PropertyRef = PropertyRef("AlarmArn", extra_index=True)
17
+ alarm_name: PropertyRef = PropertyRef("AlarmName")
18
+ alarm_description: PropertyRef = PropertyRef("AlarmDescription")
19
+ state_value: PropertyRef = PropertyRef("StateValue")
20
+ state_reason: PropertyRef = PropertyRef("StateReason")
21
+ actions_enabled: PropertyRef = PropertyRef("ActionsEnabled")
22
+ comparison_operator: PropertyRef = PropertyRef("ComparisonOperator")
23
+ region: PropertyRef = PropertyRef("Region", set_in_kwargs=True)
24
+ lastupdated: PropertyRef = PropertyRef("lastupdated", set_in_kwargs=True)
25
+
26
+
27
+ @dataclass(frozen=True)
28
+ class CloudWatchMetricAlarmToAwsAccountRelProperties(CartographyRelProperties):
29
+ lastupdated: PropertyRef = PropertyRef("lastupdated", set_in_kwargs=True)
30
+
31
+
32
+ @dataclass(frozen=True)
33
+ class CloudWatchMetricAlarmToAWSAccountRel(CartographyRelSchema):
34
+ target_node_label: str = "AWSAccount"
35
+ target_node_matcher: TargetNodeMatcher = make_target_node_matcher(
36
+ {"id": PropertyRef("AWS_ID", set_in_kwargs=True)},
37
+ )
38
+ direction: LinkDirection = LinkDirection.INWARD
39
+ rel_label: str = "RESOURCE"
40
+ properties: CloudWatchMetricAlarmToAwsAccountRelProperties = (
41
+ CloudWatchMetricAlarmToAwsAccountRelProperties()
42
+ )
43
+
44
+
45
+ @dataclass(frozen=True)
46
+ class CloudWatchMetricAlarmSchema(CartographyNodeSchema):
47
+ label: str = "CloudWatchMetricAlarm"
48
+ properties: CloudWatchMetricAlarmNodeProperties = (
49
+ CloudWatchMetricAlarmNodeProperties()
50
+ )
51
+ sub_resource_relationship: CloudWatchMetricAlarmToAWSAccountRel = (
52
+ CloudWatchMetricAlarmToAWSAccountRel()
53
+ )
@@ -0,0 +1,65 @@
1
+ from dataclasses import dataclass
2
+
3
+ from cartography.models.aws.ec2.auto_scaling_groups import EC2SubnetToAWSAccountRel
4
+ from cartography.models.core.common import PropertyRef
5
+ from cartography.models.core.nodes import CartographyNodeProperties
6
+ from cartography.models.core.nodes import CartographyNodeSchema
7
+ from cartography.models.core.relationships import CartographyRelProperties
8
+ from cartography.models.core.relationships import CartographyRelSchema
9
+ from cartography.models.core.relationships import LinkDirection
10
+ from cartography.models.core.relationships import make_target_node_matcher
11
+ from cartography.models.core.relationships import OtherRelationships
12
+ from cartography.models.core.relationships import TargetNodeMatcher
13
+
14
+
15
+ @dataclass(frozen=True)
16
+ class EC2SubnetNodeProperties(CartographyNodeProperties):
17
+ id: PropertyRef = PropertyRef("SubnetId")
18
+ subnetid: PropertyRef = PropertyRef("SubnetId", extra_index=True)
19
+ subnet_id: PropertyRef = PropertyRef("SubnetId", extra_index=True)
20
+ subnet_arn: PropertyRef = PropertyRef("SubnetArn")
21
+ name: PropertyRef = PropertyRef("CidrBlock")
22
+ cidr_block: PropertyRef = PropertyRef("CidrBlock")
23
+ available_ip_address_count: PropertyRef = PropertyRef("AvailableIpAddressCount")
24
+ default_for_az: PropertyRef = PropertyRef("DefaultForAz")
25
+ map_customer_owned_ip_on_launch: PropertyRef = PropertyRef(
26
+ "MapCustomerOwnedIpOnLaunch"
27
+ )
28
+ state: PropertyRef = PropertyRef("State")
29
+ assignipv6addressoncreation: PropertyRef = PropertyRef(
30
+ "AssignIpv6AddressOnCreation"
31
+ )
32
+ map_public_ip_on_launch: PropertyRef = PropertyRef("MapPublicIpOnLaunch")
33
+ availability_zone: PropertyRef = PropertyRef("AvailabilityZone")
34
+ availability_zone_id: PropertyRef = PropertyRef("AvailabilityZoneId")
35
+ vpc_id: PropertyRef = PropertyRef("VpcId")
36
+ region: PropertyRef = PropertyRef("Region", set_in_kwargs=True)
37
+ lastupdated: PropertyRef = PropertyRef("lastupdated", set_in_kwargs=True)
38
+
39
+
40
+ @dataclass(frozen=True)
41
+ class EC2SubnetToVpcRelProperties(CartographyRelProperties):
42
+ lastupdated: PropertyRef = PropertyRef("lastupdated", set_in_kwargs=True)
43
+
44
+
45
+ @dataclass(frozen=True)
46
+ class EC2SubnetToVpcRel(CartographyRelSchema):
47
+ target_node_label: str = "AWSVpc"
48
+ target_node_matcher: TargetNodeMatcher = make_target_node_matcher(
49
+ {"id": PropertyRef("VpcId")}
50
+ )
51
+ direction: LinkDirection = LinkDirection.OUTWARD
52
+ rel_label: str = "MEMBER_OF_AWS_VPC"
53
+ properties: EC2SubnetToVpcRelProperties = EC2SubnetToVpcRelProperties()
54
+
55
+
56
+ @dataclass(frozen=True)
57
+ class EC2SubnetSchema(CartographyNodeSchema):
58
+ label: str = "EC2Subnet"
59
+ properties: EC2SubnetNodeProperties = EC2SubnetNodeProperties()
60
+ sub_resource_relationship: EC2SubnetToAWSAccountRel = EC2SubnetToAWSAccountRel()
61
+ other_relationships: OtherRelationships = OtherRelationships(
62
+ [
63
+ EC2SubnetToVpcRel(),
64
+ ]
65
+ )
File without changes
@@ -0,0 +1,41 @@
1
+ from dataclasses import dataclass
2
+
3
+ from cartography.models.core.common import PropertyRef
4
+ from cartography.models.core.nodes import CartographyNodeProperties
5
+ from cartography.models.core.nodes import CartographyNodeSchema
6
+ from cartography.models.core.relationships import CartographyRelProperties
7
+ from cartography.models.core.relationships import CartographyRelSchema
8
+ from cartography.models.core.relationships import LinkDirection
9
+ from cartography.models.core.relationships import make_target_node_matcher
10
+ from cartography.models.core.relationships import TargetNodeMatcher
11
+
12
+
13
+ @dataclass(frozen=True)
14
+ class ECRImageNodeProperties(CartographyNodeProperties):
15
+ id: PropertyRef = PropertyRef("imageDigest")
16
+ digest: PropertyRef = PropertyRef("imageDigest")
17
+ region: PropertyRef = PropertyRef("Region", set_in_kwargs=True)
18
+ lastupdated: PropertyRef = PropertyRef("lastupdated", set_in_kwargs=True)
19
+
20
+
21
+ @dataclass(frozen=True)
22
+ class ECRImageToAWSAccountRelProperties(CartographyRelProperties):
23
+ lastupdated: PropertyRef = PropertyRef("lastupdated", set_in_kwargs=True)
24
+
25
+
26
+ @dataclass(frozen=True)
27
+ class ECRImageToAWSAccountRel(CartographyRelSchema):
28
+ target_node_label: str = "AWSAccount"
29
+ target_node_matcher: TargetNodeMatcher = make_target_node_matcher(
30
+ {"id": PropertyRef("AWS_ID", set_in_kwargs=True)}
31
+ )
32
+ direction: LinkDirection = LinkDirection.INWARD
33
+ rel_label: str = "RESOURCE"
34
+ properties: ECRImageToAWSAccountRelProperties = ECRImageToAWSAccountRelProperties()
35
+
36
+
37
+ @dataclass(frozen=True)
38
+ class ECRImageSchema(CartographyNodeSchema):
39
+ label: str = "ECRImage"
40
+ properties: ECRImageNodeProperties = ECRImageNodeProperties()
41
+ sub_resource_relationship: ECRImageToAWSAccountRel = ECRImageToAWSAccountRel()
@@ -0,0 +1,72 @@
1
+ from dataclasses import dataclass
2
+
3
+ from cartography.models.core.common import PropertyRef
4
+ from cartography.models.core.nodes import CartographyNodeProperties
5
+ from cartography.models.core.nodes import CartographyNodeSchema
6
+ from cartography.models.core.relationships import CartographyRelProperties
7
+ from cartography.models.core.relationships import CartographyRelSchema
8
+ from cartography.models.core.relationships import LinkDirection
9
+ from cartography.models.core.relationships import make_target_node_matcher
10
+ from cartography.models.core.relationships import OtherRelationships
11
+ from cartography.models.core.relationships import TargetNodeMatcher
12
+
13
+
14
+ @dataclass(frozen=True)
15
+ class ECRRepositoryNodeProperties(CartographyNodeProperties):
16
+ id: PropertyRef = PropertyRef("repositoryArn")
17
+ arn: PropertyRef = PropertyRef("repositoryArn", extra_index=True)
18
+ name: PropertyRef = PropertyRef("repositoryName", extra_index=True)
19
+ uri: PropertyRef = PropertyRef("repositoryUri", extra_index=True)
20
+ created_at: PropertyRef = PropertyRef("createdAt")
21
+ region: PropertyRef = PropertyRef("Region", set_in_kwargs=True)
22
+ lastupdated: PropertyRef = PropertyRef("lastupdated", set_in_kwargs=True)
23
+
24
+
25
+ @dataclass(frozen=True)
26
+ class ECRRepositoryToAWSAccountRelProperties(CartographyRelProperties):
27
+ lastupdated: PropertyRef = PropertyRef("lastupdated", set_in_kwargs=True)
28
+
29
+
30
+ @dataclass(frozen=True)
31
+ class ECRRepositoryToAWSAccountRel(CartographyRelSchema):
32
+ target_node_label: str = "AWSAccount"
33
+ target_node_matcher: TargetNodeMatcher = make_target_node_matcher(
34
+ {"id": PropertyRef("AWS_ID", set_in_kwargs=True)}
35
+ )
36
+ direction: LinkDirection = LinkDirection.INWARD
37
+ rel_label: str = "RESOURCE"
38
+ properties: ECRRepositoryToAWSAccountRelProperties = (
39
+ ECRRepositoryToAWSAccountRelProperties()
40
+ )
41
+
42
+
43
+ @dataclass(frozen=True)
44
+ class ECRRepositoryToRepositoryImageRelProperties(CartographyRelProperties):
45
+ lastupdated: PropertyRef = PropertyRef("lastupdated", set_in_kwargs=True)
46
+
47
+
48
+ @dataclass(frozen=True)
49
+ class ECRRepositoryToRepositoryImageRel(CartographyRelSchema):
50
+ target_node_label: str = "ECRRepositoryImage"
51
+ target_node_matcher: TargetNodeMatcher = make_target_node_matcher(
52
+ {"id": PropertyRef("id")}
53
+ )
54
+ direction: LinkDirection = LinkDirection.OUTWARD
55
+ rel_label: str = "REPO_IMAGE"
56
+ properties: ECRRepositoryToRepositoryImageRelProperties = (
57
+ ECRRepositoryToRepositoryImageRelProperties()
58
+ )
59
+
60
+
61
+ @dataclass(frozen=True)
62
+ class ECRRepositorySchema(CartographyNodeSchema):
63
+ label: str = "ECRRepository"
64
+ properties: ECRRepositoryNodeProperties = ECRRepositoryNodeProperties()
65
+ sub_resource_relationship: ECRRepositoryToAWSAccountRel = (
66
+ ECRRepositoryToAWSAccountRel()
67
+ )
68
+ other_relationships: OtherRelationships = OtherRelationships(
69
+ [
70
+ ECRRepositoryToRepositoryImageRel(),
71
+ ]
72
+ )