cartography 0.100.0rc4__py3-none-any.whl → 0.101.0rc2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of cartography might be problematic. Click here for more details.

cartography/_version.py CHANGED
@@ -17,5 +17,5 @@ __version__: str
17
17
  __version_tuple__: VERSION_TUPLE
18
18
  version_tuple: VERSION_TUPLE
19
19
 
20
- __version__ = version = '0.100.0rc4'
21
- __version_tuple__ = version_tuple = (0, 100, 0)
20
+ __version__ = version = '0.101.0rc2'
21
+ __version_tuple__ = version_tuple = (0, 101, 0)
@@ -0,0 +1,15 @@
1
+ {
2
+ "name": "EC2 Instances assume IAM roles",
3
+ "statements": [
4
+ {
5
+ "__comment": "Create STS_ASSUMEROLE_ALLOW relationships from EC2 instances to the IAM roles they can assume via their iaminstanceprofiles",
6
+ "query":"MATCH (aa:AWSAccount{id: $AWS_ID})-[:RESOURCE]->(i:EC2Instance)-[:INSTANCE_PROFILE]->(p:AWSInstanceProfile)-[:ASSOCIATED_WITH]->(r:AWSRole)\nMERGE (i)-[s:STS_ASSUMEROLE_ALLOW]->(r)\nON CREATE SET s.firstseen = timestamp(), s.lastupdated = $UPDATE_TAG",
7
+ "iterative": true
8
+ },
9
+ {
10
+ "__comment": "Cleanup",
11
+ "query":"MATCH (aa:AWSAccount{id: $AWS_ID})-[:RESOURCE]->(:EC2Instance)-[s:STS_ASSUMEROLE_ALLOW]->(:AWSRole)\nWHERE s.lastupdated <> $UPDATE_TAG\nDELETE s",
12
+ "iterative": true
13
+ }
14
+ ]
15
+ }
@@ -109,7 +109,10 @@ def _build_match_clause(matcher: TargetNodeMatcher) -> str:
109
109
  return ', '.join(match.safe_substitute(Key=key, PropRef=prop_ref) for key, prop_ref in matcher_asdict.items())
110
110
 
111
111
 
112
- def _build_where_clause_for_rel_match(node_var: str, matcher: TargetNodeMatcher) -> str:
112
+ def _build_where_clause_for_rel_match(
113
+ node_var: str,
114
+ matcher: TargetNodeMatcher,
115
+ ) -> str:
113
116
  """
114
117
  Same as _build_match_clause, but puts the matching logic in a WHERE clause.
115
118
  This is intended specifically to use for joining with relationships where we need a case-insensitive match.
@@ -119,6 +122,8 @@ def _build_where_clause_for_rel_match(node_var: str, matcher: TargetNodeMatcher)
119
122
  match = Template("$node_var.$key = $prop_ref")
120
123
  case_insensitive_match = Template("toLower($node_var.$key) = toLower($prop_ref)")
121
124
  fuzzy_and_ignorecase_match = Template("toLower($node_var.$key) CONTAINS toLower($prop_ref)")
125
+ # This assumes that item.$prop_ref points to a list available on the data object
126
+ one_to_many_match = Template("$node_var.$key IN $prop_ref")
122
127
 
123
128
  matcher_asdict = asdict(matcher)
124
129
 
@@ -128,6 +133,9 @@ def _build_where_clause_for_rel_match(node_var: str, matcher: TargetNodeMatcher)
128
133
  prop_line = case_insensitive_match.safe_substitute(node_var=node_var, key=key, prop_ref=prop_ref)
129
134
  elif prop_ref.fuzzy_and_ignore_case:
130
135
  prop_line = fuzzy_and_ignorecase_match.safe_substitute(node_var=node_var, key=key, prop_ref=prop_ref)
136
+ elif prop_ref.one_to_many:
137
+ # Allow a single node to be attached to multiple others at once using a list of IDs provided in kwargs
138
+ prop_line = one_to_many_match.safe_substitute(node_var=node_var, key=key, prop_ref=prop_ref)
131
139
  else:
132
140
  # Exact match (default; most efficient)
133
141
  prop_line = match.safe_substitute(node_var=node_var, key=key, prop_ref=prop_ref)
@@ -73,7 +73,8 @@ class GraphStatement:
73
73
  if self.iterative:
74
74
  self._run_iterative(session)
75
75
  else:
76
- session.write_transaction(self._run_noniterative).consume()
76
+ session.write_transaction(self._run_noniterative)
77
+
77
78
  logger.info(f"Completed {self.parent_job_name} statement #{self.parent_job_sequence_num}")
78
79
 
79
80
  def as_dict(self) -> Dict[str, Any]:
@@ -87,14 +88,17 @@ class GraphStatement:
87
88
  "iterationsize": self.iterationsize,
88
89
  }
89
90
 
90
- def _run_noniterative(self, tx: neo4j.Transaction) -> neo4j.Result:
91
+ def _run_noniterative(self, tx: neo4j.Transaction) -> neo4j.ResultSummary:
91
92
  """
92
93
  Non-iterative statement execution.
94
+ Returns a ResultSummary instead of Result to avoid ResultConsumedError.
93
95
  """
94
96
  result: neo4j.Result = tx.run(self.query, self.parameters)
95
97
 
96
- # Handle stats
98
+ # Ensure we consume the result inside the transaction
97
99
  summary: neo4j.ResultSummary = result.consume()
100
+
101
+ # Handle stats
98
102
  stat_handler.incr('constraints_added', summary.counters.constraints_added)
99
103
  stat_handler.incr('constraints_removed', summary.counters.constraints_removed)
100
104
  stat_handler.incr('indexes_added', summary.counters.indexes_added)
@@ -107,7 +111,7 @@ class GraphStatement:
107
111
  stat_handler.incr('relationships_created', summary.counters.relationships_created)
108
112
  stat_handler.incr('relationships_deleted', summary.counters.relationships_deleted)
109
113
 
110
- return result
114
+ return summary
111
115
 
112
116
  def _run_iterative(self, session: neo4j.Session) -> None:
113
117
  """
