cartography 0.97.0__py3-none-any.whl → 0.98.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 +16 -0
- cartography/client/core/tx.py +3 -0
- cartography/data/indexes.cypher +0 -4
- cartography/data/jobs/analysis/aws_ec2_asset_exposure.json +17 -2
- cartography/data/jobs/analysis/gcp_compute_asset_inet_exposure.json +1 -1
- cartography/graph/cleanupbuilder.py +47 -4
- cartography/intel/aws/ec2/instances.py +5 -4
- cartography/intel/aws/ec2/key_pairs.py +44 -35
- cartography/intel/aws/identitycenter.py +3 -20
- cartography/intel/aws/resourcegroupstaggingapi.py +3 -0
- cartography/intel/gcp/__init__.py +6 -6
- cartography/intel/github/users.py +10 -3
- cartography/intel/gsuite/__init__.py +29 -23
- cartography/intel/gsuite/api.py +2 -2
- cartography/models/aws/ec2/{keypairs.py → keypair.py} +13 -23
- cartography/models/aws/ec2/keypair_instance.py +69 -0
- cartography/util.py +2 -6
- cartography-0.98.0.dist-info/METADATA +187 -0
- {cartography-0.97.0.dist-info → cartography-0.98.0.dist-info}/RECORD +23 -22
- {cartography-0.97.0.dist-info → cartography-0.98.0.dist-info}/WHEEL +1 -1
- cartography/data/jobs/cleanup/github_org_and_users_cleanup.json +0 -28
- cartography-0.97.0.dist-info/METADATA +0 -53
- {cartography-0.97.0.dist-info → cartography-0.98.0.dist-info}/LICENSE +0 -0
- {cartography-0.97.0.dist-info → cartography-0.98.0.dist-info}/entry_points.txt +0 -0
- {cartography-0.97.0.dist-info → cartography-0.98.0.dist-info}/top_level.txt +0 -0
cartography/_version.py
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# file generated by setuptools_scm
|
|
2
|
+
# don't change, don't track in version control
|
|
3
|
+
TYPE_CHECKING = False
|
|
4
|
+
if TYPE_CHECKING:
|
|
5
|
+
from typing import Tuple, Union
|
|
6
|
+
VERSION_TUPLE = Tuple[Union[int, str], ...]
|
|
7
|
+
else:
|
|
8
|
+
VERSION_TUPLE = object
|
|
9
|
+
|
|
10
|
+
version: str
|
|
11
|
+
__version__: str
|
|
12
|
+
__version_tuple__: VERSION_TUPLE
|
|
13
|
+
version_tuple: VERSION_TUPLE
|
|
14
|
+
|
|
15
|
+
__version__ = version = '0.98.0'
|
|
16
|
+
__version_tuple__ = version_tuple = (0, 98, 0)
|
cartography/client/core/tx.py
CHANGED
|
@@ -249,6 +249,9 @@ def load(
|
|
|
249
249
|
:param kwargs: Allows additional keyword args to be supplied to the Neo4j query.
|
|
250
250
|
:return: None
|
|
251
251
|
"""
|
|
252
|
+
if len(dict_list) == 0:
|
|
253
|
+
# If there is no data to load, save some time.
|
|
254
|
+
return
|
|
252
255
|
ensure_indexes(neo4j_session, node_schema)
|
|
253
256
|
ingestion_query = build_ingestion_query(node_schema)
|
|
254
257
|
load_graph_data(neo4j_session, ingestion_query, dict_list, **kwargs)
|
cartography/data/indexes.cypher
CHANGED
|
@@ -161,12 +161,8 @@ CREATE INDEX IF NOT EXISTS FOR (n:GCPSubnet) ON (n.id);
|
|
|
161
161
|
CREATE INDEX IF NOT EXISTS FOR (n:GCPSubnet) ON (n.lastupdated);
|
|
162
162
|
CREATE INDEX IF NOT EXISTS FOR (n:GCPVpc) ON (n.id);
|
|
163
163
|
CREATE INDEX IF NOT EXISTS FOR (n:GCPVpc) ON (n.lastupdated);
|
|
164
|
-
CREATE INDEX IF NOT EXISTS FOR (n:GitHubOrganization) ON (n.id);
|
|
165
|
-
CREATE INDEX IF NOT EXISTS FOR (n:GitHubOrganization) ON (n.lastupdated);
|
|
166
164
|
CREATE INDEX IF NOT EXISTS FOR (n:GitHubRepository) ON (n.id);
|
|
167
165
|
CREATE INDEX IF NOT EXISTS FOR (n:GitHubRepository) ON (n.lastupdated);
|
|
168
|
-
CREATE INDEX IF NOT EXISTS FOR (n:GitHubUser) ON (n.id);
|
|
169
|
-
CREATE INDEX IF NOT EXISTS FOR (n:GitHubUser) ON (n.lastupdated);
|
|
170
166
|
CREATE INDEX IF NOT EXISTS FOR (n:GKECluster) ON (n.id);
|
|
171
167
|
CREATE INDEX IF NOT EXISTS FOR (n:GKECluster) ON (n.lastupdated);
|
|
172
168
|
CREATE INDEX IF NOT EXISTS FOR (n:GSuiteGroup) ON (n.email);
|
|
@@ -1,12 +1,27 @@
|
|
|
1
1
|
{
|
|
2
2
|
"statements": [
|
|
3
3
|
{
|
|
4
|
-
"query": "MATCH (n) where n.exposed_internet IS NOT NULL
|
|
4
|
+
"query": "MATCH (n:AutoScalingGroup) where n.exposed_internet IS NOT NULL WITH n LIMIT $LIMIT_SIZE REMOVE n.exposed_internet, n.exposed_internet_type",
|
|
5
5
|
"iterative": true,
|
|
6
6
|
"iterationsize": 1000
|
|
7
7
|
},
|
|
8
8
|
{
|
|
9
|
-
"query": "MATCH (:
|
|
9
|
+
"query": "MATCH (n:EC2Instance) where n.exposed_internet IS NOT NULL WITH n LIMIT $LIMIT_SIZE REMOVE n.exposed_internet, n.exposed_internet_type",
|
|
10
|
+
"iterative": true,
|
|
11
|
+
"iterationsize": 1000
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
"query": "MATCH (n:LoadBalancer) where n.exposed_internet IS NOT NULL WITH n LIMIT $LIMIT_SIZE REMOVE n.exposed_internet, n.exposed_internet_type",
|
|
15
|
+
"iterative": true,
|
|
16
|
+
"iterationsize": 1000
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
"query": "MATCH (n:LoadBalancerV2) where n.exposed_internet IS NOT NULL WITH n LIMIT $LIMIT_SIZE REMOVE n.exposed_internet, n.exposed_internet_type",
|
|
20
|
+
"iterative": true,
|
|
21
|
+
"iterationsize": 1000
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
"query": "MATCH (:IpRange{id: '0.0.0.0/0'})-[:MEMBER_OF_IP_RULE]->(:IpPermissionInbound)-[:MEMBER_OF_EC2_SECURITY_GROUP]->(group:EC2SecurityGroup)<-[:MEMBER_OF_EC2_SECURITY_GROUP|NETWORK_INTERFACE*..2]-(instance:EC2Instance) WITH instance WHERE (instance.publicipaddress IS NOT NULL) AND (instance.exposed_internet_type IS NULL OR NOT 'direct' IN instance.exposed_internet_type) SET instance.exposed_internet = true, instance.exposed_internet_type = CASE WHEN instance.exposed_internet_type IS NULL THEN ['direct'] WHEN NOT 'direct' IN instance.exposed_internet_type THEN instance.exposed_internet_type + ['direct'] ELSE instance.exposed_internet_type END;",
|
|
10
25
|
"iterative": false
|
|
11
26
|
},
|
|
12
27
|
{
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"statements": [
|
|
3
3
|
{
|
|
4
|
-
"query": "MATCH (n) where n.exposed_internet IS NOT NULL
|
|
4
|
+
"query": "MATCH (n:GCPInstance) where n.exposed_internet IS NOT NULL WITH n LIMIT $LIMIT_SIZE REMOVE n.exposed_internet, n.exposed_internet_type",
|
|
5
5
|
"iterative": true,
|
|
6
6
|
"iterationsize": 1000,
|
|
7
7
|
"__comment__": "Delete exposed_internet off nodes so we can start fresh"
|
|
@@ -16,7 +16,17 @@ def build_cleanup_queries(node_schema: CartographyNodeSchema) -> List[str]:
|
|
|
16
16
|
"""
|
|
17
17
|
Generates queries to clean up stale nodes and relationships from the given CartographyNodeSchema.
|
|
18
18
|
Note that auto-cleanups for a node with no relationships is not currently supported.
|
|
19
|
+
|
|
19
20
|
Algorithm:
|
|
21
|
+
1. If node_schema has no relationships at all, return empty.
|
|
22
|
+
|
|
23
|
+
Otherwise,
|
|
24
|
+
|
|
25
|
+
1. If node_schema doesn't have a sub_resource relationship, generate queries only to clean up its other
|
|
26
|
+
relationships. No nodes will be cleaned up.
|
|
27
|
+
|
|
28
|
+
Otherwise,
|
|
29
|
+
|
|
20
30
|
1. First delete all stale nodes attached to the node_schema's sub resource
|
|
21
31
|
2. Delete all stale node to sub resource relationships
|
|
22
32
|
- We don't expect this to be very common (never for AWS resources, at least), but in case it is possible for an
|
|
@@ -25,11 +35,16 @@ def build_cleanup_queries(node_schema: CartographyNodeSchema) -> List[str]:
|
|
|
25
35
|
:param node_schema: The given CartographyNodeSchema
|
|
26
36
|
:return: A list of Neo4j queries to clean up nodes and relationships.
|
|
27
37
|
"""
|
|
38
|
+
if not node_schema.sub_resource_relationship and not node_schema.other_relationships:
|
|
39
|
+
return []
|
|
40
|
+
|
|
28
41
|
if not node_schema.sub_resource_relationship:
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
42
|
+
queries = []
|
|
43
|
+
other_rels = node_schema.other_relationships.rels if node_schema.other_relationships else []
|
|
44
|
+
for rel in other_rels:
|
|
45
|
+
query = _build_cleanup_rel_query_no_sub_resource(node_schema, rel)
|
|
46
|
+
queries.append(query)
|
|
47
|
+
return queries
|
|
33
48
|
|
|
34
49
|
result = _build_cleanup_node_and_rel_queries(node_schema, node_schema.sub_resource_relationship)
|
|
35
50
|
if node_schema.other_relationships:
|
|
@@ -41,6 +56,34 @@ def build_cleanup_queries(node_schema: CartographyNodeSchema) -> List[str]:
|
|
|
41
56
|
return result
|
|
42
57
|
|
|
43
58
|
|
|
59
|
+
def _build_cleanup_rel_query_no_sub_resource(
|
|
60
|
+
node_schema: CartographyNodeSchema,
|
|
61
|
+
selected_relationship: CartographyRelSchema,
|
|
62
|
+
) -> str:
|
|
63
|
+
"""
|
|
64
|
+
Helper function to delete stale relationships for node_schemas that have no sub resource relationship defined.
|
|
65
|
+
"""
|
|
66
|
+
if node_schema.sub_resource_relationship:
|
|
67
|
+
raise ValueError(
|
|
68
|
+
f'Expected {node_schema.label} to not exist. '
|
|
69
|
+
'This function is intended for node_schemas without sub_resource_relationships.',
|
|
70
|
+
)
|
|
71
|
+
# Ensure the node is attached to the sub resource and delete the node
|
|
72
|
+
query_template = Template(
|
|
73
|
+
"""
|
|
74
|
+
MATCH (n:$node_label)
|
|
75
|
+
$selected_rel_clause
|
|
76
|
+
WHERE r.lastupdated <> $UPDATE_TAG
|
|
77
|
+
WITH r LIMIT $LIMIT_SIZE
|
|
78
|
+
DELETE r;
|
|
79
|
+
""",
|
|
80
|
+
)
|
|
81
|
+
return query_template.safe_substitute(
|
|
82
|
+
node_label=node_schema.label,
|
|
83
|
+
selected_rel_clause=_build_selected_rel_clause(selected_relationship),
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
|
|
44
87
|
def _build_cleanup_node_and_rel_queries(
|
|
45
88
|
node_schema: CartographyNodeSchema,
|
|
46
89
|
selected_relationship: CartographyRelSchema,
|
|
@@ -13,7 +13,7 @@ from cartography.graph.job import GraphJob
|
|
|
13
13
|
from cartography.intel.aws.ec2.util import get_botocore_config
|
|
14
14
|
from cartography.models.aws.ec2.auto_scaling_groups import EC2InstanceAutoScalingGroupSchema
|
|
15
15
|
from cartography.models.aws.ec2.instances import EC2InstanceSchema
|
|
16
|
-
from cartography.models.aws.ec2.
|
|
16
|
+
from cartography.models.aws.ec2.keypair_instance import EC2KeyPairInstanceSchema
|
|
17
17
|
from cartography.models.aws.ec2.networkinterface_instance import EC2NetworkInterfaceInstanceSchema
|
|
18
18
|
from cartography.models.aws.ec2.reservations import EC2ReservationSchema
|
|
19
19
|
from cartography.models.aws.ec2.securitygroup_instance import EC2SecurityGroupInstanceSchema
|
|
@@ -193,16 +193,17 @@ def load_ec2_subnets(
|
|
|
193
193
|
|
|
194
194
|
|
|
195
195
|
@timeit
|
|
196
|
-
def
|
|
196
|
+
def load_ec2_keypair_instances(
|
|
197
197
|
neo4j_session: neo4j.Session,
|
|
198
198
|
key_pair_list: List[Dict[str, Any]],
|
|
199
199
|
region: str,
|
|
200
200
|
current_aws_account_id: str,
|
|
201
201
|
update_tag: int,
|
|
202
202
|
) -> None:
|
|
203
|
+
# Load EC2 keypairs as known by describe-instances.
|
|
203
204
|
load(
|
|
204
205
|
neo4j_session,
|
|
205
|
-
|
|
206
|
+
EC2KeyPairInstanceSchema(),
|
|
206
207
|
key_pair_list,
|
|
207
208
|
Region=region,
|
|
208
209
|
AWS_ID=current_aws_account_id,
|
|
@@ -299,7 +300,7 @@ def load_ec2_instance_data(
|
|
|
299
300
|
load_ec2_instance_nodes(neo4j_session, instance_list, region, current_aws_account_id, update_tag)
|
|
300
301
|
load_ec2_subnets(neo4j_session, subnet_list, region, current_aws_account_id, update_tag)
|
|
301
302
|
load_ec2_security_groups(neo4j_session, sg_list, region, current_aws_account_id, update_tag)
|
|
302
|
-
|
|
303
|
+
load_ec2_keypair_instances(neo4j_session, key_pair_list, region, current_aws_account_id, update_tag)
|
|
303
304
|
load_ec2_network_interfaces(neo4j_session, nic_list, region, current_aws_account_id, update_tag)
|
|
304
305
|
load_ec2_instance_ebs_volumes(neo4j_session, ebs_volumes_list, region, current_aws_account_id, update_tag)
|
|
305
306
|
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import logging
|
|
2
|
+
from typing import Any
|
|
2
3
|
from typing import Dict
|
|
3
|
-
from typing import List
|
|
4
4
|
|
|
5
5
|
import boto3
|
|
6
6
|
import neo4j
|
|
7
7
|
|
|
8
|
-
from .
|
|
8
|
+
from cartography.client.core.tx import load
|
|
9
9
|
from cartography.graph.job import GraphJob
|
|
10
|
-
from cartography.
|
|
10
|
+
from cartography.intel.aws.ec2.util import get_botocore_config
|
|
11
|
+
from cartography.models.aws.ec2.keypair import EC2KeyPairSchema
|
|
11
12
|
from cartography.util import aws_handle_regions
|
|
12
13
|
from cartography.util import timeit
|
|
13
14
|
|
|
@@ -16,42 +17,45 @@ logger = logging.getLogger(__name__)
|
|
|
16
17
|
|
|
17
18
|
@timeit
|
|
18
19
|
@aws_handle_regions
|
|
19
|
-
def get_ec2_key_pairs(boto3_session: boto3.session.Session, region: str) ->
|
|
20
|
+
def get_ec2_key_pairs(boto3_session: boto3.session.Session, region: str) -> list[dict[str, Any]]:
|
|
20
21
|
client = boto3_session.client('ec2', region_name=region, config=get_botocore_config())
|
|
21
22
|
return client.describe_key_pairs()['KeyPairs']
|
|
22
23
|
|
|
23
24
|
|
|
25
|
+
def transform_ec2_key_pairs(
|
|
26
|
+
key_pairs: list[dict[str, Any]],
|
|
27
|
+
region: str,
|
|
28
|
+
current_aws_account_id: str,
|
|
29
|
+
) -> list[dict[str, Any]]:
|
|
30
|
+
transformed_key_pairs = []
|
|
31
|
+
for key_pair in key_pairs:
|
|
32
|
+
key_name = key_pair["KeyName"]
|
|
33
|
+
transformed_key_pairs.append({
|
|
34
|
+
'KeyPairArn': f'arn:aws:ec2:{region}:{current_aws_account_id}:key-pair/{key_name}',
|
|
35
|
+
'KeyName': key_name,
|
|
36
|
+
'KeyFingerprint': key_pair.get("KeyFingerprint"),
|
|
37
|
+
})
|
|
38
|
+
return transformed_key_pairs
|
|
39
|
+
|
|
40
|
+
|
|
24
41
|
@timeit
|
|
25
42
|
def load_ec2_key_pairs(
|
|
26
|
-
|
|
27
|
-
|
|
43
|
+
neo4j_session: neo4j.Session,
|
|
44
|
+
data: list[dict[str, Any]],
|
|
45
|
+
region: str,
|
|
46
|
+
current_aws_account_id: str,
|
|
47
|
+
update_tag: int,
|
|
28
48
|
) -> None:
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
"""
|
|
40
|
-
|
|
41
|
-
for key_pair in data:
|
|
42
|
-
key_name = key_pair["KeyName"]
|
|
43
|
-
key_fingerprint = key_pair.get("KeyFingerprint")
|
|
44
|
-
key_pair_arn = f'arn:aws:ec2:{region}:{current_aws_account_id}:key-pair/{key_name}'
|
|
45
|
-
|
|
46
|
-
neo4j_session.run(
|
|
47
|
-
ingest_key_pair,
|
|
48
|
-
ARN=key_pair_arn,
|
|
49
|
-
KeyName=key_name,
|
|
50
|
-
KeyFingerprint=key_fingerprint,
|
|
51
|
-
AWS_ACCOUNT_ID=current_aws_account_id,
|
|
52
|
-
Region=region,
|
|
53
|
-
update_tag=update_tag,
|
|
54
|
-
)
|
|
49
|
+
# Load EC2 keypairs as known by describe-key-pairs
|
|
50
|
+
logger.info(f"Loading {len(data)} EC2 keypairs for region '{region}' into graph.")
|
|
51
|
+
load(
|
|
52
|
+
neo4j_session,
|
|
53
|
+
EC2KeyPairSchema(),
|
|
54
|
+
data,
|
|
55
|
+
Region=region,
|
|
56
|
+
AWS_ID=current_aws_account_id,
|
|
57
|
+
lastupdated=update_tag,
|
|
58
|
+
)
|
|
55
59
|
|
|
56
60
|
|
|
57
61
|
@timeit
|
|
@@ -61,11 +65,16 @@ def cleanup_ec2_key_pairs(neo4j_session: neo4j.Session, common_job_parameters: D
|
|
|
61
65
|
|
|
62
66
|
@timeit
|
|
63
67
|
def sync_ec2_key_pairs(
|
|
64
|
-
neo4j_session: neo4j.Session,
|
|
65
|
-
|
|
68
|
+
neo4j_session: neo4j.Session,
|
|
69
|
+
boto3_session: boto3.session.Session,
|
|
70
|
+
regions: list[str],
|
|
71
|
+
current_aws_account_id: str,
|
|
72
|
+
update_tag: int,
|
|
73
|
+
common_job_parameters: dict[str, Any],
|
|
66
74
|
) -> None:
|
|
67
75
|
for region in regions:
|
|
68
76
|
logger.info("Syncing EC2 key pairs for region '%s' in account '%s'.", region, current_aws_account_id)
|
|
69
77
|
data = get_ec2_key_pairs(boto3_session, region)
|
|
70
|
-
|
|
78
|
+
transformed_data = transform_ec2_key_pairs(data, region, current_aws_account_id)
|
|
79
|
+
load_ec2_key_pairs(neo4j_session, transformed_data, region, current_aws_account_id, update_tag)
|
|
71
80
|
cleanup_ec2_key_pairs(neo4j_session, common_job_parameters)
|
|
@@ -82,26 +82,6 @@ def get_permission_sets(boto3_session: boto3.session.Session, instance_arn: str,
|
|
|
82
82
|
return permission_sets
|
|
83
83
|
|
|
84
84
|
|
|
85
|
-
@timeit
|
|
86
|
-
def get_permission_set_roles(
|
|
87
|
-
boto3_session: boto3.session.Session,
|
|
88
|
-
instance_arn: str,
|
|
89
|
-
permission_set_arn: str,
|
|
90
|
-
region: str,
|
|
91
|
-
) -> List[Dict]:
|
|
92
|
-
"""
|
|
93
|
-
Get all accounts associated with a given permission set
|
|
94
|
-
"""
|
|
95
|
-
client = boto3_session.client('sso-admin', region_name=region)
|
|
96
|
-
accounts = []
|
|
97
|
-
|
|
98
|
-
paginator = client.get_paginator('list_accounts_for_provisioned_permission_set')
|
|
99
|
-
for page in paginator.paginate(InstanceArn=instance_arn, PermissionSetArn=permission_set_arn):
|
|
100
|
-
accounts.extend(page.get('AccountIds', []))
|
|
101
|
-
|
|
102
|
-
return accounts
|
|
103
|
-
|
|
104
|
-
|
|
105
85
|
@timeit
|
|
106
86
|
def load_permission_sets(
|
|
107
87
|
neo4j_session: neo4j.Session,
|
|
@@ -128,6 +108,7 @@ def load_permission_sets(
|
|
|
128
108
|
|
|
129
109
|
|
|
130
110
|
@timeit
|
|
111
|
+
@aws_handle_regions
|
|
131
112
|
def get_sso_users(
|
|
132
113
|
boto3_session: boto3.session.Session,
|
|
133
114
|
identity_store_id: str,
|
|
@@ -176,6 +157,7 @@ def load_sso_users(
|
|
|
176
157
|
|
|
177
158
|
|
|
178
159
|
@timeit
|
|
160
|
+
@aws_handle_regions
|
|
179
161
|
def get_role_assignments(
|
|
180
162
|
boto3_session: boto3.session.Session,
|
|
181
163
|
users: List[Dict],
|
|
@@ -231,6 +213,7 @@ def load_role_assignments(
|
|
|
231
213
|
)
|
|
232
214
|
|
|
233
215
|
|
|
216
|
+
@timeit
|
|
234
217
|
def cleanup(neo4j_session: neo4j.Session, common_job_parameters: Dict[str, Any]) -> None:
|
|
235
218
|
GraphJob.from_node_schema(AWSIdentityCenterInstanceSchema(), common_job_parameters).run(neo4j_session)
|
|
236
219
|
GraphJob.from_node_schema(AWSPermissionSetSchema(), common_job_parameters).run(neo4j_session)
|
|
@@ -188,6 +188,9 @@ def load_tags(
|
|
|
188
188
|
current_aws_account_id: str,
|
|
189
189
|
aws_update_tag: int,
|
|
190
190
|
) -> None:
|
|
191
|
+
if len(tag_data) == 0:
|
|
192
|
+
# If there is no data to load, save some time.
|
|
193
|
+
return
|
|
191
194
|
for tag_data_batch in batch(tag_data, size=100):
|
|
192
195
|
neo4j_session.write_transaction(
|
|
193
196
|
_load_tags_tx,
|
|
@@ -7,9 +7,10 @@ from typing import Set
|
|
|
7
7
|
|
|
8
8
|
import googleapiclient.discovery
|
|
9
9
|
import neo4j
|
|
10
|
+
from google.auth import default
|
|
11
|
+
from google.auth.credentials import Credentials as GoogleCredentials
|
|
12
|
+
from google.auth.exceptions import DefaultCredentialsError
|
|
10
13
|
from googleapiclient.discovery import Resource
|
|
11
|
-
from oauth2client.client import ApplicationDefaultCredentialsError
|
|
12
|
-
from oauth2client.client import GoogleCredentials
|
|
13
14
|
|
|
14
15
|
from cartography.config import Config
|
|
15
16
|
from cartography.intel.gcp import compute
|
|
@@ -295,10 +296,9 @@ def get_gcp_credentials() -> GoogleCredentials:
|
|
|
295
296
|
"""
|
|
296
297
|
try:
|
|
297
298
|
# Explicitly use Application Default Credentials.
|
|
298
|
-
# See https://
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
except ApplicationDefaultCredentialsError as e:
|
|
299
|
+
# See https://google-auth.readthedocs.io/en/master/user-guide.html#application-default-credentials
|
|
300
|
+
credentials, project_id = default()
|
|
301
|
+
except DefaultCredentialsError as e:
|
|
302
302
|
logger.debug("Error occurred calling GoogleCredentials.get_application_default().", exc_info=True)
|
|
303
303
|
logger.error(
|
|
304
304
|
(
|
|
@@ -8,13 +8,13 @@ from typing import Tuple
|
|
|
8
8
|
import neo4j
|
|
9
9
|
|
|
10
10
|
from cartography.client.core.tx import load
|
|
11
|
+
from cartography.graph.job import GraphJob
|
|
11
12
|
from cartography.intel.github.util import fetch_all
|
|
12
13
|
from cartography.models.github.orgs import GitHubOrganizationSchema
|
|
13
14
|
from cartography.models.github.users import GitHubOrganizationUserSchema
|
|
14
15
|
from cartography.models.github.users import GitHubUnaffiliatedUserSchema
|
|
15
16
|
from cartography.stats import get_stats_client
|
|
16
17
|
from cartography.util import merge_module_sync_metadata
|
|
17
|
-
from cartography.util import run_cleanup_job
|
|
18
18
|
from cartography.util import timeit
|
|
19
19
|
|
|
20
20
|
logger = logging.getLogger(__name__)
|
|
@@ -210,6 +210,13 @@ def load_organization(
|
|
|
210
210
|
)
|
|
211
211
|
|
|
212
212
|
|
|
213
|
+
@timeit
|
|
214
|
+
def cleanup(neo4j_session: neo4j.Session, common_job_parameters: dict[str, Any]) -> None:
|
|
215
|
+
logger.info("Cleaning up GitHub users")
|
|
216
|
+
GraphJob.from_node_schema(GitHubOrganizationUserSchema(), common_job_parameters).run(neo4j_session)
|
|
217
|
+
GraphJob.from_node_schema(GitHubUnaffiliatedUserSchema(), common_job_parameters).run(neo4j_session)
|
|
218
|
+
|
|
219
|
+
|
|
213
220
|
@timeit
|
|
214
221
|
def sync(
|
|
215
222
|
neo4j_session: neo4j.Session,
|
|
@@ -236,8 +243,8 @@ def sync(
|
|
|
236
243
|
neo4j_session, GitHubUnaffiliatedUserSchema(), processed_unaffiliated_user_data, org_data,
|
|
237
244
|
common_job_parameters['UPDATE_TAG'],
|
|
238
245
|
)
|
|
239
|
-
|
|
240
|
-
|
|
246
|
+
cleanup(neo4j_session, common_job_parameters)
|
|
247
|
+
|
|
241
248
|
merge_module_sync_metadata(
|
|
242
249
|
neo4j_session,
|
|
243
250
|
group_type='GitHubOrganization',
|
|
@@ -5,11 +5,14 @@ import os
|
|
|
5
5
|
from collections import namedtuple
|
|
6
6
|
|
|
7
7
|
import googleapiclient.discovery
|
|
8
|
-
import httplib2
|
|
9
8
|
import neo4j
|
|
9
|
+
from google.auth.exceptions import DefaultCredentialsError
|
|
10
|
+
from google.auth.transport.requests import Request
|
|
11
|
+
from google.oauth2 import credentials
|
|
12
|
+
from google.oauth2 import service_account
|
|
13
|
+
from google.oauth2.credentials import Credentials as OAuth2Credentials
|
|
14
|
+
from google.oauth2.service_account import Credentials as ServiceAccountCredentials
|
|
10
15
|
from googleapiclient.discovery import Resource
|
|
11
|
-
from oauth2client.client import ApplicationDefaultCredentialsError
|
|
12
|
-
from oauth2client.client import GoogleCredentials
|
|
13
16
|
|
|
14
17
|
from cartography.config import Config
|
|
15
18
|
from cartography.intel.gsuite import api
|
|
@@ -26,21 +29,21 @@ logger = logging.getLogger(__name__)
|
|
|
26
29
|
Resources = namedtuple('Resources', 'admin')
|
|
27
30
|
|
|
28
31
|
|
|
29
|
-
def _get_admin_resource(credentials:
|
|
32
|
+
def _get_admin_resource(credentials: OAuth2Credentials | ServiceAccountCredentials) -> Resource:
|
|
30
33
|
"""
|
|
31
34
|
Instantiates a Google API resource object to call the Google API.
|
|
32
35
|
Used to pull users and groups. See https://developers.google.com/admin-sdk/directory/v1/guides/manage-users
|
|
33
36
|
|
|
34
|
-
:param credentials: The
|
|
37
|
+
:param credentials: The credentials object
|
|
35
38
|
:return: An admin api resource object
|
|
36
39
|
"""
|
|
37
40
|
return googleapiclient.discovery.build('admin', 'directory_v1', credentials=credentials, cache_discovery=False)
|
|
38
41
|
|
|
39
42
|
|
|
40
|
-
def _initialize_resources(credentials:
|
|
43
|
+
def _initialize_resources(credentials: OAuth2Credentials | ServiceAccountCredentials) -> Resources:
|
|
41
44
|
"""
|
|
42
45
|
Create namedtuple of all resource objects necessary for Google API data gathering.
|
|
43
|
-
:param credentials: The
|
|
46
|
+
:param credentials: The credentials object
|
|
44
47
|
:return: namedtuple of all resource objects
|
|
45
48
|
"""
|
|
46
49
|
return Resources(
|
|
@@ -61,14 +64,17 @@ def start_gsuite_ingestion(neo4j_session: neo4j.Session, config: Config) -> None
|
|
|
61
64
|
"UPDATE_TAG": config.update_tag,
|
|
62
65
|
}
|
|
63
66
|
|
|
67
|
+
creds: OAuth2Credentials | ServiceAccountCredentials
|
|
64
68
|
if config.gsuite_auth_method == 'delegated': # Legacy delegated method
|
|
65
69
|
logger.info('Attempting to authenticate to GSuite using legacy delegated method')
|
|
66
70
|
try:
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
71
|
+
creds = service_account.Credentials.from_service_account_file(
|
|
72
|
+
config.gsuite_config,
|
|
73
|
+
scopes=OAUTH_SCOPE,
|
|
74
|
+
)
|
|
75
|
+
creds = creds.with_subject(os.environ.get('GSUITE_DELEGATED_ADMIN'))
|
|
70
76
|
|
|
71
|
-
except
|
|
77
|
+
except DefaultCredentialsError as e:
|
|
72
78
|
logger.error(
|
|
73
79
|
(
|
|
74
80
|
"Unable to initialize GSuite creds. If you don't have GSuite data or don't want to load "
|
|
@@ -83,18 +89,18 @@ def start_gsuite_ingestion(neo4j_session: neo4j.Session, config: Config) -> None
|
|
|
83
89
|
auth_tokens = json.loads(str(base64.b64decode(config.gsuite_config).decode()))
|
|
84
90
|
logger.info('Attempting to authenticate to GSuite using OAuth')
|
|
85
91
|
try:
|
|
86
|
-
|
|
87
|
-
None,
|
|
88
|
-
auth_tokens['client_id'],
|
|
89
|
-
auth_tokens['client_secret'],
|
|
90
|
-
auth_tokens['refresh_token'],
|
|
91
|
-
None,
|
|
92
|
-
auth_tokens['token_uri'],
|
|
93
|
-
|
|
92
|
+
creds = credentials.Credentials(
|
|
93
|
+
token=None,
|
|
94
|
+
client_id=auth_tokens['client_id'],
|
|
95
|
+
client_secret=auth_tokens['client_secret'],
|
|
96
|
+
refresh_token=auth_tokens['refresh_token'],
|
|
97
|
+
expiry=None,
|
|
98
|
+
token_uri=auth_tokens['token_uri'],
|
|
99
|
+
scopes=OAUTH_SCOPE,
|
|
94
100
|
)
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
except
|
|
101
|
+
creds.refresh(Request())
|
|
102
|
+
creds = creds.create_scoped(OAUTH_SCOPE)
|
|
103
|
+
except DefaultCredentialsError as e:
|
|
98
104
|
logger.error(
|
|
99
105
|
(
|
|
100
106
|
"Unable to initialize GSuite creds. If you don't have GSuite data or don't want to load "
|
|
@@ -106,6 +112,6 @@ def start_gsuite_ingestion(neo4j_session: neo4j.Session, config: Config) -> None
|
|
|
106
112
|
)
|
|
107
113
|
return
|
|
108
114
|
|
|
109
|
-
resources = _initialize_resources(
|
|
115
|
+
resources = _initialize_resources(creds)
|
|
110
116
|
api.sync_gsuite_users(neo4j_session, resources.admin, config.update_tag, common_job_parameters)
|
|
111
117
|
api.sync_gsuite_groups(neo4j_session, resources.admin, config.update_tag, common_job_parameters)
|
cartography/intel/gsuite/api.py
CHANGED
|
@@ -41,7 +41,7 @@ def transform_groups(response_objects: List[Dict]) -> List[Dict]:
|
|
|
41
41
|
""" Strips list of API response objects to return list of group objects only
|
|
42
42
|
|
|
43
43
|
:param response_objects:
|
|
44
|
-
:return: list of dictionary objects as defined in /docs/
|
|
44
|
+
:return: list of dictionary objects as defined in /docs/root/modules/gsuite/schema.md
|
|
45
45
|
"""
|
|
46
46
|
groups: List[Dict] = []
|
|
47
47
|
for response_object in response_objects:
|
|
@@ -54,7 +54,7 @@ def transform_groups(response_objects: List[Dict]) -> List[Dict]:
|
|
|
54
54
|
def transform_users(response_objects: List[Dict]) -> List[Dict]:
|
|
55
55
|
""" Strips list of API response objects to return list of group objects only
|
|
56
56
|
:param response_objects:
|
|
57
|
-
:return: list of dictionary objects as defined in /docs/
|
|
57
|
+
:return: list of dictionary objects as defined in /docs/root/modules/gsuite/schema.md
|
|
58
58
|
"""
|
|
59
59
|
users: List[Dict] = []
|
|
60
60
|
for response_object in response_objects:
|
|
@@ -3,19 +3,23 @@ from dataclasses import dataclass
|
|
|
3
3
|
from cartography.models.core.common import PropertyRef
|
|
4
4
|
from cartography.models.core.nodes import CartographyNodeProperties
|
|
5
5
|
from cartography.models.core.nodes import CartographyNodeSchema
|
|
6
|
+
from cartography.models.core.nodes import ExtraNodeLabels
|
|
6
7
|
from cartography.models.core.relationships import CartographyRelProperties
|
|
7
8
|
from cartography.models.core.relationships import CartographyRelSchema
|
|
8
9
|
from cartography.models.core.relationships import LinkDirection
|
|
9
10
|
from cartography.models.core.relationships import make_target_node_matcher
|
|
10
|
-
from cartography.models.core.relationships import OtherRelationships
|
|
11
11
|
from cartography.models.core.relationships import TargetNodeMatcher
|
|
12
12
|
|
|
13
13
|
|
|
14
14
|
@dataclass(frozen=True)
|
|
15
15
|
class EC2KeyPairNodeProperties(CartographyNodeProperties):
|
|
16
|
+
"""
|
|
17
|
+
Properties for EC2 keypairs from describe-key-pairs
|
|
18
|
+
"""
|
|
16
19
|
id: PropertyRef = PropertyRef('KeyPairArn')
|
|
17
20
|
arn: PropertyRef = PropertyRef('KeyPairArn', extra_index=True)
|
|
18
21
|
keyname: PropertyRef = PropertyRef('KeyName')
|
|
22
|
+
keyfingerprint: PropertyRef = PropertyRef('KeyFingerprint')
|
|
19
23
|
region: PropertyRef = PropertyRef('Region', set_in_kwargs=True)
|
|
20
24
|
lastupdated: PropertyRef = PropertyRef('lastupdated', set_in_kwargs=True)
|
|
21
25
|
|
|
@@ -27,38 +31,24 @@ class EC2KeyPairToAwsAccountRelProperties(CartographyRelProperties):
|
|
|
27
31
|
|
|
28
32
|
@dataclass(frozen=True)
|
|
29
33
|
class EC2KeyPairToAWSAccount(CartographyRelSchema):
|
|
34
|
+
"""
|
|
35
|
+
Relationship schema for EC2 keypairs to AWS Accounts
|
|
36
|
+
"""
|
|
30
37
|
target_node_label: str = 'AWSAccount'
|
|
31
38
|
target_node_matcher: TargetNodeMatcher = make_target_node_matcher(
|
|
32
39
|
{'id': PropertyRef('AWS_ID', set_in_kwargs=True)},
|
|
33
40
|
)
|
|
34
41
|
direction: LinkDirection = LinkDirection.INWARD
|
|
35
|
-
rel_label: str =
|
|
42
|
+
rel_label: str = 'RESOURCE'
|
|
36
43
|
properties: EC2KeyPairToAwsAccountRelProperties = EC2KeyPairToAwsAccountRelProperties()
|
|
37
44
|
|
|
38
45
|
|
|
39
|
-
@dataclass(frozen=True)
|
|
40
|
-
class EC2KeyPairToEC2InstanceRelProperties(CartographyRelProperties):
|
|
41
|
-
lastupdated: PropertyRef = PropertyRef('lastupdated', set_in_kwargs=True)
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
@dataclass(frozen=True)
|
|
45
|
-
class EC2KeyPairToEC2Instance(CartographyRelSchema):
|
|
46
|
-
target_node_label: str = 'EC2Instance'
|
|
47
|
-
target_node_matcher: TargetNodeMatcher = make_target_node_matcher(
|
|
48
|
-
{'id': PropertyRef('InstanceId')},
|
|
49
|
-
)
|
|
50
|
-
direction: LinkDirection = LinkDirection.OUTWARD
|
|
51
|
-
rel_label: str = "SSH_LOGIN_TO"
|
|
52
|
-
properties: EC2KeyPairToEC2InstanceRelProperties = EC2KeyPairToEC2InstanceRelProperties()
|
|
53
|
-
|
|
54
|
-
|
|
55
46
|
@dataclass(frozen=True)
|
|
56
47
|
class EC2KeyPairSchema(CartographyNodeSchema):
|
|
48
|
+
"""
|
|
49
|
+
Schema for EC2 keypairs from describe-key-pairs
|
|
50
|
+
"""
|
|
57
51
|
label: str = 'EC2KeyPair'
|
|
52
|
+
extra_node_labels: ExtraNodeLabels = ExtraNodeLabels(['KeyPair'])
|
|
58
53
|
properties: EC2KeyPairNodeProperties = EC2KeyPairNodeProperties()
|
|
59
54
|
sub_resource_relationship: EC2KeyPairToAWSAccount = EC2KeyPairToAWSAccount()
|
|
60
|
-
other_relationships: OtherRelationships = OtherRelationships(
|
|
61
|
-
[
|
|
62
|
-
EC2KeyPairToEC2Instance(),
|
|
63
|
-
],
|
|
64
|
-
)
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
|
|
3
|
+
from cartography.models.core.common import PropertyRef
|
|
4
|
+
from cartography.models.core.nodes import CartographyNodeProperties
|
|
5
|
+
from cartography.models.core.nodes import CartographyNodeSchema
|
|
6
|
+
from cartography.models.core.nodes import ExtraNodeLabels
|
|
7
|
+
from cartography.models.core.relationships import CartographyRelProperties
|
|
8
|
+
from cartography.models.core.relationships import CartographyRelSchema
|
|
9
|
+
from cartography.models.core.relationships import LinkDirection
|
|
10
|
+
from cartography.models.core.relationships import make_target_node_matcher
|
|
11
|
+
from cartography.models.core.relationships import OtherRelationships
|
|
12
|
+
from cartography.models.core.relationships import TargetNodeMatcher
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@dataclass(frozen=True)
|
|
16
|
+
class EC2KeyPairInstanceNodeProperties(CartographyNodeProperties):
|
|
17
|
+
id: PropertyRef = PropertyRef('KeyPairArn')
|
|
18
|
+
arn: PropertyRef = PropertyRef('KeyPairArn', extra_index=True)
|
|
19
|
+
keyname: PropertyRef = PropertyRef('KeyName')
|
|
20
|
+
region: PropertyRef = PropertyRef('Region', set_in_kwargs=True)
|
|
21
|
+
lastupdated: PropertyRef = PropertyRef('lastupdated', set_in_kwargs=True)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@dataclass(frozen=True)
|
|
25
|
+
class EC2KeyPairInstanceToAwsAccountRelProperties(CartographyRelProperties):
|
|
26
|
+
lastupdated: PropertyRef = PropertyRef('lastupdated', set_in_kwargs=True)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@dataclass(frozen=True)
|
|
30
|
+
class EC2KeyPairInstanceToAWSAccount(CartographyRelSchema):
|
|
31
|
+
target_node_label: str = 'AWSAccount'
|
|
32
|
+
target_node_matcher: TargetNodeMatcher = make_target_node_matcher(
|
|
33
|
+
{'id': PropertyRef('AWS_ID', set_in_kwargs=True)},
|
|
34
|
+
)
|
|
35
|
+
direction: LinkDirection = LinkDirection.INWARD
|
|
36
|
+
rel_label: str = "RESOURCE"
|
|
37
|
+
properties: EC2KeyPairInstanceToAwsAccountRelProperties = EC2KeyPairInstanceToAwsAccountRelProperties()
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
@dataclass(frozen=True)
|
|
41
|
+
class EC2KeyPairInstanceToEC2InstanceRelProperties(CartographyRelProperties):
|
|
42
|
+
lastupdated: PropertyRef = PropertyRef('lastupdated', set_in_kwargs=True)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
@dataclass(frozen=True)
|
|
46
|
+
class EC2KeyPairInstanceToEC2Instance(CartographyRelSchema):
|
|
47
|
+
target_node_label: str = 'EC2Instance'
|
|
48
|
+
target_node_matcher: TargetNodeMatcher = make_target_node_matcher(
|
|
49
|
+
{'id': PropertyRef('InstanceId')},
|
|
50
|
+
)
|
|
51
|
+
direction: LinkDirection = LinkDirection.OUTWARD
|
|
52
|
+
rel_label: str = "SSH_LOGIN_TO"
|
|
53
|
+
properties: EC2KeyPairInstanceToEC2InstanceRelProperties = EC2KeyPairInstanceToEC2InstanceRelProperties()
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
@dataclass(frozen=True)
|
|
57
|
+
class EC2KeyPairInstanceSchema(CartographyNodeSchema):
|
|
58
|
+
"""
|
|
59
|
+
EC2 keypairs as known by describe-instances.
|
|
60
|
+
"""
|
|
61
|
+
label: str = 'EC2KeyPair'
|
|
62
|
+
extra_node_labels: ExtraNodeLabels = ExtraNodeLabels(['KeyPair'])
|
|
63
|
+
properties: EC2KeyPairInstanceNodeProperties = EC2KeyPairInstanceNodeProperties()
|
|
64
|
+
sub_resource_relationship: EC2KeyPairInstanceToAWSAccount = EC2KeyPairInstanceToAWSAccount()
|
|
65
|
+
other_relationships: OtherRelationships = OtherRelationships(
|
|
66
|
+
[
|
|
67
|
+
EC2KeyPairInstanceToEC2Instance(),
|
|
68
|
+
],
|
|
69
|
+
)
|
cartography/util.py
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import asyncio
|
|
2
2
|
import logging
|
|
3
3
|
import re
|
|
4
|
-
import sys
|
|
5
4
|
from functools import partial
|
|
6
5
|
from functools import wraps
|
|
6
|
+
from importlib.resources import open_binary
|
|
7
|
+
from importlib.resources import read_text
|
|
7
8
|
from string import Template
|
|
8
9
|
from typing import Any
|
|
9
10
|
from typing import Awaitable
|
|
@@ -30,11 +31,6 @@ from cartography.stats import get_stats_client
|
|
|
30
31
|
from cartography.stats import ScopedStatsClient
|
|
31
32
|
|
|
32
33
|
|
|
33
|
-
if sys.version_info >= (3, 7):
|
|
34
|
-
from importlib.resources import open_binary, read_text
|
|
35
|
-
else:
|
|
36
|
-
from importlib_resources import open_binary, read_text
|
|
37
|
-
|
|
38
34
|
logger = logging.getLogger(__name__)
|
|
39
35
|
|
|
40
36
|
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: cartography
|
|
3
|
+
Version: 0.98.0
|
|
4
|
+
Summary: Explore assets and their relationships across your technical infrastructure.
|
|
5
|
+
Maintainer: Cartography Contributors
|
|
6
|
+
License: apache2
|
|
7
|
+
Project-URL: Homepage, https://cartography-cncf.github.io/cartography
|
|
8
|
+
Project-URL: Documentation, https://cartography-cncf.github.io/cartography
|
|
9
|
+
Project-URL: Repository, https://github.com/cartography-cncf/cartography
|
|
10
|
+
Project-URL: Issues, https://github.com/cartography-cncf/cartography/issues
|
|
11
|
+
Project-URL: Changelog, https://github.com/cartography-cncf/cartography/releases
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: License :: OSI Approved :: Apache Software License
|
|
15
|
+
Classifier: Natural Language :: English
|
|
16
|
+
Classifier: Programming Language :: Python
|
|
17
|
+
Classifier: Programming Language :: Python :: 3
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
19
|
+
Classifier: Topic :: Security
|
|
20
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
21
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
22
|
+
Requires-Python: >=3.10
|
|
23
|
+
Description-Content-Type: text/markdown
|
|
24
|
+
License-File: LICENSE
|
|
25
|
+
Requires-Dist: backoff>=2.1.2
|
|
26
|
+
Requires-Dist: boto3>=1.15.1
|
|
27
|
+
Requires-Dist: botocore>=1.18.1
|
|
28
|
+
Requires-Dist: dnspython>=1.15.0
|
|
29
|
+
Requires-Dist: neo4j<5.0.0,>=4.4.4
|
|
30
|
+
Requires-Dist: policyuniverse>=1.1.0.0
|
|
31
|
+
Requires-Dist: google-api-python-client>=1.7.8
|
|
32
|
+
Requires-Dist: google-auth>=2.37.0
|
|
33
|
+
Requires-Dist: marshmallow>=3.0.0rc7
|
|
34
|
+
Requires-Dist: oci>=2.71.0
|
|
35
|
+
Requires-Dist: okta<1.0.0
|
|
36
|
+
Requires-Dist: pyyaml>=5.3.1
|
|
37
|
+
Requires-Dist: requests>=2.22.0
|
|
38
|
+
Requires-Dist: statsd
|
|
39
|
+
Requires-Dist: packaging
|
|
40
|
+
Requires-Dist: python-digitalocean>=1.16.0
|
|
41
|
+
Requires-Dist: adal>=1.2.4
|
|
42
|
+
Requires-Dist: azure-cli-core>=2.26.0
|
|
43
|
+
Requires-Dist: azure-mgmt-compute>=5.0.0
|
|
44
|
+
Requires-Dist: azure-mgmt-resource>=10.2.0
|
|
45
|
+
Requires-Dist: azure-mgmt-cosmosdb>=6.0.0
|
|
46
|
+
Requires-Dist: msrestazure>=0.6.4
|
|
47
|
+
Requires-Dist: azure-mgmt-storage>=16.0.0
|
|
48
|
+
Requires-Dist: azure-mgmt-sql<=1.0.0
|
|
49
|
+
Requires-Dist: azure-identity>=1.5.0
|
|
50
|
+
Requires-Dist: kubernetes>=22.6.0
|
|
51
|
+
Requires-Dist: pdpyras>=4.3.0
|
|
52
|
+
Requires-Dist: crowdstrike-falconpy>=0.5.1
|
|
53
|
+
Requires-Dist: python-dateutil
|
|
54
|
+
Requires-Dist: xmltodict
|
|
55
|
+
Requires-Dist: duo-client
|
|
56
|
+
Provides-Extra: dev
|
|
57
|
+
Requires-Dist: backoff>=2.1.2; extra == "dev"
|
|
58
|
+
Requires-Dist: moto; extra == "dev"
|
|
59
|
+
Requires-Dist: pre-commit; extra == "dev"
|
|
60
|
+
Requires-Dist: pytest>=6.2.4; extra == "dev"
|
|
61
|
+
Requires-Dist: pytest-mock; extra == "dev"
|
|
62
|
+
Requires-Dist: pytest-cov==2.10.0; extra == "dev"
|
|
63
|
+
Requires-Dist: pytest-rerunfailures; extra == "dev"
|
|
64
|
+
Requires-Dist: types-PyYAML; extra == "dev"
|
|
65
|
+
Requires-Dist: types-requests<2.31.0.7; extra == "dev"
|
|
66
|
+
|
|
67
|
+

|
|
68
|
+
|
|
69
|
+
Cartography is a Python tool that consolidates infrastructure assets and the relationships between them in an intuitive graph view powered by a [Neo4j](https://www.neo4j.com) database.
|
|
70
|
+
|
|
71
|
+

|
|
72
|
+
|
|
73
|
+
## Why Cartography?
|
|
74
|
+
Cartography aims to enable a broad set of exploration and automation scenarios. It is particularly good at exposing otherwise hidden dependency relationships between your service's assets so that you may validate assumptions about security risks.
|
|
75
|
+
|
|
76
|
+
Service owners can generate asset reports, Red Teamers can discover attack paths, and Blue Teamers can identify areas for security improvement. All can benefit from using the graph for manual exploration through a web frontend interface, or in an automated fashion by calling the APIs.
|
|
77
|
+
|
|
78
|
+
Cartography is not the only [security](https://github.com/dowjones/hammer) [graph](https://github.com/BloodHoundAD/BloodHound) [tool](https://github.com/Netflix/security_monkey) [out](https://github.com/vysecurity/ANGRYPUPPY) [there](https://github.com/duo-labs/cloudmapper), but it differentiates itself by being fully-featured yet generic and [extensible](https://cartography-cncf.github.io/cartography/dev/writing-analysis-jobs.html) enough to help make anyone better understand their risk exposure, regardless of what platforms they use. Rather than being focused on one core scenario or attack vector like the other linked tools, Cartography focuses on flexibility and exploration.
|
|
79
|
+
|
|
80
|
+
You can learn more about the story behind Cartography in our [presentation at BSidesSF 2019](https://www.youtube.com/watch?v=ZukUmZSKSek).
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
## Supported platforms
|
|
84
|
+
|
|
85
|
+
- [Amazon Web Services](https://cartography-cncf.github.io/cartography/modules/aws/index.html) - API Gateway, Config, EC2, ECS, ECR, Elasticsearch, Elastic Kubernetes Service (EKS), DynamoDB, IAM, Inspector, KMS, Lambda, RDS, Redshift, Route53, S3, Secrets Manager, Security Hub, SQS, SSM, STS, Tags
|
|
86
|
+
- [Google Cloud Platform](https://cartography-cncf.github.io/cartography/modules/gcp/index.html) - Cloud Resource Manager, Compute, DNS, Storage, Google Kubernetes Engine
|
|
87
|
+
- [Google GSuite](https://cartography-cncf.github.io/cartography/modules/gsuite/index.html) - users, groups
|
|
88
|
+
- [Oracle Cloud Infrastructure](docs/setup/config/oci.md) - IAM
|
|
89
|
+
- [Okta](https://cartography-cncf.github.io/cartography/modules/okta/index.html) - users, groups, organizations, roles, applications, factors, trusted origins, reply URIs
|
|
90
|
+
- [GitHub](https://cartography-cncf.github.io/cartography/modules/github/index.html) - repos, branches, users, teams
|
|
91
|
+
- [DigitalOcean](https://cartography-cncf.github.io/cartography/modules/digitalocean/index.html)
|
|
92
|
+
- [Microsoft Azure](https://cartography-cncf.github.io/cartography/modules/azure/index.html) - CosmosDB, SQL, Storage, Virtual Machine
|
|
93
|
+
- [Kubernetes](https://cartography-cncf.github.io/cartography/modules/kubernetes/index.html) - Cluster, Namespace, Service, Pod, Container
|
|
94
|
+
- [PagerDuty](https://cartography-cncf.github.io/cartography/modules/pagerduty/index.html) - Users, teams, services, schedules, escalation policies, integrations, vendors
|
|
95
|
+
- [Crowdstrike Falcon](https://cartography-cncf.github.io/cartography/modules/crowdstrike/index.html) - Hosts, Spotlight vulnerabilities, CVEs
|
|
96
|
+
- [NIST CVE](https://cartography-cncf.github.io/cartography/modules/cve/index.html) - Common Vulnerabilities and Exposures (CVE) data from NIST database
|
|
97
|
+
- [Lastpass](https://cartography-cncf.github.io/cartography/modules/lastpass/index.html) - users
|
|
98
|
+
- [BigFix](https://cartography-cncf.github.io/cartography/modules/bigfix/index.html) - Computers
|
|
99
|
+
- [Duo](https://cartography-cncf.github.io/cartography/modules/duo/index.html) - Users, Groups, Endpoints
|
|
100
|
+
- [Kandji](https://cartography-cncf.github.io/cartography/modules/kandji/index.html) - Devices
|
|
101
|
+
- [SnipeIT](https://cartography-cncf.github.io/cartography/modules/snipeit/index.html) - Users, Assets
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
## Philosophy
|
|
105
|
+
Here are some points that can help you decide if adopting Cartography is a good fit for your problem.
|
|
106
|
+
|
|
107
|
+
### What Cartography is
|
|
108
|
+
- A simple Python script that pulls data from multiple providers and writes it to a Neo4j graph database in batches.
|
|
109
|
+
- A powerful analysis tool that captures the current snapshot of the environment, building a uniquely useful inventory where you can ask complex questions such as:
|
|
110
|
+
- Which identities have access to which datastores?
|
|
111
|
+
- What are the cross-tenant permission relationships in the environment?
|
|
112
|
+
- What are the network paths in and out of the environment?
|
|
113
|
+
- What are the backup policies for my datastores?
|
|
114
|
+
- Battle-tested in production by [many companies](#who-uses-cartography).
|
|
115
|
+
- Straightforward to extend with your own custom plugins.
|
|
116
|
+
- Provides a useful data-plane that you can build automation and CSPM (Cloud Security Posture Management) applications on top of.
|
|
117
|
+
|
|
118
|
+
### What Cartography is not
|
|
119
|
+
- A near-real time capability.
|
|
120
|
+
- Cartography is not designed for very fast updates. Cartography writes to the database in a batches (not streamed).
|
|
121
|
+
- Cartography is also limited by how most upstream sources only provide APIs to retrieve assets in a batched manner.
|
|
122
|
+
- By itself, Cartography does not capture data changes over time.
|
|
123
|
+
- Although we do include a [drift detection](https://cartography-cncf.github.io/cartography/usage/drift-detect.html) feature.
|
|
124
|
+
- It's also possible to implement other processes in your Cartography installation to make this happen.
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
## Install and configure
|
|
128
|
+
|
|
129
|
+
### Trying out Cartography on a test machine
|
|
130
|
+
Start [here](https://cartography-cncf.github.io/cartography/install.html) to set up a test graph and get data into it.
|
|
131
|
+
|
|
132
|
+
### Setting up Cartography in production
|
|
133
|
+
When you are ready to try it in production, read [here](https://cartography-cncf.github.io/cartography/ops.html) for recommendations on getting cartography spun up in your environment.
|
|
134
|
+
|
|
135
|
+
## Usage
|
|
136
|
+
|
|
137
|
+
### Querying the database directly
|
|
138
|
+
|
|
139
|
+

|
|
140
|
+
|
|
141
|
+
Now that data is in the graph, you can quickly start with our [querying tutorial](https://cartography-cncf.github.io/cartography/usage/tutorial.html). Our [data schema](https://cartography-cncf.github.io/cartography/usage/schema.html) is a helpful reference when you get stuck.
|
|
142
|
+
|
|
143
|
+
### Building applications around Cartography
|
|
144
|
+
Directly querying Neo4j is already very useful as a sort of "swiss army knife" for security data problems, but you can also build applications and data pipelines around Cartography. View this doc on [applications](https://cartography-cncf.github.io/cartography/usage/applications.html).
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
## Community
|
|
148
|
+
|
|
149
|
+
- Hang out with us on Slack: Join the CNCF Slack workspace [here](https://communityinviter.com/apps/cloud-native/cncf), and then join the `#cartography` channel.
|
|
150
|
+
- Talk to us and see what we're working on at our [monthly community meeting](https://calendar.google.com/calendar/embed?src=lyft.com_p10o6ceuiieq9sqcn1ef61v1io%40group.calendar.google.com&ctz=America%2FLos_Angeles).
|
|
151
|
+
- Meeting minutes are [here](https://docs.google.com/document/d/1VyRKmB0dpX185I15BmNJZpfAJ_Ooobwz0U1WIhjDxvw).
|
|
152
|
+
- Recorded videos are posted [here](https://www.youtube.com/playlist?list=PLMga2YJvAGzidUWJB_fnG7EHI4wsDDsE1).
|
|
153
|
+
|
|
154
|
+
## License
|
|
155
|
+
|
|
156
|
+
This project is licensed under the [Apache 2.0 License](LICENSE).
|
|
157
|
+
|
|
158
|
+
## Contributing
|
|
159
|
+
Thank you for considering contributing to Cartography!
|
|
160
|
+
|
|
161
|
+
### Code of conduct
|
|
162
|
+
All contributors and participants of this project must follow the [CNCF code of conduct](https://github.com/cncf/foundation/blob/main/code-of-conduct.md).
|
|
163
|
+
|
|
164
|
+
### Bug reports and feature requests and discussions
|
|
165
|
+
Submit a GitHub issue to report a bug or request a new feature. If we decide that the issue needs more discussion - usually because the scope is too large or we need to make careful decision - we will convert the issue to a [GitHub Discussion](https://github.com/lyft/cartography/discussions).
|
|
166
|
+
|
|
167
|
+
### Developing Cartography
|
|
168
|
+
|
|
169
|
+
Get started with our [developer documentation](https://cartography-cncf.github.io/cartography/dev/developer-guide.html). Please feel free to submit your own PRs to update documentation if you've found a better way to explain something.
|
|
170
|
+
|
|
171
|
+
## Who uses Cartography?
|
|
172
|
+
|
|
173
|
+
1. [Lyft](https://www.lyft.com)
|
|
174
|
+
1. [Thought Machine](https://thoughtmachine.net/)
|
|
175
|
+
1. [MessageBird](https://messagebird.com)
|
|
176
|
+
1. [Cloudanix](https://www.cloudanix.com/)
|
|
177
|
+
1. [Corelight](https://www.corelight.com/)
|
|
178
|
+
1. {Your company here} :-)
|
|
179
|
+
|
|
180
|
+
If your organization uses Cartography, please file a PR and update this list. Say hi on Slack too!
|
|
181
|
+
|
|
182
|
+
---
|
|
183
|
+
|
|
184
|
+
Cartography is a [Cloud Native Computing Foundation](https://www.cncf.io/) sandbox project.<br>
|
|
185
|
+
<div style="background-color: white; display: inline-block; padding: 10px;">
|
|
186
|
+
<img src="docs/root/images/cncf-color.png" alt="CNCF Logo" width="200">
|
|
187
|
+
</div>
|
|
@@ -1,22 +1,23 @@
|
|
|
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=4jVixOK9_PQOEIMhE_GIqcqb9BjuSb8yjAWba2VL3gw,413
|
|
3
4
|
cartography/cli.py,sha256=LPjeOkx-cKhRkuhqMicB-0X3SHOjLXxEeGqsp2FtpC0,33285
|
|
4
5
|
cartography/config.py,sha256=ZcadsKmooAkti9Kv0eDl8Ec1PcZDu3lWobtNaCnwY3k,11872
|
|
5
6
|
cartography/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
6
7
|
cartography/stats.py,sha256=dbybb9V2FuvSuHjjNwz6Vjwnd1hap2C7h960rLoKcl8,4406
|
|
7
8
|
cartography/sync.py,sha256=ziD63T_774gXSuD5zdz6fLGvv1Kt2ntQySSVbmcCZb8,9708
|
|
8
|
-
cartography/util.py,sha256=
|
|
9
|
+
cartography/util.py,sha256=VZgiHcAprn3nGzItee4_TggfsGWxWPTkLN-2MIhYUqM,14999
|
|
9
10
|
cartography/client/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
10
11
|
cartography/client/aws/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
11
12
|
cartography/client/aws/iam.py,sha256=dYsGikc36DEsSeR2XVOVFFUDwuU9yWj_EVkpgVYCFgM,1293
|
|
12
13
|
cartography/client/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
13
|
-
cartography/client/core/tx.py,sha256=
|
|
14
|
+
cartography/client/core/tx.py,sha256=55Cf9DJGHHXQk4HmPOdFwr1eh9Pr1nzmIvs4XoCVr0g,10892
|
|
14
15
|
cartography/data/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
15
|
-
cartography/data/indexes.cypher,sha256=
|
|
16
|
+
cartography/data/indexes.cypher,sha256=KMhj8DoKl3d-o6Zx4PY6IZ_8eZNPqZWWVNnOzIi5dRw,26946
|
|
16
17
|
cartography/data/permission_relationships.yaml,sha256=RuKGGc_3ZUQ7ag0MssB8k_zaonCkVM5E8I_svBWTmGc,969
|
|
17
18
|
cartography/data/jobs/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
18
19
|
cartography/data/jobs/analysis/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
19
|
-
cartography/data/jobs/analysis/aws_ec2_asset_exposure.json,sha256=
|
|
20
|
+
cartography/data/jobs/analysis/aws_ec2_asset_exposure.json,sha256=wSR_-0TbieAMSFOWpNZohgbDHooa-F6jHm8DxdntLOY,3397
|
|
20
21
|
cartography/data/jobs/analysis/aws_ec2_iaminstance.json,sha256=98wfe8pjwS-iQIjUpM1rxuASdxk7BGl9ooigrwfpmvU,500
|
|
21
22
|
cartography/data/jobs/analysis/aws_ec2_iaminstanceprofile.json,sha256=7M8k0p0SvSYVzCk358FnUQ865IX25TyumtHTXqZS-t8,526
|
|
22
23
|
cartography/data/jobs/analysis/aws_ec2_keypair_analysis.json,sha256=_ZBczunZbx5NHAbfVc4v6vJsbyl1xaePLzj4cyxW-4A,1741
|
|
@@ -24,7 +25,7 @@ cartography/data/jobs/analysis/aws_eks_asset_exposure.json,sha256=Z6z4YTNJJqUJl2
|
|
|
24
25
|
cartography/data/jobs/analysis/aws_foreign_accounts.json,sha256=b8Li_KQLwIvNXxWt0F1bVTV1Vg9dxsbr7ZE8LH2-woc,686
|
|
25
26
|
cartography/data/jobs/analysis/aws_lambda_ecr.json,sha256=wM10Gn0HoNP-sOj3S_Sjqh4mLsh-f2QkonkFuOohs_U,641
|
|
26
27
|
cartography/data/jobs/analysis/aws_s3acl_analysis.json,sha256=ihTzHXAjpulNo0F1swZlZtxqKsZwtt2QXoB7yaX6gKA,2737
|
|
27
|
-
cartography/data/jobs/analysis/gcp_compute_asset_inet_exposure.json,sha256=
|
|
28
|
+
cartography/data/jobs/analysis/gcp_compute_asset_inet_exposure.json,sha256=lXuxrcHLom_DvsDvCQjZsqwRLMkwtKtjj6CRmo0eweI,4640
|
|
28
29
|
cartography/data/jobs/analysis/gcp_gke_asset_exposure.json,sha256=7SJc9TeeIWFMDmHOWgmjgaIzjmCClLjJTPS4bGlaEF0,643
|
|
29
30
|
cartography/data/jobs/analysis/gcp_gke_basic_auth.json,sha256=qLkrw1eZvV9ETtkIQN3v9hXnYN3ujAMyfIpqUj5YGo8,681
|
|
30
31
|
cartography/data/jobs/analysis/gsuite_human_link.json,sha256=ArWA51fNIeeXwYiroJbKnuqN8y-TLrndOq0ZdC9q5os,541
|
|
@@ -102,7 +103,6 @@ cartography/data/jobs/cleanup/gcp_crm_project_cleanup.json,sha256=JImcuuz9HI2TL0
|
|
|
102
103
|
cartography/data/jobs/cleanup/gcp_dns_cleanup.json,sha256=NGs5UYFmm65Rq8gyqbzIe8_OnFchfpNFf9iAcIj_hyY,1286
|
|
103
104
|
cartography/data/jobs/cleanup/gcp_gke_cluster_cleanup.json,sha256=3bMEJ44AEvjkj_1ibclk6Ys5r1LniUWefpZ_U5hTwHI,671
|
|
104
105
|
cartography/data/jobs/cleanup/gcp_storage_bucket_cleanup.json,sha256=sGygB_meoCpGdGgEZtIlC4L-19meAXdfP99gkNJHD7o,1288
|
|
105
|
-
cartography/data/jobs/cleanup/github_org_and_users_cleanup.json,sha256=vjaOlWdnjaCHmvmaWadOzHXqFnjpR1wW8cykb_M54fM,1010
|
|
106
106
|
cartography/data/jobs/cleanup/github_repos_cleanup.json,sha256=Ko6zya72yLjLt_m-wogAUmuzzL2-IJiO457clEHFxPk,3676
|
|
107
107
|
cartography/data/jobs/cleanup/gsuite_ingest_groups_cleanup.json,sha256=ddXAUi6aVi2htf5R1bNn6YrC3SjshjLBgWtlzBgZ9Do,961
|
|
108
108
|
cartography/data/jobs/cleanup/gsuite_ingest_users_cleanup.json,sha256=0qMLbVSTyq5F9vt4-TvVa3YAAvZCpPtzF9EwblaTxWg,353
|
|
@@ -133,7 +133,7 @@ cartography/driftdetect/shortcut.py,sha256=0AvX9vZGNRWeLiRqxU7eOOo77gcfufHx4IHZv
|
|
|
133
133
|
cartography/driftdetect/storage.py,sha256=6ziscwJh34_1ylrSAK6B-U6o09xDhIa7Yb-V1kisveQ,1549
|
|
134
134
|
cartography/driftdetect/util.py,sha256=Lqxv8QoFn3_3Fz18qCOjkjJ6yBwgrHjrxXmArBAEdkc,929
|
|
135
135
|
cartography/graph/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
136
|
-
cartography/graph/cleanupbuilder.py,sha256=
|
|
136
|
+
cartography/graph/cleanupbuilder.py,sha256=zZc7SvRz23sU_VN-RrHoyhug7dRZlqbbLwHbrlPdVik,9411
|
|
137
137
|
cartography/graph/context.py,sha256=RGxGb8EnxowcqjR0nFF86baNhgRHeUF9wjIoFUoG8LU,1230
|
|
138
138
|
cartography/graph/job.py,sha256=RZWsbNhHuJlcSpw4C73ZuovRTp7kGrcm3X9yUH8vT1Q,7488
|
|
139
139
|
cartography/graph/querybuilder.py,sha256=7DtIGPlfeKcIWKw_ReCAX1OeUkhSnIRV_reeVaKL6I4,20416
|
|
@@ -153,7 +153,7 @@ cartography/intel/aws/elasticache.py,sha256=fCI47aDFmIDyE26GiReKYb6XIZUwrzcvsXBQ
|
|
|
153
153
|
cartography/intel/aws/elasticsearch.py,sha256=ZL7MkXF_bXRSoXuDSI1dwGckRLG2zDB8LuAD07vSLnE,8374
|
|
154
154
|
cartography/intel/aws/emr.py,sha256=xhWBVZngxJRFjMEDxwq3G6SgytRGLq0v2a_CeDvByR0,3372
|
|
155
155
|
cartography/intel/aws/iam.py,sha256=zRlF9cKcYm44iL63G6bd-_flNOFHVrjsEfW0jZHpUNg,32387
|
|
156
|
-
cartography/intel/aws/identitycenter.py,sha256=
|
|
156
|
+
cartography/intel/aws/identitycenter.py,sha256=OnyvsSfn6AszB2e9dPAsGmzgne0zq0_7Ta0A6k_l_TE,8956
|
|
157
157
|
cartography/intel/aws/inspector.py,sha256=S22ZgRKEnmnBTJ-u0rodqRPB7_LkSIek47NeBxN4XJw,9336
|
|
158
158
|
cartography/intel/aws/kms.py,sha256=bZUzMxAH_DsAcGTJBs08gg2tLKYu-QWjvMvV9C-6v50,11731
|
|
159
159
|
cartography/intel/aws/lambda_function.py,sha256=KKTyn53xpaMI9WvIqxmsOASFwflHt-2_5ow-zUFc2wg,9890
|
|
@@ -161,7 +161,7 @@ cartography/intel/aws/organizations.py,sha256=HaQZ3J5XF15BuykuDypqFORDYpnoHuRRr4
|
|
|
161
161
|
cartography/intel/aws/permission_relationships.py,sha256=IarV9gt5BaplZ5TPo_mfypt9bTKfT9qDtqC3Ob89qGI,14904
|
|
162
162
|
cartography/intel/aws/rds.py,sha256=vnlNYmrO2Cc0PNn31CeG2QwYhwjVosbQFE9Ol1vQyLE,25252
|
|
163
163
|
cartography/intel/aws/redshift.py,sha256=KOqiXIllHmtPTeaNGl-cX4srY5pFE6o12j8MQ5-zWpc,6694
|
|
164
|
-
cartography/intel/aws/resourcegroupstaggingapi.py,sha256=
|
|
164
|
+
cartography/intel/aws/resourcegroupstaggingapi.py,sha256=I2VrL-oplGj2Jyhbo312V6vnER_FXpJRrc6OBJ-_VmI,10375
|
|
165
165
|
cartography/intel/aws/resources.py,sha256=A8Dc3PtCfDyk5a1ZgAoHthhDPS6aWN_kR0PLwnHdC0Q,3370
|
|
166
166
|
cartography/intel/aws/route53.py,sha256=IYqeQud1HuHnf11A7T-Jeif5DWgjpaaU-Jfr2cLUc_o,14099
|
|
167
167
|
cartography/intel/aws/s3.py,sha256=SVxUMtMSkbdjZv5qOSYIbYb8BQa-QTojbHG85-EFWLA,27034
|
|
@@ -173,9 +173,9 @@ cartography/intel/aws/ec2/__init__.py,sha256=IDK2Yap7mllK_ab6yVMLXatJ94znIkn-szv
|
|
|
173
173
|
cartography/intel/aws/ec2/auto_scaling_groups.py,sha256=ylfks8_oC0-fVMnToFbmRcbKdEN0H17LlN1-ZqW6ULc,8148
|
|
174
174
|
cartography/intel/aws/ec2/elastic_ip_addresses.py,sha256=0k4NwS73VyWbEj5jXvSkaq2RNvmAlBlrN-UKa_Bj0uk,3957
|
|
175
175
|
cartography/intel/aws/ec2/images.py,sha256=heElwHJGqVD3iUJjxwA_Sdc3CmE4HPs00CTMHuQ1wkc,3782
|
|
176
|
-
cartography/intel/aws/ec2/instances.py,sha256=
|
|
176
|
+
cartography/intel/aws/ec2/instances.py,sha256=uI8eVJmeEybS8y_T8CVKAkwxJyVDCH7sbuEJYeWGSWY,12468
|
|
177
177
|
cartography/intel/aws/ec2/internet_gateways.py,sha256=dI-4-85_3DGGZZBcY_DN6XqESx9P26S6jKok314lcnQ,2883
|
|
178
|
-
cartography/intel/aws/ec2/key_pairs.py,sha256=
|
|
178
|
+
cartography/intel/aws/ec2/key_pairs.py,sha256=g4imIo_5jk8upq9J4--erg-OZXG2i3cJMe6SnNCYj9s,2635
|
|
179
179
|
cartography/intel/aws/ec2/launch_templates.py,sha256=aeqaL8On38ET8nM8bISsIXLy6PkZoV-tqSWG38YXgkI,6010
|
|
180
180
|
cartography/intel/aws/ec2/load_balancer_v2s.py,sha256=95FfQQn740gexINIHDJizOM4OKzRtQT_y2XQMipQ5Dg,8661
|
|
181
181
|
cartography/intel/aws/ec2/load_balancers.py,sha256=1GwErzGqi3BKCARqfGJcD_r_D84rFKVy5kNMas9jAok,6756
|
|
@@ -222,7 +222,7 @@ cartography/intel/duo/phones.py,sha256=ueJheqSLD2xYcMus5eOiixPYS3_xVjgQzeomjV2a6
|
|
|
222
222
|
cartography/intel/duo/tokens.py,sha256=bEEnjfc4waQnkRHVSnZLAeGE8wHOOZL7FA9m80GGQdQ,2396
|
|
223
223
|
cartography/intel/duo/users.py,sha256=lc7ly_XKeUjJ50szw31WT_GiCrZfGKJv1zVUpmTchh4,4097
|
|
224
224
|
cartography/intel/duo/web_authn_credentials.py,sha256=IbDf3CWqfEyI7f9zJugUvoDd6vZOECfb_7ANZaRYzuk,2636
|
|
225
|
-
cartography/intel/gcp/__init__.py,sha256=
|
|
225
|
+
cartography/intel/gcp/__init__.py,sha256=raPnE8b4WAwLfWJwU2D3JJwSnENHBRi_Bv9x-pMavdQ,15813
|
|
226
226
|
cartography/intel/gcp/compute.py,sha256=CH2cBdOwbLZCAbkfRJkkI-sFybXVKRWEUGDJANQmvyA,48333
|
|
227
227
|
cartography/intel/gcp/crm.py,sha256=Uw5PILhVFhpM8gq7uu2v7F_YikDW3gsTZ3d7-e8Z1_k,12324
|
|
228
228
|
cartography/intel/gcp/dns.py,sha256=y2pvbmV04cnrMyuu_nbW3oc7uwHX6yEzn1n7veCsjmk,7748
|
|
@@ -231,10 +231,10 @@ cartography/intel/gcp/storage.py,sha256=oO_ayEhkXlj2Gn7T5MU41ZXiqwRwe6Ud4wzqyRTs
|
|
|
231
231
|
cartography/intel/github/__init__.py,sha256=y876JJGTDJZEOFCDiNCJfcLNxN24pVj4s2N0YmuuoaE,1914
|
|
232
232
|
cartography/intel/github/repos.py,sha256=MmpxZASDJFQxDeSMxX3pZcpxCHFPos4_uYC_cX9KjOg,29865
|
|
233
233
|
cartography/intel/github/teams.py,sha256=AltQSmBHHmyzBtnRkez9Bo5yChEKBSt3wwzhGcfqmX4,14180
|
|
234
|
-
cartography/intel/github/users.py,sha256=
|
|
234
|
+
cartography/intel/github/users.py,sha256=MCLE0V0UCzQm3k3KmrNe6PYkI6usRQZYy2rCN3mT8o0,8948
|
|
235
235
|
cartography/intel/github/util.py,sha256=K0cXOPuhnGvN-aqcSUBO3vTuKQLjufVal9kn2HwOpbo,8110
|
|
236
|
-
cartography/intel/gsuite/__init__.py,sha256=
|
|
237
|
-
cartography/intel/gsuite/api.py,sha256=
|
|
236
|
+
cartography/intel/gsuite/__init__.py,sha256=Ed5Lab8E_OpRY1JM7NBaQwajfbG2MCACU21xKS9_ETY,4636
|
|
237
|
+
cartography/intel/gsuite/api.py,sha256=qgEnAcajYGsgC5XNKMnYxOli8Su9wooaEnBBEpsk2EY,10336
|
|
238
238
|
cartography/intel/jamf/__init__.py,sha256=Nof-LrUeevoieo6oP2GyfTwx8k5TUIgreW6hSj53YjQ,419
|
|
239
239
|
cartography/intel/jamf/computers.py,sha256=EfjlupQ-9HYTjOrmuwrGuJDy9ApAnJvk8WrYcp6_Jkk,1673
|
|
240
240
|
cartography/intel/jamf/util.py,sha256=EAyP8VpOY2uAvW3HtX6r7qORNjGa1Tr3fuqezuLQ0j4,1017
|
|
@@ -288,7 +288,8 @@ cartography/models/aws/ec2/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJ
|
|
|
288
288
|
cartography/models/aws/ec2/auto_scaling_groups.py,sha256=ImIu-i5iU_CJQ25oa2MDnOhOjL4jcpBagPiB95JUvSE,8660
|
|
289
289
|
cartography/models/aws/ec2/images.py,sha256=uGhXS7Xb6sKUdwwkS0O0dWP4sIREjusUaV_unODv9gE,3012
|
|
290
290
|
cartography/models/aws/ec2/instances.py,sha256=cNMHngdGNRhxoyID6AmG2F7CQGC1fYani8DV8lSKvsI,3902
|
|
291
|
-
cartography/models/aws/ec2/
|
|
291
|
+
cartography/models/aws/ec2/keypair.py,sha256=UzKj1-Oyd-xMAJeuELzkGlDNAih1H9KpBwEgCCp54T8,2235
|
|
292
|
+
cartography/models/aws/ec2/keypair_instance.py,sha256=M1Ru8Z_2izW0cADAnQVVHaKsT_4FucNZnjr2DIGncJY,2943
|
|
292
293
|
cartography/models/aws/ec2/launch_configurations.py,sha256=zdfWJEx93HXDXd_IzSEkhvcztkJI7_v_TCE_d8ZNAyI,2764
|
|
293
294
|
cartography/models/aws/ec2/launch_template_versions.py,sha256=RitfnAuAj0XpFsCXkRbtUhHMAi8Vsvmtury231eKvGU,3897
|
|
294
295
|
cartography/models/aws/ec2/launch_templates.py,sha256=GqiwFuMp72LNSt2eQlp2WfdU_vHsom-xKV5AaUewSHQ,2157
|
|
@@ -353,9 +354,9 @@ cartography/models/snipeit/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJ
|
|
|
353
354
|
cartography/models/snipeit/asset.py,sha256=FyRAaeXuZjMy0eUQcSDFcgEAF5lbLMlvqp1Tv9d3Lv4,3238
|
|
354
355
|
cartography/models/snipeit/tenant.py,sha256=p4rFnpNNuF1W5ilGBbexDaETWTwavfb38RcQGoImkQI,679
|
|
355
356
|
cartography/models/snipeit/user.py,sha256=MsB4MiCVNTH6JpESime7cOkB89autZOXQpL6Z0l7L6o,2113
|
|
356
|
-
cartography-0.
|
|
357
|
-
cartography-0.
|
|
358
|
-
cartography-0.
|
|
359
|
-
cartography-0.
|
|
360
|
-
cartography-0.
|
|
361
|
-
cartography-0.
|
|
357
|
+
cartography-0.98.0.dist-info/LICENSE,sha256=kvLEBRYaQ1RvUni6y7Ti9uHeooqnjPoo6n_-0JO1ETc,11351
|
|
358
|
+
cartography-0.98.0.dist-info/METADATA,sha256=EVM33emFVVSN4bpq4GQ2tr6H6YbCt1CTm6B2zGASCe8,11470
|
|
359
|
+
cartography-0.98.0.dist-info/WHEEL,sha256=A3WOREP4zgxI0fKrHUG8DC8013e3dK3n7a6HDbcEIwE,91
|
|
360
|
+
cartography-0.98.0.dist-info/entry_points.txt,sha256=GVIAWD0o0_K077qMA_k1oZU4v-M0a8GLKGJR8tZ-qH8,112
|
|
361
|
+
cartography-0.98.0.dist-info/top_level.txt,sha256=BHqsNJQiI6Q72DeypC1IINQJE59SLhU4nllbQjgJi9g,12
|
|
362
|
+
cartography-0.98.0.dist-info/RECORD,,
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"statements": [{
|
|
3
|
-
"query": "MATCH (n:GitHubUser) WHERE n.lastupdated <> $UPDATE_TAG WITH n LIMIT $LIMIT_SIZE DETACH DELETE (n)",
|
|
4
|
-
"iterative": true,
|
|
5
|
-
"iterationsize": 100
|
|
6
|
-
},
|
|
7
|
-
{
|
|
8
|
-
"query": "MATCH (n:GitHubOrganization) WHERE n.lastupdated <> $UPDATE_TAG WITH n LIMIT $LIMIT_SIZE DETACH DELETE (n)",
|
|
9
|
-
"iterative": true,
|
|
10
|
-
"iterationsize": 100
|
|
11
|
-
},
|
|
12
|
-
{
|
|
13
|
-
"query": "MATCH (:GitHubUser)-[r:OWNER]->(:GitHubRepository) WHERE r.lastupdated <> $UPDATE_TAG WITH r LIMIT $LIMIT_SIZE DELETE (r)",
|
|
14
|
-
"iterative": true,
|
|
15
|
-
"iterationsize": 100
|
|
16
|
-
},
|
|
17
|
-
{
|
|
18
|
-
"query": "MATCH (:GitHubUser)-[r:MEMBER_OF]->(:GitHubOrganization) WHERE r.lastupdated <> $UPDATE_TAG WITH r LIMIT $LIMIT_SIZE DELETE (r)",
|
|
19
|
-
"iterative": true,
|
|
20
|
-
"iterationsize": 100
|
|
21
|
-
},
|
|
22
|
-
{
|
|
23
|
-
"query": "MATCH (:GitHubUser)-[r:UNAFFILIATED]->(:GitHubOrganization) WHERE r.lastupdated <> $UPDATE_TAG WITH r LIMIT $LIMIT_SIZE DELETE (r)",
|
|
24
|
-
"iterative": true,
|
|
25
|
-
"iterationsize": 100
|
|
26
|
-
}],
|
|
27
|
-
"name": "cleanup GitHub users data"
|
|
28
|
-
}
|
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.1
|
|
2
|
-
Name: cartography
|
|
3
|
-
Version: 0.97.0
|
|
4
|
-
Summary: Explore assets and their relationships across your technical infrastructure.
|
|
5
|
-
Home-page: https://www.github.com/cartography-cncf/cartography
|
|
6
|
-
Maintainer: Cartography Contributors
|
|
7
|
-
License: apache2
|
|
8
|
-
Classifier: Development Status :: 4 - Beta
|
|
9
|
-
Classifier: Intended Audience :: Developers
|
|
10
|
-
Classifier: License :: OSI Approved :: Apache Software License
|
|
11
|
-
Classifier: Natural Language :: English
|
|
12
|
-
Classifier: Programming Language :: Python
|
|
13
|
-
Classifier: Programming Language :: Python :: 3
|
|
14
|
-
Classifier: Programming Language :: Python :: 3.10
|
|
15
|
-
Classifier: Topic :: Security
|
|
16
|
-
Classifier: Topic :: Software Development :: Libraries
|
|
17
|
-
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
18
|
-
Description-Content-Type: text/markdown
|
|
19
|
-
License-File: LICENSE
|
|
20
|
-
Requires-Dist: backoff>=2.1.2
|
|
21
|
-
Requires-Dist: boto3>=1.15.1
|
|
22
|
-
Requires-Dist: botocore>=1.18.1
|
|
23
|
-
Requires-Dist: dnspython>=1.15.0
|
|
24
|
-
Requires-Dist: neo4j<5.0.0,>=4.4.4
|
|
25
|
-
Requires-Dist: policyuniverse>=1.1.0.0
|
|
26
|
-
Requires-Dist: google-api-python-client>=1.7.8
|
|
27
|
-
Requires-Dist: oauth2client>=4.1.3
|
|
28
|
-
Requires-Dist: marshmallow>=3.0.0rc7
|
|
29
|
-
Requires-Dist: oci>=2.71.0
|
|
30
|
-
Requires-Dist: okta<1.0.0
|
|
31
|
-
Requires-Dist: pyyaml>=5.3.1
|
|
32
|
-
Requires-Dist: requests>=2.22.0
|
|
33
|
-
Requires-Dist: statsd
|
|
34
|
-
Requires-Dist: packaging
|
|
35
|
-
Requires-Dist: python-digitalocean>=1.16.0
|
|
36
|
-
Requires-Dist: adal>=1.2.4
|
|
37
|
-
Requires-Dist: azure-cli-core>=2.26.0
|
|
38
|
-
Requires-Dist: azure-mgmt-compute>=5.0.0
|
|
39
|
-
Requires-Dist: azure-mgmt-resource>=10.2.0
|
|
40
|
-
Requires-Dist: azure-mgmt-cosmosdb>=6.0.0
|
|
41
|
-
Requires-Dist: msrestazure>=0.6.4
|
|
42
|
-
Requires-Dist: azure-mgmt-storage>=16.0.0
|
|
43
|
-
Requires-Dist: azure-mgmt-sql<=1.0.0
|
|
44
|
-
Requires-Dist: azure-identity>=1.5.0
|
|
45
|
-
Requires-Dist: kubernetes>=22.6.0
|
|
46
|
-
Requires-Dist: pdpyras>=4.3.0
|
|
47
|
-
Requires-Dist: crowdstrike-falconpy>=0.5.1
|
|
48
|
-
Requires-Dist: python-dateutil
|
|
49
|
-
Requires-Dist: xmltodict
|
|
50
|
-
Requires-Dist: duo-client
|
|
51
|
-
Requires-Dist: importlib-resources; python_version < "3.7"
|
|
52
|
-
|
|
53
|
-
file: README.md
|
|
File without changes
|
|
File without changes
|
|
File without changes
|