cartography 0.110.0rc2__py3-none-any.whl → 0.111.0rc1__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 (24) hide show
  1. cartography/_version.py +16 -3
  2. cartography/data/indexes.cypher +0 -2
  3. cartography/graph/querybuilder.py +70 -0
  4. cartography/intel/aws/apigateway.py +111 -4
  5. cartography/intel/aws/ec2/vpc.py +140 -124
  6. cartography/intel/aws/eventbridge.py +73 -0
  7. cartography/intel/github/repos.py +9 -2
  8. cartography/models/aws/apigateway/apigatewaydeployment.py +74 -0
  9. cartography/models/aws/ec2/vpc.py +46 -0
  10. cartography/models/aws/ec2/vpc_cidr.py +102 -0
  11. cartography/models/aws/eventbridge/target.py +71 -0
  12. cartography/models/tailscale/device.py +1 -0
  13. {cartography-0.110.0rc2.dist-info → cartography-0.111.0rc1.dist-info}/METADATA +1 -1
  14. {cartography-0.110.0rc2.dist-info → cartography-0.111.0rc1.dist-info}/RECORD +23 -20
  15. cartography/data/jobs/cleanup/aws_import_vpc_cleanup.json +0 -23
  16. /cartography/models/aws/{__init__.py → apigateway/__init__.py} +0 -0
  17. /cartography/models/aws/{apigateway.py → apigateway/apigateway.py} +0 -0
  18. /cartography/models/aws/{apigatewaycertificate.py → apigateway/apigatewaycertificate.py} +0 -0
  19. /cartography/models/aws/{apigatewayresource.py → apigateway/apigatewayresource.py} +0 -0
  20. /cartography/models/aws/{apigatewaystage.py → apigateway/apigatewaystage.py} +0 -0
  21. {cartography-0.110.0rc2.dist-info → cartography-0.111.0rc1.dist-info}/WHEEL +0 -0
  22. {cartography-0.110.0rc2.dist-info → cartography-0.111.0rc1.dist-info}/entry_points.txt +0 -0
  23. {cartography-0.110.0rc2.dist-info → cartography-0.111.0rc1.dist-info}/licenses/LICENSE +0 -0
  24. {cartography-0.110.0rc2.dist-info → cartography-0.111.0rc1.dist-info}/top_level.txt +0 -0
cartography/_version.py CHANGED
@@ -1,7 +1,14 @@
1
1
  # file generated by setuptools-scm
2
2
  # don't change, don't track in version control
3
3
 
4
- __all__ = ["__version__", "__version_tuple__", "version", "version_tuple"]
4
+ __all__ = [
5
+ "__version__",
6
+ "__version_tuple__",
7
+ "version",
8
+ "version_tuple",
9
+ "__commit_id__",
10
+ "commit_id",
11
+ ]
5
12
 
6
13
  TYPE_CHECKING = False
7
14
  if TYPE_CHECKING:
@@ -9,13 +16,19 @@ if TYPE_CHECKING:
9
16
  from typing import Union
10
17
 
11
18
  VERSION_TUPLE = Tuple[Union[int, str], ...]
19
+ COMMIT_ID = Union[str, None]
12
20
  else:
13
21
  VERSION_TUPLE = object
22
+ COMMIT_ID = object
14
23
 
15
24
  version: str
16
25
  __version__: str
17
26
  __version_tuple__: VERSION_TUPLE
18
27
  version_tuple: VERSION_TUPLE
28
+ commit_id: COMMIT_ID
29
+ __commit_id__: COMMIT_ID
19
30
 
