cartography 0.109.0rc1__py3-none-any.whl → 0.110.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.
- cartography/_version.py +2 -2
- cartography/cli.py +14 -0
- cartography/config.py +4 -0
- cartography/data/indexes.cypher +0 -15
- cartography/data/jobs/analysis/aws_ec2_keypair_analysis.json +2 -2
- cartography/intel/aws/cloudtrail_management_events.py +21 -0
- cartography/intel/aws/cognito.py +201 -0
- cartography/intel/aws/ecs.py +7 -1
- cartography/intel/aws/eventbridge.py +91 -0
- cartography/intel/aws/glue.py +181 -0
- cartography/intel/aws/identitycenter.py +71 -23
- cartography/intel/aws/kms.py +173 -201
- cartography/intel/aws/lambda_function.py +206 -190
- cartography/intel/aws/rds.py +335 -445
- cartography/intel/aws/resources.py +6 -0
- cartography/intel/aws/route53.py +336 -332
- cartography/intel/aws/s3.py +104 -0
- cartography/intel/github/__init__.py +21 -25
- cartography/intel/github/repos.py +4 -36
- cartography/intel/kubernetes/__init__.py +4 -0
- cartography/intel/kubernetes/rbac.py +464 -0
- cartography/intel/kubernetes/util.py +17 -0
- cartography/intel/trivy/__init__.py +73 -13
- cartography/intel/trivy/scanner.py +115 -92
- cartography/models/aws/cognito/__init__.py +0 -0
- cartography/models/aws/cognito/identity_pool.py +70 -0
- cartography/models/aws/cognito/user_pool.py +47 -0
- cartography/models/aws/ec2/security_groups.py +1 -1
- cartography/models/aws/ecs/services.py +17 -0
- cartography/models/aws/ecs/tasks.py +1 -0
- cartography/models/aws/eventbridge/__init__.py +0 -0
- cartography/models/aws/eventbridge/rule.py +77 -0
- cartography/models/aws/glue/__init__.py +0 -0
- cartography/models/aws/glue/connection.py +51 -0
- cartography/models/aws/glue/job.py +69 -0
- cartography/models/aws/identitycenter/awspermissionset.py +44 -0
- cartography/models/aws/kms/__init__.py +0 -0
- cartography/models/aws/kms/aliases.py +86 -0
- cartography/models/aws/kms/grants.py +65 -0
- cartography/models/aws/kms/keys.py +88 -0
- cartography/models/aws/lambda_function/__init__.py +0 -0
- cartography/models/aws/lambda_function/alias.py +74 -0
- cartography/models/aws/lambda_function/event_source_mapping.py +88 -0
- cartography/models/aws/lambda_function/lambda_function.py +89 -0
- cartography/models/aws/lambda_function/layer.py +72 -0
- cartography/models/aws/rds/__init__.py +0 -0
- cartography/models/aws/rds/cluster.py +89 -0
- cartography/models/aws/rds/event_subscription.py +146 -0
- cartography/models/aws/rds/instance.py +154 -0
- cartography/models/aws/rds/snapshot.py +108 -0
- cartography/models/aws/rds/subnet_group.py +101 -0
- cartography/models/aws/route53/__init__.py +0 -0
- cartography/models/aws/route53/dnsrecord.py +235 -0
- cartography/models/aws/route53/nameserver.py +63 -0
- cartography/models/aws/route53/subzone.py +40 -0
- cartography/models/aws/route53/zone.py +47 -0
- cartography/models/github/dependencies.py +1 -2
- cartography/models/kubernetes/clusterrolebindings.py +98 -0
- cartography/models/kubernetes/clusterroles.py +52 -0
- cartography/models/kubernetes/rolebindings.py +119 -0
- cartography/models/kubernetes/roles.py +76 -0
- cartography/models/kubernetes/serviceaccounts.py +77 -0
- cartography/models/snipeit/asset.py +1 -0
- cartography/util.py +8 -1
- {cartography-0.109.0rc1.dist-info → cartography-0.110.0.dist-info}/METADATA +3 -3
- {cartography-0.109.0rc1.dist-info → cartography-0.110.0.dist-info}/RECORD +71 -41
- cartography/data/jobs/cleanup/aws_dns_cleanup.json +0 -65
- cartography/data/jobs/cleanup/aws_import_identity_center_cleanup.json +0 -16
- cartography/data/jobs/cleanup/aws_import_lambda_cleanup.json +0 -50
- cartography/data/jobs/cleanup/aws_import_rds_clusters_cleanup.json +0 -23
- cartography/data/jobs/cleanup/aws_import_rds_instances_cleanup.json +0 -47
- cartography/data/jobs/cleanup/aws_import_rds_snapshots_cleanup.json +0 -23
- cartography/data/jobs/cleanup/aws_kms_details.json +0 -10
- /cartography/data/jobs/{analysis → scoped_analysis}/aws_s3acl_analysis.json +0 -0
- {cartography-0.109.0rc1.dist-info → cartography-0.110.0.dist-info}/WHEEL +0 -0
- {cartography-0.109.0rc1.dist-info → cartography-0.110.0.dist-info}/entry_points.txt +0 -0
- {cartography-0.109.0rc1.dist-info → cartography-0.110.0.dist-info}/licenses/LICENSE +0 -0
- {cartography-0.109.0rc1.dist-info → cartography-0.110.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
|
-
|
|
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
|
-
|
|
333
|
-
|
|
334
|
-
|
|
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
|
-
|
|
339
|
-
|
|
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
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
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)
|
|
File without changes
|
|
@@ -0,0 +1,70 @@
|
|
|
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 CognitoIdentityPoolNodeProperties(CartographyNodeProperties):
|
|
16
|
+
id: PropertyRef = PropertyRef("IdentityPoolId")
|
|
17
|
+
arn: PropertyRef = PropertyRef("IdentityPoolId", extra_index=True)
|
|
18
|
+
region: PropertyRef = PropertyRef("Region", set_in_kwargs=True)
|
|
19
|
+
roles: PropertyRef = PropertyRef("Roles")
|
|
20
|
+
lastupdated: PropertyRef = PropertyRef("lastupdated", set_in_kwargs=True)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@dataclass(frozen=True)
|
|
24
|
+
class CognitoIdentityPoolToAwsAccountRelProperties(CartographyRelProperties):
|
|
25
|
+
lastupdated: PropertyRef = PropertyRef("lastupdated", set_in_kwargs=True)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@dataclass(frozen=True)
|
|
29
|
+
class CognitoIdentityPoolToAWSAccountRel(CartographyRelSchema):
|
|
30
|
+
target_node_label: str = "AWSAccount"
|
|
31
|
+
target_node_matcher: TargetNodeMatcher = make_target_node_matcher(
|
|
32
|
+
{"id": PropertyRef("AWS_ID", set_in_kwargs=True)},
|
|
33
|
+
)
|
|
34
|
+
direction: LinkDirection = LinkDirection.INWARD
|
|
35
|
+
rel_label: str = "RESOURCE"
|
|
36
|
+
properties: CognitoIdentityPoolToAwsAccountRelProperties = (
|
|
37
|
+
CognitoIdentityPoolToAwsAccountRelProperties()
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
@dataclass(frozen=True)
|
|
42
|
+
class CognitoIdentityPoolToAWSRoleRelProperties(CartographyRelProperties):
|
|
43
|
+
lastupdated: PropertyRef = PropertyRef("lastupdated", set_in_kwargs=True)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
@dataclass(frozen=True)
|
|
47
|
+
class CognitoIdentityPoolToAWSRoleRel(CartographyRelSchema):
|
|
48
|
+
target_node_label: str = "AWSRole"
|
|
49
|
+
target_node_matcher: TargetNodeMatcher = make_target_node_matcher(
|
|
50
|
+
{"arn": PropertyRef("Roles", one_to_many=True)},
|
|
51
|
+
)
|
|
52
|
+
direction: LinkDirection = LinkDirection.OUTWARD
|
|
53
|
+
rel_label: str = "ASSOCIATED_WITH"
|
|
54
|
+
properties: CognitoIdentityPoolToAWSRoleRelProperties = (
|
|
55
|
+
CognitoIdentityPoolToAWSRoleRelProperties()
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
@dataclass(frozen=True)
|
|
60
|
+
class CognitoIdentityPoolSchema(CartographyNodeSchema):
|
|
61
|
+
label: str = "CognitoIdentityPool"
|
|
62
|
+
properties: CognitoIdentityPoolNodeProperties = CognitoIdentityPoolNodeProperties()
|
|
63
|
+
sub_resource_relationship: CognitoIdentityPoolToAWSAccountRel = (
|
|
64
|
+
CognitoIdentityPoolToAWSAccountRel()
|
|
65
|
+
)
|
|
66
|
+
other_relationships: OtherRelationships = OtherRelationships(
|
|
67
|
+
[
|
|
68
|
+
CognitoIdentityPoolToAWSRoleRel(),
|
|
69
|
+
]
|
|
70
|
+
)
|
|
@@ -0,0 +1,47 @@
|
|
|
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 CognitoUserPoolNodeProperties(CartographyNodeProperties):
|
|
15
|
+
id: PropertyRef = PropertyRef("Id")
|
|
16
|
+
arn: PropertyRef = PropertyRef("Id", extra_index=True)
|
|
17
|
+
region: PropertyRef = PropertyRef("Region", set_in_kwargs=True)
|
|
18
|
+
name: PropertyRef = PropertyRef("Name")
|
|
19
|
+
status: PropertyRef = PropertyRef("Status")
|
|
20
|
+
lastupdated: PropertyRef = PropertyRef("lastupdated", set_in_kwargs=True)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@dataclass(frozen=True)
|
|
24
|
+
class CognitoUserPoolToAwsAccountRelProperties(CartographyRelProperties):
|
|
25
|
+
lastupdated: PropertyRef = PropertyRef("lastupdated", set_in_kwargs=True)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@dataclass(frozen=True)
|
|
29
|
+
class CognitoUserPoolToAWSAccountRel(CartographyRelSchema):
|
|
30
|
+
target_node_label: str = "AWSAccount"
|
|
31
|
+
target_node_matcher: TargetNodeMatcher = make_target_node_matcher(
|
|
32
|
+
{"id": PropertyRef("AWS_ID", set_in_kwargs=True)},
|
|
33
|
+
)
|
|
34
|
+
direction: LinkDirection = LinkDirection.INWARD
|
|
35
|
+
rel_label: str = "RESOURCE"
|
|
36
|
+
properties: CognitoUserPoolToAwsAccountRelProperties = (
|
|
37
|
+
CognitoUserPoolToAwsAccountRelProperties()
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
@dataclass(frozen=True)
|
|
42
|
+
class CognitoUserPoolSchema(CartographyNodeSchema):
|
|
43
|
+
label: str = "CognitoUserPool"
|
|
44
|
+
properties: CognitoUserPoolNodeProperties = CognitoUserPoolNodeProperties()
|
|
45
|
+
sub_resource_relationship: CognitoUserPoolToAWSAccountRel = (
|
|
46
|
+
CognitoUserPoolToAWSAccountRel()
|
|
47
|
+
)
|
|
@@ -50,7 +50,7 @@ class EC2SecurityGroupToVpcRel(CartographyRelSchema):
|
|
|
50
50
|
target_node_matcher: TargetNodeMatcher = make_target_node_matcher(
|
|
51
51
|
{"vpcid": PropertyRef("VpcId")}
|
|
52
52
|
)
|
|
53
|
-
direction: LinkDirection = LinkDirection.
|
|
53
|
+
direction: LinkDirection = LinkDirection.INWARD
|
|
54
54
|
rel_label: str = "MEMBER_OF_EC2_SECURITY_GROUP"
|
|
55
55
|
properties: EC2SecurityGroupToVpcRelProperties = (
|
|
56
56
|
EC2SecurityGroupToVpcRelProperties()
|
|
@@ -104,6 +104,22 @@ class ECSServiceToAWSAccountRel(CartographyRelSchema):
|
|
|
104
104
|
)
|
|
105
105
|
|
|
106
106
|
|
|
107
|
+
@dataclass(frozen=True)
|
|
108
|
+
class ECSServiceToECSTaskRelProperties(CartographyRelProperties):
|
|
109
|
+
lastupdated: PropertyRef = PropertyRef("lastupdated", set_in_kwargs=True)
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
@dataclass(frozen=True)
|
|
113
|
+
class ECSServiceToECSTaskRel(CartographyRelSchema):
|
|
114
|
+
target_node_label: str = "ECSTask"
|
|
115
|
+
target_node_matcher: TargetNodeMatcher = make_target_node_matcher(
|
|
116
|
+
{"service_name": PropertyRef("serviceName")}
|
|
117
|
+
)
|
|
118
|
+
direction: LinkDirection = LinkDirection.OUTWARD
|
|
119
|
+
rel_label: str = "HAS_TASK"
|
|
120
|
+
properties: ECSServiceToECSTaskRelProperties = ECSServiceToECSTaskRelProperties()
|
|
121
|
+
|
|
122
|
+
|
|
107
123
|
@dataclass(frozen=True)
|
|
108
124
|
class ECSServiceSchema(CartographyNodeSchema):
|
|
109
125
|
label: str = "ECSService"
|
|
@@ -113,5 +129,6 @@ class ECSServiceSchema(CartographyNodeSchema):
|
|
|
113
129
|
[
|
|
114
130
|
ECSServiceToECSClusterRel(),
|
|
115
131
|
ECSServiceToTaskDefinitionRel(),
|
|
132
|
+
ECSServiceToECSTaskRel(),
|
|
116
133
|
]
|
|
117
134
|
)
|
|
@@ -27,6 +27,7 @@ class ECSTaskNodeProperties(CartographyNodeProperties):
|
|
|
27
27
|
enable_execute_command: PropertyRef = PropertyRef("enableExecuteCommand")
|
|
28
28
|
execution_stopped_at: PropertyRef = PropertyRef("executionStoppedAt")
|
|
29
29
|
group: PropertyRef = PropertyRef("group")
|
|
30
|
+
service_name: PropertyRef = PropertyRef("serviceName")
|
|
30
31
|
health_status: PropertyRef = PropertyRef("healthStatus")
|
|
31
32
|
last_status: PropertyRef = PropertyRef("lastStatus")
|
|
32
33
|
launch_type: PropertyRef = PropertyRef("launchType")
|
|
File without changes
|
|
@@ -0,0 +1,77 @@
|
|
|
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 EventBridgeRuleNodeProperties(CartographyNodeProperties):
|
|
16
|
+
id: PropertyRef = PropertyRef("Arn")
|
|
17
|
+
arn: PropertyRef = PropertyRef("Arn", extra_index=True)
|
|
18
|
+
name: PropertyRef = PropertyRef("Name")
|
|
19
|
+
region: PropertyRef = PropertyRef("Region", set_in_kwargs=True)
|
|
20
|
+
event_pattern: PropertyRef = PropertyRef("EventPattern")
|
|
21
|
+
state: PropertyRef = PropertyRef("State")
|
|
22
|
+
description: PropertyRef = PropertyRef("Description")
|
|
23
|
+
schedule_expression: PropertyRef = PropertyRef("ScheduleExpression")
|
|
24
|
+
role_arn: PropertyRef = PropertyRef("RoleArn")
|
|
25
|
+
managed_by: PropertyRef = PropertyRef("ManagedBy")
|
|
26
|
+
event_bus_name: PropertyRef = PropertyRef("EventBusName")
|
|
27
|
+
lastupdated: PropertyRef = PropertyRef("lastupdated", set_in_kwargs=True)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@dataclass(frozen=True)
|
|
31
|
+
class EventBridgeRuleToAwsAccountRelProperties(CartographyRelProperties):
|
|
32
|
+
lastupdated: PropertyRef = PropertyRef("lastupdated", set_in_kwargs=True)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
@dataclass(frozen=True)
|
|
36
|
+
class EventBridgeRuleToAWSAccountRel(CartographyRelSchema):
|
|
37
|
+
target_node_label: str = "AWSAccount"
|
|
38
|
+
target_node_matcher: TargetNodeMatcher = make_target_node_matcher(
|
|
39
|
+
{"id": PropertyRef("AWS_ID", set_in_kwargs=True)},
|
|
40
|
+
)
|
|
41
|
+
direction: LinkDirection = LinkDirection.INWARD
|
|
42
|
+
rel_label: str = "RESOURCE"
|
|
43
|
+
properties: EventBridgeRuleToAwsAccountRelProperties = (
|
|
44
|
+
EventBridgeRuleToAwsAccountRelProperties()
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
@dataclass(frozen=True)
|
|
49
|
+
class EventBridgeRuleToAWSRoleRelProperties(CartographyRelProperties):
|
|
50
|
+
lastupdated: PropertyRef = PropertyRef("lastupdated", set_in_kwargs=True)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
@dataclass(frozen=True)
|
|
54
|
+
class EventBridgeRuleToAWSRoleRel(CartographyRelSchema):
|
|
55
|
+
target_node_label: str = "AWSRole"
|
|
56
|
+
target_node_matcher: TargetNodeMatcher = make_target_node_matcher(
|
|
57
|
+
{"arn": PropertyRef("RoleArn")},
|
|
58
|
+
)
|
|
59
|
+
direction: LinkDirection = LinkDirection.OUTWARD
|
|
60
|
+
rel_label: str = "ASSOCIATED_WITH"
|
|
61
|
+
properties: EventBridgeRuleToAWSRoleRelProperties = (
|
|
62
|
+
EventBridgeRuleToAWSRoleRelProperties()
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
@dataclass(frozen=True)
|
|
67
|
+
class EventBridgeRuleSchema(CartographyNodeSchema):
|
|
68
|
+
label: str = "EventBridgeRule"
|
|
69
|
+
properties: EventBridgeRuleNodeProperties = EventBridgeRuleNodeProperties()
|
|
70
|
+
sub_resource_relationship: EventBridgeRuleToAWSAccountRel = (
|
|
71
|
+
EventBridgeRuleToAWSAccountRel()
|
|
72
|
+
)
|
|
73
|
+
other_relationships: OtherRelationships = OtherRelationships(
|
|
74
|
+
[
|
|
75
|
+
EventBridgeRuleToAWSRoleRel(),
|
|
76
|
+
]
|
|
77
|
+
)
|
|
File without changes
|
|
@@ -0,0 +1,51 @@
|
|
|
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 GlueConnectionNodeProperties(CartographyNodeProperties):
|
|
15
|
+
id: PropertyRef = PropertyRef("Name")
|
|
16
|
+
arn: PropertyRef = PropertyRef("Name", extra_index=True)
|
|
17
|
+
region: PropertyRef = PropertyRef("Region", set_in_kwargs=True)
|
|
18
|
+
description: PropertyRef = PropertyRef("Description")
|
|
19
|
+
connection_type: PropertyRef = PropertyRef("ConnectionType")
|
|
20
|
+
status: PropertyRef = PropertyRef("Status")
|
|
21
|
+
status_reason: PropertyRef = PropertyRef("StatusReason")
|
|
22
|
+
authentication_type: PropertyRef = PropertyRef("AuthenticationType")
|
|
23
|
+
secret_arn: PropertyRef = PropertyRef("SecretArn")
|
|
24
|
+
lastupdated: PropertyRef = PropertyRef("lastupdated", set_in_kwargs=True)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
@dataclass(frozen=True)
|
|
28
|
+
class GlueConnectionToAwsAccountRelProperties(CartographyRelProperties):
|
|
29
|
+
lastupdated: PropertyRef = PropertyRef("lastupdated", set_in_kwargs=True)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
@dataclass(frozen=True)
|
|
33
|
+
class GlueConnectionToAWSAccountRel(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: GlueConnectionToAwsAccountRelProperties = (
|
|
41
|
+
GlueConnectionToAwsAccountRelProperties()
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
@dataclass(frozen=True)
|
|
46
|
+
class GlueConnectionSchema(CartographyNodeSchema):
|
|
47
|
+
label: str = "GlueConnection"
|
|
48
|
+
properties: GlueConnectionNodeProperties = GlueConnectionNodeProperties()
|
|
49
|
+
sub_resource_relationship: GlueConnectionToAWSAccountRel = (
|
|
50
|
+
GlueConnectionToAWSAccountRel()
|
|
51
|
+
)
|
|
@@ -0,0 +1,69 @@
|
|
|
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 GlueJobNodeProperties(CartographyNodeProperties):
|
|
16
|
+
id: PropertyRef = PropertyRef("Name")
|
|
17
|
+
arn: PropertyRef = PropertyRef("Name", extra_index=True)
|
|
18
|
+
profile_name: PropertyRef = PropertyRef("ProfileName")
|
|
19
|
+
job_mode: PropertyRef = PropertyRef("JobMode")
|
|
20
|
+
connections: PropertyRef = PropertyRef("Connections")
|
|
21
|
+
region: PropertyRef = PropertyRef("Region", set_in_kwargs=True)
|
|
22
|
+
description: PropertyRef = PropertyRef("Description")
|
|
23
|
+
lastupdated: PropertyRef = PropertyRef("lastupdated", set_in_kwargs=True)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
@dataclass(frozen=True)
|
|
27
|
+
class GlueJobToAwsAccountRelProperties(CartographyRelProperties):
|
|
28
|
+
lastupdated: PropertyRef = PropertyRef("lastupdated", set_in_kwargs=True)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
@dataclass(frozen=True)
|
|
32
|
+
class GlueJobToAWSAccountRel(CartographyRelSchema):
|
|
33
|
+
target_node_label: str = "AWSAccount"
|
|
34
|
+
target_node_matcher: TargetNodeMatcher = make_target_node_matcher(
|
|
35
|
+
{"id": PropertyRef("AWS_ID", set_in_kwargs=True)},
|
|
36
|
+
)
|
|
37
|
+
direction: LinkDirection = LinkDirection.INWARD
|
|
38
|
+
rel_label: str = "RESOURCE"
|
|
39
|
+
properties: GlueJobToAwsAccountRelProperties = GlueJobToAwsAccountRelProperties()
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
@dataclass(frozen=True)
|
|
43
|
+
class GlueJobToGlueConnectionRelProperties(CartographyRelProperties):
|
|
44
|
+
lastupdated: PropertyRef = PropertyRef("lastupdated", set_in_kwargs=True)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
@dataclass(frozen=True)
|
|
48
|
+
class GlueJobToGlueConnectionRel(CartographyRelSchema):
|
|
49
|
+
target_node_label: str = "GlueConnection"
|
|
50
|
+
target_node_matcher: TargetNodeMatcher = make_target_node_matcher(
|
|
51
|
+
{"id": PropertyRef("Connections", one_to_many=True)},
|
|
52
|
+
)
|
|
53
|
+
direction: LinkDirection = LinkDirection.OUTWARD
|
|
54
|
+
rel_label: str = "USES"
|
|
55
|
+
properties: GlueJobToGlueConnectionRelProperties = (
|
|
56
|
+
GlueJobToGlueConnectionRelProperties()
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
@dataclass(frozen=True)
|
|
61
|
+
class GlueJobSchema(CartographyNodeSchema):
|
|
62
|
+
label: str = "GlueJob"
|
|
63
|
+
properties: GlueJobNodeProperties = GlueJobNodeProperties()
|
|
64
|
+
sub_resource_relationship: GlueJobToAWSAccountRel = GlueJobToAWSAccountRel()
|
|
65
|
+
other_relationships: OtherRelationships = OtherRelationships(
|
|
66
|
+
[
|
|
67
|
+
GlueJobToGlueConnectionRel(),
|
|
68
|
+
]
|
|
69
|
+
)
|
|
@@ -6,8 +6,10 @@ from cartography.models.core.nodes import CartographyNodeSchema
|
|
|
6
6
|
from cartography.models.core.relationships import CartographyRelProperties
|
|
7
7
|
from cartography.models.core.relationships import CartographyRelSchema
|
|
8
8
|
from cartography.models.core.relationships import LinkDirection
|
|
9
|
+
from cartography.models.core.relationships import make_source_node_matcher
|
|
9
10
|
from cartography.models.core.relationships import make_target_node_matcher
|
|
10
11
|
from cartography.models.core.relationships import OtherRelationships
|
|
12
|
+
from cartography.models.core.relationships import SourceNodeMatcher
|
|
11
13
|
from cartography.models.core.relationships import TargetNodeMatcher
|
|
12
14
|
|
|
13
15
|
|
|
@@ -77,6 +79,48 @@ class AWSPermissionSetToAWSAccountRel(CartographyRelSchema):
|
|
|
77
79
|
)
|
|
78
80
|
|
|
79
81
|
|
|
82
|
+
@dataclass(frozen=True)
|
|
83
|
+
class RoleAssignmentAllowedByRelProperties(CartographyRelProperties):
|
|
84
|
+
"""
|
|
85
|
+
Properties for the ALLOWED_BY relationship between AWSRole and AWSSSOUser.
|
|
86
|
+
"""
|
|
87
|
+
|
|
88
|
+
# Mandatory fields for MatchLinks
|
|
89
|
+
lastupdated: PropertyRef = PropertyRef("lastupdated", set_in_kwargs=True)
|
|
90
|
+
_sub_resource_label: PropertyRef = PropertyRef(
|
|
91
|
+
"_sub_resource_label", set_in_kwargs=True
|
|
92
|
+
)
|
|
93
|
+
_sub_resource_id: PropertyRef = PropertyRef("_sub_resource_id", set_in_kwargs=True)
|
|
94
|
+
|
|
95
|
+
# Role assignment specific properties
|
|
96
|
+
permission_set_arn: PropertyRef = PropertyRef("PermissionSetArn")
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
@dataclass(frozen=True)
|
|
100
|
+
class RoleAssignmentAllowedByMatchLink(CartographyRelSchema):
|
|
101
|
+
"""
|
|
102
|
+
MatchLink schema for ALLOWED_BY relationships from role assignments.
|
|
103
|
+
Creates relationships like: (AWSRole)-[:ALLOWED_BY]->(AWSSSOUser)
|
|
104
|
+
"""
|
|
105
|
+
|
|
106
|
+
# MatchLink-specific fields for AWSRole as source
|
|
107
|
+
source_node_label: str = "AWSRole"
|
|
108
|
+
source_node_matcher: SourceNodeMatcher = make_source_node_matcher(
|
|
109
|
+
{"arn": PropertyRef("RoleArn")},
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
# Standard CartographyRelSchema fields for AWSSSOUser as target
|
|
113
|
+
target_node_label: str = "AWSSSOUser"
|
|
114
|
+
target_node_matcher: TargetNodeMatcher = make_target_node_matcher(
|
|
115
|
+
{"id": PropertyRef("UserId")},
|
|
116
|
+
)
|
|
117
|
+
direction: LinkDirection = LinkDirection.OUTWARD
|
|
118
|
+
rel_label: str = "ALLOWED_BY"
|
|
119
|
+
properties: RoleAssignmentAllowedByRelProperties = (
|
|
120
|
+
RoleAssignmentAllowedByRelProperties()
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
|
|
80
124
|
@dataclass(frozen=True)
|
|
81
125
|
class AWSPermissionSetSchema(CartographyNodeSchema):
|
|
82
126
|
label: str = "AWSPermissionSet"
|
|
File without changes
|