cartography 0.101.0rc2__py3-none-any.whl → 0.101.1__py3-none-any.whl

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

Potentially problematic release.


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

cartography/_version.py CHANGED
@@ -17,5 +17,5 @@ __version__: str
17
17
  __version_tuple__: VERSION_TUPLE
18
18
  version_tuple: VERSION_TUPLE
19
19
 
20
- __version__ = version = '0.101.0rc2'
21
- __version_tuple__ = version_tuple = (0, 101, 0)
20
+ __version__ = version = '0.101.1'
21
+ __version_tuple__ = version_tuple = (0, 101, 1)
@@ -65,9 +65,6 @@ CREATE INDEX IF NOT EXISTS FOR (n:AccountAccessKey) ON (n.accesskeyid);
65
65
  CREATE INDEX IF NOT EXISTS FOR (n:AccountAccessKey) ON (n.lastupdated);
66
66
  CREATE INDEX IF NOT EXISTS FOR (n:AutoScalingGroup) ON (n.arn);
67
67
  CREATE INDEX IF NOT EXISTS FOR (n:AutoScalingGroup) ON (n.lastupdated);
68
- CREATE INDEX IF NOT EXISTS FOR (n:CrowdstrikeHost) ON (n.id);
69
- CREATE INDEX IF NOT EXISTS FOR (n:CrowdstrikeHost) ON (n.instance_id);
70
- CREATE INDEX IF NOT EXISTS FOR (n:CrowdstrikeHost) ON (n.lastupdated);
71
68
  CREATE INDEX IF NOT EXISTS FOR (n:CVE) ON (n.id);
72
69
  CREATE INDEX IF NOT EXISTS FOR (n:CVE) ON (n.lastupdated);
73
70
  CREATE INDEX IF NOT EXISTS FOR (n:Dependency) ON (n.id);
@@ -194,9 +191,6 @@ CREATE INDEX IF NOT EXISTS FOR (n:KMSGrant) ON (n.lastupdated);
194
191
  CREATE INDEX IF NOT EXISTS FOR (n:LaunchConfiguration) ON (n.id);
195
192
  CREATE INDEX IF NOT EXISTS FOR (n:LaunchConfiguration) ON (n.name);
196
193
  CREATE INDEX IF NOT EXISTS FOR (n:LaunchConfiguration) ON (n.lastupdated);
197
- CREATE INDEX IF NOT EXISTS FOR (n:LoadBalancer) ON (n.dnsname);
198
- CREATE INDEX IF NOT EXISTS FOR (n:LoadBalancer) ON (n.id);
199
- CREATE INDEX IF NOT EXISTS FOR (n:LoadBalancer) ON (n.lastupdated);
200
194
  CREATE INDEX IF NOT EXISTS FOR (n:LoadBalancerV2) ON (n.dnsname);
201
195
  CREATE INDEX IF NOT EXISTS FOR (n:LoadBalancerV2) ON (n.id);
202
196
  CREATE INDEX IF NOT EXISTS FOR (n:LoadBalancerV2) ON (n.lastupdated);
@@ -5,11 +5,6 @@
5
5
  "iterative": true,
6
6
  "iterationsize": 100
7
7
  },
