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.

@@ -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)
@@ -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)
@@ -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 AND labels(n) IN ['AutoScalingGroup', 'EC2Instance', 'LoadBalancer', 'LoadBalancerV2'] WITH n LIMIT $LIMIT_SIZE REMOVE n.exposed_internet, n.exposed_internet_type return COUNT(*) as TotalCompleted",
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 (: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)\nWITH instance\nWHERE (instance.publicipaddress IS NOT NULL) AND (instance.exposed_internet_type IS NULL) OR (NOT 'direct' IN instance.exposed_internet_type)\nSET instance.exposed_internet = true, instance.exposed_internet_type = coalesce(instance.exposed_internet_type , []) + 'direct';",
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 AND labels(n) IN ['GCPInstance'] WITH n LIMIT $LIMIT_SIZE REMOVE n.exposed_internet, n.exposed_internet_type return COUNT(*) as TotalCompleted",
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
- raise ValueError(
30
- "Auto-creating a cleanup job for a node_schema without a sub resource relationship is not supported. "
31
- f'Please check the class definition of "{node_schema.__class__.__name__}".',
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.keypairs import EC2KeyPairSchema
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 load_ec2_key_pairs(
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
- EC2KeyPairSchema(),
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
- load_ec2_key_pairs(neo4j_session, key_pair_list, region, current_aws_account_id, update_tag)
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 .util import get_botocore_config
8
+ from cartography.client.core.tx import load
9
9
  from cartography.graph.job import GraphJob
10
- from cartography.models.aws.ec2.keypairs import EC2KeyPairSchema
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) -> List[Dict]:
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
- neo4j_session: neo4j.Session, data: List[Dict], region: str, current_aws_account_id: str,
27
- update_tag: int,
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
- ingest_key_pair = """
30
- MERGE (keypair:KeyPair:EC2KeyPair{arn: $ARN, id: $ARN})
31
- ON CREATE SET keypair.firstseen = timestamp()
32
- SET keypair.keyname = $KeyName, keypair.keyfingerprint = $KeyFingerprint, keypair.region = $Region,
33
- keypair.lastupdated = $update_tag
34
- WITH keypair
35
- MATCH (aa:AWSAccount{id: $AWS_ACCOUNT_ID})
36
- MERGE (aa)-[r:RESOURCE]->(keypair)
37
- ON CREATE SET r.firstseen = timestamp()
38
- SET r.lastupdated = $update_tag
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, boto3_session: boto3.session.Session, regions: List[str], current_aws_account_id: str,
65
- update_tag: int, common_job_parameters: Dict,
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
- load_ec2_key_pairs(neo4j_session, data, region, current_aws_account_id, update_tag)
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://oauth2client.readthedocs.io/en/latest/source/
299
- # oauth2client.client.html#oauth2client.client.OAuth2Credentials
300
- credentials = GoogleCredentials.get_application_default()
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
- # no automated cleanup job for users because user node has no sub_resource_relationship
240
- run_cleanup_job('github_org_and_users_cleanup.json', neo4j_session, common_job_parameters)
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: GoogleCredentials) -> Resource:
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 GoogleCredentials object
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: GoogleCredentials) -> Resources:
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 GoogleCredentials object
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
- credentials = GoogleCredentials.from_stream(config.gsuite_config)
68
- credentials = credentials.create_scoped(OAUTH_SCOPE)
69
- credentials = credentials.create_delegated(os.environ.get('GSUITE_DELEGATED_ADMIN'))
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 ApplicationDefaultCredentialsError as e:
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
- credentials = GoogleCredentials(
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
- 'Cartography',
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
- credentials.refresh(httplib2.Http())
96
- credentials = credentials.create_scoped(OAUTH_SCOPE)
97
- except ApplicationDefaultCredentialsError as e:
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(credentials)
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)
@@ -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/schema/gsuite.md
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/schema/gsuite.md
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 = "RESOURCE"
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
+ ![Cartography](docs/root/images/logo-horizontal.png)
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
+ ![Visualization of RDS nodes and AWS nodes](docs/root/images/accountsandrds.png)
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
+ ![poweruser.png](docs/root/images/poweruser.png)
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=T51p1WrA9sdyDnHM3H9RN25OR2xClXww33SBUkM_HgA,15080
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=FcUPfmNIKSXkN5UK3nL5-GFoGkPYXmAxIWP7knvrHWg,10794
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=zMEzytgcvMLgclMmCkFUdWp4t_EFsOEOp1M2v1vGctM,27208
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=PRM7TJwO3gLkGtez-hg5v5avDkuAcF-bQQCK6stecLg,2726
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=lIurpVOAHZ3u_pfpRhcC5zprxXKqOWG4miGIkMeCfcE,4695
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=87vFrOJo66hOrrqeNwXp18WrNQEheHTlZko9KUkXWhY,8021
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=Mgd7ZNj8W7IghVSXovgYyQDwRt_MjxrBPGyDKxlFsoQ,9495
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=aq4kPF6t8QZZoTxdkQVLXH65Di41CDJVM9llJNe6iaY,10278
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=IJS9TJhk0aqFaRbmUOCE_sHVhZJLg8fPTF8zEnTlyvk,12372
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=SvRgd56vE4eouvTSNoFK8PP8HYoECO91goxc36oq_FY,2508
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=jJOT6Ys97qm1W0jGXFwtSOOSGPa7q-Xxr1YIR8fKejo,15849
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=f_YIDwdNECRg7wRwY8qZ5mztm7m3SIgOz8BbfveRzy8,8734
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=AGIUskGlLCVGHbnQicNpNWi9AvmV7_7hUKTt-hsB2J8,4306
237
- cartography/intel/gsuite/api.py,sha256=J0dkNdfBVMrEv8vvStQu7YKVxXSyV45WueFhUS4aOG4,10310
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/keypairs.py,sha256=scKC3SdExHAWkPNmb6tT9LK-9q4sweqS2ejFzMec10M,2630
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.97.0.dist-info/LICENSE,sha256=kvLEBRYaQ1RvUni6y7Ti9uHeooqnjPoo6n_-0JO1ETc,11351
357
- cartography-0.97.0.dist-info/METADATA,sha256=KXebB76HbcUfcT1umMgUjh0c8lw_ah1wCW1SEtZ5rWU,1938
358
- cartography-0.97.0.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
359
- cartography-0.97.0.dist-info/entry_points.txt,sha256=GVIAWD0o0_K077qMA_k1oZU4v-M0a8GLKGJR8tZ-qH8,112
360
- cartography-0.97.0.dist-info/top_level.txt,sha256=BHqsNJQiI6Q72DeypC1IINQJE59SLhU4nllbQjgJi9g,12
361
- cartography-0.97.0.dist-info/RECORD,,
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,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (75.6.0)
2
+ Generator: setuptools (75.7.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -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