cartography 0.110.0rc1__py3-none-any.whl → 0.111.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 (87) hide show
  1. cartography/_version.py +16 -3
  2. cartography/cli.py +46 -8
  3. cartography/config.py +16 -9
  4. cartography/data/indexes.cypher +0 -2
  5. cartography/data/jobs/analysis/aws_ec2_keypair_analysis.json +2 -2
  6. cartography/data/jobs/analysis/keycloak_inheritance.json +30 -0
  7. cartography/graph/querybuilder.py +70 -0
  8. cartography/intel/aws/apigateway.py +113 -4
  9. cartography/intel/aws/cognito.py +201 -0
  10. cartography/intel/aws/ec2/vpc.py +140 -124
  11. cartography/intel/aws/ecs.py +7 -1
  12. cartography/intel/aws/eventbridge.py +73 -0
  13. cartography/intel/aws/glue.py +64 -0
  14. cartography/intel/aws/kms.py +13 -1
  15. cartography/intel/aws/rds.py +105 -0
  16. cartography/intel/aws/resources.py +2 -0
  17. cartography/intel/aws/route53.py +3 -1
  18. cartography/intel/aws/s3.py +104 -0
  19. cartography/intel/entra/__init__.py +41 -43
  20. cartography/intel/entra/applications.py +2 -1
  21. cartography/intel/entra/ou.py +1 -1
  22. cartography/intel/github/__init__.py +21 -25
  23. cartography/intel/github/repos.py +32 -48
  24. cartography/intel/github/util.py +12 -0
  25. cartography/intel/keycloak/__init__.py +153 -0
  26. cartography/intel/keycloak/authenticationexecutions.py +322 -0
  27. cartography/intel/keycloak/authenticationflows.py +77 -0
  28. cartography/intel/keycloak/clients.py +187 -0
  29. cartography/intel/keycloak/groups.py +126 -0
  30. cartography/intel/keycloak/identityproviders.py +94 -0
  31. cartography/intel/keycloak/organizations.py +163 -0
  32. cartography/intel/keycloak/realms.py +61 -0
  33. cartography/intel/keycloak/roles.py +202 -0
  34. cartography/intel/keycloak/scopes.py +73 -0
  35. cartography/intel/keycloak/users.py +70 -0
  36. cartography/intel/keycloak/util.py +47 -0
  37. cartography/intel/kubernetes/__init__.py +4 -0
  38. cartography/intel/kubernetes/rbac.py +464 -0
  39. cartography/intel/kubernetes/util.py +17 -0
  40. cartography/models/aws/apigateway/apigatewaydeployment.py +74 -0
  41. cartography/models/aws/cognito/__init__.py +0 -0
  42. cartography/models/aws/cognito/identity_pool.py +70 -0
  43. cartography/models/aws/cognito/user_pool.py +47 -0
  44. cartography/models/aws/ec2/security_groups.py +1 -1
  45. cartography/models/aws/ec2/vpc.py +46 -0
  46. cartography/models/aws/ec2/vpc_cidr.py +102 -0
  47. cartography/models/aws/ecs/services.py +17 -0
  48. cartography/models/aws/ecs/tasks.py +1 -0
  49. cartography/models/aws/eventbridge/target.py +71 -0
  50. cartography/models/aws/glue/job.py +69 -0
  51. cartography/models/aws/rds/event_subscription.py +146 -0
  52. cartography/models/aws/route53/dnsrecord.py +21 -0
  53. cartography/models/github/dependencies.py +1 -2
  54. cartography/models/keycloak/__init__.py +0 -0
  55. cartography/models/keycloak/authenticationexecution.py +160 -0
  56. cartography/models/keycloak/authenticationflow.py +54 -0
  57. cartography/models/keycloak/client.py +177 -0
  58. cartography/models/keycloak/group.py +101 -0
  59. cartography/models/keycloak/identityprovider.py +89 -0
  60. cartography/models/keycloak/organization.py +116 -0
  61. cartography/models/keycloak/organizationdomain.py +73 -0
  62. cartography/models/keycloak/realm.py +173 -0
  63. cartography/models/keycloak/role.py +126 -0
  64. cartography/models/keycloak/scope.py +73 -0
  65. cartography/models/keycloak/user.py +51 -0
  66. cartography/models/kubernetes/clusterrolebindings.py +98 -0
  67. cartography/models/kubernetes/clusterroles.py +52 -0
  68. cartography/models/kubernetes/rolebindings.py +119 -0
  69. cartography/models/kubernetes/roles.py +76 -0
  70. cartography/models/kubernetes/serviceaccounts.py +77 -0
  71. cartography/models/tailscale/device.py +1 -0
  72. cartography/sync.py +2 -0
  73. cartography/util.py +8 -0
  74. {cartography-0.110.0rc1.dist-info → cartography-0.111.0.dist-info}/METADATA +4 -3
  75. {cartography-0.110.0rc1.dist-info → cartography-0.111.0.dist-info}/RECORD +85 -46
  76. cartography/data/jobs/cleanup/aws_import_vpc_cleanup.json +0 -23
  77. cartography/intel/entra/resources.py +0 -20
  78. /cartography/data/jobs/{analysis → scoped_analysis}/aws_s3acl_analysis.json +0 -0
  79. /cartography/models/aws/{__init__.py → apigateway/__init__.py} +0 -0
  80. /cartography/models/aws/{apigateway.py → apigateway/apigateway.py} +0 -0
  81. /cartography/models/aws/{apigatewaycertificate.py → apigateway/apigatewaycertificate.py} +0 -0
  82. /cartography/models/aws/{apigatewayresource.py → apigateway/apigatewayresource.py} +0 -0
  83. /cartography/models/aws/{apigatewaystage.py → apigateway/apigatewaystage.py} +0 -0
  84. {cartography-0.110.0rc1.dist-info → cartography-0.111.0.dist-info}/WHEEL +0 -0
  85. {cartography-0.110.0rc1.dist-info → cartography-0.111.0.dist-info}/entry_points.txt +0 -0
  86. {cartography-0.110.0rc1.dist-info → cartography-0.111.0.dist-info}/licenses/LICENSE +0 -0
  87. {cartography-0.110.0rc1.dist-info → cartography-0.111.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,201 @@
1
+ import logging
2
+ from typing import Any
3
+ from typing import Dict
4
+ from typing import List
5
+
6
+ import boto3
7
+ import neo4j
8
+
9
+ from cartography.client.core.tx import load
10
+ from cartography.graph.job import GraphJob
11
+ from cartography.intel.aws.ec2.util import get_botocore_config
12
+ from cartography.models.aws.cognito.identity_pool import CognitoIdentityPoolSchema
13
+ from cartography.models.aws.cognito.user_pool import CognitoUserPoolSchema
14
+ from cartography.util import aws_handle_regions
15
+ from cartography.util import timeit
16
+
17
+ logger = logging.getLogger(__name__)
18
+
19
+
20
+ @timeit
21
+ @aws_handle_regions
22
+ def get_identity_pools(
23
+ boto3_session: boto3.Session, region: str
24
+ ) -> List[Dict[str, Any]]:
25
+ client = boto3_session.client(
26
+ "cognito-identity", region_name=region, config=get_botocore_config()
27
+ )
28
+ paginator = client.get_paginator("list_identity_pools")
29
+
30
+ all_identity_pools = []
31
+
32
+ for page in paginator.paginate(MaxResults=50):
33
+ identity_pools = page.get("IdentityPools", [])
34
+ all_identity_pools.extend(identity_pools)
35
+ return all_identity_pools
36
+
37
+
38
+ @timeit
39
+ @aws_handle_regions
40
+ def get_identity_pool_roles(
41
+ boto3_session: boto3.Session, identity_pools: List[Dict[str, Any]], region: str
42
+ ) -> List[Dict[str, Any]]:
43
+ client = boto3_session.client(
44
+ "cognito-identity", region_name=region, config=get_botocore_config()
45
+ )
46
+ all_identity_pool_details = []
47
+ for identity_pool in identity_pools:
48
+ response = client.get_identity_pool_roles(
49
+ IdentityPoolId=identity_pool["IdentityPoolId"]
50
+ )
51
+ all_identity_pool_details.append(response)
52
+ return all_identity_pool_details
53
+
54
+
55
+ @timeit
56
+ @aws_handle_regions
57
+ def get_user_pools(boto3_session: boto3.Session, region: str) -> List[Dict[str, Any]]:
58
+ client = boto3_session.client(
59
+ "cognito-idp", region_name=region, config=get_botocore_config()
60
+ )
61
+ paginator = client.get_paginator("list_user_pools")
62
+ all_user_pools = []
63
+
64
+ for page in paginator.paginate(MaxResults=50):
65
+ user_pools = page.get("UserPools", [])
66
+ all_user_pools.extend(user_pools)
67
+ return all_user_pools
68
+
69
+
70
+ def transform_identity_pools(
71
+ identity_pools: List[Dict[str, Any]], region: str
72
+ ) -> List[Dict[str, Any]]:
73
+ transformed_identity_pools = []
74
+ for pool in identity_pools:
75
+ transformed_pool = {
76
+ "IdentityPoolId": pool["IdentityPoolId"],
77
+ "Region": region,
78
+ "Roles": list(pool.get("Roles", {}).values()),
79
+ }
80
+ transformed_identity_pools.append(transformed_pool)
81
+ return transformed_identity_pools
82
+
83
+
84
+ def transform_user_pools(
85
+ user_pools: List[Dict[str, Any]], region: str
86
+ ) -> List[Dict[str, Any]]:
87
+ transformed_user_pools = []
88
+ for pool in user_pools:
89
+ transformed_pool = {
90
+ "Id": pool["Id"],
91
+ "Region": region,
92
+ "Name": pool["Name"],
93
+ "Status": pool.get("Status"),
94
+ }
95
+ transformed_user_pools.append(transformed_pool)
96
+ return transformed_user_pools
97
+
98
+
99
+ @timeit
100
+ def load_identity_pools(
101
+ neo4j_session: neo4j.Session,
102
+ data: List[Dict[str, Any]],
103
+ region: str,
104
+ current_aws_account_id: str,
105
+ aws_update_tag: int,
106
+ ) -> None:
107
+ logger.info(
108
+ f"Loading Cognito Identity Pools {len(data)} for region '{region}' into graph.",
109
+ )
110
+ load(
111
+ neo4j_session,
112
+ CognitoIdentityPoolSchema(),
113
+ data,
114
+ lastupdated=aws_update_tag,
115
+ Region=region,
116
+ AWS_ID=current_aws_account_id,
117
+ )
118
+
119
+
120
+ @timeit
121
+ def load_user_pools(
122
+ neo4j_session: neo4j.Session,
123
+ data: List[Dict[str, Any]],
124
+ region: str,
125
+ current_aws_account_id: str,
126
+ aws_update_tag: int,
127
+ ) -> None:
128
+ logger.info(
129
+ f"Loading Cognito User Pools {len(data)} for region '{region}' into graph.",
130
+ )
131
+ load(
132
+ neo4j_session,
133
+ CognitoUserPoolSchema(),
134
+ data,
135
+ lastupdated=aws_update_tag,
136
+ Region=region,
137
+ AWS_ID=current_aws_account_id,
138
+ )
139
+
140
+
141
+ @timeit
142
+ def cleanup(
143
+ neo4j_session: neo4j.Session,
144
+ common_job_parameters: Dict[str, Any],
145
+ ) -> None:
146
+ logger.debug("Running Efs cleanup job.")
147
+ GraphJob.from_node_schema(CognitoIdentityPoolSchema(), common_job_parameters).run(
148
+ neo4j_session
149
+ )
150
+ GraphJob.from_node_schema(CognitoUserPoolSchema(), common_job_parameters).run(
151
+ neo4j_session
152
+ )
153
+
154
+
155
+ @timeit
156
+ def sync(
157
+ neo4j_session: neo4j.Session,
158
+ boto3_session: boto3.session.Session,
159
+ regions: List[str],
160
+ current_aws_account_id: str,
161
+ update_tag: int,
162
+ common_job_parameters: Dict[str, Any],
163
+ ) -> None:
164
+ for region in regions:
165
+ logger.info(
166
+ f"Syncing Cognito Identity Pools for region '{region}' in account '{current_aws_account_id}'.",
167
+ )
168
+
169
+ identity_pools = get_identity_pools(boto3_session, region)
170
+ if not identity_pools:
171
+ logger.info(
172
+ f"No Cognito Identity Pools found in region '{region}'. Skipping sync."
173
+ )
174
+ else:
175
+ identity_pool_roles = get_identity_pool_roles(
176
+ boto3_session, identity_pools, region
177
+ )
178
+ transformed_identity_pools = transform_identity_pools(
179
+ identity_pool_roles, region
180
+ )
181
+
182
+ load_identity_pools(
183
+ neo4j_session,
184
+ transformed_identity_pools,
185
+ region,
186
+ current_aws_account_id,
187
+ update_tag,
188
+ )
189
+
190
+ user_pools = get_user_pools(boto3_session, region)
191
+ transformed_user_pools = transform_user_pools(user_pools, region)
192
+
193
+ load_user_pools(
194
+ neo4j_session,
195
+ transformed_user_pools,
196
+ region,
197
+ current_aws_account_id,
198
+ update_tag,
199
+ )
200
+
201
+ cleanup(neo4j_session, common_job_parameters)
@@ -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)
@@ -171,9 +171,15 @@ def _get_containers_from_tasks(tasks: list[dict[str, Any]]) -> list[dict[str, An
171
171
 
172
172
  def transform_ecs_tasks(tasks: list[dict[str, Any]]) -> list[dict[str, Any]]:
173
173
  """
174
- Extract network interface ID from task attachments.
174
+ Extract network interface ID from task attachments and service name from group.
175
175
  """
176
176
  for task in tasks:
177
+ # Extract serviceName from group field
178
+ group = task.get("group")
179
+ if group and group.startswith("service:"):
180
+ task["serviceName"] = group.split("service:", 1)[1]
181
+
182
+ # Extract network interface ID from task attachments
177
183
  for attachment in task.get("attachments", []):
178
184
  if attachment.get("type") == "ElasticNetworkInterface":
179
185
  details = attachment.get("details", [])
@@ -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)