8
- {
9
- "query": "MATCH (h:CrowdstrikeHost) WHERE h.lastupdated <> $UPDATE_TAG WITH h LIMIT $LIMIT_SIZE DETACH DELETE (h)",
10
- "iterative": true,
11
- "iterationsize": 100
12
- },
13
8
  {
14
9
  "query": "MATCH (:CrowdstrikeFinding)<-[hc:HAS_CVE]-(:SpotlightVulnerability) WHERE hc.lastupdated <> $UPDATE_TAG WITH hc LIMIT $LIMIT_SIZE DELETE (hc)",
15
10
  "iterative": true,
@@ -2,6 +2,7 @@ import logging
2
2
  from typing import Any
3
3
 
4
4
  import boto3
5
+ import botocore
5
6
  import neo4j
6
7
 
7
8
  from .util import get_botocore_config
@@ -56,15 +57,27 @@ def get_launch_template_versions_by_template(
56
57
  client = boto3_session.client('ec2', region_name=region, config=get_botocore_config())
57
58
  v_paginator = client.get_paginator('describe_launch_template_versions')
58
59
  template_versions = []
59
- for versions in v_paginator.paginate(LaunchTemplateId=launch_template_id):
60
- template_versions.extend(versions['LaunchTemplateVersions'])
60
+ try:
61
+ for versions in v_paginator.paginate(LaunchTemplateId=launch_template_id):
62
+ template_versions.extend(versions['LaunchTemplateVersions'])
63
+ except botocore.exceptions.ClientError as e:
64
+ error_code = e.response['Error']['Code']
65
+ if error_code == 'InvalidLaunchTemplateId.NotFound':
66
+ logger.warning("Launch template %s no longer exists in region %s", launch_template_id, region)
67
+ else:
68
+ raise
61
69
  return template_versions
62
70
 
63
71
 
64
- def transform_launch_templates(templates: list[dict[str, Any]]) -> list[dict[str, Any]]:
72
+ def transform_launch_templates(templates: list[dict[str, Any]], versions: list[dict[str, Any]]) -> list[dict[str, Any]]:
73
+ valid_template_ids = {v['LaunchTemplateId'] for v in versions}
65
74
  result: list[dict[str, Any]] = []
66
75
  for template in templates:
76
+ if template['LaunchTemplateId'] not in valid_template_ids:
77
+ continue
78
+
67
79
  current = template.copy()
80
+ # Convert CreateTime to timestamp string
68
81
  current['CreateTime'] = str(int(current['CreateTime'].timestamp()))
69
82
  result.append(current)
70
83
  return result
@@ -157,9 +170,13 @@ def sync_ec2_launch_templates(
157
170
  logger.info(f"Syncing launch templates for region '{region}' in account '{current_aws_account_id}'.")
158
171
  templates = get_launch_templates(boto3_session, region)
159
172
  versions = get_launch_template_versions(boto3_session, region, templates)
160
- templates = transform_launch_templates(templates)
161
- load_launch_templates(neo4j_session, templates, region, current_aws_account_id, update_tag)
162
- versions = transform_launch_template_versions(versions)
163
- load_launch_template_versions(neo4j_session, versions, region, current_aws_account_id, update_tag)
173
+
174
+ # Transform and load the templates that have versions
175
+ transformed_templates = transform_launch_templates(templates, versions)
176
+ load_launch_templates(neo4j_session, transformed_templates, region, current_aws_account_id, update_tag)
177
+
178
+ # Transform and load the versions
179
+ transformed_versions = transform_launch_template_versions(versions)
180
+ load_launch_template_versions(neo4j_session, transformed_versions, region, current_aws_account_id, update_tag)
164
181
 
165
182
  cleanup(neo4j_session, common_job_parameters)
@@ -1,190 +1,168 @@
1
1
  import logging
2
- from typing import Dict
3
- from typing import List
4
2
 
5
3
  import boto3
6
4
  import neo4j
7
5
 
8
6
  from .util import get_botocore_config
7
+ from cartography.client.core.tx import load
8
+ from cartography.graph.job import GraphJob
9
+ from cartography.models.aws.ec2.load_balancer_listeners import ELBListenerSchema
10
+ from cartography.models.aws.ec2.load_balancers import LoadBalancerSchema
9
11
  from cartography.util import aws_handle_regions
10
- from cartography.util import run_cleanup_job
11
12
  from cartography.util import timeit
12
13
 
13
14
  logger = logging.getLogger(__name__)
14
15
 
15
16
 
17
+ def _get_listener_id(load_balancer_id: str, port: int, protocol: str) -> str:
18
+ """
19
+ Generate a unique ID for a load balancer listener.
20
+
21
+ Args:
22
+ load_balancer_id: The ID of the load balancer
23
+ port: The listener port
24
+ protocol: The listener protocol
25
+
26
+ Returns:
27
+ A unique ID string for the listener
28
+ """
29
+ return f"{load_balancer_id}{port}{protocol}"
30
+
31
+
32
+ def transform_load_balancer_listener_data(load_balancer_id: str, listener_data: list[dict]) -> list[dict]:
33
+ """
34
+ Transform load balancer listener data into a format suitable for cartography ingestion.
35
+
36
+ Args:
37
+ load_balancer_id: The ID of the load balancer
38
+ listener_data: List of listener data from AWS API
39
+
40
+ Returns:
41
+ List of transformed listener data
42
+ """
43
+ transformed = []
44
+ for listener in listener_data:
45
+ listener_info = listener['Listener']
46
+ transformed_listener = {
47
+ 'id': _get_listener_id(load_balancer_id, listener_info['LoadBalancerPort'], listener_info['Protocol']),
48
+ 'port': listener_info.get('LoadBalancerPort'),
49
+ 'protocol': listener_info.get('Protocol'),
50
+ 'instance_port': listener_info.get('InstancePort'),
51
+ 'instance_protocol': listener_info.get('InstanceProtocol'),
52
+ 'policy_names': listener.get('PolicyNames', []),
53
+ 'LoadBalancerId': load_balancer_id,
54
+ }
55
+ transformed.append(transformed_listener)
56
+ return transformed
57
+
58
+
59
+ def transform_load_balancer_data(load_balancers: list[dict]) -> tuple[list[dict], list[dict]]:
60
+ """
61
+ Transform load balancer data into a format suitable for cartography ingestion.
62
+
63
+ Args:
64
+ load_balancers: List of load balancer data from AWS API
65
+
66
+ Returns:
67
+ Tuple of (transformed load balancer data, transformed listener data)
68
+ """
69
+ transformed = []
70
+ listener_data = []
71
+
72
+ for lb in load_balancers:
73
+ load_balancer_id = lb['DNSName']
74
+ transformed_lb = {
75
+ 'id': load_balancer_id,
76
+ 'name': lb['LoadBalancerName'],
77
+ 'dnsname': lb['DNSName'],
78
+ 'canonicalhostedzonename': lb.get('CanonicalHostedZoneName'),
79
+ 'canonicalhostedzonenameid': lb.get('CanonicalHostedZoneNameID'),
80
+ 'scheme': lb.get('Scheme'),
81
+ 'createdtime': str(lb['CreatedTime']),
82
+ 'GROUP_NAME': lb.get('SourceSecurityGroup', {}).get('GroupName'),
83
+ 'GROUP_IDS': [str(group) for group in lb.get('SecurityGroups', [])],
84
+ 'INSTANCE_IDS': [instance['InstanceId'] for instance in lb.get('Instances', [])],
85
+ 'LISTENER_IDS': [
86
+ _get_listener_id(
87
+ load_balancer_id,
88
+ listener['Listener']['LoadBalancerPort'],
89
+ listener['Listener']['Protocol'],
90
+ ) for listener in lb.get('ListenerDescriptions', [])
91
+ ],
92
+ }
93
+ transformed.append(transformed_lb)
94
+
95
+ # Classic ELB listeners are not returned anywhere else in AWS, so we must parse them out
96
+ # of the describe_load_balancers response.
97
+ if lb.get('ListenerDescriptions'):
98
+ listener_data.extend(
99
+ transform_load_balancer_listener_data(
100
+ load_balancer_id,
101
+ lb.get('ListenerDescriptions', []),
102
+ ),
103
+ )
104
+
105
+ return transformed, listener_data
106
+
107
+
16
108
  @timeit
17
109
  @aws_handle_regions
18
- def get_loadbalancer_data(boto3_session: boto3.session.Session, region: str) -> List[Dict]:
110
+ def get_loadbalancer_data(boto3_session: boto3.session.Session, region: str) -> list[dict]:
19
111
  client = boto3_session.client('elb', region_name=region, config=get_botocore_config())
20
112
  paginator = client.get_paginator('describe_load_balancers')
21
- elbs: List[Dict] = []
113
+ elbs: list[dict] = []
22
114
  for page in paginator.paginate():
23
115
  elbs.extend(page['LoadBalancerDescriptions'])
24
116
  return elbs
25
117
 
26
118
 
27
119
  @timeit
28
- def load_load_balancer_listeners(
29
- neo4j_session: neo4j.Session, load_balancer_id: str, listener_data: List[Dict],
120
+ def load_load_balancers(
121
+ neo4j_session: neo4j.Session, data: list[dict], region: str, current_aws_account_id: str,
30
122
  update_tag: int,
31
123
  ) -> None:
32
- ingest_listener = """
33
- MATCH (elb:LoadBalancer{id: $LoadBalancerId})
34
- WITH elb
35
- UNWIND $Listeners as data
36
- MERGE (l:Endpoint:ELBListener{id: elb.id + toString(data.Listener.LoadBalancerPort) +
37
- toString(data.Listener.Protocol)})
38
- ON CREATE SET l.port = data.Listener.LoadBalancerPort, l.protocol = data.Listener.Protocol,
39
- l.firstseen = timestamp()
40
- SET l.instance_port = data.Listener.InstancePort, l.instance_protocol = data.Listener.InstanceProtocol,
41
- l.policy_names = data.PolicyNames,
42
- l.lastupdated = $update_tag
43
- WITH l, elb
44
- MERGE (elb)-[r:ELB_LISTENER]->(l)
45
- ON CREATE SET r.firstseen = timestamp()
46
- SET r.lastupdated = $update_tag
47
- """
48
-
49
- neo4j_session.run(
50
- ingest_listener,
51
- LoadBalancerId=load_balancer_id,
52
- Listeners=listener_data,
53
- update_tag=update_tag,
124
+ load(
125
+ neo4j_session,
126
+ LoadBalancerSchema(),
127
+ data,
128
+ Region=region,
129
+ AWS_ID=current_aws_account_id,
130
+ lastupdated=update_tag,
54
131
  )
55
132
 
56
133
 
57
134
  @timeit
58
- def load_load_balancer_subnets(
59
- neo4j_session: neo4j.Session, load_balancer_id: str, subnets_data: List[Dict],
60
- update_tag: int,
61
- ) -> None:
62
- ingest_load_balancer_subnet = """
63
- MATCH (elb:LoadBalancer{id: $ID}), (subnet:EC2Subnet{subnetid: $SUBNET_ID})
64
- MERGE (elb)-[r:SUBNET]->(subnet)
65
- ON CREATE SET r.firstseen = timestamp()
66
- SET r.lastupdated = $update_tag
67
- """
68
-
69
- for subnet_id in subnets_data:
70
- neo4j_session.run(
71
- ingest_load_balancer_subnet,
72
- ID=load_balancer_id,
73
- SUBNET_ID=subnet_id,
74
- update_tag=update_tag,
75
- )
76
-
77
-
78
- @timeit
79
- def load_load_balancers(
80
- neo4j_session: neo4j.Session, data: List[Dict], region: str, current_aws_account_id: str,
135
+ def load_load_balancer_listeners(
136
+ neo4j_session: neo4j.Session, data: list[dict], region: str, current_aws_account_id: str,
81
137
  update_tag: int,
82
138
  ) -> None:
83
- ingest_load_balancer = """
84
- MERGE (elb:LoadBalancer{id: $ID})
85
- ON CREATE SET elb.firstseen = timestamp(), elb.createdtime = $CREATED_TIME
86
- SET elb.lastupdated = $update_tag, elb.name = $NAME, elb.dnsname = $DNS_NAME,
87
- elb.canonicalhostedzonename = $HOSTED_ZONE_NAME, elb.canonicalhostedzonenameid = $HOSTED_ZONE_NAME_ID,
88
- elb.scheme = $SCHEME, elb.region = $Region
89
- WITH elb
90
- MATCH (aa:AWSAccount{id: $AWS_ACCOUNT_ID})
91
- MERGE (aa)-[r:RESOURCE]->(elb)
92
- ON CREATE SET r.firstseen = timestamp()
93
- SET r.lastupdated = $update_tag
94
- """
95
-
96
- ingest_load_balancersource_security_group = """
97
- MATCH (elb:LoadBalancer{id: $ID}),
98
- (group:EC2SecurityGroup{name: $GROUP_NAME})
99
- MERGE (elb)-[r:SOURCE_SECURITY_GROUP]->(group)
100
- ON CREATE SET r.firstseen = timestamp()
101
- SET r.lastupdated = $update_tag
102
- """
103
-
104
- ingest_load_balancer_security_group = """
105
- MATCH (elb:LoadBalancer{id: $ID}),
106
- (group:EC2SecurityGroup{groupid: $GROUP_ID})
107
- MERGE (elb)-[r:MEMBER_OF_EC2_SECURITY_GROUP]->(group)
108
- ON CREATE SET r.firstseen = timestamp()
109
- SET r.lastupdated = $update_tag
110
- """
111
-
112
- ingest_instances = """
113
- MATCH (elb:LoadBalancer{id: $ID}), (instance:EC2Instance{instanceid: $INSTANCE_ID})
114
- MERGE (elb)-[r:EXPOSE]->(instance)
115
- ON CREATE SET r.firstseen = timestamp()
116
- SET r.lastupdated = $update_tag
117
- WITH instance
118
- MATCH (aa:AWSAccount{id: $AWS_ACCOUNT_ID})
119
- MERGE (aa)-[r:RESOURCE]->(instance)
120
- ON CREATE SET r.firstseen = timestamp()
121
- SET r.lastupdated = $update_tag
122
- """
123
-
124
- for lb in data:
125
- load_balancer_id = lb["DNSName"]
126
-
127
- neo4j_session.run(
128
- ingest_load_balancer,
129
- ID=load_balancer_id,
130
- CREATED_TIME=str(lb["CreatedTime"]),
131
- NAME=lb["LoadBalancerName"],
132
- DNS_NAME=load_balancer_id,
133
- HOSTED_ZONE_NAME=lb.get("CanonicalHostedZoneName"),
134
- HOSTED_ZONE_NAME_ID=lb.get("CanonicalHostedZoneNameID"),
135
- SCHEME=lb.get("Scheme", ""),
136
- AWS_ACCOUNT_ID=current_aws_account_id,
137
- Region=region,
138
- update_tag=update_tag,
139
- )
140
-
141
- if lb["Subnets"]:
142
- load_load_balancer_subnets(neo4j_session, load_balancer_id, lb["Subnets"], update_tag)
143
-
144
- if lb["SecurityGroups"]:
145
- for group in lb["SecurityGroups"]:
146
- neo4j_session.run(
147
- ingest_load_balancer_security_group,
148
- ID=load_balancer_id,
149
- GROUP_ID=str(group),
150
- update_tag=update_tag,
151
- )
152
-
153
- if lb["SourceSecurityGroup"]:
154
- source_group = lb["SourceSecurityGroup"]
155
- neo4j_session.run(
156
- ingest_load_balancersource_security_group,
157
- ID=load_balancer_id,
158
- GROUP_NAME=source_group["GroupName"],
159
- update_tag=update_tag,
160
- )
161
-
162
- if lb["Instances"]:
163
- for instance in lb["Instances"]:
164
- neo4j_session.run(
165
- ingest_instances,
166
- ID=load_balancer_id,
167
- INSTANCE_ID=instance["InstanceId"],
168
- AWS_ACCOUNT_ID=current_aws_account_id,
169
- update_tag=update_tag,
170
- )
171
-
172
- if lb["ListenerDescriptions"]:
173
- load_load_balancer_listeners(neo4j_session, load_balancer_id, lb["ListenerDescriptions"], update_tag)
139
+ load(
140
+ neo4j_session,
141
+ ELBListenerSchema(),
142
+ data,
143
+ Region=region,
144
+ AWS_ID=current_aws_account_id,
145
+ lastupdated=update_tag,
146
+ )
174
147
 
175
148
 
176
149
  @timeit
177
- def cleanup_load_balancers(neo4j_session: neo4j.Session, common_job_parameters: Dict) -> None:
178
- run_cleanup_job('aws_ingest_load_balancers_cleanup.json', neo4j_session, common_job_parameters)
150
+ def cleanup_load_balancers(neo4j_session: neo4j.Session, common_job_parameters: dict) -> None:
151
+ GraphJob.from_node_schema(ELBListenerSchema(), common_job_parameters).run(neo4j_session)
152
+ GraphJob.from_node_schema(LoadBalancerSchema(), common_job_parameters).run(neo4j_session)
179
153
 
180
154
 
181
155
  @timeit
182
156
  def sync_load_balancers(
183
- neo4j_session: neo4j.Session, boto3_session: boto3.session.Session, regions: List[str], current_aws_account_id: str,
184
- update_tag: int, common_job_parameters: Dict,
157
+ neo4j_session: neo4j.Session, boto3_session: boto3.session.Session, regions: list[str], current_aws_account_id: str,
158
+ update_tag: int, common_job_parameters: dict,
185
159
  ) -> None:
186
160
  for region in regions:
187
161
  logger.info("Syncing EC2 load balancers for region '%s' in account '%s'.", region, current_aws_account_id)
188
162
  data = get_loadbalancer_data(boto3_session, region)
189
- load_load_balancers(neo4j_session, data, region, current_aws_account_id, update_tag)
163
+ transformed_data, listener_data = transform_load_balancer_data(data)
164
+
165
+ load_load_balancers(neo4j_session, transformed_data, region, current_aws_account_id, update_tag)
166
+ load_load_balancer_listeners(neo4j_session, listener_data, region, current_aws_account_id, update_tag)
167
+
190
168
  cleanup_load_balancers(neo4j_session, common_job_parameters)
@@ -1,11 +1,14 @@
1
1
  import logging
2
+ from typing import Any
2
3
 
3
4
  import neo4j
4
5
 
5
6
  from cartography.config import Config
7
+ from cartography.graph.job import GraphJob
6
8
  from cartography.intel.crowdstrike.endpoints import sync_hosts
7
9
  from cartography.intel.crowdstrike.spotlight import sync_vulnerabilities
8
10
  from cartography.intel.crowdstrike.util import get_authorization
11
+ from cartography.models.crowdstrike.hosts import CrowdstrikeHostSchema
9
12
  from cartography.stats import get_stats_client
10
13
  from cartography.util import merge_module_sync_metadata
11
14
  from cartography.util import run_cleanup_job
@@ -50,11 +53,7 @@ def start_crowdstrike_ingestion(
50
53
  config.update_tag,
51
54
  authorization,
52
55
  )
53
- run_cleanup_job(
54
- "crowdstrike_import_cleanup.json",
55
- neo4j_session,
56
- common_job_parameters,
57
- )
56
+ cleanup(neo4j_session, common_job_parameters)
58
57
 
59
58
  group_id = "public"
60
59
  if config.crowdstrike_api_url:
@@ -67,3 +66,16 @@ def start_crowdstrike_ingestion(
67
66
  update_tag=config.update_tag,
68
67
  stat_handler=stat_handler,
69
68
  )
69
+
70
+
71
+ @timeit
72
+ def cleanup(neo4j_session: neo4j.Session, common_job_parameters: dict[str, Any]) -> None:
73
+ logger.info("Running Crowdstrike cleanup")
74
+ GraphJob.from_node_schema(CrowdstrikeHostSchema(), common_job_parameters).run(neo4j_session)
75
+
76
+ # Cleanup other crowdstrike assets not handled by the data model
77
+ run_cleanup_job(
78
+ "crowdstrike_import_cleanup.json",
79
+ neo4j_session,
80
+ common_job_parameters,
81
+ )
@@ -6,6 +6,8 @@ import neo4j
6
6
  from falconpy.hosts import Hosts
7
7
  from falconpy.oauth2 import OAuth2
8
8
 
9
+ from cartography.client.core.tx import load
10
+ from cartography.models.crowdstrike.hosts import CrowdstrikeHostSchema
9
11
  from cartography.util import timeit
10
12
 
11
13
  logger = logging.getLogger(__name__)
@@ -24,55 +26,21 @@ def sync_hosts(
24
26
  load_host_data(neo4j_session, host_data, update_tag)
25
27
 
26
28
 
29
+ @timeit
27
30
  def load_host_data(
28
- neo4j_session: neo4j.Session, data: List[Dict], update_tag: int,
31
+ neo4j_session: neo4j.Session,
32
+ data: List[Dict],
33
+ update_tag: int,
29
34
  ) -> None:
30
35
  """
31
- Transform and load scan information
32
- """
33
- ingestion_cypher_query = """
34
- UNWIND $Hosts AS host
35
- MERGE (h:CrowdstrikeHost{id: host.device_id})
36
- ON CREATE SET h.cid = host.cid,
37
- h.cid = host.cid,
38
- h.instance_id = host.instance_id,
39
- h.firstseen = timestamp()
40
- SET h.status = host.status,
41
- h.hostname = host.hostname,
42
- h.machine_domain = host.machine_domain,
43
- h.crowdstrike_first_seen = host.first_seen,
44
- h.crowdstrike_last_seen = host.last_seen,
45
- h.local_ip = host.local_ip,
46
- h.external_ip = host.external_ip,
47
- h.cpu_signature = host.cpu_signature,
48
- h.bios_manufacturer = host.bios_manufacturer,
49
- h.bios_version = host.bios_version,
50
- h.mac_address = host.mac_address,
51
- h.os_version = host.os_version,
52
- h.os_build = host.os_build,
53
- h.platform_id = host.platform_id,
54
- h.platform_name = host.platform_name,
55
- h.service_provider = host.service_provider,
56
- h.service_provider_account_id = host.service_provider_account_id,
57
- h.agent_version = host.agent_version,
58
- h.system_manufacturer = host.system_manufacturer,
59
- h.system_product_name = host.system_product_name,
60
- h.product_type = host.product_type,
61
- h.product_type_desc = host.product_type_desc,
62
- h.provision_status = host.provision_status,
63
- h.reduced_functionality_mode = host.reduced_functionality_mode,
64
- h.kernel_version = host.kernel_version,
65
- h.major_version = host.major_version,
66
- h.minor_version = host.minor_version,
67
- h.tags = host.tags,
68
- h.modified_timestamp = host.modified_timestamp,
69
- h.lastupdated = $update_tag
36
+ Load Crowdstrike host data into Neo4j.
70
37
  """
71
38
  logger.info(f"Loading {len(data)} crowdstrike hosts.")
72
- neo4j_session.run(
73
- ingestion_cypher_query,
74
- Hosts=data,
75
- update_tag=update_tag,
39
+ load(
40
+ neo4j_session,
41
+ CrowdstrikeHostSchema(),
42
+ data,
43
+ lastupdated=update_tag,
76
44
  )
77
45
 
78
46
 
@@ -3,6 +3,7 @@ import logging
3
3
  from collections import namedtuple
4
4
  from typing import Dict
5
5
  from typing import List
6
+ from typing import Optional
6
7
  from typing import Set
7
8
 
8
9
  import googleapiclient.discovery
@@ -328,7 +329,7 @@ def _sync_multiple_projects(
328
329
 
329
330
 
330
331
  @timeit
331
- def get_gcp_credentials() -> GoogleCredentials:
332
+ def get_gcp_credentials() -> Optional[GoogleCredentials]:
332
333
  """
333
334
  Gets access tokens for GCP API access.
334
335
  :param: None
@@ -338,6 +339,7 @@ def get_gcp_credentials() -> GoogleCredentials:
338
339
  # Explicitly use Application Default Credentials.
339
340
  # See https://google-auth.readthedocs.io/en/master/user-guide.html#application-default-credentials
340
341
  credentials, project_id = default()
342
+ return credentials
341
343
  except DefaultCredentialsError as e:
342
344
  logger.debug("Error occurred calling GoogleCredentials.get_application_default().", exc_info=True)
343
345
  logger.error(
@@ -349,7 +351,7 @@ def get_gcp_credentials() -> GoogleCredentials:
349
351
  ),
350
352
  e,
351
353
  )
352
- return credentials
354
+ return None
353
355
 
354
356
 
355
357
  @timeit
@@ -367,6 +369,9 @@ def start_gcp_ingestion(neo4j_session: neo4j.Session, config: Config) -> None:
367
369
  }
368
370
 
369
371
  credentials = get_gcp_credentials()
372
+ if credentials is None:
373
+ logger.warning("Unable to initialize GCP credentials. Skipping module.")
374
+ return
370
375
 
371
376
  resources = _initialize_resources(credentials)
372
377
 
@@ -67,6 +67,14 @@ def start_gsuite_ingestion(neo4j_session: neo4j.Session, config: Config) -> None
67
67
 
68
68
  creds: OAuth2Credentials | ServiceAccountCredentials
69
69
  if config.gsuite_auth_method == 'delegated': # Legacy delegated method
70
+ if config.gsuite_config is None or not os.path.isfile(config.gsuite_config):
71
+ logger.warning(
72
+ (
73
+ "The GSuite config file is not set or is not a valid file."
74
+ "Skipping GSuite ingestion."
75
+ ),
76
+ )
77
+ return
70
78
  logger.info('Attempting to authenticate to GSuite using legacy delegated method')
71
79
  try:
72
80
  creds = service_account.Credentials.from_service_account_file(
@@ -25,10 +25,34 @@ def get(kandji_base_uri: str, kandji_token: str) -> List[Dict[str, Any]]:
25
25
  'Authorization': f'Bearer {kandji_token}',
26
26
  }
27
27
 
28
+ offset = 0
29
+ limit = 300
30
+ params: dict[str, str | int] = {
31
+ "sort": "serial_number",
32
+ "limit": limit,
33
+ "offset": offset,
34
+ }
35
+
36
+ devices: List[Dict[str, Any]] = []
28
37
  session = Session()
29
- req = session.get(api_endpoint, headers=headers, timeout=_TIMEOUT)
30
- req.raise_for_status()
31
- return req.json()
38
+ while True:
39
+ logger.debug("Kandji device offset: %s", offset)
40
+
41
+ params["offset"] = offset
42
+ response = session.get(api_endpoint, headers=headers, timeout=_TIMEOUT, params=params)
43
+ response.raise_for_status()
44
+
45
+ result = response.json()
46
+ # If no more result, we are done
47
+ if len(result) == 0:
48
+ break
49
+
50
+ devices.extend(result)
51
+
52
+ offset += limit
53
+
54
+ logger.debug("Kandji device count: %d", len(devices))
55
+ return devices
32
56
 
33
57
 
34
58
  @timeit
@@ -0,0 +1,68 @@
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 ELBListenerNodeProperties(CartographyNodeProperties):
17
+ id: PropertyRef = PropertyRef('id')
18
+ port: PropertyRef = PropertyRef('port')
19
+ protocol: PropertyRef = PropertyRef('protocol')
20
+ instance_port: PropertyRef = PropertyRef('instance_port')
21
+ instance_protocol: PropertyRef = PropertyRef('instance_protocol')
22
+ policy_names: PropertyRef = PropertyRef('policy_names')
23
+ lastupdated: PropertyRef = PropertyRef('lastupdated', set_in_kwargs=True)
24
+
25
+
26
+ @dataclass(frozen=True)
27
+ class ELBListenerToLoadBalancerRelProperties(CartographyRelProperties):
28
+ lastupdated: PropertyRef = PropertyRef('lastupdated', set_in_kwargs=True)
29
+
30
+
31
+ @dataclass(frozen=True)
32
+ class ELBListenerToLoadBalancer(CartographyRelSchema):
33
+ target_node_label: str = 'LoadBalancer'
34
+ target_node_matcher: TargetNodeMatcher = make_target_node_matcher(
35
+ {'id': PropertyRef('LoadBalancerId')},
36
+ )
37
+ direction: LinkDirection = LinkDirection.INWARD
38
+ rel_label: str = "ELB_LISTENER"
39
+ properties: ELBListenerToLoadBalancerRelProperties = ELBListenerToLoadBalancerRelProperties()
40
+
41
+
42
+ @dataclass(frozen=True)
43
+ class ELBListenerToAWSAccountRelProperties(CartographyRelProperties):
44
+ lastupdated: PropertyRef = PropertyRef('lastupdated', set_in_kwargs=True)
45
+
46
+
47
+ @dataclass(frozen=True)
48
+ class ELBListenerToAWSAccount(CartographyRelSchema):
49
+ target_node_label: str = 'AWSAccount'
50
+ target_node_matcher: TargetNodeMatcher = make_target_node_matcher(
51
+ {'id': PropertyRef('AWS_ID', set_in_kwargs=True)},
52
+ )
53
+ direction: LinkDirection = LinkDirection.INWARD
54
+ rel_label: str = "RESOURCE"
55
+ properties: ELBListenerToAWSAccountRelProperties = ELBListenerToAWSAccountRelProperties()
56
+
57
+
58
+ @dataclass(frozen=True)
59
+ class ELBListenerSchema(CartographyNodeSchema):
60
+ label: str = 'ELBListener'
61
+ properties: ELBListenerNodeProperties = ELBListenerNodeProperties()
62
+ extra_node_labels: ExtraNodeLabels = ExtraNodeLabels(['Endpoint'])
63
+ sub_resource_relationship: ELBListenerToAWSAccount = ELBListenerToAWSAccount()
64
+ other_relationships: OtherRelationships = OtherRelationships(
65
+ [
66
+ ELBListenerToLoadBalancer(),
67
+ ],
68
+ )
@@ -0,0 +1,102 @@
1
+ from dataclasses import dataclass
2
+
3
+ from cartography.models.core.common import PropertyRef
4
+ from cartography.models.core.nodes import CartographyNodeProperties
5
+ from cartography.models.core.nodes import CartographyNodeSchema
6
+ from cartography.models.core.relationships import CartographyRelProperties
7
+ from cartography.models.core.relationships import CartographyRelSchema
8
+ from cartography.models.core.relationships import LinkDirection
9
+ from cartography.models.core.relationships import make_target_node_matcher
10
+ from cartography.models.core.relationships import OtherRelationships
11
+ from cartography.models.core.relationships import TargetNodeMatcher
12
+
13
+
14
+ @dataclass(frozen=True)
15
+ class LoadBalancerNodeProperties(CartographyNodeProperties):
16
+ id: PropertyRef = PropertyRef('id')
17
+ name: PropertyRef = PropertyRef('name')
18
+ dnsname: PropertyRef = PropertyRef('dnsname', extra_index=True)
19
+ canonicalhostedzonename: PropertyRef = PropertyRef('canonicalhostedzonename')
20
+ canonicalhostedzonenameid: PropertyRef = PropertyRef('canonicalhostedzonenameid')
21
+ scheme: PropertyRef = PropertyRef('scheme')
22
+ region: PropertyRef = PropertyRef('Region', set_in_kwargs=True)
23
+ createdtime: PropertyRef = PropertyRef('createdtime')
24
+ lastupdated: PropertyRef = PropertyRef('lastupdated', set_in_kwargs=True)
25
+
26
+
27
+ @dataclass(frozen=True)
28
+ class LoadBalancerToAWSAccountRelProperties(CartographyRelProperties):
29
+ lastupdated: PropertyRef = PropertyRef('lastupdated', set_in_kwargs=True)
30
+
31
+
32
+ @dataclass(frozen=True)
33
+ class LoadBalancerToAWSAccount(CartographyRelSchema):
34
+ target_node_label: str = 'AWSAccount'
35
+ target_node_matcher: TargetNodeMatcher = make_target_node_matcher(
36
+ {'id': PropertyRef('AWS_ID', set_in_kwargs=True)},
37
+ )
38
+ direction: LinkDirection = LinkDirection.INWARD
39
+ rel_label: str = "RESOURCE"
40
+ properties: LoadBalancerToAWSAccountRelProperties = LoadBalancerToAWSAccountRelProperties()
41
+
42
+
43
+ @dataclass(frozen=True)
44
+ class LoadBalancerToSecurityGroupRelProperties(CartographyRelProperties):
45
+ lastupdated: PropertyRef = PropertyRef('lastupdated', set_in_kwargs=True)
46
+
47
+
48
+ @dataclass(frozen=True)
49
+ class LoadBalancerToSourceSecurityGroup(CartographyRelSchema):
50
+ target_node_label: str = 'EC2SecurityGroup'
51
+ target_node_matcher: TargetNodeMatcher = make_target_node_matcher(
52
+ {'name': PropertyRef('GROUP_NAME')},
53
+ )
54
+ direction: LinkDirection = LinkDirection.OUTWARD
55
+ rel_label: str = "SOURCE_SECURITY_GROUP"
56
+ properties: LoadBalancerToSecurityGroupRelProperties = LoadBalancerToSecurityGroupRelProperties()
57
+
58
+
59
+ @dataclass(frozen=True)
60
+ class LoadBalancerToEC2SecurityGroupRelProperties(CartographyRelProperties):
61
+ lastupdated: PropertyRef = PropertyRef('lastupdated', set_in_kwargs=True)
62
+
63
+
64
+ @dataclass(frozen=True)
65
+ class LoadBalancerToEC2SecurityGroup(CartographyRelSchema):
66
+ target_node_label: str = 'EC2SecurityGroup'
67
+ target_node_matcher: TargetNodeMatcher = make_target_node_matcher(
68
+ {'groupid': PropertyRef('GROUP_IDS', one_to_many=True)},
69
+ )
70
+ direction: LinkDirection = LinkDirection.OUTWARD
71
+ rel_label: str = "MEMBER_OF_EC2_SECURITY_GROUP"
72
+ properties: LoadBalancerToEC2SecurityGroupRelProperties = LoadBalancerToEC2SecurityGroupRelProperties()
73
+
74
+
75
+ @dataclass(frozen=True)
76
+ class LoadBalancerToEC2InstanceRelProperties(CartographyRelProperties):
77
+ lastupdated: PropertyRef = PropertyRef('lastupdated', set_in_kwargs=True)
78
+
79
+
80
+ @dataclass(frozen=True)
81
+ class LoadBalancerToEC2Instance(CartographyRelSchema):
82
+ target_node_label: str = 'EC2Instance'
83
+ target_node_matcher: TargetNodeMatcher = make_target_node_matcher(
84
+ {'instanceid': PropertyRef('INSTANCE_IDS', one_to_many=True)},
85
+ )
86
+ direction: LinkDirection = LinkDirection.OUTWARD
87
+ rel_label: str = "EXPOSE"
88
+ properties: LoadBalancerToEC2InstanceRelProperties = LoadBalancerToEC2InstanceRelProperties()
89
+
90
+
91
+ @dataclass(frozen=True)
92
+ class LoadBalancerSchema(CartographyNodeSchema):
93
+ label: str = 'LoadBalancer'
94
+ properties: LoadBalancerNodeProperties = LoadBalancerNodeProperties()
95
+ sub_resource_relationship: LoadBalancerToAWSAccount = LoadBalancerToAWSAccount()
96
+ other_relationships: OtherRelationships = OtherRelationships(
97
+ [
98
+ LoadBalancerToSourceSecurityGroup(),
99
+ LoadBalancerToEC2SecurityGroup(),
100
+ LoadBalancerToEC2Instance(),
101
+ ],
102
+ )
File without changes
@@ -0,0 +1,49 @@
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
+
7
+
8
+ @dataclass(frozen=True)
9
+ class CrowdstrikeHostNodeProperties(CartographyNodeProperties):
10
+ id: PropertyRef = PropertyRef('device_id')
11
+ cid: PropertyRef = PropertyRef('cid')
12
+ instance_id: PropertyRef = PropertyRef('instance_id', extra_index=True)
13
+ serial_number: PropertyRef = PropertyRef('serial_number', extra_index=True)
14
+ status: PropertyRef = PropertyRef('status')
15
+ hostname: PropertyRef = PropertyRef('hostname')
16
+ machine_domain: PropertyRef = PropertyRef('machine_domain')
17
+ crowdstrike_first_seen: PropertyRef = PropertyRef('first_seen')
18
+ crowdstrike_last_seen: PropertyRef = PropertyRef('last_seen')
19
+ local_ip: PropertyRef = PropertyRef('local_ip')
20
+ external_ip: PropertyRef = PropertyRef('external_ip')
21
+ cpu_signature: PropertyRef = PropertyRef('cpu_signature')
22
+ bios_manufacturer: PropertyRef = PropertyRef('bios_manufacturer')
23
+ bios_version: PropertyRef = PropertyRef('bios_version')
24
+ mac_address: PropertyRef = PropertyRef('mac_address')
25
+ os_version: PropertyRef = PropertyRef('os_version')
26
+ os_build: PropertyRef = PropertyRef('os_build')
27
+ platform_id: PropertyRef = PropertyRef('platform_id')
28
+ platform_name: PropertyRef = PropertyRef('platform_name')
29
+ service_provider: PropertyRef = PropertyRef('service_provider')
30
+ service_provider_account_id: PropertyRef = PropertyRef('service_provider_account_id')
31
+ agent_version: PropertyRef = PropertyRef('agent_version')
32
+ system_manufacturer: PropertyRef = PropertyRef('system_manufacturer')
33
+ system_product_name: PropertyRef = PropertyRef('system_product_name')
34
+ product_type: PropertyRef = PropertyRef('product_type')
35
+ product_type_desc: PropertyRef = PropertyRef('product_type_desc')
36
+ provision_status: PropertyRef = PropertyRef('provision_status')
37
+ reduced_functionality_mode: PropertyRef = PropertyRef('reduced_functionality_mode')
38
+ kernel_version: PropertyRef = PropertyRef('kernel_version')
39
+ major_version: PropertyRef = PropertyRef('major_version')
40
+ minor_version: PropertyRef = PropertyRef('minor_version')
41
+ tags: PropertyRef = PropertyRef('tags')
42
+ modified_timestamp: PropertyRef = PropertyRef('modified_timestamp')
43
+ lastupdated: PropertyRef = PropertyRef('lastupdated', set_in_kwargs=True)
44
+
45
+
46
+ @dataclass(frozen=True)
47
+ class CrowdstrikeHostSchema(CartographyNodeSchema):
48
+ label: str = 'CrowdstrikeHost'
49
+ properties: CrowdstrikeHostNodeProperties = CrowdstrikeHostNodeProperties()
cartography/stats.py CHANGED
@@ -97,7 +97,7 @@ def set_stats_client(stats_client: StatsClient) -> None:
97
97
  """
98
98
  This is used to set the module level stats client configured to talk with a statsd host
99
99
  """
100
- global _scoped_stats_client
100
+ global _scoped_stats_client # noqa: F824
101
101
  _scoped_stats_client.set_stats_client(stats_client)
102
102
 
103
103
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cartography
3
- Version: 0.101.0rc2
3
+ Version: 0.101.1
4
4
  Summary: Explore assets and their relationships across your technical infrastructure.
5
5
  Maintainer: Cartography Contributors
6
6
  License: apache2
@@ -59,10 +59,10 @@ Requires-Dist: moto; extra == "dev"
59
59
  Requires-Dist: pre-commit; extra == "dev"
60
60
  Requires-Dist: pytest>=6.2.4; extra == "dev"
61
61
  Requires-Dist: pytest-mock; extra == "dev"
62
- Requires-Dist: pytest-cov==6.0.0; extra == "dev"
62
+ Requires-Dist: pytest-cov==6.1.1; extra == "dev"
63
63
  Requires-Dist: pytest-rerunfailures; extra == "dev"
64
64
  Requires-Dist: types-PyYAML; extra == "dev"
65
- Requires-Dist: types-requests<2.32.0.20250307; extra == "dev"
65
+ Requires-Dist: types-requests<2.32.0.20250329; extra == "dev"
66
66
  Dynamic: license-file
67
67
 
68
68
  ![Cartography](docs/root/images/logo-horizontal.png)
@@ -182,6 +182,7 @@ Get started with our [developer documentation](https://cartography-cncf.github.i
182
182
  1. [MessageBird](https://messagebird.com)
183
183
  1. [Cloudanix](https://www.cloudanix.com/)
184
184
  1. [Corelight](https://www.corelight.com/)
185
+ 1. [SubImage](https://subimage.io)
185
186
  1. {Your company here} :-)
186
187
 
187
188
  If your organization uses Cartography, please file a PR and update this list. Say hi on Slack too!
@@ -1,10 +1,10 @@
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=IBK6fMF_VPqJpOaJyK9R7AWPwDA7KRC-K8f8sNJSVFk,518
3
+ cartography/_version.py,sha256=XS7fJZNkjxFRd5K2jAXua6bEuLkbnwzpGqJLVBZkoGU,515
4
4
  cartography/cli.py,sha256=-77DOKUQn3N-TDIi55V4RHLb3k36ZGZ64o1XgiT0qmE,33370
5
5
  cartography/config.py,sha256=ZcadsKmooAkti9Kv0eDl8Ec1PcZDu3lWobtNaCnwY3k,11872
6
6
  cartography/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
7
- cartography/stats.py,sha256=dbybb9V2FuvSuHjjNwz6Vjwnd1hap2C7h960rLoKcl8,4406
7
+ cartography/stats.py,sha256=-KiqrNfUe_39z9TKAQamJwKs5XePnzXscEJocAuNiJs,4420
8
8
  cartography/sync.py,sha256=ziD63T_774gXSuD5zdz6fLGvv1Kt2ntQySSVbmcCZb8,9708
9
9
  cartography/util.py,sha256=VZgiHcAprn3nGzItee4_TggfsGWxWPTkLN-2MIhYUqM,14999
10
10
  cartography/client/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -13,7 +13,7 @@ cartography/client/aws/iam.py,sha256=dYsGikc36DEsSeR2XVOVFFUDwuU9yWj_EVkpgVYCFgM
13
13
  cartography/client/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
14
14
  cartography/client/core/tx.py,sha256=55Cf9DJGHHXQk4HmPOdFwr1eh9Pr1nzmIvs4XoCVr0g,10892
15
15
  cartography/data/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
16
- cartography/data/indexes.cypher,sha256=KMhj8DoKl3d-o6Zx4PY6IZ_8eZNPqZWWVNnOzIi5dRw,26946
16
+ cartography/data/indexes.cypher,sha256=aUHMiLPsEt09W61GyjJHfpkRJ07S2sGcpU9IReYxKC0,26551
17
17
  cartography/data/permission_relationships.yaml,sha256=RuKGGc_3ZUQ7ag0MssB8k_zaonCkVM5E8I_svBWTmGc,969
18
18
  cartography/data/jobs/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
19
19
  cartography/data/jobs/analysis/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -85,7 +85,7 @@ cartography/data/jobs/cleanup/azure_sql_server_cleanup.json,sha256=97I2jIMBkqR3a
85
85
  cartography/data/jobs/cleanup/azure_storage_account_cleanup.json,sha256=XZdjKDOjTcvn9XYScFzHts6cbtYvXETaU142N_R2qlY,5431
86
86
  cartography/data/jobs/cleanup/azure_subscriptions_cleanup.json,sha256=bowUBCjHYlC4Xd60lv33sxRi-bv1wiT5gAOStaHMX4k,430
87
87
  cartography/data/jobs/cleanup/azure_tenant_cleanup.json,sha256=jcjmZH6kfVGZ9q68rfvnroF0kNNHZ2uTZQ17Rmd4FH0,220
88
- cartography/data/jobs/cleanup/crowdstrike_import_cleanup.json,sha256=bBPwftvz1iMUKrqKFCFZEH3LgVRzg-t5fRUh6Chx-vo,1426
88
+ cartography/data/jobs/cleanup/crowdstrike_import_cleanup.json,sha256=mkC96aKS_m7THGQ3LOE_ROXxIk11moK6Fd5cSPkcYsA,1239
89
89
  cartography/data/jobs/cleanup/digitalocean_droplet_cleanup.json,sha256=f26TdPUPYnIp45ipPys5M6VVfConUZySIbkgSr3iQno,703
90
90
  cartography/data/jobs/cleanup/digitalocean_project_cleanup.json,sha256=5mo9vPshCdUZfgTWd_22_TLSyfe6hd41u7z-B8H1qgY,702
91
91
  cartography/data/jobs/cleanup/gcp_compute_firewall_cleanup.json,sha256=FVNJ8EPaPhmQ_sh4vyTdMyEgs6Y-DIoFTdWJaELgz44,1904
@@ -174,9 +174,9 @@ cartography/intel/aws/ec2/images.py,sha256=SLoxcy_PQgNomVMDMdutm0zXJCOLosiHJlN63
174
174
  cartography/intel/aws/ec2/instances.py,sha256=uI8eVJmeEybS8y_T8CVKAkwxJyVDCH7sbuEJYeWGSWY,12468
175
175
  cartography/intel/aws/ec2/internet_gateways.py,sha256=dI-4-85_3DGGZZBcY_DN6XqESx9P26S6jKok314lcnQ,2883
176
176
  cartography/intel/aws/ec2/key_pairs.py,sha256=g4imIo_5jk8upq9J4--erg-OZXG2i3cJMe6SnNCYj9s,2635
177
- cartography/intel/aws/ec2/launch_templates.py,sha256=uw0_knQW0DGFgD4BUyaxYW0RpWyA_oXcDe-oT6MEaps,5902
177
+ cartography/intel/aws/ec2/launch_templates.py,sha256=JNCT7WKvHcM8Z6D1MDR65GsBiqmd6bMuRQAMu9BWRKY,6634
178
178
  cartography/intel/aws/ec2/load_balancer_v2s.py,sha256=95FfQQn740gexINIHDJizOM4OKzRtQT_y2XQMipQ5Dg,8661
179
- cartography/intel/aws/ec2/load_balancers.py,sha256=1GwErzGqi3BKCARqfGJcD_r_D84rFKVy5kNMas9jAok,6756
179
+ cartography/intel/aws/ec2/load_balancers.py,sha256=ah9-lXvipzVDjGFqfpNCrEyBfdu-BdDeV2ZcPwJM78M,6013
180
180
  cartography/intel/aws/ec2/network_acls.py,sha256=_UiOx79OxcqH0ecRjcVMglAzz5XJ4aVYLlv6dl_ism4,6809
181
181
  cartography/intel/aws/ec2/network_interfaces.py,sha256=CzF8PooCYUQ2pk8DR8JDAhkWRUQSBj_27OsIfkL_-Cs,9199
182
182
  cartography/intel/aws/ec2/reserved_instances.py,sha256=jv8-VLI5KL8jN1QRI20yim8lzZ7I7wR8a5EF8DckahA,3122
@@ -202,8 +202,8 @@ cartography/intel/azure/util/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NM
202
202
  cartography/intel/azure/util/credentials.py,sha256=99PjTs0vZ2iu0tHD7TohN1VJYjuXYstfMg27F4CE0xU,7416
203
203
  cartography/intel/bigfix/__init__.py,sha256=3LoDCm01VNNaDRGsRiykJm1GJgUQ8zI1HO6NodLFVIA,1058
204
204
  cartography/intel/bigfix/computers.py,sha256=HAwA-muDBLu2xkFFL2Ae-xjCH1gxKKwxGMZM4BE_Qdo,5909
205
- cartography/intel/crowdstrike/__init__.py,sha256=dAtgI-0vZAQZ3cTFQhMEzzt7aqiNSNuiIYiw0qbut-I,1896
206
- cartography/intel/crowdstrike/endpoints.py,sha256=tdqokMDW3p4fK3dHKKb2T1DTogvOJBCpwyrxdQlbUhw,3815
205
+ cartography/intel/crowdstrike/__init__.py,sha256=qDFCtNzUSnvMZFFoLjRAPK3pbKpIO6BaerY3bDyEASc,2397
206
+ cartography/intel/crowdstrike/endpoints.py,sha256=MVuUw7yXesJmQL79lbPxOnHjO48lD3WCH5WzL5asKUU,2144
207
207
  cartography/intel/crowdstrike/spotlight.py,sha256=yNhj44-RYF6ubck-hHMKhKiNU0fCfhQf4Oagopc31EM,4754
208
208
  cartography/intel/crowdstrike/util.py,sha256=gfJ6Ptr6YdbBS9Qj9a_-Jc-IJroADDRcXqjh5TW0qXE,277
209
209
  cartography/intel/cve/__init__.py,sha256=u9mv5O_qkSLmdhLhLm1qbwmhoeLQ3A3fQTjNyLQpEyI,3656
@@ -220,7 +220,7 @@ cartography/intel/duo/phones.py,sha256=ueJheqSLD2xYcMus5eOiixPYS3_xVjgQzeomjV2a6
220
220
  cartography/intel/duo/tokens.py,sha256=bEEnjfc4waQnkRHVSnZLAeGE8wHOOZL7FA9m80GGQdQ,2396
221
221
  cartography/intel/duo/users.py,sha256=lc7ly_XKeUjJ50szw31WT_GiCrZfGKJv1zVUpmTchh4,4097
222
222
  cartography/intel/duo/web_authn_credentials.py,sha256=IbDf3CWqfEyI7f9zJugUvoDd6vZOECfb_7ANZaRYzuk,2636
223
- cartography/intel/gcp/__init__.py,sha256=iJPcmkXjMITBf2h9CFJ5-vpLMNRzReoKIazNihRPrNA,17472
223
+ cartography/intel/gcp/__init__.py,sha256=sZHPfDCPZFCE5d6aj20Ow4AC0vrFxV7RCn_cMinCDmI,17650
224
224
  cartography/intel/gcp/compute.py,sha256=CH2cBdOwbLZCAbkfRJkkI-sFybXVKRWEUGDJANQmvyA,48333
225
225
  cartography/intel/gcp/crm.py,sha256=Uw5PILhVFhpM8gq7uu2v7F_YikDW3gsTZ3d7-e8Z1_k,12324
226
226
  cartography/intel/gcp/dns.py,sha256=y2pvbmV04cnrMyuu_nbW3oc7uwHX6yEzn1n7veCsjmk,7748
@@ -232,13 +232,13 @@ cartography/intel/github/repos.py,sha256=MmpxZASDJFQxDeSMxX3pZcpxCHFPos4_uYC_cX9
232
232
  cartography/intel/github/teams.py,sha256=AltQSmBHHmyzBtnRkez9Bo5yChEKBSt3wwzhGcfqmX4,14180
233
233
  cartography/intel/github/users.py,sha256=MCLE0V0UCzQm3k3KmrNe6PYkI6usRQZYy2rCN3mT8o0,8948
234
234
  cartography/intel/github/util.py,sha256=K0cXOPuhnGvN-aqcSUBO3vTuKQLjufVal9kn2HwOpbo,8110
235
- cartography/intel/gsuite/__init__.py,sha256=R3hMZkaRQKHIPcvNpupwJuj7w1RuBrT4skGQASqxyOk,5412
235
+ cartography/intel/gsuite/__init__.py,sha256=S8LxQxQ6CP7g2U5x-XnJFz17OmAgvGdzyhA-CZqg-_g,5724
236
236
  cartography/intel/gsuite/api.py,sha256=bx0mPn6x6fgssxgm343NHdwjbtFkO6SZTucOsoW0Hgk,11143
237
237
  cartography/intel/jamf/__init__.py,sha256=Nof-LrUeevoieo6oP2GyfTwx8k5TUIgreW6hSj53YjQ,419
238
238
  cartography/intel/jamf/computers.py,sha256=EfjlupQ-9HYTjOrmuwrGuJDy9ApAnJvk8WrYcp6_Jkk,1673
239
239
  cartography/intel/jamf/util.py,sha256=EAyP8VpOY2uAvW3HtX6r7qORNjGa1Tr3fuqezuLQ0j4,1017
240
240
  cartography/intel/kandji/__init__.py,sha256=Y38bVRmrGVJRy0mSof8xU-cuEyJ7N_oI7KekYjYyuiQ,1076
241
- cartography/intel/kandji/devices.py,sha256=j_rP6rQ5VPT_XEcGXx7Yt6eCOm1Oe3I2qWIxXODXEcA,2224
241
+ cartography/intel/kandji/devices.py,sha256=bRePUC0xFeaST3PQdJ6Af2MdTeFZuIVq4r7CY5YEid0,2797
242
242
  cartography/intel/kubernetes/__init__.py,sha256=jaOTEanWnTrYvcBN1XUC5oqBhz1AJbFmzoT9uu_VBSg,1481
243
243
  cartography/intel/kubernetes/namespaces.py,sha256=6o-FgAX_Ai5NCj2xOWM-RNWEvn0gZjVQnZSGCJlcIhw,2710
244
244
  cartography/intel/kubernetes/pods.py,sha256=aX3pP_vs6icMe2vK4vgMak6HZ64okhRzoihpkPHscGU,4502
@@ -296,6 +296,8 @@ cartography/models/aws/ec2/keypair_instance.py,sha256=M1Ru8Z_2izW0cADAnQVVHaKsT_
296
296
  cartography/models/aws/ec2/launch_configurations.py,sha256=zdfWJEx93HXDXd_IzSEkhvcztkJI7_v_TCE_d8ZNAyI,2764
297
297
  cartography/models/aws/ec2/launch_template_versions.py,sha256=RitfnAuAj0XpFsCXkRbtUhHMAi8Vsvmtury231eKvGU,3897
298
298
  cartography/models/aws/ec2/launch_templates.py,sha256=GqiwFuMp72LNSt2eQlp2WfdU_vHsom-xKV5AaUewSHQ,2157
299
+ cartography/models/aws/ec2/load_balancer_listeners.py,sha256=M7oYOinOQkEUijJjhs1oCffB5VmLWYSa92tVWKwMSJQ,2879
300
+ cartography/models/aws/ec2/load_balancers.py,sha256=qJbPWePdO2vuyKzcVcSvKtHlEFMBkkUovZ826BaAcwg,4347
299
301
  cartography/models/aws/ec2/loadbalancerv2.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
300
302
  cartography/models/aws/ec2/network_acl_rules.py,sha256=4Rq2J-Dce8J6y9J6YIalmYtuQRWLp652LXO1Xg6XGPE,3951
301
303
  cartography/models/aws/ec2/network_acls.py,sha256=pJKsXdMLB8L79lmTYpLJfFJ6p7PWpf3rBN6eW6y-5hY,3419
@@ -329,6 +331,8 @@ cartography/models/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG
329
331
  cartography/models/core/common.py,sha256=Bk0dCBkO9Udj9v4e8WE9mgBGaZTI170m9QpoiiYoz48,6206
330
332
  cartography/models/core/nodes.py,sha256=h5dwBOk_a2uCHZWeQz3pidr7gkqMKf7buIZgl6M1Ox4,3699
331
333
  cartography/models/core/relationships.py,sha256=6AwXvk0dq48BxqyxBpHyBXZ3dJNm65t1y4vNg4n25uA,5103
334
+ cartography/models/crowdstrike/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
335
+ cartography/models/crowdstrike/hosts.py,sha256=5PiDAhCPWDUd9kE61FliaPzsuIQBZRIQ87eHMgQF_M4,2672
332
336
  cartography/models/cve/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
333
337
  cartography/models/cve/cve.py,sha256=zaCLcGuEL2EnScExjbmpPBNVBivMBKnnRajCYL0LJFE,3720
334
338
  cartography/models/cve/cve_feed.py,sha256=A_h9ET2vQYGpgMr8_--iqdQfF5ZyUNM6aQDruUuA5OU,740
@@ -360,9 +364,9 @@ cartography/models/snipeit/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJ
360
364
  cartography/models/snipeit/asset.py,sha256=FyRAaeXuZjMy0eUQcSDFcgEAF5lbLMlvqp1Tv9d3Lv4,3238
361
365
  cartography/models/snipeit/tenant.py,sha256=p4rFnpNNuF1W5ilGBbexDaETWTwavfb38RcQGoImkQI,679
362
366
  cartography/models/snipeit/user.py,sha256=MsB4MiCVNTH6JpESime7cOkB89autZOXQpL6Z0l7L6o,2113
363
- cartography-0.101.0rc2.dist-info/licenses/LICENSE,sha256=kvLEBRYaQ1RvUni6y7Ti9uHeooqnjPoo6n_-0JO1ETc,11351
364
- cartography-0.101.0rc2.dist-info/METADATA,sha256=Hw5knSxZNezo5gXVd5_2dMrhDDP0Vki78Nb48XgQHW0,11874
365
- cartography-0.101.0rc2.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
366
- cartography-0.101.0rc2.dist-info/entry_points.txt,sha256=GVIAWD0o0_K077qMA_k1oZU4v-M0a8GLKGJR8tZ-qH8,112
367
- cartography-0.101.0rc2.dist-info/top_level.txt,sha256=BHqsNJQiI6Q72DeypC1IINQJE59SLhU4nllbQjgJi9g,12
368
- cartography-0.101.0rc2.dist-info/RECORD,,
367
+ cartography-0.101.1.dist-info/licenses/LICENSE,sha256=kvLEBRYaQ1RvUni6y7Ti9uHeooqnjPoo6n_-0JO1ETc,11351
368
+ cartography-0.101.1.dist-info/METADATA,sha256=_H3KYZBGizgjTNVWB6poKHrcpAg_0Ftt8ltY6E91sZ4,11906
369
+ cartography-0.101.1.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
370
+ cartography-0.101.1.dist-info/entry_points.txt,sha256=GVIAWD0o0_K077qMA_k1oZU4v-M0a8GLKGJR8tZ-qH8,112
371
+ cartography-0.101.1.dist-info/top_level.txt,sha256=BHqsNJQiI6Q72DeypC1IINQJE59SLhU4nllbQjgJi9g,12
372
+ cartography-0.101.1.dist-info/RECORD,,