@@ -118,14 +122,10 @@ class GraphStatement:
118
122
  self.parameters["LIMIT_SIZE"] = self.iterationsize
119
123
 
120
124
  while True:
121
- result: neo4j.Result = session.write_transaction(self._run_noniterative)
125
+ summary: neo4j.ResultSummary = session.write_transaction(self._run_noniterative)
122
126
 
123
- # Exit if we have finished processing all items
124
- if not result.consume().counters.contains_updates:
125
- # Ensure network buffers are cleared
126
- result.consume()
127
+ if not summary.counters.contains_updates:
127
128
  break
128
- result.consume()
129
129
 
130
130
  @classmethod
131
131
  def create_from_json(
@@ -20,6 +20,7 @@ from cartography.util import merge_module_sync_metadata
20
20
  from cartography.util import run_analysis_and_ensure_deps
21
21
  from cartography.util import run_analysis_job
22
22
  from cartography.util import run_cleanup_job
23
+ from cartography.util import run_scoped_analysis_job
23
24
  from cartography.util import timeit
24
25
 
25
26
 
@@ -75,7 +76,7 @@ def _sync_one_account(
75
76
  if 'resourcegroupstaggingapi' in aws_requested_syncs:
76
77
  RESOURCE_FUNCTIONS['resourcegroupstaggingapi'](**sync_args)
77
78
 
78
- run_analysis_job(
79
+ run_scoped_analysis_job(
79
80
  'aws_ec2_iaminstanceprofile.json',
80
81
  neo4j_session,
81
82
  common_job_parameters,
@@ -211,6 +212,9 @@ def _perform_aws_analysis(
211
212
  neo4j_session: neo4j.Session,
212
213
  common_job_parameters: Dict[str, Any],
213
214
  ) -> None:
215
+ """
216
+ Performs AWS analysis jobs that span multiple accounts.
217
+ """
214
218
  requested_syncs_as_set = set(requested_syncs)
215
219
 
216
220
  ec2_asset_exposure_requirements = {
@@ -35,13 +35,29 @@ def get_launch_templates(
35
35
  def get_launch_template_versions(
36
36
  boto3_session: boto3.session.Session,
37
37
  region: str,
38
+ launch_templates: list[dict[str, Any]],
38
39
  ) -> list[dict[str, Any]]:
39
- client = boto3_session.client('ec2', region_name=region, config=get_botocore_config())
40
- paginator = client.get_paginator('describe_launch_template_versions')
41
40
  template_versions: list[dict[str, Any]] = []
42
- for page in paginator.paginate():
43
- paginated_versions = page['LaunchTemplateVersions']
44
- template_versions.extend(paginated_versions)
41
+ for template in launch_templates:
42
+ launch_template_id = template['LaunchTemplateId']
43
+ versions = get_launch_template_versions_by_template(boto3_session, launch_template_id, region)
44
+ template_versions.extend(versions)
45
+
46
+ return template_versions
47
+
48
+
49
+ @timeit
50
+ @aws_handle_regions
51
+ def get_launch_template_versions_by_template(
52
+ boto3_session: boto3.session.Session,
53
+ launch_template_id: str,
54
+ region: str,
55
+ ) -> list[dict[str, Any]]:
56
+ client = boto3_session.client('ec2', region_name=region, config=get_botocore_config())
57
+ v_paginator = client.get_paginator('describe_launch_template_versions')
58
+ template_versions = []
59
+ for versions in v_paginator.paginate(LaunchTemplateId=launch_template_id):
60
+ template_versions.extend(versions['LaunchTemplateVersions'])
45
61
  return template_versions
46
62
 
47
63
 
@@ -140,7 +156,7 @@ def sync_ec2_launch_templates(
140
156
  for region in regions:
141
157
  logger.info(f"Syncing launch templates for region '{region}' in account '{current_aws_account_id}'.")
142
158
  templates = get_launch_templates(boto3_session, region)
143
- versions = get_launch_template_versions(boto3_session, region)
159
+ versions = get_launch_template_versions(boto3_session, region, templates)
144
160
  templates = transform_launch_templates(templates)
145
161
  load_launch_templates(neo4j_session, templates, region, current_aws_account_id, update_tag)
146
162
  versions = transform_launch_template_versions(versions)
@@ -0,0 +1,73 @@
1
+ from typing import Any
2
+
3
+ import boto3
4
+ import neo4j
5
+
6
+ from cartography.client.core.tx import load
7
+ from cartography.intel.aws.ec2.util import get_botocore_config
8
+ from cartography.models.aws.iam.instanceprofile import InstanceProfileSchema
9
+ from cartography.util import aws_handle_regions
10
+ from cartography.util import timeit
11
+
12
+
13
+ @timeit
14
+ @aws_handle_regions
15
+ def get_iam_instance_profiles(boto3_session: boto3.Session) -> list[dict[str, Any]]:
16
+ client = boto3_session.client('iam', config=get_botocore_config())
17
+ paginator = client.get_paginator('list_instance_profiles')
18
+ instance_profiles = []
19
+ for page in paginator.paginate():
20
+ instance_profiles.extend(page['InstanceProfiles'])
21
+ return instance_profiles
22
+
23
+
24
+ def transform_instance_profiles(data: list[dict[str, Any]]) -> list[dict[str, Any]]:
25
+ transformed = []
26
+ for profile in data:
27
+ transformed_profile = {
28
+ 'Arn': profile['Arn'],
29
+ 'CreateDate': profile['CreateDate'],
30
+ 'InstanceProfileId': profile['InstanceProfileId'],
31
+ 'InstanceProfileName': profile['InstanceProfileName'],
32
+ 'Path': profile['Path'],
33
+ 'Roles': [role['Arn'] for role in profile.get('Roles', [])],
34
+ }
35
+ transformed.append(transformed_profile)
36
+ return transformed
37
+
38
+
39
+ @timeit
40
+ def load_iam_instance_profiles(
41
+ neo4j_session: neo4j.Session,
42
+ data: list[dict[str, Any]],
43
+ current_aws_account_id: str,
44
+ update_tag: int,
45
+ common_job_parameters: dict[str, Any],
46
+ ) -> None:
47
+ load(
48
+ neo4j_session,
49
+ InstanceProfileSchema(),
50
+ data,
51
+ AWS_ID=current_aws_account_id,
52
+ lastupdated=update_tag,
53
+ )
54
+
55
+
56
+ @timeit
57
+ def sync_iam_instance_profiles(
58
+ boto3_session: boto3.Session,
59
+ neo4j_session: neo4j.Session,
60
+ current_aws_account_id: str,
61
+ update_tag: int,
62
+ regions: list[str],
63
+ common_job_parameters: dict[str, Any],
64
+ ) -> None:
65
+ profiles = get_iam_instance_profiles(boto3_session)
66
+ profiles = transform_instance_profiles(profiles)
67
+ load_iam_instance_profiles(
68
+ neo4j_session,
69
+ profiles,
70
+ common_job_parameters['AWS_ID'],
71
+ common_job_parameters['UPDATE_TAG'],
72
+ common_job_parameters,
73
+ )
@@ -1,3 +1,4 @@
1
+ from typing import Callable
1
2
  from typing import Dict
2
3
 
3
4
  from . import apigateway
@@ -43,9 +44,11 @@ from .ec2.tgw import sync_transit_gateways
43
44
  from .ec2.volumes import sync_ebs_volumes
44
45
  from .ec2.vpc import sync_vpc
45
46
  from .ec2.vpc_peerings import sync_vpc_peerings
47
+ from .iam_instance_profiles import sync_iam_instance_profiles
46
48
 
47
- RESOURCE_FUNCTIONS: Dict = {
49
+ RESOURCE_FUNCTIONS: Dict[str, Callable[..., None]] = {
48
50
  'iam': iam.sync,
51
+ 'iaminstanceprofiles': sync_iam_instance_profiles,
49
52
  's3': s3.sync,
50
53
  'dynamodb': dynamodb.sync,
51
54
  'ec2:launch_templates': sync_ec2_launch_templates,
@@ -254,6 +254,30 @@ def _sync_single_project_dns(
254
254
  dns.sync(neo4j_session, dns_cred, project_id, gcp_update_tag, common_job_parameters)
255
255
 
256
256
 
257
+ def _sync_single_project_iam(
258
+ neo4j_session: neo4j.Session,
259
+ resources: Resource,
260
+ project_id: str,
261
+ gcp_update_tag: int,
262
+ common_job_parameters: Dict,
263
+ ) -> None:
264
+ """
265
+ Handles graph sync for a single GCP project's IAM resources.
266
+ :param neo4j_session: The Neo4j session
267
+ :param resources: namedtuple of the GCP resource objects
268
+ :param project_id: The project ID number to sync. See the `projectId` field in
269
+ https://cloud.google.com/resource-manager/reference/rest/v1/projects
270
+ :param gcp_update_tag: The timestamp value to set our new Neo4j nodes with
271
+ :param common_job_parameters: Other parameters sent to Neo4j
272
+ :return: Nothing
273
+ """
274
+ # Determine if IAM service is enabled
275
+ enabled_services = _services_enabled_on_project(resources.serviceusage, project_id)
276
+ iam_cred = _get_iam_resource(get_gcp_credentials())
277
+ if service_names.iam in enabled_services:
278
+ iam.sync(neo4j_session, iam_cred, project_id, gcp_update_tag, common_job_parameters)
279
+
280
+
257
281
  def _sync_multiple_projects(
258
282
  neo4j_session: neo4j.Session, resources: Resource, projects: List[Dict],
259
283
  gcp_update_tag: int, common_job_parameters: Dict,
@@ -300,13 +324,7 @@ def _sync_multiple_projects(
300
324
  for project in projects:
301
325
  project_id = project['projectId']
302
326
  logger.info("Syncing GCP project %s for IAM", project_id)
303
- iam.sync(
304
- neo4j_session,
305
- resources.iam,
306
- project_id,
307
- gcp_update_tag,
308
- common_job_parameters,
309
- )
327
+ _sync_single_project_iam(neo4j_session, resources, project_id, gcp_update_tag, common_job_parameters)
310
328
 
311
329
 
312
330
  @timeit
@@ -23,7 +23,6 @@ OAUTH_SCOPES = [
23
23
  'https://www.googleapis.com/auth/admin.directory.user.readonly',
24
24
  'https://www.googleapis.com/auth/admin.directory.group.readonly',
25
25
  'https://www.googleapis.com/auth/admin.directory.group.member',
26
- 'https://www.googleapis.com/auth/cloud-platform',
27
26
  ]
28
27
 
29
28
  logger = logging.getLogger(__name__)
@@ -71,6 +71,22 @@ class EC2InstanceToEC2Reservation(CartographyRelSchema):
71
71
  properties: EC2InstanceToEC2ReservationRelProperties = EC2InstanceToEC2ReservationRelProperties()
72
72
 
73
73
 
74
+ @dataclass(frozen=True)
75
+ class EC2InstanceToInstanceProfileRelProperties(CartographyRelProperties):
76
+ lastupdated: PropertyRef = PropertyRef('lastupdated', set_in_kwargs=True)
77
+
78
+
79
+ @dataclass(frozen=True)
80
+ class EC2InstanceToInstanceProfile(CartographyRelSchema):
81
+ target_node_label: str = 'AWSInstanceProfile'
82
+ target_node_matcher: TargetNodeMatcher = make_target_node_matcher(
83
+ {'arn': PropertyRef('IamInstanceProfile')},
84
+ )
85
+ direction: LinkDirection = LinkDirection.OUTWARD
86
+ rel_label: str = "INSTANCE_PROFILE"
87
+ properties: EC2InstanceToInstanceProfileRelProperties = EC2InstanceToInstanceProfileRelProperties()
88
+
89
+
74
90
  @dataclass(frozen=True)
75
91
  class EC2InstanceSchema(CartographyNodeSchema):
76
92
  label: str = 'EC2Instance'
@@ -79,5 +95,6 @@ class EC2InstanceSchema(CartographyNodeSchema):
79
95
  other_relationships: OtherRelationships = OtherRelationships(
80
96
  [
81
97
  EC2InstanceToEC2Reservation(),
98
+ EC2InstanceToInstanceProfile(), # Add the new relationship
82
99
  ],
83
100
  )
File without changes
@@ -0,0 +1,67 @@
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 InstanceProfileNodeProperties(CartographyNodeProperties):
16
+ """
17
+ Schema describing a InstanceProfile.
18
+ """
19
+ arn: PropertyRef = PropertyRef('Arn')
20
+ createdate: PropertyRef = PropertyRef('CreateDate')
21
+ id: PropertyRef = PropertyRef('Arn')
22
+ instance_profile_id: PropertyRef = PropertyRef('InstanceProfileId')
23
+ instance_profile_name: PropertyRef = PropertyRef('InstanceProfileName')
24
+ path: PropertyRef = PropertyRef('Path')
25
+ lastupdated: PropertyRef = PropertyRef('lastupdated', set_in_kwargs=True)
26
+
27
+
28
+ @dataclass(frozen=True)
29
+ class InstanceProfileToAwsAccountRelProperties(CartographyRelProperties):
30
+ lastupdated: PropertyRef = PropertyRef('lastupdated', set_in_kwargs=True)
31
+
32
+
33
+ @dataclass(frozen=True)
34
+ class InstanceProfileToAWSAccount(CartographyRelSchema):
35
+ target_node_label: str = 'AWSAccount'
36
+ target_node_matcher: TargetNodeMatcher = make_target_node_matcher(
37
+ {'id': PropertyRef('AWS_ID', set_in_kwargs=True)},
38
+ )
39
+ direction: LinkDirection = LinkDirection.INWARD
40
+ rel_label: str = "RESOURCE"
41
+ properties: InstanceProfileToAwsAccountRelProperties = InstanceProfileToAwsAccountRelProperties()
42
+
43
+
44
+ @dataclass(frozen=True)
45
+ class InstanceProfileToAWSRoleRelProperties(CartographyRelProperties):
46
+ lastupdated: PropertyRef = PropertyRef('lastupdated', set_in_kwargs=True)
47
+
48
+
49
+ @dataclass(frozen=True)
50
+ class InstanceProfileToAWSRole(CartographyRelSchema):
51
+ target_node_label: str = 'AWSRole'
52
+ target_node_matcher: TargetNodeMatcher = make_target_node_matcher(
53
+ {'arn': PropertyRef('Roles', one_to_many=True)},
54
+ )
55
+ direction: LinkDirection = LinkDirection.OUTWARD
56
+ rel_label: str = "ASSOCIATED_WITH"
57
+ properties: InstanceProfileToAWSRoleRelProperties = InstanceProfileToAWSRoleRelProperties()
58
+
59
+
60
+ @dataclass(frozen=True)
61
+ class InstanceProfileSchema(CartographyNodeSchema):
62
+ label: str = 'AWSInstanceProfile'
63
+ properties: InstanceProfileNodeProperties = InstanceProfileNodeProperties()
64
+ sub_resource_relationship: InstanceProfileToAWSAccount = InstanceProfileToAWSAccount()
65
+ other_relationships: OtherRelationships = OtherRelationships([
66
+ InstanceProfileToAWSRole(),
67
+ ])
@@ -9,12 +9,13 @@ class PropertyRef:
9
9
  """
10
10
 
11
11
  def __init__(
12
- self,
13
- name: str,
14
- set_in_kwargs=False,
15
- extra_index=False,
16
- ignore_case=False,
17
- fuzzy_and_ignore_case=False,
12
+ self,
13
+ name: str,
14
+ set_in_kwargs=False,
15
+ extra_index=False,
16
+ ignore_case=False,
17
+ fuzzy_and_ignore_case=False,
18
+ one_to_many=False,
18
19
  ):
19
20
  """
20
21
  :param name: The name of the property
@@ -44,19 +45,49 @@ class PropertyRef:
44
45
  this property using the `CONTAINS` operator.
45
46
  query. Defaults to False. This only has effect as part of a TargetNodeMatcher and is not supported for the
46
47
  sub resource relationship.
48
+ :param one_to_many: Indicates that this property is meant to create one-to-many associations. If set to True,
49
+ this property ref points to a list stored on the data dict where each item is an ID. Only has effect as
50
+ part of a TargetNodeMatcher and is not supported for the sub resource relationship. Defaults to False.
51
+ Example on why you would set this to True:
52
+ AWS IAM instance profiles can be associated with one or more roles. This is reflected in their API object:
53
+ when we call describe-iam-instance-profiles, the `Roles` field contains a list of all the roles that the
54
+ profile is associated with. So, to create AWSInstanceProfile nodes while attaching them to multiple roles,
55
+ we can create a CartographyRelSchema with
56
+ ```
57
+ class InstanceProfileSchema(Schema):
58
+ target_node_label: str = 'AWSRole'
59
+ target_node_matcher: TargetNodeMatcher = make_target_node_matcher(
60
+ 'arn': PropertyRef('Roles', one_to_many=True),
61
+ )
62
+ ...
63
+ ```
64
+ This means that as we create AWSInstanceProfile nodes, we will search for AWSRoles to attach to, and we do
65
+ this by checking if each role's `arn` field is in the `Roles` list of the data dict.
47
66
  """
48
67
  self.name = name
49
68
  self.set_in_kwargs = set_in_kwargs
50
69
  self.extra_index = extra_index
51
70
  self.ignore_case = ignore_case
52
71
  self.fuzzy_and_ignore_case = fuzzy_and_ignore_case
72
+ self.one_to_many = one_to_many
73
+
53
74
  if self.fuzzy_and_ignore_case and self.ignore_case:
54
75
  raise ValueError(
55
76
  f'Error setting PropertyRef "{self.name}": ignore_case cannot be used together with'
56
77
  'fuzzy_and_ignore_case. Pick one or the other.',
57
78
  )
58
79
 
80
+ if self.one_to_many and (self.ignore_case or self.fuzzy_and_ignore_case):
81
+ raise ValueError(
82
+ f'Error setting PropertyRef "{self.name}": one_to_many cannot be used together with '
83
+ '`ignore_case` or `fuzzy_and_ignore_case`.',
84
+ )
85
+
59
86
  def _parameterize_name(self) -> str:
87
+ """
88
+ Prefixes the name of the property ref with a '$' so that we can receive keyword args. See docs on __repr__ for
89
+ PropertyRef.
90
+ """
60
91
  return f"${self.name}"
61
92
 
62
93
  def __repr__(self) -> str:
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.2
1
+ Metadata-Version: 2.4
2
2
  Name: cartography
3
- Version: 0.100.0rc4
3
+ Version: 0.101.0rc2
4
4
  Summary: Explore assets and their relationships across your technical infrastructure.
5
5
  Maintainer: Cartography Contributors
6
6
  License: apache2
@@ -26,7 +26,7 @@ Requires-Dist: backoff>=2.1.2
26
26
  Requires-Dist: boto3>=1.15.1
27
27
  Requires-Dist: botocore>=1.18.1
28
28
  Requires-Dist: dnspython>=1.15.0
29
- Requires-Dist: neo4j<5.0.0,>=4.4.4
29
+ Requires-Dist: neo4j>=4.4.4
30
30
  Requires-Dist: policyuniverse>=1.1.0.0
31
31
  Requires-Dist: google-api-python-client>=1.7.8
32
32
  Requires-Dist: google-auth>=2.37.0
@@ -62,7 +62,8 @@ Requires-Dist: pytest-mock; extra == "dev"
62
62
  Requires-Dist: pytest-cov==6.0.0; extra == "dev"
63
63
  Requires-Dist: pytest-rerunfailures; extra == "dev"
64
64
  Requires-Dist: types-PyYAML; extra == "dev"
65
- Requires-Dist: types-requests<2.32.0.20250302; extra == "dev"
65
+ Requires-Dist: types-requests<2.32.0.20250307; extra == "dev"
66
+ Dynamic: license-file
66
67
 
67
68
  ![Cartography](docs/root/images/logo-horizontal.png)
68
69
 
@@ -1,6 +1,6 @@
1
1
  cartography/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
2
  cartography/__main__.py,sha256=JftXT_nUPkqcEh8uxCCT4n-OyHYqbldEgrDS-4ygy0U,101
3
- cartography/_version.py,sha256=XNabPAacgLtT7W1DDFP4RS2MjLNRDWXmvHKjCNXBM9A,518
3
+ cartography/_version.py,sha256=IBK6fMF_VPqJpOaJyK9R7AWPwDA7KRC-K8f8sNJSVFk,518
4
4
  cartography/cli.py,sha256=-77DOKUQn3N-TDIi55V4RHLb3k36ZGZ64o1XgiT0qmE,33370
5
5
  cartography/config.py,sha256=ZcadsKmooAkti9Kv0eDl8Ec1PcZDu3lWobtNaCnwY3k,11872
6
6
  cartography/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -18,8 +18,6 @@ cartography/data/permission_relationships.yaml,sha256=RuKGGc_3ZUQ7ag0MssB8k_zaon
18
18
  cartography/data/jobs/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
19
19
  cartography/data/jobs/analysis/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
20
20
  cartography/data/jobs/analysis/aws_ec2_asset_exposure.json,sha256=wSR_-0TbieAMSFOWpNZohgbDHooa-F6jHm8DxdntLOY,3397
21
- cartography/data/jobs/analysis/aws_ec2_iaminstance.json,sha256=98wfe8pjwS-iQIjUpM1rxuASdxk7BGl9ooigrwfpmvU,500
22
- cartography/data/jobs/analysis/aws_ec2_iaminstanceprofile.json,sha256=7M8k0p0SvSYVzCk358FnUQ865IX25TyumtHTXqZS-t8,526
23
21
  cartography/data/jobs/analysis/aws_ec2_keypair_analysis.json,sha256=_ZBczunZbx5NHAbfVc4v6vJsbyl1xaePLzj4cyxW-4A,1741
24
22
  cartography/data/jobs/analysis/aws_eks_asset_exposure.json,sha256=Z6z4YTNJJqUJl2JqONeAYAvfH_2A9qEBxkFn-aou8D8,561
25
23
  cartography/data/jobs/analysis/aws_foreign_accounts.json,sha256=b8Li_KQLwIvNXxWt0F1bVTV1Vg9dxsbr7ZE8LH2-woc,686
@@ -116,6 +114,7 @@ cartography/data/jobs/cleanup/okta_groups_cleanup.json,sha256=cBI3f_okl4pnVH48L1
116
114
  cartography/data/jobs/cleanup/okta_import_cleanup.json,sha256=4XQwYpY9vITLhnLpijMVa5PxO0Tm38CcMydnbPdQPm0,3798
117
115
  cartography/data/jobs/cleanup/pagerduty_import_cleanup.json,sha256=RJqG_Uw_QEGTer_-s2IuZ3a2kykhUcCdDNZu0S7SEB4,4457
118
116
  cartography/data/jobs/scoped_analysis/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
117
+ cartography/data/jobs/scoped_analysis/aws_ec2_iaminstanceprofile.json,sha256=hzFlYNozrAiTh96qGlQSwTt3yzlDEUXF3OfxMMZTGgU,808
119
118
  cartography/data/jobs/scoped_analysis/semgrep_sca_risk_analysis.json,sha256=eIYxbl5TdgVzN8En2JozWoyKAiIh3Dp8wUMkTDPGZY0,6485
120
119
  cartography/driftdetect/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
121
120
  cartography/driftdetect/__main__.py,sha256=Sz24Kxy5x6RC3GQEkuUDXzjOV3SvlHVkZdvPl1GLl5E,125
@@ -134,13 +133,13 @@ cartography/graph/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU
134
133
  cartography/graph/cleanupbuilder.py,sha256=zZc7SvRz23sU_VN-RrHoyhug7dRZlqbbLwHbrlPdVik,9411
135
134
  cartography/graph/context.py,sha256=RGxGb8EnxowcqjR0nFF86baNhgRHeUF9wjIoFUoG8LU,1230
136
135
  cartography/graph/job.py,sha256=RZWsbNhHuJlcSpw4C73ZuovRTp7kGrcm3X9yUH8vT1Q,7488
137
- cartography/graph/querybuilder.py,sha256=7DtIGPlfeKcIWKw_ReCAX1OeUkhSnIRV_reeVaKL6I4,20416
138
- cartography/graph/statement.py,sha256=VsqG46ty_Mm87fr8YdIwfr6a82OUXU7yZe6S-Py9hZg,5345
136
+ cartography/graph/querybuilder.py,sha256=m-BQ70yB8auP3qsa5nlsj6bwela_5A6f0vuFI0YoAXk,20839
137
+ cartography/graph/statement.py,sha256=Dr6wrlmodUwwh8jnMVaHZAuxrkYyTASokQcmn9AnA9E,5311
139
138
  cartography/intel/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
140
139
  cartography/intel/analysis.py,sha256=gHtN42NqqLL1G5MOm2Q6rMyg-V5lU_wqbnKp5hbOOao,1499
141
140
  cartography/intel/create_indexes.py,sha256=HM2jB2v3NZvGqgVDtNoBQRVpkei_JXcYXqM14w8Rjss,741
142
141
  cartography/intel/dns.py,sha256=M-WSiGQoxWZsl0sg-2SDR8OD8e1Rexkt2Tbb2OpeioA,5596
143
- cartography/intel/aws/__init__.py,sha256=siRhVNypfGxMPNIXHSjfYbLX9qlB6RYyN6aWX64N-t8,11076
142
+ cartography/intel/aws/__init__.py,sha256=g9NPDlIlvOi2oDCq0VY09kxv0TaD1wihFQLdOcJyk18,11212
144
143
  cartography/intel/aws/apigateway.py,sha256=w4QdxlwnVegKKsZFfoI36i-FGlIUjzxzaayZyz9oQvM,11654
145
144
  cartography/intel/aws/config.py,sha256=wrZbz7bc8vImLmRvTLkPcWnjjPzk3tOG4bB_BFS2lq8,7329
146
145
  cartography/intel/aws/dynamodb.py,sha256=LZ6LGNThLi0zC3eLMq2JN3mwiSwZeaH58YQQHvsXMGE,5013
@@ -151,6 +150,7 @@ cartography/intel/aws/elasticache.py,sha256=fCI47aDFmIDyE26GiReKYb6XIZUwrzcvsXBQ
151
150
  cartography/intel/aws/elasticsearch.py,sha256=ZL7MkXF_bXRSoXuDSI1dwGckRLG2zDB8LuAD07vSLnE,8374
152
151
  cartography/intel/aws/emr.py,sha256=xhWBVZngxJRFjMEDxwq3G6SgytRGLq0v2a_CeDvByR0,3372
153
152
  cartography/intel/aws/iam.py,sha256=zRlF9cKcYm44iL63G6bd-_flNOFHVrjsEfW0jZHpUNg,32387
153
+ cartography/intel/aws/iam_instance_profiles.py,sha256=-bjBqhqni6n3SN2eFwy8bUufNAoshjRuxFB5dxD31ow,2255
154
154
  cartography/intel/aws/identitycenter.py,sha256=OnyvsSfn6AszB2e9dPAsGmzgne0zq0_7Ta0A6k_l_TE,8956
155
155
  cartography/intel/aws/inspector.py,sha256=S22ZgRKEnmnBTJ-u0rodqRPB7_LkSIek47NeBxN4XJw,9336
156
156
  cartography/intel/aws/kms.py,sha256=bZUzMxAH_DsAcGTJBs08gg2tLKYu-QWjvMvV9C-6v50,11731
@@ -160,7 +160,7 @@ cartography/intel/aws/permission_relationships.py,sha256=IarV9gt5BaplZ5TPo_mfypt
160
160
  cartography/intel/aws/rds.py,sha256=vnlNYmrO2Cc0PNn31CeG2QwYhwjVosbQFE9Ol1vQyLE,25252
161
161
  cartography/intel/aws/redshift.py,sha256=KOqiXIllHmtPTeaNGl-cX4srY5pFE6o12j8MQ5-zWpc,6694
162
162
  cartography/intel/aws/resourcegroupstaggingapi.py,sha256=I2VrL-oplGj2Jyhbo312V6vnER_FXpJRrc6OBJ-_VmI,10375
163
- cartography/intel/aws/resources.py,sha256=A8Dc3PtCfDyk5a1ZgAoHthhDPS6aWN_kR0PLwnHdC0Q,3370
163
+ cartography/intel/aws/resources.py,sha256=lg5j0z6Iwj16Gv25yuAb3gHG6x4vOfRNm43FoOVRfqg,3541
164
164
  cartography/intel/aws/route53.py,sha256=IYqeQud1HuHnf11A7T-Jeif5DWgjpaaU-Jfr2cLUc_o,14099
165
165
  cartography/intel/aws/s3.py,sha256=SVxUMtMSkbdjZv5qOSYIbYb8BQa-QTojbHG85-EFWLA,27034
166
166
  cartography/intel/aws/secretsmanager.py,sha256=YogwRPT6qZPVg5HrND71zI-nNn60oxoWaW7eUlhuTS0,3304
@@ -174,7 +174,7 @@ cartography/intel/aws/ec2/images.py,sha256=SLoxcy_PQgNomVMDMdutm0zXJCOLosiHJlN63
174
174
  cartography/intel/aws/ec2/instances.py,sha256=uI8eVJmeEybS8y_T8CVKAkwxJyVDCH7sbuEJYeWGSWY,12468
175
175
  cartography/intel/aws/ec2/internet_gateways.py,sha256=dI-4-85_3DGGZZBcY_DN6XqESx9P26S6jKok314lcnQ,2883
176
176
  cartography/intel/aws/ec2/key_pairs.py,sha256=g4imIo_5jk8upq9J4--erg-OZXG2i3cJMe6SnNCYj9s,2635
177
- cartography/intel/aws/ec2/launch_templates.py,sha256=9s7opSzdpErDzxknAsPOXb63Cr0BJM4S-wcxFuInFXs,5346
177
+ cartography/intel/aws/ec2/launch_templates.py,sha256=uw0_knQW0DGFgD4BUyaxYW0RpWyA_oXcDe-oT6MEaps,5902
178
178
  cartography/intel/aws/ec2/load_balancer_v2s.py,sha256=95FfQQn740gexINIHDJizOM4OKzRtQT_y2XQMipQ5Dg,8661
179
179
  cartography/intel/aws/ec2/load_balancers.py,sha256=1GwErzGqi3BKCARqfGJcD_r_D84rFKVy5kNMas9jAok,6756
180
180
  cartography/intel/aws/ec2/network_acls.py,sha256=_UiOx79OxcqH0ecRjcVMglAzz5XJ4aVYLlv6dl_ism4,6809
@@ -220,7 +220,7 @@ cartography/intel/duo/phones.py,sha256=ueJheqSLD2xYcMus5eOiixPYS3_xVjgQzeomjV2a6
220
220
  cartography/intel/duo/tokens.py,sha256=bEEnjfc4waQnkRHVSnZLAeGE8wHOOZL7FA9m80GGQdQ,2396
221
221
  cartography/intel/duo/users.py,sha256=lc7ly_XKeUjJ50szw31WT_GiCrZfGKJv1zVUpmTchh4,4097
222
222
  cartography/intel/duo/web_authn_credentials.py,sha256=IbDf3CWqfEyI7f9zJugUvoDd6vZOECfb_7ANZaRYzuk,2636
223
- cartography/intel/gcp/__init__.py,sha256=csFnE0_lznp3UfJZZSCCWF0gzdf_qz7sXuMzzZs_NvY,16516
223
+ cartography/intel/gcp/__init__.py,sha256=iJPcmkXjMITBf2h9CFJ5-vpLMNRzReoKIazNihRPrNA,17472
224
224
  cartography/intel/gcp/compute.py,sha256=CH2cBdOwbLZCAbkfRJkkI-sFybXVKRWEUGDJANQmvyA,48333
225
225
  cartography/intel/gcp/crm.py,sha256=Uw5PILhVFhpM8gq7uu2v7F_YikDW3gsTZ3d7-e8Z1_k,12324
226
226
  cartography/intel/gcp/dns.py,sha256=y2pvbmV04cnrMyuu_nbW3oc7uwHX6yEzn1n7veCsjmk,7748
@@ -232,7 +232,7 @@ cartography/intel/github/repos.py,sha256=MmpxZASDJFQxDeSMxX3pZcpxCHFPos4_uYC_cX9
232
232
  cartography/intel/github/teams.py,sha256=AltQSmBHHmyzBtnRkez9Bo5yChEKBSt3wwzhGcfqmX4,14180
233
233
  cartography/intel/github/users.py,sha256=MCLE0V0UCzQm3k3KmrNe6PYkI6usRQZYy2rCN3mT8o0,8948
234
234
  cartography/intel/github/util.py,sha256=K0cXOPuhnGvN-aqcSUBO3vTuKQLjufVal9kn2HwOpbo,8110
235
- cartography/intel/gsuite/__init__.py,sha256=FZS4WVCjOCvGqrq2E-yhZ_dtViQSy1srs_YEohibpWU,5466
235
+ cartography/intel/gsuite/__init__.py,sha256=R3hMZkaRQKHIPcvNpupwJuj7w1RuBrT4skGQASqxyOk,5412
236
236
  cartography/intel/gsuite/api.py,sha256=bx0mPn6x6fgssxgm343NHdwjbtFkO6SZTucOsoW0Hgk,11143
237
237
  cartography/intel/jamf/__init__.py,sha256=Nof-LrUeevoieo6oP2GyfTwx8k5TUIgreW6hSj53YjQ,419
238
238
  cartography/intel/jamf/computers.py,sha256=EfjlupQ-9HYTjOrmuwrGuJDy9ApAnJvk8WrYcp6_Jkk,1673
@@ -290,7 +290,7 @@ cartography/models/aws/dynamodb/tables.py,sha256=iNTnI0FLdqHs0-EYGqWLGHWEy4mkSED
290
290
  cartography/models/aws/ec2/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
291
291
  cartography/models/aws/ec2/auto_scaling_groups.py,sha256=ImIu-i5iU_CJQ25oa2MDnOhOjL4jcpBagPiB95JUvSE,8660
292
292
  cartography/models/aws/ec2/images.py,sha256=uGhXS7Xb6sKUdwwkS0O0dWP4sIREjusUaV_unODv9gE,3012
293
- cartography/models/aws/ec2/instances.py,sha256=cNMHngdGNRhxoyID6AmG2F7CQGC1fYani8DV8lSKvsI,3902
293
+ cartography/models/aws/ec2/instances.py,sha256=H2z-0pMVOmpsYORB9WfS8hHz3n-eY8qoKg5FMeXkpbg,4613
294
294
  cartography/models/aws/ec2/keypair.py,sha256=UzKj1-Oyd-xMAJeuELzkGlDNAih1H9KpBwEgCCp54T8,2235
295
295
  cartography/models/aws/ec2/keypair_instance.py,sha256=M1Ru8Z_2izW0cADAnQVVHaKsT_4FucNZnjr2DIGncJY,2943
296
296
  cartography/models/aws/ec2/launch_configurations.py,sha256=zdfWJEx93HXDXd_IzSEkhvcztkJI7_v_TCE_d8ZNAyI,2764
@@ -310,6 +310,8 @@ cartography/models/aws/ec2/subnet_networkinterface.py,sha256=JHlxfBojBw7LfJS4a5L
310
310
  cartography/models/aws/ec2/volumes.py,sha256=WSP7YNZeJE3s4wnY9QrIAbcJN3OathqNgEBX0cVahDg,4470
311
311
  cartography/models/aws/eks/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
312
312
  cartography/models/aws/eks/clusters.py,sha256=WeuC1wcjB_twsvgS0EMvU2wENhD-pm4t6N3HZ19x3vk,2293
313
+ cartography/models/aws/iam/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
314
+ cartography/models/aws/iam/instanceprofile.py,sha256=tZL3vuWdimrrg4Fbx8JlVfaFWZEnHVMpdoBJrduBiKM,2838
313
315
  cartography/models/aws/identitycenter/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
314
316
  cartography/models/aws/identitycenter/awsidentitycenter.py,sha256=lmpo-qqmMN6BMGEkBQyxmb0Nfal0qI1HetqmRmOWkSU,1988
315
317
  cartography/models/aws/identitycenter/awspermissionset.py,sha256=30BBY-XG3-rJMihKKTdUVobkdqQYWWGsFD83uTVJqkI,3539
@@ -324,7 +326,7 @@ cartography/models/bigfix/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJW
324
326
  cartography/models/bigfix/bigfix_computer.py,sha256=HQQsQPUphfkBsW8jIP0b9InNAb3vtOSWVD_GqSikkm4,3520
325
327
  cartography/models/bigfix/bigfix_root.py,sha256=GkyI0Gmnat8Q-3aQWJdCaNzKB89fY0ee4-lRvvPRLns,598
326
328
  cartography/models/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
327
- cartography/models/core/common.py,sha256=uAMmUAY7_ENDsq-CvEZ0bQNAwdWOFRCzIQEFz7NOtmI,4316
329
+ cartography/models/core/common.py,sha256=Bk0dCBkO9Udj9v4e8WE9mgBGaZTI170m9QpoiiYoz48,6206
328
330
  cartography/models/core/nodes.py,sha256=h5dwBOk_a2uCHZWeQz3pidr7gkqMKf7buIZgl6M1Ox4,3699
329
331
  cartography/models/core/relationships.py,sha256=6AwXvk0dq48BxqyxBpHyBXZ3dJNm65t1y4vNg4n25uA,5103
330
332
  cartography/models/cve/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -358,9 +360,9 @@ cartography/models/snipeit/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJ
358
360
  cartography/models/snipeit/asset.py,sha256=FyRAaeXuZjMy0eUQcSDFcgEAF5lbLMlvqp1Tv9d3Lv4,3238
359
361
  cartography/models/snipeit/tenant.py,sha256=p4rFnpNNuF1W5ilGBbexDaETWTwavfb38RcQGoImkQI,679
360
362
  cartography/models/snipeit/user.py,sha256=MsB4MiCVNTH6JpESime7cOkB89autZOXQpL6Z0l7L6o,2113
361
- cartography-0.100.0rc4.dist-info/LICENSE,sha256=kvLEBRYaQ1RvUni6y7Ti9uHeooqnjPoo6n_-0JO1ETc,11351
362
- cartography-0.100.0rc4.dist-info/METADATA,sha256=Ybi85b9tT6kYixO3WY-Y5TDc1El7FmVRTG_7oWyH95g,11859
363
- cartography-0.100.0rc4.dist-info/WHEEL,sha256=52BFRY2Up02UkjOa29eZOS2VxUrpPORXg1pkohGGUS8,91
364
- cartography-0.100.0rc4.dist-info/entry_points.txt,sha256=GVIAWD0o0_K077qMA_k1oZU4v-M0a8GLKGJR8tZ-qH8,112
365
- cartography-0.100.0rc4.dist-info/top_level.txt,sha256=BHqsNJQiI6Q72DeypC1IINQJE59SLhU4nllbQjgJi9g,12
366
- cartography-0.100.0rc4.dist-info/RECORD,,
363
+ cartography-0.101.0rc2.dist-info/licenses/LICENSE,sha256=kvLEBRYaQ1RvUni6y7Ti9uHeooqnjPoo6n_-0JO1ETc,11351
364
+ cartography-0.101.0rc2.dist-info/METADATA,sha256=Hw5knSxZNezo5gXVd5_2dMrhDDP0Vki78Nb48XgQHW0,11874
365
+ cartography-0.101.0rc2.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
366
+ cartography-0.101.0rc2.dist-info/entry_points.txt,sha256=GVIAWD0o0_K077qMA_k1oZU4v-M0a8GLKGJR8tZ-qH8,112
367
+ cartography-0.101.0rc2.dist-info/top_level.txt,sha256=BHqsNJQiI6Q72DeypC1IINQJE59SLhU4nllbQjgJi9g,12
368
+ cartography-0.101.0rc2.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (76.0.0)
2
+ Generator: setuptools (78.1.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -1,10 +0,0 @@
1
- {
2
- "name": "EC2 Instances assume IAM roles",
3
- "statements": [
4
- {
5
- "__comment": "Create STS_ASSUMEROLE_ALLOW realtionship from ec2 instance to the associated iaminstance iam role",
6
- "query":"MATCH (aa:AWSAccount)-[:RESOURCE]->(i:EC2Instance)\nWITH SPLIT(i.iaminstanceprofile, '/')[-1] AS role_name, aa, i\nMATCH (aa)-[:RESOURCE]->(r:AWSRole)\nWHERE r.arn ENDS WITH role_name\nMERGE (i)-[:STS_ASSUMEROLE_ALLOW]->(r)",
7
- "iterative": false
8
- }
9
- ]
10
- }
@@ -1,10 +0,0 @@
1
- {
2
- "name": "EC2 Instances assume IAM roles",
3
- "statements": [
4
- {
5
- "__comment": "Create STS_ASSUMEROLE_ALLOW realtionships from EC2 instances to the IAM roles they can assume via their iaminstanceprofiles",
6
- "query":"MATCH (aa:AWSAccount)-[:RESOURCE]->(i:EC2Instance)\nWITH SPLIT(i.iaminstanceprofile, '/')[-1] AS role_name, aa, i\nMATCH (aa)-[:RESOURCE]->(r:AWSRole)\nWHERE r.arn ENDS WITH role_name\nMERGE (i)-[:STS_ASSUMEROLE_ALLOW]->(r)",
7
- "iterative": false
8
- }
9
- ]
10
- }