cartography 0.100.0rc4__py3-none-any.whl → 0.101.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of cartography might be problematic. Click here for more details.
- cartography/_version.py +2 -2
- cartography/data/jobs/scoped_analysis/aws_ec2_iaminstanceprofile.json +15 -0
- cartography/graph/querybuilder.py +9 -1
- cartography/graph/statement.py +10 -10
- cartography/intel/aws/__init__.py +5 -1
- cartography/intel/aws/ec2/launch_templates.py +22 -6
- cartography/intel/aws/iam_instance_profiles.py +73 -0
- cartography/intel/aws/resources.py +4 -1
- cartography/intel/crowdstrike/endpoints.py +1 -1
- cartography/intel/gcp/__init__.py +32 -9
- cartography/intel/gsuite/__init__.py +8 -1
- cartography/models/aws/ec2/instances.py +17 -0
- cartography/models/aws/iam/__init__.py +0 -0
- cartography/models/aws/iam/instanceprofile.py +67 -0
- cartography/models/core/common.py +37 -6
- {cartography-0.100.0rc4.dist-info → cartography-0.101.0.dist-info}/METADATA +7 -5
- {cartography-0.100.0rc4.dist-info → cartography-0.101.0.dist-info}/RECORD +21 -19
- {cartography-0.100.0rc4.dist-info → cartography-0.101.0.dist-info}/WHEEL +1 -1
- cartography/data/jobs/analysis/aws_ec2_iaminstance.json +0 -10
- cartography/data/jobs/analysis/aws_ec2_iaminstanceprofile.json +0 -10
- {cartography-0.100.0rc4.dist-info → cartography-0.101.0.dist-info}/entry_points.txt +0 -0
- {cartography-0.100.0rc4.dist-info → cartography-0.101.0.dist-info/licenses}/LICENSE +0 -0
- {cartography-0.100.0rc4.dist-info → cartography-0.101.0.dist-info}/top_level.txt +0 -0
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.
|
|
21
|
-
__version_tuple__ = version_tuple = (0,
|
|
20
|
+
__version__ = version = '0.101.0'
|
|
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(
|
|
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)
|
cartography/graph/statement.py
CHANGED
|
@@ -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)
|
|
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.
|
|
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
|
-
#
|
|
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
|
|
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
|
-
|
|
125
|
+
summary: neo4j.ResultSummary = session.write_transaction(self._run_noniterative)
|
|
122
126
|
|
|
123
|
-
|
|
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
|
-
|
|
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
|
|
43
|
-
|
|
44
|
-
|
|
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,
|
|
@@ -34,8 +34,8 @@ def load_host_data(
|
|
|
34
34
|
UNWIND $Hosts AS host
|
|
35
35
|
MERGE (h:CrowdstrikeHost{id: host.device_id})
|
|
36
36
|
ON CREATE SET h.cid = host.cid,
|
|
37
|
-
h.cid = host.cid,
|
|
38
37
|
h.instance_id = host.instance_id,
|
|
38
|
+
h.serial_number = host.serial_number,
|
|
39
39
|
h.firstseen = timestamp()
|
|
40
40
|
SET h.status = host.status,
|
|
41
41
|
h.hostname = host.hostname,
|
|
@@ -3,6 +3,7 @@ import logging
|
|
|
3
3
|
from collections import namedtuple
|
|
4
4
|
from typing import Dict
|
|
5
5
|
from typing import List
|
|
6
|
+
from typing import Optional
|
|
6
7
|
from typing import Set
|
|
7
8
|
|
|
8
9
|
import googleapiclient.discovery
|
|
@@ -254,6 +255,30 @@ def _sync_single_project_dns(
|
|
|
254
255
|
dns.sync(neo4j_session, dns_cred, project_id, gcp_update_tag, common_job_parameters)
|
|
255
256
|
|
|
256
257
|
|
|
258
|
+
def _sync_single_project_iam(
|
|
259
|
+
neo4j_session: neo4j.Session,
|
|
260
|
+
resources: Resource,
|
|
261
|
+
project_id: str,
|
|
262
|
+
gcp_update_tag: int,
|
|
263
|
+
common_job_parameters: Dict,
|
|
264
|
+
) -> None:
|
|
265
|
+
"""
|
|
266
|
+
Handles graph sync for a single GCP project's IAM resources.
|
|
267
|
+
:param neo4j_session: The Neo4j session
|
|
268
|
+
:param resources: namedtuple of the GCP resource objects
|
|
269
|
+
:param project_id: The project ID number to sync. See the `projectId` field in
|
|
270
|
+
https://cloud.google.com/resource-manager/reference/rest/v1/projects
|
|
271
|
+
:param gcp_update_tag: The timestamp value to set our new Neo4j nodes with
|
|
272
|
+
:param common_job_parameters: Other parameters sent to Neo4j
|
|
273
|
+
:return: Nothing
|
|
274
|
+
"""
|
|
275
|
+
# Determine if IAM service is enabled
|
|
276
|
+
enabled_services = _services_enabled_on_project(resources.serviceusage, project_id)
|
|
277
|
+
iam_cred = _get_iam_resource(get_gcp_credentials())
|
|
278
|
+
if service_names.iam in enabled_services:
|
|
279
|
+
iam.sync(neo4j_session, iam_cred, project_id, gcp_update_tag, common_job_parameters)
|
|
280
|
+
|
|
281
|
+
|
|
257
282
|
def _sync_multiple_projects(
|
|
258
283
|
neo4j_session: neo4j.Session, resources: Resource, projects: List[Dict],
|
|
259
284
|
gcp_update_tag: int, common_job_parameters: Dict,
|
|
@@ -300,17 +325,11 @@ def _sync_multiple_projects(
|
|
|
300
325
|
for project in projects:
|
|
301
326
|
project_id = project['projectId']
|
|
302
327
|
logger.info("Syncing GCP project %s for IAM", project_id)
|
|
303
|
-
|
|
304
|
-
neo4j_session,
|
|
305
|
-
resources.iam,
|
|
306
|
-
project_id,
|
|
307
|
-
gcp_update_tag,
|
|
308
|
-
common_job_parameters,
|
|
309
|
-
)
|
|
328
|
+
_sync_single_project_iam(neo4j_session, resources, project_id, gcp_update_tag, common_job_parameters)
|
|
310
329
|
|
|
311
330
|
|
|
312
331
|
@timeit
|
|
313
|
-
def get_gcp_credentials() -> GoogleCredentials:
|
|
332
|
+
def get_gcp_credentials() -> Optional[GoogleCredentials]:
|
|
314
333
|
"""
|
|
315
334
|
Gets access tokens for GCP API access.
|
|
316
335
|
:param: None
|
|
@@ -320,6 +339,7 @@ def get_gcp_credentials() -> GoogleCredentials:
|
|
|
320
339
|
# Explicitly use Application Default Credentials.
|
|
321
340
|
# See https://google-auth.readthedocs.io/en/master/user-guide.html#application-default-credentials
|
|
322
341
|
credentials, project_id = default()
|
|
342
|
+
return credentials
|
|
323
343
|
except DefaultCredentialsError as e:
|
|
324
344
|
logger.debug("Error occurred calling GoogleCredentials.get_application_default().", exc_info=True)
|
|
325
345
|
logger.error(
|
|
@@ -331,7 +351,7 @@ def get_gcp_credentials() -> GoogleCredentials:
|
|
|
331
351
|
),
|
|
332
352
|
e,
|
|
333
353
|
)
|
|
334
|
-
|
|
354
|
+
return None
|
|
335
355
|
|
|
336
356
|
|
|
337
357
|
@timeit
|
|
@@ -349,6 +369,9 @@ def start_gcp_ingestion(neo4j_session: neo4j.Session, config: Config) -> None:
|
|
|
349
369
|
}
|
|
350
370
|
|
|
351
371
|
credentials = get_gcp_credentials()
|
|
372
|
+
if credentials is None:
|
|
373
|
+
logger.warning("Unable to initialize GCP credentials. Skipping module.")
|
|
374
|
+
return
|
|
352
375
|
|
|
353
376
|
resources = _initialize_resources(credentials)
|
|
354
377
|
|
|
@@ -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__)
|
|
@@ -68,6 +67,14 @@ def start_gsuite_ingestion(neo4j_session: neo4j.Session, config: Config) -> None
|
|
|
68
67
|
|
|
69
68
|
creds: OAuth2Credentials | ServiceAccountCredentials
|
|
70
69
|
if config.gsuite_auth_method == 'delegated': # Legacy delegated method
|
|
70
|
+
if config.gsuite_config is None or not os.path.isfile(config.gsuite_config):
|
|
71
|
+
logger.warning(
|
|
72
|
+
(
|
|
73
|
+
"The GSuite config file is not set or is not a valid file."
|
|
74
|
+
"Skipping GSuite ingestion."
|
|
75
|
+
),
|
|
76
|
+
)
|
|
77
|
+
return
|
|
71
78
|
logger.info('Attempting to authenticate to GSuite using legacy delegated method')
|
|
72
79
|
try:
|
|
73
80
|
creds = service_account.Credentials.from_service_account_file(
|
|
@@ -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
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
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.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: cartography
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.101.0
|
|
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
|
|
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
|
|
@@ -59,10 +59,11 @@ Requires-Dist: moto; extra == "dev"
|
|
|
59
59
|
Requires-Dist: pre-commit; extra == "dev"
|
|
60
60
|
Requires-Dist: pytest>=6.2.4; extra == "dev"
|
|
61
61
|
Requires-Dist: pytest-mock; extra == "dev"
|
|
62
|
-
Requires-Dist: pytest-cov==6.
|
|
62
|
+
Requires-Dist: pytest-cov==6.1.1; 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.
|
|
65
|
+
Requires-Dist: types-requests<2.32.0.20250329; extra == "dev"
|
|
66
|
+
Dynamic: license-file
|
|
66
67
|
|
|
67
68
|

|
|
68
69
|
|
|
@@ -181,6 +182,7 @@ Get started with our [developer documentation](https://cartography-cncf.github.i
|
|
|
181
182
|
1. [MessageBird](https://messagebird.com)
|
|
182
183
|
1. [Cloudanix](https://www.cloudanix.com/)
|
|
183
184
|
1. [Corelight](https://www.corelight.com/)
|
|
185
|
+
1. [SubImage](https://subimage.io)
|
|
184
186
|
1. {Your company here} :-)
|
|
185
187
|
|
|
186
188
|
If your organization uses Cartography, please file a PR and update this list. Say hi on Slack too!
|
|
@@ -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=
|
|
3
|
+
cartography/_version.py,sha256=NYvTyAkYOp9IDdCX_ppdU6S1p6LSJGNSVwwkd3oCJqU,515
|
|
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=
|
|
138
|
-
cartography/graph/statement.py,sha256=
|
|
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=
|
|
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=
|
|
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=
|
|
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
|
|
@@ -203,7 +203,7 @@ cartography/intel/azure/util/credentials.py,sha256=99PjTs0vZ2iu0tHD7TohN1VJYjuXY
|
|
|
203
203
|
cartography/intel/bigfix/__init__.py,sha256=3LoDCm01VNNaDRGsRiykJm1GJgUQ8zI1HO6NodLFVIA,1058
|
|
204
204
|
cartography/intel/bigfix/computers.py,sha256=HAwA-muDBLu2xkFFL2Ae-xjCH1gxKKwxGMZM4BE_Qdo,5909
|
|
205
205
|
cartography/intel/crowdstrike/__init__.py,sha256=dAtgI-0vZAQZ3cTFQhMEzzt7aqiNSNuiIYiw0qbut-I,1896
|
|
206
|
-
cartography/intel/crowdstrike/endpoints.py,sha256=
|
|
206
|
+
cartography/intel/crowdstrike/endpoints.py,sha256=1Sn8jC5YARKbec-idUZniqCTLldde5ymwpBzSeGZVoM,3835
|
|
207
207
|
cartography/intel/crowdstrike/spotlight.py,sha256=yNhj44-RYF6ubck-hHMKhKiNU0fCfhQf4Oagopc31EM,4754
|
|
208
208
|
cartography/intel/crowdstrike/util.py,sha256=gfJ6Ptr6YdbBS9Qj9a_-Jc-IJroADDRcXqjh5TW0qXE,277
|
|
209
209
|
cartography/intel/cve/__init__.py,sha256=u9mv5O_qkSLmdhLhLm1qbwmhoeLQ3A3fQTjNyLQpEyI,3656
|
|
@@ -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=
|
|
223
|
+
cartography/intel/gcp/__init__.py,sha256=sZHPfDCPZFCE5d6aj20Ow4AC0vrFxV7RCn_cMinCDmI,17650
|
|
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=
|
|
235
|
+
cartography/intel/gsuite/__init__.py,sha256=S8LxQxQ6CP7g2U5x-XnJFz17OmAgvGdzyhA-CZqg-_g,5724
|
|
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=
|
|
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=
|
|
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.
|
|
362
|
-
cartography-0.
|
|
363
|
-
cartography-0.
|
|
364
|
-
cartography-0.
|
|
365
|
-
cartography-0.
|
|
366
|
-
cartography-0.
|
|
363
|
+
cartography-0.101.0.dist-info/licenses/LICENSE,sha256=kvLEBRYaQ1RvUni6y7Ti9uHeooqnjPoo6n_-0JO1ETc,11351
|
|
364
|
+
cartography-0.101.0.dist-info/METADATA,sha256=Q9WdVqqbg4d2FtLj7xGJhTjQuDKiXrGVX4J2oaoLB2w,11906
|
|
365
|
+
cartography-0.101.0.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
|
|
366
|
+
cartography-0.101.0.dist-info/entry_points.txt,sha256=GVIAWD0o0_K077qMA_k1oZU4v-M0a8GLKGJR8tZ-qH8,112
|
|
367
|
+
cartography-0.101.0.dist-info/top_level.txt,sha256=BHqsNJQiI6Q72DeypC1IINQJE59SLhU4nllbQjgJi9g,12
|
|
368
|
+
cartography-0.101.0.dist-info/RECORD,,
|
|
@@ -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
|
-
}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|