20
- __version__ = version = '0.110.0rc2'
21
- __version_tuple__ = version_tuple = (0, 110, 0, 'rc2')
31
+ __version__ = version = '0.111.0rc1'
32
+ __version_tuple__ = version_tuple = (0, 111, 0, 'rc1')
33
+
34
+ __commit_id__ = commit_id = None
@@ -51,8 +51,6 @@ CREATE INDEX IF NOT EXISTS FOR (n:AWSTransitGatewayAttachment) ON (n.lastupdated
51
51
  CREATE INDEX IF NOT EXISTS FOR (n:AWSUser) ON (n.arn);
52
52
  CREATE INDEX IF NOT EXISTS FOR (n:AWSUser) ON (n.name);
53
53
  CREATE INDEX IF NOT EXISTS FOR (n:AWSUser) ON (n.lastupdated);
54
- CREATE INDEX IF NOT EXISTS FOR (n:AWSVpc) ON (n.id);
55
- CREATE INDEX IF NOT EXISTS FOR (n:AWSVpc) ON (n.lastupdated);
56
54
  CREATE INDEX IF NOT EXISTS FOR (n:AccountAccessKey) ON (n.accesskeyid);
57
55
  CREATE INDEX IF NOT EXISTS FOR (n:AccountAccessKey) ON (n.lastupdated);
58
56
  CREATE INDEX IF NOT EXISTS FOR (n:AutoScalingGroup) ON (n.arn);
@@ -1,5 +1,7 @@
1
1
  import logging
2
2
  from dataclasses import asdict
3
+ from importlib.metadata import PackageNotFoundError
4
+ from importlib.metadata import version
3
5
  from string import Template
4
6
  from typing import Dict
5
7
  from typing import List
@@ -223,6 +225,8 @@ def _build_attach_sub_resource_statement(
223
225
  $RelMergeClause
224
226
  ON CREATE SET r.firstseen = timestamp()
225
227
  SET
228
+ r._module_name = "$module_name",
229
+ r._module_version = "$module_version",
226
230
  $set_rel_properties_statement
227
231
  """,
228
232
  )
@@ -244,6 +248,8 @@ def _build_attach_sub_resource_statement(
244
248
  SubResourceLabel=sub_resource_link.target_node_label,
245
249
  MatchClause=_build_match_clause(sub_resource_link.target_node_matcher),
246
250
  RelMergeClause=rel_merge_clause,
251
+ module_name=_get_module_from_schema(sub_resource_link),
252
+ module_version=_get_cartography_version(),
247
253
  SubResourceRelLabel=sub_resource_link.rel_label,
248
254
  set_rel_properties_statement=_build_rel_properties_statement(
249
255
  "r",
@@ -278,6 +284,8 @@ def _build_attach_additional_links_statement(
278
284
  $RelMerge
279
285
  ON CREATE SET $rel_var.firstseen = timestamp()
280
286
  SET
287
+ $rel_var._module_name = "$module_name",
288
+ $rel_var._module_version = "$module_version",
281
289
  $set_rel_properties_statement
282
290
  """,
283
291
  )
@@ -312,6 +320,8 @@ def _build_attach_additional_links_statement(
312
320
  node_var=node_var,
313
321
  rel_var=rel_var,
314
322
  RelMerge=rel_merge,
323
+ module_name=_get_module_from_schema(link),
324
+ module_version=_get_cartography_version(),
315
325
  set_rel_properties_statement=_build_rel_properties_statement(
316
326
  rel_var,
317
327
  rel_props_as_dict,
@@ -453,6 +463,8 @@ def build_ingestion_query(
453
463
  MERGE (i:$node_label{id: $dict_id_field})
454
464
  ON CREATE SET i.firstseen = timestamp()
455
465
  SET
466
+ i._module_name = "$module_name",
467
+ i._module_version = "$module_version",
456
468
  $set_node_properties_statement
457
469
  $attach_relationships_statement
458
470
  """,
@@ -475,6 +487,8 @@ def build_ingestion_query(
475
487
  ingest_query = query_template.safe_substitute(
476
488
  node_label=node_schema.label,
477
489
  dict_id_field=node_props.id,
490
+ module_name=_get_module_from_schema(node_schema),
491
+ module_version=_get_cartography_version(),
478
492
  set_node_properties_statement=_build_node_properties_statement(
479
493
  node_props_as_dict,
480
494
  node_schema.extra_node_labels,
@@ -650,6 +664,8 @@ def build_matchlink_query(rel_schema: CartographyRelSchema) -> str:
650
664
  MERGE $rel
651
665
  ON CREATE SET r.firstseen = timestamp()
652
666
  SET
667
+ r._module_name = "$module_name",
668
+ r._module_version = "$module_version",
653
669
  $set_rel_properties_statement;
654
670
  """
655
671
  )
@@ -677,8 +693,62 @@ def build_matchlink_query(rel_schema: CartographyRelSchema) -> str:
677
693
  source_match=source_match,
678
694
  target_match=target_match,
679
695
  rel=rel,
696
+ module_name=_get_module_from_schema(rel_schema),
697
+ module_version=_get_cartography_version(),
680
698
  set_rel_properties_statement=_build_rel_properties_statement(
681
699
  "r",
682
700
  rel_props_as_dict,
683
701
  ),
684
702
  )
703
+
704
+
705
+ def _get_cartography_version() -> str:
706
+ """
707
+ Get the current version of the cartography package.
708
+
709
+ This function attempts to retrieve the version of the installed cartography package
710
+ using importlib.metadata. If the package is not found (typically in development
711
+ or testing environments), it returns 'dev' as a fallback.
712
+
713
+ Returns:
714
+ The version string of the cartography package, or 'dev' if not found
715
+ """
716
+ try:
717
+ return version("cartography")
718
+ except PackageNotFoundError:
719
+ # This can occured if the cartography package is not installed in the environment, typically in development or testing environments.
720
+ logger.warning("cartography package not found. Returning 'dev' version.")
721
+ # Fallback to reading the VERSION file if the package is not found
722
+ return "dev"
723
+
724
+
725
+ def _get_module_from_schema(
726
+ schema, #: "CartographyNodeSchema" | "CartographyRelSchema",
727
+ ) -> str:
728
+ """
729
+ Extract the module name from a Cartography schema object.
730
+
731
+ This function extracts and formats the module name from a CartographyNodeSchema
732
+ or CartographyRelSchema object. It expects schemas to be part of the official
733
+ cartography.models package hierarchy and returns a formatted string indicating
734
+ the specific cartography module.
735
+
736
+ Args:
737
+ schema: A CartographyNodeSchema or CartographyRelSchema object
738
+
739
+ Returns:
740
+ A formatted module name string in the format 'cartography:<module_name>'
741
+ or 'unknown:<full_module_path>' if the schema is not from cartography.models
742
+ """
743
+ # If the entity schema does not belong to the cartography.models package,
744
+ # we log a warning and return the full module path.
745
+ if not schema.__module__.startswith("cartography.models."):
746
+ logger.warning(
747
+ "The schema %s does not start with 'cartography.models.'. "
748
+ "This may indicate that the schema is not part of the official cartography models.",
749
+ schema.__module__,
750
+ )
751
+ return f"unknown:{schema.__module__}"
752
+ # Otherwise, we return the module path as a string.
753
+ parts = schema.__module__.split(".")
754
+ return f"cartography:{parts[2]}"
@@ -14,12 +14,18 @@ from policyuniverse.policy import Policy
14
14
 
15
15
  from cartography.client.core.tx import load
16
16
  from cartography.graph.job import GraphJob
17
- from cartography.models.aws.apigateway import APIGatewayRestAPISchema
18
- from cartography.models.aws.apigatewaycertificate import (
17
+ from cartography.intel.aws.ec2.util import get_botocore_config
18
+ from cartography.models.aws.apigateway.apigateway import APIGatewayRestAPISchema
19
+ from cartography.models.aws.apigateway.apigatewaycertificate import (
19
20
  APIGatewayClientCertificateSchema,
20
21
  )
21
- from cartography.models.aws.apigatewayresource import APIGatewayResourceSchema
22
- from cartography.models.aws.apigatewaystage import APIGatewayStageSchema
22
+ from cartography.models.aws.apigateway.apigatewaydeployment import (
23
+ APIGatewayDeploymentSchema,
24
+ )
25
+ from cartography.models.aws.apigateway.apigatewayresource import (
26
+ APIGatewayResourceSchema,
27
+ )
28
+ from cartography.models.aws.apigateway.apigatewaystage import APIGatewayStageSchema
23
29
  from cartography.util import aws_handle_regions
24
30
  from cartography.util import timeit
25
31
 
@@ -40,6 +46,38 @@ def get_apigateway_rest_apis(
40
46
  return apis
41
47
 
42
48
 
49
+ def get_rest_api_ids(
50
+ rest_apis: List[Dict],
51
+ ) -> List[str]:
52
+ """
53
+ Extracts the IDs of the REST APIs from the provided list.
54
+ """
55
+ return [api["id"] for api in rest_apis if "id" in api]
56
+
57
+
58
+ @timeit
59
+ @aws_handle_regions
60
+ def get_rest_api_deployments(
61
+ boto3_session: boto3.session.Session,
62
+ rest_api_ids: List[str],
63
+ region: str,
64
+ ) -> List[Dict[str, Any]]:
65
+ """
66
+ Retrieves the deployments for each REST API in the provided list.
67
+ """
68
+ client = boto3_session.client(
69
+ "apigateway", region_name=region, config=get_botocore_config()
70
+ )
71
+ deployments: List[Dict[str, Any]] = []
72
+ for api_id in rest_api_ids:
73
+ paginator = client.get_paginator("get_deployments")
74
+ for page in paginator.paginate(restApiId=api_id):
75
+ for deployment in page.get("items", []):
76
+ deployment["api_id"] = api_id
77
+ deployments.append(deployment)
78
+ return deployments
79
+
80
+
43
81
  @timeit
44
82
  @aws_handle_regions
45
83
  def get_rest_api_details(
@@ -244,6 +282,25 @@ def transform_rest_api_details(
244
282
  return stages, certificates, resources
245
283
 
246
284
 
285
+ def transform_apigateway_deployments(
286
+ deployments: List[Dict[str, Any]],
287
+ region: str,
288
+ ) -> List[Dict[str, Any]]:
289
+ """
290
+ Transform API Gateway Deployment data for ingestion
291
+ """
292
+ transformed_deployments = []
293
+ for deployment in deployments:
294
+ transformed_deployment = {
295
+ "id": f"{deployment['api_id']}/{deployment['id']}",
296
+ "api_id": deployment["api_id"],
297
+ "description": deployment.get("description"),
298
+ "region": region,
299
+ }
300
+ transformed_deployments.append(transformed_deployment)
301
+ return transformed_deployments
302
+
303
+
247
304
  @timeit
248
305
  def load_rest_api_details(
249
306
  neo4j_session: neo4j.Session,
@@ -283,6 +340,30 @@ def load_rest_api_details(
283
340
  )
284
341
 
285
342
 
343
+ @timeit
344
+ def load_apigateway_deployments(
345
+ neo4j_session: neo4j.Session,
346
+ data: List[Dict[str, Any]],
347
+ region: str,
348
+ current_aws_account_id: str,
349
+ aws_update_tag: int,
350
+ ) -> None:
351
+ """
352
+ Load API Gateway Deployment data into neo4j.
353
+ """
354
+ logger.info(
355
+ f"Loading API Gateway {len(data)} deployments for region '{region}' into graph.",
356
+ )
357
+ load(
358
+ neo4j_session,
359
+ APIGatewayDeploymentSchema(),
360
+ data,
361
+ region=region,
362
+ lastupdated=aws_update_tag,
363
+ AWS_ID=current_aws_account_id,
364
+ )
365
+
366
+
286
367
  @timeit
287
368
  def parse_policy(api_id: str, policy: Policy) -> Optional[Dict[Any, Any]]:
288
369
  """
@@ -345,6 +426,12 @@ def cleanup(neo4j_session: neo4j.Session, common_job_parameters: Dict) -> None:
345
426
  )
346
427
  cleanup_job.run(neo4j_session)
347
428
 
429
+ cleanup_job = GraphJob.from_node_schema(
430
+ APIGatewayDeploymentSchema(),
431
+ common_job_parameters,
432
+ )
433
+ cleanup_job.run(neo4j_session)
434
+
348
435
 
349
436
  @timeit
350
437
  def sync_apigateway_rest_apis(
@@ -375,6 +462,19 @@ def sync_apigateway_rest_apis(
375
462
  current_aws_account_id,
376
463
  aws_update_tag,
377
464
  )
465
+
466
+ api_ids = get_rest_api_ids(rest_apis)
467
+ deployments = get_rest_api_deployments(
468
+ boto3_session,
469
+ api_ids,
470
+ region,
471
+ )
472
+
473
+ transformed_deployments = transform_apigateway_deployments(
474
+ deployments,
475
+ region,
476
+ )
477
+
378
478
  load_apigateway_rest_apis(
379
479
  neo4j_session,
380
480
  transformed_apis,
@@ -388,6 +488,13 @@ def sync_apigateway_rest_apis(
388
488
  current_aws_account_id,
389
489
  aws_update_tag,
390
490
  )
491
+ load_apigateway_deployments(
492
+ neo4j_session,
493
+ transformed_deployments,
494
+ region,
495
+ current_aws_account_id,
496
+ aws_update_tag,
497
+ )
391
498
 
392
499
 
393
500
  @timeit
@@ -1,13 +1,15 @@
1
1
  import logging
2
- from string import Template
3
- from typing import Dict
4
- from typing import List
2
+ from typing import Any
5
3
 
6
4
  import boto3
7
5
  import neo4j
8
6
 
7
+ from cartography.client.core.tx import load
8
+ from cartography.graph.job import GraphJob
9
+ from cartography.models.aws.ec2.vpc import AWSVpcSchema
10
+ from cartography.models.aws.ec2.vpc_cidr import AWSIPv4CidrBlockSchema
11
+ from cartography.models.aws.ec2.vpc_cidr import AWSIPv6CidrBlockSchema
9
12
  from cartography.util import aws_handle_regions
10
- from cartography.util import run_cleanup_job
11
13
  from cartography.util import timeit
12
14
 
13
15
  from .util import get_botocore_config
@@ -17,87 +19,78 @@ logger = logging.getLogger(__name__)
17
19
 
18
20
  @timeit
19
21
  @aws_handle_regions
20
- def get_ec2_vpcs(boto3_session: boto3.session.Session, region: str) -> List[Dict]:
22
+ def get_ec2_vpcs(
23
+ boto3_session: boto3.session.Session,
24
+ region: str,
25
+ ) -> list[dict[str, Any]]:
21
26
  client = boto3_session.client(
22
27
  "ec2",
23
28
  region_name=region,
24
29
  config=get_botocore_config(),
25
30
  )
26
- return client.describe_vpcs()["Vpcs"]
27
-
28
-
29
- def _get_cidr_association_statement(block_type: str) -> str:
30
- INGEST_CIDR_TEMPLATE = Template(
31
- """
32
- MATCH (vpc:AWSVpc{id: $VpcId})
33
- WITH vpc
34
- UNWIND $CidrBlock as block_data
35
- MERGE (new_block:$block_label{id: $VpcId + '|' + block_data.$block_cidr})
36
- ON CREATE SET new_block.firstseen = timestamp()
37
- SET new_block.association_id = block_data.AssociationId,
38
- new_block.cidr_block = block_data.$block_cidr,
39
- new_block.block_state = block_data.$state_name.State,
40
- new_block.block_state_message = block_data.$state_name.StatusMessage,
41
- new_block.lastupdated = $update_tag
42
- WITH vpc, new_block
43
- MERGE (vpc)-[r:BLOCK_ASSOCIATION]->(new_block)
44
- ON CREATE SET r.firstseen = timestamp()
45
- SET r.lastupdated = $update_tag""",
46
- )
47
-
48
- BLOCK_CIDR = "CidrBlock"
49
- STATE_NAME = "CidrBlockState"
50
-
51
- # base label type. We add the AWS ipv4 or 6 depending on block type
52
- BLOCK_TYPE = "AWSCidrBlock"
53
-
54
- if block_type == "ipv6":
55
- BLOCK_CIDR = "Ipv6" + BLOCK_CIDR
56
- STATE_NAME = "Ipv6" + STATE_NAME
57
- BLOCK_TYPE = BLOCK_TYPE + ":AWSIpv6CidrBlock"
58
- elif block_type == "ipv4":
59
- BLOCK_TYPE = BLOCK_TYPE + ":AWSIpv4CidrBlock"
60
- else:
61
- raise ValueError(f"Unsupported block type specified - {block_type}")
62
-
63
- return INGEST_CIDR_TEMPLATE.safe_substitute(
64
- block_label=BLOCK_TYPE,
65
- block_cidr=BLOCK_CIDR,
66
- state_name=STATE_NAME,
67
- )
68
-
69
-
70
- @timeit
71
- def load_cidr_association_set(
72
- neo4j_session: neo4j.Session,
73
- vpc_id: str,
74
- vpc_data: Dict,
75
- block_type: str,
76
- update_tag: int,
77
- ) -> None:
78
- ingest_statement = _get_cidr_association_statement(block_type)
79
-
80
- if block_type == "ipv6":
81
- data = vpc_data.get("Ipv6CidrBlockAssociationSet", [])
82
- else:
83
- data = vpc_data.get("CidrBlockAssociationSet", [])
84
-
85
- neo4j_session.run(
86
- ingest_statement,
87
- VpcId=vpc_id,
88
- CidrBlock=data,
89
- update_tag=update_tag,
90
- )
31
+ return client.describe_vpcs().get("Vpcs", [])
32
+
33
+
34
+ def transform_vpc_data(
35
+ vpc_list: list[dict[str, Any]], region: str
36
+ ) -> tuple[list[dict[str, Any]], list[dict[str, Any]], list[dict[str, Any]]]:
37
+
38
+ vpc_data: list[dict[str, Any]] = []
39
+ ipv4_cidr_blocks: list[dict[str, Any]] = []
40
+ ipv6_cidr_blocks: list[dict[str, Any]] = []
41
+
42
+ for vpc in vpc_list:
43
+ vpc_record = {
44
+ "VpcId": vpc.get("VpcId"),
45
+ "InstanceTenancy": vpc.get("InstanceTenancy"),
46
+ "State": vpc.get("State"),
47
+ "IsDefault": vpc.get("IsDefault"),
48
+ "PrimaryCIDRBlock": vpc.get("CidrBlock"),
49
+ "DhcpOptionsId": vpc.get("DhcpOptionsId"),
50
+ "lastupdated": vpc.get("lastupdated"),
51
+ }
52
+ vpc_data.append(vpc_record)
53
+
54
+ ipv4_associations = vpc.get("CidrBlockAssociationSet", [])
55
+ for association in ipv4_associations:
56
+ ipv4_block = {
57
+ "Id": vpc["VpcId"] + "|" + association.get("CidrBlock"),
58
+ "VpcId": vpc["VpcId"],
59
+ "AssociationId": association.get("AssociationId"),
60
+ "CidrBlock": association.get("CidrBlock"),
61
+ "BlockState": association.get("CidrBlockState", {}).get("State"),
62
+ "BlockStateMessage": association.get("CidrBlockState", {}).get(
63
+ "StatusMessage"
64
+ ),
65
+ }
66
+ ipv4_cidr_blocks.append(ipv4_block)
67
+
68
+ ipv6_associations = vpc.get("Ipv6CidrBlockAssociationSet", [])
69
+ for association in ipv6_associations:
70
+ ipv6_block = {
71
+ "Id": vpc["VpcId"] + "|" + association.get("Ipv6CidrBlock"),
72
+ "VpcId": vpc["VpcId"],
73
+ "AssociationId": association.get("AssociationId"),
74
+ "CidrBlock": association.get("Ipv6CidrBlock"),
75
+ "BlockState": association.get("Ipv6CidrBlockState", {}).get("State"),
76
+ "BlockStateMessage": association.get("Ipv6CidrBlockState", {}).get(
77
+ "StatusMessage"
78
+ ),
79
+ }
80
+ ipv6_cidr_blocks.append(ipv6_block)
81
+
82
+ return vpc_data, ipv4_cidr_blocks, ipv6_cidr_blocks
91
83
 
92
84
 
93
85
  @timeit
94
86
  def load_ec2_vpcs(
95
87
  neo4j_session: neo4j.Session,
96
- data: List[Dict],
88
+ vpcs: list[dict[str, Any]],
97
89
  region: str,
98
- current_aws_account_id: str,
90
+ aws_account_id: str,
99
91
  update_tag: int,
100
92
  ) -> None:
93
+ logger.info(f"Loading {len(vpcs)} EC2 VPCs for region '{region}' into graph.")
101
94
  # https://docs.aws.amazon.com/cli/latest/reference/ec2/describe-vpcs.html
102
95
  # {
103
96
  # "Vpcs": [
@@ -126,69 +119,69 @@ def load_ec2_vpcs(
126
119
  # }
127
120
  # ]
128
121
  # }
122
+ load(
123
+ neo4j_session,
124
+ AWSVpcSchema(),
125
+ vpcs,
126
+ lastupdated=update_tag,
127
+ Region=region,
128
+ AWS_ID=aws_account_id,
129
+ )
129
130
 
130
- ingest_vpc = """
131
- MERGE (new_vpc:AWSVpc{id: $VpcId})
132
- ON CREATE SET new_vpc.firstseen = timestamp(), new_vpc.vpcid =$VpcId
133
- SET new_vpc.instance_tenancy = $InstanceTenancy,
134
- new_vpc.state = $State,
135
- new_vpc.is_default = $IsDefault,
136
- new_vpc.primary_cidr_block = $PrimaryCIDRBlock,
137
- new_vpc.dhcp_options_id = $DhcpOptionsId,
138
- new_vpc.region = $Region,
139
- new_vpc.lastupdated = $update_tag
140
- WITH new_vpc
141
- MATCH (awsAccount:AWSAccount{id: $AWS_ACCOUNT_ID})
142
- MERGE (awsAccount)-[r:RESOURCE]->(new_vpc)
143
- ON CREATE SET r.firstseen = timestamp()
144
- SET r.lastupdated = $update_tag"""
145
-
146
- for vpc in data:
147
- vpc_id = vpc["VpcId"] # fail if not present
148
-
149
- neo4j_session.run(
150
- ingest_vpc,
151
- VpcId=vpc_id,
152
- InstanceTenancy=vpc.get("InstanceTenancy", None),
153
- State=vpc.get("State", None),
154
- IsDefault=vpc.get("IsDefault", None),
155
- PrimaryCIDRBlock=vpc.get("CidrBlock", None),
156
- DhcpOptionsId=vpc.get("DhcpOptionsId", None),
157
- Region=region,
158
- AWS_ACCOUNT_ID=current_aws_account_id,
159
- update_tag=update_tag,
160
- )
161
131
 
162
- load_cidr_association_set(
163
- neo4j_session,
164
- vpc_id=vpc_id,
165
- block_type="ipv4",
166
- vpc_data=vpc,
167
- update_tag=update_tag,
168
- )
132
+ @timeit
133
+ def load_ipv4_cidr_blocks(
134
+ neo4j_session: neo4j.Session,
135
+ ipv4_cidr_blocks: list[dict[str, Any]],
136
+ region: str,
137
+ aws_account_id: str,
138
+ update_tag: int,
139
+ ) -> None:
140
+ load(
141
+ neo4j_session,
142
+ AWSIPv4CidrBlockSchema(),
143
+ ipv4_cidr_blocks,
144
+ lastupdated=update_tag,
145
+ )
169
146
 
170
- load_cidr_association_set(
171
- neo4j_session,
172
- vpc_id=vpc_id,
173
- block_type="ipv6",
174
- vpc_data=vpc,
175
- update_tag=update_tag,
176
- )
147
+
148
+ @timeit
149
+ def load_ipv6_cidr_blocks(
150
+ neo4j_session: neo4j.Session,
151
+ ipv6_cidr_blocks: list[dict[str, Any]],
152
+ region: str,
153
+ aws_account_id: str,
154
+ update_tag: int,
155
+ ) -> None:
156
+ load(
157
+ neo4j_session,
158
+ AWSIPv6CidrBlockSchema(),
159
+ ipv6_cidr_blocks,
160
+ lastupdated=update_tag,
161
+ )
177
162
 
178
163
 
179
164
  @timeit
180
- def cleanup_ec2_vpcs(neo4j_session: neo4j.Session, common_job_parameters: Dict) -> None:
181
- run_cleanup_job("aws_import_vpc_cleanup.json", neo4j_session, common_job_parameters)
165
+ def cleanup(
166
+ neo4j_session: neo4j.Session, common_job_parameters: dict[str, Any]
167
+ ) -> None:
168
+ GraphJob.from_node_schema(AWSIPv6CidrBlockSchema(), common_job_parameters).run(
169
+ neo4j_session
170
+ )
171
+ GraphJob.from_node_schema(AWSIPv4CidrBlockSchema(), common_job_parameters).run(
172
+ neo4j_session
173
+ )
174
+ GraphJob.from_node_schema(AWSVpcSchema(), common_job_parameters).run(neo4j_session)
182
175
 
183
176
 
184
177
  @timeit
185
178
  def sync_vpc(
186
179
  neo4j_session: neo4j.Session,
187
180
  boto3_session: boto3.session.Session,
188
- regions: List[str],
181
+ regions: list[str],
189
182
  current_aws_account_id: str,
190
183
  update_tag: int,
191
- common_job_parameters: Dict,
184
+ common_job_parameters: dict[str, Any],
192
185
  ) -> None:
193
186
  for region in regions:
194
187
  logger.info(
@@ -196,6 +189,29 @@ def sync_vpc(
196
189
  region,
197
190
  current_aws_account_id,
198
191
  )
199
- data = get_ec2_vpcs(boto3_session, region)
200
- load_ec2_vpcs(neo4j_session, data, region, current_aws_account_id, update_tag)
201
- cleanup_ec2_vpcs(neo4j_session, common_job_parameters)
192
+ raw_vpc_data = get_ec2_vpcs(boto3_session, region)
193
+ vpc_data, ipv4_cidr_blocks, ipv6_cidr_blocks = transform_vpc_data(
194
+ raw_vpc_data, region
195
+ )
196
+ load_ec2_vpcs(
197
+ neo4j_session,
198
+ vpc_data,
199
+ region,
200
+ current_aws_account_id,
201
+ update_tag,
202
+ )
203
+ load_ipv4_cidr_blocks(
204
+ neo4j_session,
205
+ ipv4_cidr_blocks,
206
+ region,
207
+ current_aws_account_id,
208
+ update_tag,
209
+ )
210
+ load_ipv6_cidr_blocks(
211
+ neo4j_session,
212
+ ipv6_cidr_blocks,
213
+ region,
214
+ current_aws_account_id,
215
+ update_tag,
216
+ )
217
+ cleanup(neo4j_session, common_job_parameters)
@@ -10,6 +10,7 @@ from cartography.client.core.tx import load
10
10
  from cartography.graph.job import GraphJob
11
11
  from cartography.intel.aws.ec2.util import get_botocore_config
12
12
  from cartography.models.aws.eventbridge.rule import EventBridgeRuleSchema
13
+ from cartography.models.aws.eventbridge.target import EventBridgeTargetSchema
13
14
  from cartography.util import aws_handle_regions
14
15
  from cartography.util import timeit
15
16
 
@@ -33,6 +34,44 @@ def get_eventbridge_rules(
33
34
  return rules
34
35
 
35
36
 
37
+ @timeit
38
+ @aws_handle_regions
39
+ def get_eventbridge_targets(
40
+ boto3_session: boto3.Session, region: str, rules: List[Dict[str, Any]]
41
+ ) -> List[Dict[str, Any]]:
42
+ client = boto3_session.client(
43
+ "events", region_name=region, config=get_botocore_config()
44
+ )
45
+ targets = []
46
+ for rule in rules:
47
+ paginator = client.get_paginator("list_targets_by_rule")
48
+ for page in paginator.paginate(Rule=rule["Name"]):
49
+ for target in page.get("Targets", []):
50
+ target["RuleArn"] = rule["Arn"]
51
+ targets.append(target)
52
+ return targets
53
+
54
+
55
+ def transform_eventbridge_targets(
56
+ targets: List[Dict[str, Any]],
57
+ region: str,
58
+ ) -> List[Dict[str, Any]]:
59
+ """
60
+ Transform EventBridge target data for ingestion into Neo4j.
61
+ """
62
+ transformed_data = []
63
+ for target in targets:
64
+ transformed_target = {
65
+ "Id": target["Arn"],
66
+ "Arn": target["Arn"],
67
+ "RuleArn": target["RuleArn"],
68
+ "RoleArn": target.get("RoleArn"),
69
+ "Region": region,
70
+ }
71
+ transformed_data.append(transformed_target)
72
+ return transformed_data
73
+
74
+
36
75
  @timeit
37
76
  def load_eventbridge_rules(
38
77
  neo4j_session: neo4j.Session,
@@ -54,6 +93,27 @@ def load_eventbridge_rules(
54
93
  )
55
94
 
56
95
 
96
+ @timeit
97
+ def load_eventbridge_targets(
98
+ neo4j_session: neo4j.Session,
99
+ data: List[Dict[str, Any]],
100
+ region: str,
101
+ current_aws_account_id: str,
102
+ aws_update_tag: int,
103
+ ) -> None:
104
+ logger.info(
105
+ f"Loading EventBridge {len(data)} targets for region '{region}' into graph.",
106
+ )
107
+ load(
108
+ neo4j_session,
109
+ EventBridgeTargetSchema(),
110
+ data,
111
+ lastupdated=aws_update_tag,
112
+ Region=region,
113
+ AWS_ID=current_aws_account_id,
114
+ )
115
+
116
+
57
117
  @timeit
58
118
  def cleanup(
59
119
  neo4j_session: neo4j.Session,
@@ -63,6 +123,9 @@ def cleanup(
63
123
  GraphJob.from_node_schema(EventBridgeRuleSchema(), common_job_parameters).run(
64
124
  neo4j_session
65
125
  )
126
+ GraphJob.from_node_schema(EventBridgeTargetSchema(), common_job_parameters).run(
127
+ neo4j_session
128
+ )
66
129
 
67
130
 
68
131
  @timeit
@@ -88,4 +151,14 @@ def sync(
88
151
  update_tag,
89
152
  )
90
153
 
154
+ targets = get_eventbridge_targets(boto3_session, region, rules)
155
+ transformed_targets = transform_eventbridge_targets(targets, region)
156
+ load_eventbridge_targets(
157
+ neo4j_session,
158
+ transformed_targets,
159
+ region,
160
+ current_aws_account_id,
161
+ update_tag,
162
+ )
163
+
91
164
  cleanup(neo4j_session, common_job_parameters)
@@ -405,9 +405,16 @@ def _create_default_branch_id(repo_url: str, default_branch_ref_id: str) -> str:
405
405
 
406
406
  def _create_git_url_from_ssh_url(ssh_url: str) -> str:
407
407
  """
408
- Return a git:// URL from the given ssh_url
408
+ Convert SSH URL to git:// URL.
409
+ Example:
410
+ git@github.com:cartography-cncf/cartography.git
411
+ -> git://github.com/cartography-cncf/cartography.git
409
412
  """
410
- return ssh_url.replace("/", ":").replace("git@", "git://")
413
+ # Remove the user part (e.g., "git@")
414
+ _, host_and_path = ssh_url.split("@", 1)
415
+ # Replace first ':' (separating host and repo) with '/'
416
+ host, path = host_and_path.split(":", 1)
417
+ return f"git://{host}/{path}"
411
418
 
412
419
 
413
420
  def _transform_repo_objects(input_repo_object: Dict, out_repo_list: List[Dict]) -> None:
@@ -0,0 +1,74 @@
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 APIGatewayDeploymentNodeProperties(CartographyNodeProperties):
16
+ id: PropertyRef = PropertyRef("id")
17
+ arn: PropertyRef = PropertyRef("id", extra_index=True)
18
+ description: PropertyRef = PropertyRef("description")
19
+ region: PropertyRef = PropertyRef("region", set_in_kwargs=True)
20
+ lastupdated: PropertyRef = PropertyRef("lastupdated", set_in_kwargs=True)
21
+
22
+
23
+ @dataclass(frozen=True)
24
+ class APIGatewayDeploymentToAWSAccountRelRelProperties(CartographyRelProperties):
25
+ lastupdated: PropertyRef = PropertyRef("lastupdated", set_in_kwargs=True)
26
+
27
+
28
+ @dataclass(frozen=True)
29
+ # (:APIGatewayDeployment)<-[:RESOURCE]-(:AWSAccount)
30
+ class APIGatewayDeploymentToAWSAccountRel(CartographyRelSchema):
31
+ target_node_label: str = "AWSAccount"
32
+ target_node_matcher: TargetNodeMatcher = make_target_node_matcher(
33
+ {"id": PropertyRef("AWS_ID", set_in_kwargs=True)},
34
+ )
35
+ direction: LinkDirection = LinkDirection.INWARD
36
+ rel_label: str = "RESOURCE"
37
+ properties: APIGatewayDeploymentToAWSAccountRelRelProperties = (
38
+ APIGatewayDeploymentToAWSAccountRelRelProperties()
39
+ )
40
+
41
+
42
+ @dataclass(frozen=True)
43
+ class APIGatewayDeploymentToRestAPIRelRelProperties(CartographyRelProperties):
44
+ lastupdated: PropertyRef = PropertyRef("lastupdated", set_in_kwargs=True)
45
+
46
+
47
+ @dataclass(frozen=True)
48
+ # (:APIGatewayDeployment)<-[:HAS_DEPLOYMENT]-(:APIGatewayRestAPI)
49
+ class APIGatewayDeploymentToRestAPIRel(CartographyRelSchema):
50
+ target_node_label: str = "APIGatewayRestAPI"
51
+ target_node_matcher: TargetNodeMatcher = make_target_node_matcher(
52
+ {"id": PropertyRef("api_id")},
53
+ )
54
+ direction: LinkDirection = LinkDirection.INWARD
55
+ rel_label: str = "HAS_DEPLOYMENT"
56
+ properties: APIGatewayDeploymentToRestAPIRelRelProperties = (
57
+ APIGatewayDeploymentToRestAPIRelRelProperties()
58
+ )
59
+
60
+
61
+ @dataclass(frozen=True)
62
+ class APIGatewayDeploymentSchema(CartographyNodeSchema):
63
+ label: str = "APIGatewayDeployment"
64
+ properties: APIGatewayDeploymentNodeProperties = (
65
+ APIGatewayDeploymentNodeProperties()
66
+ )
67
+ sub_resource_relationship: APIGatewayDeploymentToAWSAccountRel = (
68
+ APIGatewayDeploymentToAWSAccountRel()
69
+ )
70
+ other_relationships: OtherRelationships = OtherRelationships(
71
+ [
72
+ APIGatewayDeploymentToRestAPIRel(),
73
+ ]
74
+ )
@@ -0,0 +1,46 @@
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 VPCNodeProperties(CartographyNodeProperties):
15
+ id: PropertyRef = PropertyRef("VpcId")
16
+ vpcid: PropertyRef = PropertyRef("VpcId", extra_index=True)
17
+ primary_cidr_block: PropertyRef = PropertyRef("PrimaryCIDRBlock")
18
+ instance_tenancy: PropertyRef = PropertyRef("InstanceTenancy")
19
+ state: PropertyRef = PropertyRef("State")
20
+ is_default: PropertyRef = PropertyRef("IsDefault")
21
+ dhcp_options_id: PropertyRef = PropertyRef("DhcpOptionsId")
22
+ region: PropertyRef = PropertyRef("Region", set_in_kwargs=True)
23
+ lastupdated: PropertyRef = PropertyRef("lastupdated", set_in_kwargs=True)
24
+
25
+
26
+ @dataclass(frozen=True)
27
+ class VPCToAWSAccountRelProperties(CartographyRelProperties):
28
+ lastupdated: PropertyRef = PropertyRef("lastupdated", set_in_kwargs=True)
29
+
30
+
31
+ @dataclass(frozen=True)
32
+ class VPCToAWSAccountRel(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: VPCToAWSAccountRelProperties = VPCToAWSAccountRelProperties()
40
+
41
+
42
+ @dataclass(frozen=True)
43
+ class AWSVpcSchema(CartographyNodeSchema):
44
+ label: str = "AWSVpc"
45
+ properties: VPCNodeProperties = VPCNodeProperties()
46
+ sub_resource_relationship: VPCToAWSAccountRel = VPCToAWSAccountRel()
@@ -0,0 +1,102 @@
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.nodes import ExtraNodeLabels
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 AWSIPv4CidrBlockNodeProperties(CartographyNodeProperties):
17
+ id: PropertyRef = PropertyRef("Id")
18
+ vpcid: PropertyRef = PropertyRef("VpcId")
19
+ association_id: PropertyRef = PropertyRef("AssociationId")
20
+ cidr_block: PropertyRef = PropertyRef("CidrBlock")
21
+ block_state: PropertyRef = PropertyRef("BlockState")
22
+ block_state_message: PropertyRef = PropertyRef("BlockStateMessage")
23
+ lastupdated: PropertyRef = PropertyRef("lastupdated", set_in_kwargs=True)
24
+
25
+
26
+ @dataclass(frozen=True)
27
+ class AWSIPv4CidrBlockToAWSVpcRelProperties(CartographyRelProperties):
28
+ lastupdated: PropertyRef = PropertyRef("lastupdated", set_in_kwargs=True)
29
+
30
+
31
+ @dataclass(frozen=True)
32
+ class AWSIPv4CidrBlockToAWSVpcRel(CartographyRelSchema):
33
+ target_node_label: str = "AWSVpc"
34
+ target_node_matcher: TargetNodeMatcher = make_target_node_matcher(
35
+ {"id": PropertyRef("VpcId")}
36
+ )
37
+ direction: LinkDirection = LinkDirection.INWARD
38
+ rel_label: str = "BLOCK_ASSOCIATION"
39
+ properties: AWSIPv4CidrBlockToAWSVpcRelProperties = (
40
+ AWSIPv4CidrBlockToAWSVpcRelProperties()
41
+ )
42
+
43
+
44
+ @dataclass(frozen=True)
45
+ class AWSIPv4CidrBlockSchema(CartographyNodeSchema):
46
+ """
47
+ There is no sub-resource relationship here because a
48
+ CIDR block can be associated with more than one account
49
+ and it doesn't make sense to scope it to one.
50
+ """
51
+
52
+ label: str = "AWSCidrBlock"
53
+ properties: AWSIPv4CidrBlockNodeProperties = AWSIPv4CidrBlockNodeProperties()
54
+ other_relationships: OtherRelationships = OtherRelationships(
55
+ [AWSIPv4CidrBlockToAWSVpcRel()]
56
+ )
57
+ extra_node_labels: ExtraNodeLabels = ExtraNodeLabels(["AWSIpv4CidrBlock"])
58
+
59
+
60
+ @dataclass(frozen=True)
61
+ class AWSIPv6CidrBlockNodeProperties(CartographyNodeProperties):
62
+ id: PropertyRef = PropertyRef("Id")
63
+ vpcid: PropertyRef = PropertyRef("VpcId")
64
+ association_id: PropertyRef = PropertyRef("AssociationId")
65
+ cidr_block: PropertyRef = PropertyRef("CidrBlock")
66
+ block_state: PropertyRef = PropertyRef("BlockState")
67
+ block_state_message: PropertyRef = PropertyRef("BlockStateMessage")
68
+ lastupdated: PropertyRef = PropertyRef("lastupdated", set_in_kwargs=True)
69
+
70
+
71
+ @dataclass(frozen=True)
72
+ class AWSIPv6CidrBlockToAWSVpcRelProperties(CartographyRelProperties):
73
+ lastupdated: PropertyRef = PropertyRef("lastupdated", set_in_kwargs=True)
74
+
75
+
76
+ @dataclass(frozen=True)
77
+ class AWSIPv6CidrBlockToAWSVpcRel(CartographyRelSchema):
78
+ target_node_label: str = "AWSVpc"
79
+ target_node_matcher: TargetNodeMatcher = make_target_node_matcher(
80
+ {"id": PropertyRef("VpcId")}
81
+ )
82
+ direction: LinkDirection = LinkDirection.INWARD
83
+ rel_label: str = "BLOCK_ASSOCIATION"
84
+ properties: AWSIPv6CidrBlockToAWSVpcRelProperties = (
85
+ AWSIPv6CidrBlockToAWSVpcRelProperties()
86
+ )
87
+
88
+
89
+ @dataclass(frozen=True)
90
+ class AWSIPv6CidrBlockSchema(CartographyNodeSchema):
91
+ """
92
+ There is no sub-resource relationship here because a
93
+ CIDR block can be associated with more than one account
94
+ and it doesn't make sense to scope it to one.
95
+ """
96
+
97
+ label: str = "AWSCidrBlock"
98
+ properties: AWSIPv6CidrBlockNodeProperties = AWSIPv6CidrBlockNodeProperties()
99
+ other_relationships: OtherRelationships = OtherRelationships(
100
+ [AWSIPv6CidrBlockToAWSVpcRel()]
101
+ )
102
+ extra_node_labels: ExtraNodeLabels = ExtraNodeLabels(["AWSIpv6CidrBlock"])
@@ -0,0 +1,71 @@
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 EventBridgeTargetNodeProperties(CartographyNodeProperties):
16
+ id: PropertyRef = PropertyRef("Id")
17
+ arn: PropertyRef = PropertyRef("Arn", extra_index=True)
18
+ rule_arn: PropertyRef = PropertyRef("RuleArn")
19
+ role_arn: PropertyRef = PropertyRef("RoleArn")
20
+ region: PropertyRef = PropertyRef("Region", set_in_kwargs=True)
21
+ lastupdated: PropertyRef = PropertyRef("lastupdated", set_in_kwargs=True)
22
+
23
+
24
+ @dataclass(frozen=True)
25
+ class EventBridgeTargetToAwsAccountRelProperties(CartographyRelProperties):
26
+ lastupdated: PropertyRef = PropertyRef("lastupdated", set_in_kwargs=True)
27
+
28
+
29
+ @dataclass(frozen=True)
30
+ class EventBridgeTargetToAWSAccountRel(CartographyRelSchema):
31
+ target_node_label: str = "AWSAccount"
32
+ target_node_matcher: TargetNodeMatcher = make_target_node_matcher(
33
+ {"id": PropertyRef("AWS_ID", set_in_kwargs=True)},
34
+ )
35
+ direction: LinkDirection = LinkDirection.INWARD
36
+ rel_label: str = "RESOURCE"
37
+ properties: EventBridgeTargetToAwsAccountRelProperties = (
38
+ EventBridgeTargetToAwsAccountRelProperties()
39
+ )
40
+
41
+
42
+ @dataclass(frozen=True)
43
+ class EventBridgeTargetToEventBridgeRuleRelProperties(CartographyRelProperties):
44
+ lastupdated: PropertyRef = PropertyRef("lastupdated", set_in_kwargs=True)
45
+
46
+
47
+ @dataclass(frozen=True)
48
+ class EventBridgeTargetToEventBridgeRuleRel(CartographyRelSchema):
49
+ target_node_label: str = "EventBridgeRule"
50
+ target_node_matcher: TargetNodeMatcher = make_target_node_matcher(
51
+ {"arn": PropertyRef("RuleArn")},
52
+ )
53
+ direction: LinkDirection = LinkDirection.OUTWARD
54
+ rel_label: str = "LINKED_TO_RULE"
55
+ properties: EventBridgeTargetToEventBridgeRuleRelProperties = (
56
+ EventBridgeTargetToEventBridgeRuleRelProperties()
57
+ )
58
+
59
+
60
+ @dataclass(frozen=True)
61
+ class EventBridgeTargetSchema(CartographyNodeSchema):
62
+ label: str = "EventBridgeTarget"
63
+ properties: EventBridgeTargetNodeProperties = EventBridgeTargetNodeProperties()
64
+ sub_resource_relationship: EventBridgeTargetToAWSAccountRel = (
65
+ EventBridgeTargetToAWSAccountRel()
66
+ )
67
+ other_relationships: OtherRelationships = OtherRelationships(
68
+ [
69
+ EventBridgeTargetToEventBridgeRuleRel(),
70
+ ]
71
+ )
@@ -28,6 +28,7 @@ class TailscaleDeviceNodeProperties(CartographyNodeProperties):
28
28
  authorized: PropertyRef = PropertyRef("authorized")
29
29
  is_external: PropertyRef = PropertyRef("isExternal")
30
30
  node_key: PropertyRef = PropertyRef("nodeKey")
31
+ addresses: PropertyRef = PropertyRef("addresses")
31
32
  blocks_incoming_connections: PropertyRef = PropertyRef("blocksIncomingConnections")
32
33
  client_connectivity_endpoints: PropertyRef = PropertyRef(
33
34
  "clientConnectivity.endpoints"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cartography
3
- Version: 0.110.0rc2
3
+ Version: 0.111.0rc1
4
4
  Summary: Explore assets and their relationships across your technical infrastructure.
5
5
  Maintainer: Cartography Contributors
6
6
  License: apache2
@@ -1,6 +1,6 @@
1
1
  cartography/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
2
  cartography/__main__.py,sha256=y5iqUrj4BmqZfjeDT-9balzpXeMARgHeIedRMRI1gr8,100
3
- cartography/_version.py,sha256=f8gHwx9mg-BSoFSMQZAGikuzAcOSL2iL_f7vCTkedPA,525
3
+ cartography/_version.py,sha256=mjqbwY_wN8Z8qqEs-Mud3vkMN1NHb8-3V1jv-jutGAM,718
4
4
  cartography/cli.py,sha256=iw_Rhs4ztquxD_BaneRNOZDuwBzANq9zImBteB3shXI,47085
5
5
  cartography/config.py,sha256=CUp5Klxr8enNLFttqlo67le6Z4mXTzzEb5nYiE-pGs0,17076
6
6
  cartography/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -14,7 +14,7 @@ cartography/client/aws/iam.py,sha256=dYsGikc36DEsSeR2XVOVFFUDwuU9yWj_EVkpgVYCFgM
14
14
  cartography/client/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
15
15
  cartography/client/core/tx.py,sha256=Xx6_a5u7PE5pyREuBL_J39ORcafqieFf4KdosaEv1c4,13530
16
16
  cartography/data/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
17
- cartography/data/indexes.cypher,sha256=R1UiBIAYqWGyZmwL0EPaaVWM2P64KKClSj0xPDF715s,22472
17
+ cartography/data/indexes.cypher,sha256=Ci4Vp8UI5qqFTvIht7TIGORRY2p6seQAQlUaKJ8WxEI,22357
18
18
  cartography/data/permission_relationships.yaml,sha256=RuKGGc_3ZUQ7ag0MssB8k_zaonCkVM5E8I_svBWTmGc,969
19
19
  cartography/data/jobs/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
20
20
  cartography/data/jobs/analysis/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -54,7 +54,6 @@ cartography/data/jobs/cleanup/aws_import_sqs_queues_cleanup.json,sha256=Mla8y-CW
54
54
  cartography/data/jobs/cleanup/aws_import_tags_cleanup.json,sha256=mVOmhpoeHYItYQFATlTTeBhUt2GipAliRhh69zQcHok,7360
55
55
  cartography/data/jobs/cleanup/aws_import_tgw_cleanup.json,sha256=jHlKj2jcI3avvDMOCbFd89hTS0HcNVrZinJ4AYgykQs,2097
56
56
  cartography/data/jobs/cleanup/aws_import_users_cleanup.json,sha256=TY0VFJlbgd3ClmuHSzNuNAu-tDWFbAeEGWIZz-rmlGg,257
57
- cartography/data/jobs/cleanup/aws_import_vpc_cleanup.json,sha256=2_2mVflLBg1i33WBEJiiQoVKP3RSD3MRPe-yfxzyKh0,948
58
57
  cartography/data/jobs/cleanup/aws_import_vpc_peering_cleanup.json,sha256=jpIy2W2T9A9tB5EZQeVU2p2Z0LY52NYHS8W51NQTKaA,2040
59
58
  cartography/data/jobs/cleanup/aws_ingest_ec2_auto_scaling_groups_cleanup.json,sha256=6EXQO_xH3kXkgxN_H3j45LKEN_TZucLg__g-lD3gyfI,1802
60
59
  cartography/data/jobs/cleanup/aws_ingest_load_balancers_cleanup.json,sha256=1vidNCrITt-_bxw5lsSzyqGsuyWiNhI6-bnuv5lMvRI,1729
@@ -123,7 +122,7 @@ cartography/graph/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU
123
122
  cartography/graph/cleanupbuilder.py,sha256=uMSjMOnfkfTDrgMJwflNK8HAd4oioG8NIU0JeMN7ets,16552
124
123
  cartography/graph/context.py,sha256=RGxGb8EnxowcqjR0nFF86baNhgRHeUF9wjIoFUoG8LU,1230
125
124
  cartography/graph/job.py,sha256=5h4FsOV2tHeO5O2Gv-vglI5QUNE7V6p-RNmf9Fz5gvU,9396
126
- cartography/graph/querybuilder.py,sha256=b_dIScQ8MZfskM3CsY8vOgZjHMV1rzbJuywML1X72mw,27206
125
+ cartography/graph/querybuilder.py,sha256=-5Vpaf_HBgZpwGnzrgtUVuy_lnBXSYr8nvcYodbUMYQ,30337
127
126
  cartography/graph/statement.py,sha256=VtvZFMqzzZk-gDeOnBx6oDj90XYOCM1EWw4d55bm7SU,5383
128
127
  cartography/intel/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
129
128
  cartography/intel/analysis.py,sha256=uWIpmrk2hezkqjfepqz1VV2BopT7VZ-alBj2YekfE_c,1546
@@ -145,7 +144,7 @@ cartography/intel/anthropic/util.py,sha256=m2OopxCC6chCwOxUXmc9nWW--nOlYyBL_Nc3K
145
144
  cartography/intel/anthropic/workspaces.py,sha256=_t2kdGIFceDziC_BqIJa9-nLWIoWJbPoFNX1JYqbQ-w,2544
146
145
  cartography/intel/aws/__init__.py,sha256=lgu0kxERg_dJGuktEDU0ImpHuNGHJharoLEpirKWPZM,12330
147
146
  cartography/intel/aws/acm.py,sha256=a_i2pYPpocjlL8itwlcrmg8KrSLfjcmrkhcrPP-Omj8,3827
148
- cartography/intel/aws/apigateway.py,sha256=hOYVSY70DbrEfhi9gkX7PAMtg9WiPE0Pbp2wqGes7Vs,12198
147
+ cartography/intel/aws/apigateway.py,sha256=0aXt4IRdXYNRtTaAy-JBf-7yMoqeNHukLOwOY_mGpoQ,15101
149
148
  cartography/intel/aws/cloudtrail.py,sha256=LCqf3pQMiMY4h1Wehb_OjwXst2fMIEnNOD8HbXsK4X4,2753
150
149
  cartography/intel/aws/cloudtrail_management_events.py,sha256=z5VZH1wQvl_ROG1sB9ZPdH4uexp-BKrI-WdEkhQKbkQ,35751
151
150
  cartography/intel/aws/cloudwatch.py,sha256=bBPlx5W15nun-jhYs-UAZQpo9Qm7FcBs4gBQevTqmi4,7246
@@ -160,7 +159,7 @@ cartography/intel/aws/eks.py,sha256=bPItyEj5q0nSDltJrr0S5MIrTPV0fK3xkqF6EV8fcqA,
160
159
  cartography/intel/aws/elasticache.py,sha256=ePTi-49Lbw94L6m7id5dUk1cG6FZRizFxojxKZBk8XY,5552
161
160
  cartography/intel/aws/elasticsearch.py,sha256=8X_Rp1zejkvXho0Zz_Cr4g-w9IpompdYRc-YS595Aso,8645
162
161
  cartography/intel/aws/emr.py,sha256=EJoKjHQP7-F_A1trUNU05Sb42yNR1i0C9VIpGcCfAXw,3662
163
- cartography/intel/aws/eventbridge.py,sha256=XmF8Wfigbu94HOmpVR2w-fujpHjJi8HqJoBJ8wOZI0o,2285
162
+ cartography/intel/aws/eventbridge.py,sha256=PUq0UBxKro5oiTyq8U3gLcxomh5UaeBxoO7BhIKea6k,4442
164
163
  cartography/intel/aws/glue.py,sha256=y3RQRNQutdMugSKOVnbJfA1CFfu12g8sqSuhp_AF4aI,5211
165
164
  cartography/intel/aws/guardduty.py,sha256=QpwWisz3TzbOxkRKNjByk4WWDCCMXr8jgNOgOdjQM5g,8532
166
165
  cartography/intel/aws/iam.py,sha256=bkyIzWw2OC4MHxuoCvTiZ5eEGEQhz2asiUgY_tkX2GY,34322
@@ -203,7 +202,7 @@ cartography/intel/aws/ec2/subnets.py,sha256=uzZ_YyRY2zycKXsGS05qmT6cHHi4SNbN2v1y
203
202
  cartography/intel/aws/ec2/tgw.py,sha256=FcXWgLkr4EcD9DRXJMeyWQIgIHidaq40UrZneKhvnuE,9621
204
203
  cartography/intel/aws/ec2/util.py,sha256=t6oJX9nCTejvp0D9ihxfhmCoD1aoOXQB0cuvz0pMCe0,226
205
204
  cartography/intel/aws/ec2/volumes.py,sha256=C5V9LRE41REU4siHMAaE9yAjtbpLi8yTkaqLEw1uXMg,3726
206
- cartography/intel/aws/ec2/vpc.py,sha256=Es6ufAOEhnUyzWIb_hoQ0WtKqv6R4OzKAGHRmdaHvbk,6075
205
+ cartography/intel/aws/ec2/vpc.py,sha256=JXRy0G33PQmMx2UM788InHEq7UW45MdH_wasH22t_uE,6536
207
206
  cartography/intel/aws/ec2/vpc_peerings.py,sha256=_a2Bs5tf-Qfj8SYcmPlDiUWhyzZKhKbweVAG4WEsplY,6844
208
207
  cartography/intel/aws/util/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
209
208
  cartography/intel/aws/util/arns.py,sha256=Hwg_Lnf9ZNRTI-oEXU182S9ejOTy2-ggm2RKIxV5lGc,549
@@ -256,7 +255,7 @@ cartography/intel/gcp/gke.py,sha256=2-lhTzFEh-gAQ756Sr0kZZJY5_zcFWPS7_p0EVNzuR8,
256
255
  cartography/intel/gcp/iam.py,sha256=0aSVXeSuPLE5g0Ckl8mC0vM9d2dIi-W_8tcp1xLUhoE,7694
257
256
  cartography/intel/gcp/storage.py,sha256=ARDDySshRza90Biw33z_oNxLGJqvxLYBDyuRJ270muc,9307
258
257
  cartography/intel/github/__init__.py,sha256=qDMmYd-3DAVDhkPwCTVtkwP_0x5wuhWVp9jc0ghWtx4,1679
259
- cartography/intel/github/repos.py,sha256=JWIAS7Tq_EJc5w6XCimcu_SCYK3VhXUMzDxNAA9zM18,42036
258
+ cartography/intel/github/repos.py,sha256=tuxiF3M8g7XXSbWptRAUGETQsYKTdxCe2xfnZtFUdL8,42317
260
259
  cartography/intel/github/teams.py,sha256=JICUXVScYbowtbOtIe-D4bibPmSBbQnL5z8IjzqSJ3s,14657
261
260
  cartography/intel/github/users.py,sha256=meAkOpymGGdXXqZDCqv0hPIqCP4FHJvHkVug7L8jA10,9125
262
261
  cartography/intel/github/util.py,sha256=00MihS14j95xjwEdoSHlC8BYXQYJzDHCMc43DkCH55o,8098
@@ -358,14 +357,15 @@ cartography/models/anthropic/apikey.py,sha256=eDaaXBHkZtRrKCDgeBD5BYR4jVUH-5QTi7
358
357
  cartography/models/anthropic/organization.py,sha256=3Jr9dplEF57Jn50YKn_Jy7LMpDfSydudtiy3_Pismgc,669
359
358
  cartography/models/anthropic/user.py,sha256=SJWrQoL-RGiC7uBuJ72vstnCXbOj2umcgypHKT-F3AA,1995
360
359
  cartography/models/anthropic/workspace.py,sha256=I7-UkDhXr75b9KFA_WeuIhbTUikOP22RSEsuEQh8ppM,3684
361
- cartography/models/aws/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
362
- cartography/models/aws/apigateway.py,sha256=qwb-NQzw47qu3zH6SdGicrDfClYn19wZFYASaF_6GWo,2312
363
- cartography/models/aws/apigatewaycertificate.py,sha256=vFKip_EeOws3Ke-77xI6eyUMDJK2ZgB0XWHytIuesKA,3019
364
- cartography/models/aws/apigatewayresource.py,sha256=iyri4znJo1pgAAe4xBs-eoE5dHK8fEsg6-5OusdvwE0,2856
365
- cartography/models/aws/apigatewaystage.py,sha256=d3m82TH4ggC--0HdE3SG7n-Uwm5Gu9eZMRT23h7a2as,3179
366
360
  cartography/models/aws/emr.py,sha256=iZPzyqFrDsyRSRZqjCHlyDkO91H__6iszJq5hkNpOLU,3071
367
361
  cartography/models/aws/acm/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
368
362
  cartography/models/aws/acm/certificate.py,sha256=GvB7xKBCoPmLsV9LnqAUg8x8sGTsDDsr7WIaqh4Sc-M,3141
363
+ cartography/models/aws/apigateway/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
364
+ cartography/models/aws/apigateway/apigateway.py,sha256=qwb-NQzw47qu3zH6SdGicrDfClYn19wZFYASaF_6GWo,2312
365
+ cartography/models/aws/apigateway/apigatewaycertificate.py,sha256=vFKip_EeOws3Ke-77xI6eyUMDJK2ZgB0XWHytIuesKA,3019
366
+ cartography/models/aws/apigateway/apigatewaydeployment.py,sha256=msxbdHbTb6Upp14f6JjuP6RHe2-TFgA-Ru5wVLnxyFY,2980
367
+ cartography/models/aws/apigateway/apigatewayresource.py,sha256=iyri4znJo1pgAAe4xBs-eoE5dHK8fEsg6-5OusdvwE0,2856
368
+ cartography/models/aws/apigateway/apigatewaystage.py,sha256=d3m82TH4ggC--0HdE3SG7n-Uwm5Gu9eZMRT23h7a2as,3179
369
369
  cartography/models/aws/cloudtrail/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
370
370
  cartography/models/aws/cloudtrail/management_events.py,sha256=b-p_SoZsumJggaD3CfmSHRfIfW1G_29uFwgFIJdW0lw,6634
371
371
  cartography/models/aws/cloudtrail/trail.py,sha256=dQi5txRQBnpdoHLFSl8VaWVI2Bx5tPDevGCbT7RIr0o,4452
@@ -411,6 +411,8 @@ cartography/models/aws/ec2/subnet_instance.py,sha256=6bgrWbFcCjBIjqd_6lcKv6rWyll
411
411
  cartography/models/aws/ec2/subnet_networkinterface.py,sha256=B60S1YvEopVWNlRL5W-hMby3-P06uaNcC_8oOLoRGik,3872
412
412
  cartography/models/aws/ec2/subnets.py,sha256=BtlFgf03IaD9XFesv5uzHBCm6xr71LT435m9t4MHkY8,2915
413
413
  cartography/models/aws/ec2/volumes.py,sha256=spAi4QjoYjp5G-qNuFJdW4b-oZg7by5g5FEaqJv1mZo,5242
414
+ cartography/models/aws/ec2/vpc.py,sha256=7pJ-FzkoPKxUcdFlBSHbvGr2bQoujeACUS1BRsJD8Kc,2011
415
+ cartography/models/aws/ec2/vpc_cidr.py,sha256=8DMZZKjIbgrUpet-ojQEM-5Nlo81EPUbrdOkYerUPv4,4072
414
416
  cartography/models/aws/ecr/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
415
417
  cartography/models/aws/ecr/image.py,sha256=8atk1Z13BgUOcAq3uIK-sJ9gDxwcmeCIk7x1PW9B4BE,1753
416
418
  cartography/models/aws/ecr/repository.py,sha256=goItMJ3XnLSSk8FCMvgCpWCSsleTqdNzUT_Asvw8Yhk,2908
@@ -434,6 +436,7 @@ cartography/models/aws/elasticache/cluster.py,sha256=3tx3fL3FPSp4QX3pd42sV71t6kY
434
436
  cartography/models/aws/elasticache/topic.py,sha256=Rq-Hj6xo9xouRLw2NRNU1cmmVUlOP0ieRA8fT5GlZ2w,2757
435
437
  cartography/models/aws/eventbridge/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
436
438
  cartography/models/aws/eventbridge/rule.py,sha256=VH7oTferIyykITCJhVPWZKVx2-7H9Dxi4fm9GwDitJw,3135
439
+ cartography/models/aws/eventbridge/target.py,sha256=gncb5bEdZTfy2OI_Frcn1tY_a_W5HYEDOpjahsPskwE,2865
437
440
  cartography/models/aws/glue/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
438
441
  cartography/models/aws/glue/connection.py,sha256=wG7mDMoYnmAHXrLfi52_1wPHF6VcrLOKfDmZaJJti1Q,2213
439
442
  cartography/models/aws/glue/job.py,sha256=Y4RUgfpXKf-4z4vxGEa4eEka7oJOATGks0hy_Aezevk,2805
@@ -583,7 +586,7 @@ cartography/models/snipeit/asset.py,sha256=hFvGPH6-6kGgG1yjztkf9wrrAmxNL2p38nc08
583
586
  cartography/models/snipeit/tenant.py,sha256=E6uaY3d2W3MmfuUqF2TLpRP3T1QZkoIXRtp9BGxxSxk,695
584
587
  cartography/models/snipeit/user.py,sha256=c-XWAnPTFrFnZLzR_sQB8pKNelwrLjHu-oWQLnQFnxg,2162
585
588
  cartography/models/tailscale/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
586
- cartography/models/tailscale/device.py,sha256=hj0wRaVsSTrz__k1sUE91_pG0VWbsB8QrNrzJe3rjjU,3989
589
+ cartography/models/tailscale/device.py,sha256=aKk2tYUYekU9Nma1qSDoRpUa29h8Pt4sld7x5Q9KtLw,4043
587
590
  cartography/models/tailscale/group.py,sha256=hOtzt7TYq606EszefbW0KuBWraHJ71kl-ebjUT3Gn5I,3349
588
591
  cartography/models/tailscale/postureintegration.py,sha256=nwyzYoCZn9PPaChVxCL7ibyfEc7x2mYXmpuVW2q3by4,2598
589
592
  cartography/models/tailscale/tag.py,sha256=7a8SidgsBI8f_ZzoHo3NPAA83JSXgS785VxlDeU2cBs,3955
@@ -593,9 +596,9 @@ cartography/models/trivy/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZ
593
596
  cartography/models/trivy/findings.py,sha256=SgI_h1aRyR20uAHvuXIZ1T6r4IZJt6SVhxRaF2bTsm0,3085
594
597
  cartography/models/trivy/fix.py,sha256=ho9ENVl9HSXqyggyCwR6ilkOBKDxpQ7rGibo_j21NA4,2587
595
598
  cartography/models/trivy/package.py,sha256=IwO1RZZ-MFRtNbt8Cq6YFl6fdNJMFmULnJkkK8Q4rL4,2809
596
- cartography-0.110.0rc2.dist-info/licenses/LICENSE,sha256=kvLEBRYaQ1RvUni6y7Ti9uHeooqnjPoo6n_-0JO1ETc,11351
597
- cartography-0.110.0rc2.dist-info/METADATA,sha256=CAGzvE1j8UYuQSFr5q1ttaY0Tw2TWn7yh-aXA_w38eY,13008
598
- cartography-0.110.0rc2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
599
- cartography-0.110.0rc2.dist-info/entry_points.txt,sha256=GVIAWD0o0_K077qMA_k1oZU4v-M0a8GLKGJR8tZ-qH8,112
600
- cartography-0.110.0rc2.dist-info/top_level.txt,sha256=BHqsNJQiI6Q72DeypC1IINQJE59SLhU4nllbQjgJi9g,12
601
- cartography-0.110.0rc2.dist-info/RECORD,,
599
+ cartography-0.111.0rc1.dist-info/licenses/LICENSE,sha256=kvLEBRYaQ1RvUni6y7Ti9uHeooqnjPoo6n_-0JO1ETc,11351
600
+ cartography-0.111.0rc1.dist-info/METADATA,sha256=fVzU8tAWysJtWlHvzRl34b9_lVSm7ZrTRrOmDYoGKm0,13008
601
+ cartography-0.111.0rc1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
602
+ cartography-0.111.0rc1.dist-info/entry_points.txt,sha256=GVIAWD0o0_K077qMA_k1oZU4v-M0a8GLKGJR8tZ-qH8,112
603
+ cartography-0.111.0rc1.dist-info/top_level.txt,sha256=BHqsNJQiI6Q72DeypC1IINQJE59SLhU4nllbQjgJi9g,12
604
+ cartography-0.111.0rc1.dist-info/RECORD,,
@@ -1,23 +0,0 @@
1
- {
2
- "statements": [{
3
- "query": "MATCH (n:CidrBlock)<-[:BLOCK_ASSOCIATION]-(:AWSVpc)<-[:RESOURCE]-(:AWSAccount{id: $AWS_ID}) WHERE n.lastupdated <> $UPDATE_TAG WITH n LIMIT $LIMIT_SIZE DETACH DELETE (n)",
4
- "iterative": true,
5
- "iterationsize": 100
6
- },
7
- {
8
- "query": "MATCH (:CidrBlock)<-[r:BLOCK_ASSOCIATION]-(:AWSVpc)<-[:RESOURCE]-(:AWSAccount{id: $AWS_ID}) WHERE r.lastupdated <> $UPDATE_TAG WITH r LIMIT $LIMIT_SIZE DELETE (r)",
9
- "iterative": true,
10
- "iterationsize": 100
11
- },
12
- {
13
- "query": "MATCH (n:AWSVpc)<-[:RESOURCE]-(:AWSAccount{id: $AWS_ID}) WHERE n.lastupdated <> $UPDATE_TAG WITH n LIMIT $LIMIT_SIZE DETACH DELETE (n)",
14
- "iterative": true,
15
- "iterationsize": 100
16
- },
17
- {
18
- "query": "MATCH (:AWSVpc)<-[r:RESOURCE]-(:AWSAccount{id: $AWS_ID}) WHERE r.lastupdated <> $UPDATE_TAG WITH r LIMIT $LIMIT_SIZE DELETE (r)",
19
- "iterative": true,
20
- "iterationsize": 100
21
- }],
22
- "name": "cleanup AWS VPC information"
23
- }