cartography 0.107.0rc1__py3-none-any.whl → 0.107.0rc3__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.107.0rc1'
21
- __version_tuple__ = version_tuple = (0, 107, 0, 'rc1')
20
+ __version__ = version = '0.107.0rc3'
21
+ __version_tuple__ = version_tuple = (0, 107, 0, 'rc3')
@@ -9,6 +9,9 @@ import neo4j
9
9
  from cartography.client.core.tx import load
10
10
  from cartography.graph.job import GraphJob
11
11
  from cartography.intel.aws.ec2.util import get_botocore_config
12
+ from cartography.models.aws.cloudwatch.log_metric_filter import (
13
+ CloudWatchLogMetricFilterSchema,
14
+ )
12
15
  from cartography.models.aws.cloudwatch.loggroup import CloudWatchLogGroupSchema
13
16
  from cartography.util import aws_handle_regions
14
17
  from cartography.util import timeit
@@ -31,6 +34,47 @@ def get_cloudwatch_log_groups(
31
34
  return logGroups
32
35
 
33
36
 
37
+ @timeit
38
+ @aws_handle_regions
39
+ def get_cloudwatch_log_metric_filters(
40
+ boto3_session: boto3.Session, region: str
41
+ ) -> List[Dict[str, Any]]:
42
+ logs_client = boto3_session.client(
43
+ "logs", region_name=region, config=get_botocore_config()
44
+ )
45
+ paginator = logs_client.get_paginator("describe_metric_filters")
46
+ metric_filters = []
47
+
48
+ for page in paginator.paginate():
49
+ metric_filters.extend(page.get("metricFilters", []))
50
+
51
+ return metric_filters
52
+
53
+
54
+ def transform_metric_filters(
55
+ metric_filters: List[Dict[str, Any]], region: str
56
+ ) -> List[Dict[str, Any]]:
57
+ """
58
+ Transform CloudWatch log metric filter data for ingestion into Neo4j.
59
+ Ensures that the 'id' field is a unique combination of logGroupName and filterName.
60
+ """
61
+ transformed_filters = []
62
+ for filter in metric_filters:
63
+ transformed_filter = {
64
+ "id": f"{filter['logGroupName']}:{filter['filterName']}",
65
+ "arn": f"{filter['logGroupName']}:{filter['filterName']}",
66
+ "filterName": filter["filterName"],
67
+ "filterPattern": filter.get("filterPattern"),
68
+ "logGroupName": filter["logGroupName"],
69
+ "metricName": filter["metricTransformations"][0]["metricName"],
70
+ "metricNamespace": filter["metricTransformations"][0]["metricNamespace"],
71
+ "metricValue": filter["metricTransformations"][0]["metricValue"],
72
+ "Region": region,
73
+ }
74
+ transformed_filters.append(transformed_filter)
75
+ return transformed_filters
76
+
77
+
34
78
  @timeit
35
79
  def load_cloudwatch_log_groups(
36
80
  neo4j_session: neo4j.Session,
@@ -52,6 +96,27 @@ def load_cloudwatch_log_groups(
52
96
  )
53
97
 
54
98
 
99
+ @timeit
100
+ def load_cloudwatch_log_metric_filters(
101
+ neo4j_session: neo4j.Session,
102
+ data: List[Dict[str, Any]],
103
+ region: str,
104
+ current_aws_account_id: str,
105
+ aws_update_tag: int,
106
+ ) -> None:
107
+ logger.info(
108
+ f"Loading CloudWatch {len(data)} log metric filters for region '{region}' into graph.",
109
+ )
110
+ load(
111
+ neo4j_session,
112
+ CloudWatchLogMetricFilterSchema(),
113
+ data,
114
+ lastupdated=aws_update_tag,
115
+ Region=region,
116
+ AWS_ID=current_aws_account_id,
117
+ )
118
+
119
+
55
120
  @timeit
56
121
  def cleanup(
57
122
  neo4j_session: neo4j.Session,
@@ -62,6 +127,9 @@ def cleanup(
62
127
  CloudWatchLogGroupSchema(), common_job_parameters
63
128
  )
64
129
  cleanup_job.run(neo4j_session)
130
+ GraphJob.from_node_schema(
131
+ CloudWatchLogMetricFilterSchema(), common_job_parameters
132
+ ).run(neo4j_session)
65
133
 
66
134
 
67
135
  @timeit
@@ -90,4 +158,13 @@ def sync(
90
158
  update_tag,
91
159
  )
92
160
 
161
+ metric_filters = get_cloudwatch_log_metric_filters(boto3_session, region)
162
+ transformed_filters = transform_metric_filters(metric_filters, region)
163
+ load_cloudwatch_log_metric_filters(
164
+ neo4j_session,
165
+ transformed_filters,
166
+ region,
167
+ current_aws_account_id,
168
+ update_tag,
169
+ )
93
170
  cleanup(neo4j_session, common_job_parameters)
@@ -53,7 +53,7 @@ def load_subnets(
53
53
  snet.state = subnet.State, snet.assignipv6addressoncreation = subnet.AssignIpv6AddressOnCreation,
54
54
  snet.map_public_ip_on_launch = subnet.MapPublicIpOnLaunch, snet.subnet_arn = subnet.SubnetArn,
55
55
  snet.availability_zone = subnet.AvailabilityZone, snet.availability_zone_id = subnet.AvailabilityZoneId,
56
- snet.subnetid = subnet.SubnetId
56
+ snet.subnet_id = subnet.SubnetId
57
57
  """
58
58
 
59
59
  ingest_subnet_vpc_relations = """
@@ -169,6 +169,22 @@ def _get_containers_from_tasks(tasks: list[dict[str, Any]]) -> list[dict[str, An
169
169
  return containers
170
170
 
171
171
 
172
+ def transform_ecs_tasks(tasks: list[dict[str, Any]]) -> list[dict[str, Any]]:
173
+ """
174
+ Extract network interface ID from task attachments.
175
+ """
176
+ for task in tasks:
177
+ for attachment in task.get("attachments", []):
178
+ if attachment.get("type") == "ElasticNetworkInterface":
179
+ details = attachment.get("details", [])
180
+ for detail in details:
181
+ if detail.get("name") == "networkInterfaceId":
182
+ task["networkInterfaceId"] = detail.get("value")
183
+ break
184
+ break
185
+ return tasks
186
+
187
+
172
188
  @timeit
173
189
  def load_ecs_clusters(
174
190
  neo4j_session: neo4j.Session,
@@ -407,6 +423,7 @@ def _sync_ecs_task_and_container_defns(
407
423
  boto3_session,
408
424
  region,
409
425
  )
426
+ tasks = transform_ecs_tasks(tasks)
410
427
  containers = _get_containers_from_tasks(tasks)
411
428
  load_ecs_tasks(
412
429
  neo4j_session,
@@ -3,14 +3,17 @@ from typing import Any
3
3
  from typing import Dict
4
4
  from typing import Iterator
5
5
  from typing import List
6
+ from typing import Set
6
7
  from typing import Tuple
7
8
 
8
9
  import boto3
9
10
  import neo4j
10
11
 
11
12
  from cartography.client.core.tx import load
13
+ from cartography.client.core.tx import load_matchlinks
12
14
  from cartography.graph.job import GraphJob
13
15
  from cartography.models.aws.inspector.findings import AWSInspectorFindingSchema
16
+ from cartography.models.aws.inspector.findings import InspectorFindingToPackageMatchLink
14
17
  from cartography.models.aws.inspector.packages import AWSInspectorPackageSchema
15
18
  from cartography.util import aws_handle_regions
16
19
  from cartography.util import aws_paginate
@@ -107,9 +110,10 @@ def get_inspector_findings(
107
110
 
108
111
  def transform_inspector_findings(
109
112
  results: List[Dict[str, Any]],
110
- ) -> Tuple[List[Dict[str, Any]], List[Dict[str, Any]]]:
113
+ ) -> Tuple[List[Dict[str, Any]], List[Dict[str, Any]], List[Dict[str, str]]]:
111
114
  findings_list: List[Dict] = []
112
- packages: Dict[str, Any] = {}
115
+ packages_set: Set[frozenset] = set()
116
+ finding_to_package_map: List[Dict[str, str]] = []
113
117
 
114
118
  for f in results:
115
119
  finding: Dict = {}
@@ -163,55 +167,45 @@ def transform_inspector_findings(
163
167
  "vendorUpdatedAt",
164
168
  )
165
169
 
166
- new_packages = _process_packages(
167
- f["packageVulnerabilityDetails"],
168
- f["awsAccountId"],
169
- f["findingArn"],
170
- )
171
- finding["vulnerablepackageids"] = list(new_packages.keys())
172
- packages = {**packages, **new_packages}
173
-
170
+ packages = transform_inspector_packages(f["packageVulnerabilityDetails"])
171
+ finding["vulnerablepackageids"] = list(packages.keys())
172
+ for package_id, package in packages.items():
173
+ finding_to_package_map.append(
174
+ {
175
+ "findingarn": finding["id"],
176
+ "packageid": package_id,
177
+ "remediation": package.get("remediation"),
178
+ "fixedInVersion": package.get("fixedInVersion"),
179
+ "filePath": package.get("filePath"),
180
+ "sourceLayerHash": package.get("sourceLayerHash"),
181
+ "sourceLambdaLayerArn": package.get("sourceLambdaLayerArn"),
182
+ }
183
+ )
184
+ packages_set.add(frozenset(package.items()))
174
185
  findings_list.append(finding)
175
- packages_list = transform_inspector_packages(packages)
176
- return findings_list, packages_list
177
-
186
+ packages_list = [dict(p) for p in packages_set]
187
+ return findings_list, packages_list, finding_to_package_map
178
188
 
179
- def transform_inspector_packages(packages: Dict[str, Any]) -> List[Dict[str, Any]]:
180
- packages_list: List[Dict] = []
181
- for package_id in packages.keys():
182
- packages_list.append(packages[package_id])
183
189
 
184
- return packages_list
185
-
186
-
187
- def _process_packages(
190
+ def transform_inspector_packages(
188
191
  package_details: Dict[str, Any],
189
- aws_account_id: str,
190
- finding_arn: str,
191
192
  ) -> Dict[str, Any]:
192
193
  packages: Dict[str, Any] = {}
193
194
  for package in package_details["vulnerablePackages"]:
194
- new_package = {}
195
- new_package["id"] = (
196
- f"{package.get('name', '')}|"
197
- f"{package.get('arch', '')}|"
198
- f"{package.get('version', '')}|"
199
- f"{package.get('release', '')}|"
200
- f"{package.get('epoch', '')}"
201
- )
202
- new_package["name"] = package.get("name")
203
- new_package["arch"] = package.get("arch")
204
- new_package["version"] = package.get("version")
205
- new_package["release"] = package.get("release")
206
- new_package["epoch"] = package.get("epoch")
207
- new_package["manager"] = package.get("packageManager")
208
- new_package["filepath"] = package.get("filePath")
209
- new_package["fixedinversion"] = package.get("fixedInVersion")
210
- new_package["sourcelayerhash"] = package.get("sourceLayerHash")
211
- new_package["awsaccount"] = aws_account_id
212
- new_package["findingarn"] = finding_arn
213
-
214
- packages[new_package["id"]] = new_package
195
+ # Following RPM package naming convention for consistency
196
+ name = package["name"] # Mandatory field
197
+ epoch = str(package.get("epoch", ""))
198
+ if epoch:
199
+ epoch = f"{epoch}:"
200
+ version = package["version"] # Mandatory field
201
+ release = package.get("release", "")
202
+ if release:
203
+ release = f"-{release}"
204
+ arch = package.get("arch", "")
205
+ if arch:
206
+ arch = f".{arch}"
207
+ id = f"{name}|{epoch}{version}{release}{arch}"
208
+ packages[id] = {**package, "id": id}
215
209
 
216
210
  return packages
217
211
 
@@ -244,7 +238,6 @@ def load_inspector_findings(
244
238
  def load_inspector_packages(
245
239
  neo4j_session: neo4j.Session,
246
240
  packages: List[Dict[str, Any]],
247
- region: str,
248
241
  aws_update_tag: int,
249
242
  current_aws_account_id: str,
250
243
  ) -> None:
@@ -252,12 +245,28 @@ def load_inspector_packages(
252
245
  neo4j_session,
253
246
  AWSInspectorPackageSchema(),
254
247
  packages,
255
- Region=region,
256
248
  AWS_ID=current_aws_account_id,
257
249
  lastupdated=aws_update_tag,
258
250
  )
259
251
 
260
252
 
253
+ @timeit
254
+ def load_inspector_finding_to_package_match_links(
255
+ neo4j_session: neo4j.Session,
256
+ finding_to_package_map: List[Dict[str, str]],
257
+ aws_update_tag: int,
258
+ current_aws_account_id: str,
259
+ ) -> None:
260
+ load_matchlinks(
261
+ neo4j_session,
262
+ InspectorFindingToPackageMatchLink(),
263
+ finding_to_package_map,
264
+ lastupdated=aws_update_tag,
265
+ _sub_resource_label="AWSAccount",
266
+ _sub_resource_id=current_aws_account_id,
267
+ )
268
+
269
+
261
270
  @timeit
262
271
  def cleanup(
263
272
  neo4j_session: neo4j.Session,
@@ -270,6 +279,14 @@ def cleanup(
270
279
  GraphJob.from_node_schema(AWSInspectorPackageSchema(), common_job_parameters).run(
271
280
  neo4j_session,
272
281
  )
282
+ GraphJob.from_matchlink(
283
+ InspectorFindingToPackageMatchLink(),
284
+ "AWSAccount",
285
+ common_job_parameters["ACCOUNT_ID"],
286
+ common_job_parameters["UPDATE_TAG"],
287
+ ).run(
288
+ neo4j_session,
289
+ )
273
290
 
274
291
 
275
292
  def _sync_findings_for_account(
@@ -288,7 +305,9 @@ def _sync_findings_for_account(
288
305
  logger.info(f"No findings to sync for account {account_id} in region {region}")
289
306
  return
290
307
  for f_batch in findings:
291
- finding_data, package_data = transform_inspector_findings(f_batch)
308
+ finding_data, package_data, finding_to_package_map = (
309
+ transform_inspector_findings(f_batch)
310
+ )
292
311
  logger.info(f"Loading {len(finding_data)} findings from account {account_id}")
293
312
  load_inspector_findings(
294
313
  neo4j_session,
@@ -301,7 +320,15 @@ def _sync_findings_for_account(
301
320
  load_inspector_packages(
302
321
  neo4j_session,
303
322
  package_data,
304
- region,
323
+ update_tag,
324
+ current_aws_account_id,
325
+ )
326
+ logger.info(
327
+ f"Loading {len(finding_to_package_map)} finding to package relationships"
328
+ )
329
+ load_inspector_finding_to_package_match_links(
330
+ neo4j_session,
331
+ finding_to_package_map,
305
332
  update_tag,
306
333
  current_aws_account_id,
307
334
  )
@@ -337,5 +364,7 @@ def sync(
337
364
  update_tag,
338
365
  current_aws_account_id,
339
366
  )
367
+ common_job_parameters["ACCOUNT_ID"] = current_aws_account_id
368
+ common_job_parameters["UPDATE_TAG"] = update_tag
340
369
 
341
370
  cleanup(neo4j_session, common_job_parameters)
@@ -3,6 +3,7 @@ import logging
3
3
  import neo4j
4
4
 
5
5
  import cartography.intel.sentinelone.agent
6
+ import cartography.intel.sentinelone.application
6
7
  from cartography.config import Config
7
8
  from cartography.intel.sentinelone.account import sync_accounts
8
9
  from cartography.stats import get_stats_client
@@ -39,7 +40,7 @@ def start_sentinelone_ingestion(neo4j_session: neo4j.Session, config: Config) ->
39
40
  config.sentinelone_account_ids,
40
41
  )
41
42
 
42
- # Sync agents for each account
43
+ # Sync agents and applications for each account
43
44
  for account_id in synced_account_ids:
44
45
  # Add account-specific parameter
45
46
  common_job_parameters["S1_ACCOUNT_ID"] = account_id
@@ -49,7 +50,12 @@ def start_sentinelone_ingestion(neo4j_session: neo4j.Session, config: Config) ->
49
50
  common_job_parameters,
50
51
  )
51
52
 
52
- # Clean up account-specific parameter
53
+ cartography.intel.sentinelone.application.sync(
54
+ neo4j_session,
55
+ common_job_parameters,
56
+ )
57
+
58
+ # Clean up account-specific parameters
53
59
  del common_job_parameters["S1_ACCOUNT_ID"]
54
60
 
55
61
  # Record that the sync is complete
@@ -0,0 +1,248 @@
1
+ import logging
2
+ from typing import Any
3
+
4
+ import neo4j
5
+
6
+ from cartography.client.core.tx import load
7
+ from cartography.graph.job import GraphJob
8
+ from cartography.intel.sentinelone.api import get_paginated_results
9
+ from cartography.intel.sentinelone.utils import get_application_id
10
+ from cartography.intel.sentinelone.utils import get_application_version_id
11
+ from cartography.models.sentinelone.application import S1ApplicationSchema
12
+ from cartography.models.sentinelone.application_version import (
13
+ S1ApplicationVersionSchema,
14
+ )
15
+ from cartography.util import timeit
16
+
17
+ logger = logging.getLogger(__name__)
18
+
19
+
20
+ @timeit
21
+ def get_application_data(
22
+ account_id: str, api_url: str, api_token: str
23
+ ) -> list[dict[str, Any]]:
24
+ """
25
+ Get application data from SentinelOne API
26
+ :param account_id: SentinelOne account ID
27
+ :param api_url: SentinelOne API URL
28
+ :param api_token: SentinelOne API token
29
+ :return: A list of application data dictionaries
30
+ """
31
+ logger.info(f"Retrieving SentinelOne application data for account {account_id}")
32
+ applications = get_paginated_results(
33
+ api_url=api_url,
34
+ endpoint="/web/api/v2.1/application-management/inventory",
35
+ api_token=api_token,
36
+ params={
37
+ "accountIds": account_id,
38
+ "limit": 1000,
39
+ },
40
+ )
41
+
42
+ logger.info(f"Retrieved {len(applications)} applications from SentinelOne")
43
+ return applications
44
+
45
+
46
+ @timeit
47
+ def get_application_installs(
48
+ app_inventory: list[dict[str, Any]], account_id: str, api_url: str, api_token: str
49
+ ) -> list[dict[str, Any]]:
50
+ """
51
+ Get application installs from SentinelOne API
52
+ :param app_inventory: List of applications to get installs for
53
+ :param account_id: SentinelOne account ID
54
+ :param api_url: SentinelOne API URL
55
+ :param api_token: SentinelOne API token
56
+ :return: A list of application installs data dictionaries
57
+ """
58
+ logger.info(
59
+ f"Retrieving SentinelOne application installs for "
60
+ f"{len(app_inventory)} applications in account "
61
+ f"{account_id}",
62
+ )
63
+
64
+ application_installs = []
65
+ for i, app in enumerate(app_inventory):
66
+ logger.info(
67
+ f"Retrieving SentinelOne installs for {app.get('applicationName')} "
68
+ f"{app.get('applicationVendor')} ({i + 1}/{len(app_inventory)})",
69
+ )
70
+ name = app["applicationName"]
71
+ vendor = app["applicationVendor"]
72
+ app_installs = get_paginated_results(
73
+ api_url=api_url,
74
+ endpoint="/web/api/v2.1/application-management/inventory/endpoints",
75
+ api_token=api_token,
76
+ params={
77
+ "accountIds": account_id,
78
+ "limit": 1000,
79
+ "applicationName": name,
80
+ "applicationVendor": vendor,
81
+ },
82
+ )
83
+
84
+ # Replace applicationVendor and applicationName with original values
85
+ # for consistency with the application data
86
+ for app_install in app_installs:
87
+ app_install["applicationVendor"] = vendor
88
+ app_install["applicationName"] = name
89
+ application_installs.extend(app_installs)
90
+
91
+ return application_installs
92
+
93
+
94
+ def transform_applications(
95
+ applications_list: list[dict[str, Any]],
96
+ ) -> list[dict[str, Any]]:
97
+ """
98
+ Transform SentinelOne application data for loading into Neo4j
99
+ :param applications_list: Raw application data from the API
100
+ :return: Transformed application data
101
+ """
102
+ transformed_data = []
103
+ for app in applications_list:
104
+ vendor = app["applicationVendor"].strip()
105
+ name = app["applicationName"].strip()
106
+ app_id = get_application_id(name, vendor)
107
+ transformed_app = {
108
+ "id": app_id,
109
+ "name": name,
110
+ "vendor": vendor,
111
+ }
112
+ transformed_data.append(transformed_app)
113
+
114
+ return transformed_data
115
+
116
+
117
+ def transform_application_versions(
118
+ application_installs_list: list[dict[str, Any]],
119
+ ) -> list[dict[str, Any]]:
120
+ """
121
+ Transform SentinelOne application installs for loading into Neo4j
122
+ :param application_installs_list: Raw application installs data from the API
123
+ :return: Transformed application installs data
124
+ """
125
+ transformed_data = []
126
+ for installed_version in application_installs_list:
127
+ app_name = installed_version["applicationName"]
128
+ app_vendor = installed_version["applicationVendor"]
129
+ version = installed_version["version"]
130
+
131
+ transformed_version = {
132
+ "id": get_application_version_id(
133
+ app_name,
134
+ app_vendor,
135
+ version,
136
+ ),
137
+ "application_id": get_application_id(
138
+ app_name,
139
+ app_vendor,
140
+ ),
141
+ "application_name": app_name,
142
+ "application_vendor": app_vendor,
143
+ "agent_uuid": installed_version.get("endpointUuid"),
144
+ "installation_path": installed_version.get("applicationInstallationPath"),
145
+ "installed_dt": installed_version.get("applicationInstallationDate"),
146
+ "version": version,
147
+ }
148
+ transformed_data.append(transformed_version)
149
+
150
+ return transformed_data
151
+
152
+
153
+ @timeit
154
+ def load_application_data(
155
+ neo4j_session: neo4j.Session,
156
+ data: list[dict[str, Any]],
157
+ account_id: str,
158
+ update_tag: int,
159
+ ) -> None:
160
+ """
161
+ Load SentinelOne application data into Neo4j
162
+ :param neo4j_session: Neo4j session
163
+ :param data: The transformed application data
164
+ :param account_id: The SentinelOne account ID
165
+ :param update_tag: Update tag to set on the nodes
166
+ :return: None
167
+ """
168
+ logger.info(f"Loading {len(data)} SentinelOne applications into Neo4j")
169
+ load(
170
+ neo4j_session,
171
+ S1ApplicationSchema(),
172
+ data,
173
+ lastupdated=update_tag,
174
+ S1_ACCOUNT_ID=account_id,
175
+ )
176
+
177
+
178
+ @timeit
179
+ def load_application_versions(
180
+ neo4j_session: neo4j.Session,
181
+ data: list[dict[str, Any]],
182
+ account_id: str,
183
+ update_tag: int,
184
+ ) -> None:
185
+ logger.info(f"Loading {len(data)} SentinelOne application versions into Neo4j")
186
+ load(
187
+ neo4j_session,
188
+ S1ApplicationVersionSchema(),
189
+ data,
190
+ lastupdated=update_tag,
191
+ S1_ACCOUNT_ID=account_id,
192
+ )
193
+
194
+
195
+ @timeit
196
+ def cleanup(
197
+ neo4j_session: neo4j.Session, common_job_parameters: dict[str, Any]
198
+ ) -> None:
199
+ logger.debug("Running SentinelOne S1Application cleanup")
200
+ GraphJob.from_node_schema(S1ApplicationSchema(), common_job_parameters).run(
201
+ neo4j_session
202
+ )
203
+ logger.debug("Running SentinelOne S1ApplicationVersion cleanup")
204
+ GraphJob.from_node_schema(S1ApplicationVersionSchema(), common_job_parameters).run(
205
+ neo4j_session
206
+ )
207
+
208
+
209
+ @timeit
210
+ def sync(
211
+ neo4j_session: neo4j.Session,
212
+ common_job_parameters: dict[str, Any],
213
+ ) -> None:
214
+ """
215
+ Sync SentinelOne applications
216
+ :param neo4j_session: Neo4j session
217
+ :param common_job_parameters: Common job parameters containing API_URL, API_TOKEN, etc.
218
+ :return: None
219
+ """
220
+ logger.info("Syncing SentinelOne application data")
221
+ account_id = str(common_job_parameters["S1_ACCOUNT_ID"])
222
+ api_url = str(common_job_parameters["API_URL"])
223
+ api_token = str(common_job_parameters["API_TOKEN"])
224
+
225
+ applications = get_application_data(account_id, api_url, api_token)
226
+ application_versions = get_application_installs(
227
+ applications, account_id, api_url, api_token
228
+ )
229
+ transformed_applications = transform_applications(applications)
230
+ transformed_application_versions = transform_application_versions(
231
+ application_versions
232
+ )
233
+
234
+ load_application_data(
235
+ neo4j_session,
236
+ transformed_applications,
237
+ account_id,
238
+ common_job_parameters["UPDATE_TAG"],
239
+ )
240
+
241
+ load_application_versions(
242
+ neo4j_session,
243
+ transformed_application_versions,
244
+ account_id,
245
+ common_job_parameters["UPDATE_TAG"],
246
+ )
247
+
248
+ cleanup(neo4j_session, common_job_parameters)
@@ -2,8 +2,27 @@ import re
2
2
 
3
3
 
4
4
  def get_application_id(name: str, vendor: str) -> str:
5
+ """
6
+ Generate a normalized unique identifier for an application.
7
+ :param name: Application name
8
+ :param vendor: Application vendor/publisher
9
+ :return: Normalized unique identifier in format 'vendor:name'
10
+ """
5
11
  name_normalized = name.strip().lower().replace(" ", "_")
6
12
  vendor_normalized = vendor.strip().lower().replace(" ", "_")
7
13
  name_normalized = re.sub(r"[^\w]", "", name_normalized)
8
- vendor_normalized = re.sub(r"[^\w\s]", "", vendor_normalized)
14
+ vendor_normalized = re.sub(r"[^\w]", "", vendor_normalized)
9
15
  return f"{vendor_normalized}:{name_normalized}"
16
+
17
+
18
+ def get_application_version_id(name: str, vendor: str, version: str) -> str:
19
+ """
20
+ Generate a unique identifier for an application version
21
+ :param name: Application name
22
+ :param vendor: Application vendor
23
+ :param version: Application version
24
+ :return: Unique identifier for the application version in format 'vendor:name:version'
25
+ """
26
+ app_id = get_application_id(name, vendor)
27
+ version_normalized = version.strip().lower().replace(" ", "_")
28
+ return f"{app_id}:{version_normalized}"
@@ -0,0 +1,79 @@
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 CloudWatchLogMetricFilterNodeProperties(CartographyNodeProperties):
16
+ id: PropertyRef = PropertyRef("id")
17
+ arn: PropertyRef = PropertyRef("filterName", extra_index=True)
18
+ region: PropertyRef = PropertyRef("Region", set_in_kwargs=True)
19
+ filter_name: PropertyRef = PropertyRef("filterName")
20
+ filter_pattern: PropertyRef = PropertyRef("filterPattern")
21
+ log_group_name: PropertyRef = PropertyRef("logGroupName")
22
+ metric_name: PropertyRef = PropertyRef("metricName")
23
+ metric_namespace: PropertyRef = PropertyRef("metricNamespace")
24
+ metric_value: PropertyRef = PropertyRef("metricValue")
25
+ lastupdated: PropertyRef = PropertyRef("lastupdated", set_in_kwargs=True)
26
+
27
+
28
+ @dataclass(frozen=True)
29
+ class CloudWatchLogMetricFilterToAwsAccountRelProperties(CartographyRelProperties):
30
+ lastupdated: PropertyRef = PropertyRef("lastupdated", set_in_kwargs=True)
31
+
32
+
33
+ @dataclass(frozen=True)
34
+ class CloudWatchLogMetricFilterToAWSAccountRel(CartographyRelSchema):
35
+ target_node_label: str = "AWSAccount"
36
+ target_node_matcher: TargetNodeMatcher = make_target_node_matcher(
37
+ {"id": PropertyRef("AWS_ID", set_in_kwargs=True)},
38
+ )
39
+ direction: LinkDirection = LinkDirection.INWARD
40
+ rel_label: str = "RESOURCE"
41
+ properties: CloudWatchLogMetricFilterToAwsAccountRelProperties = (
42
+ CloudWatchLogMetricFilterToAwsAccountRelProperties()
43
+ )
44
+
45
+
46
+ @dataclass(frozen=True)
47
+ class CloudWatchLogMetricFilterToCloudWatchLogGroupRelProperties(
48
+ CartographyRelProperties
49
+ ):
50
+ lastupdated: PropertyRef = PropertyRef("lastupdated", set_in_kwargs=True)
51
+
52
+
53
+ @dataclass(frozen=True)
54
+ class CloudWatchLogMetricFilterToCloudWatchLogGroupRel(CartographyRelSchema):
55
+ target_node_label: str = "CloudWatchLogGroup"
56
+ target_node_matcher: TargetNodeMatcher = make_target_node_matcher(
57
+ {"log_group_name": PropertyRef("logGroupName")},
58
+ )
59
+ direction: LinkDirection = LinkDirection.OUTWARD
60
+ rel_label: str = "METRIC_FILTER_OF"
61
+ properties: CloudWatchLogMetricFilterToCloudWatchLogGroupRelProperties = (
62
+ CloudWatchLogMetricFilterToCloudWatchLogGroupRelProperties()
63
+ )
64
+
65
+
66
+ @dataclass(frozen=True)
67
+ class CloudWatchLogMetricFilterSchema(CartographyNodeSchema):
68
+ label: str = "CloudWatchLogMetricFilter"
69
+ properties: CloudWatchLogMetricFilterNodeProperties = (
70
+ CloudWatchLogMetricFilterNodeProperties()
71
+ )
72
+ sub_resource_relationship: CloudWatchLogMetricFilterToAWSAccountRel = (
73
+ CloudWatchLogMetricFilterToAWSAccountRel()
74
+ )
75
+ other_relationships: OtherRelationships = OtherRelationships(
76
+ [
77
+ CloudWatchLogMetricFilterToCloudWatchLogGroupRel(),
78
+ ]
79
+ )
@@ -44,7 +44,9 @@ class EC2NetworkInterfaceNodeProperties(CartographyNodeProperties):
44
44
  requester_id: PropertyRef = PropertyRef("RequesterId", extra_index=True)
45
45
  requester_managed: PropertyRef = PropertyRef("RequesterManaged")
46
46
  source_dest_check: PropertyRef = PropertyRef("SourceDestCheck")
47
+ # TODO: remove subnetid once we have migrated to subnet_id
47
48
  subnetid: PropertyRef = PropertyRef("SubnetId", extra_index=True)
49
+ subnet_id: PropertyRef = PropertyRef("SubnetId", extra_index=True)
48
50
 
49
51
 
50
52
  @dataclass(frozen=True)
@@ -15,7 +15,9 @@ from cartography.models.core.relationships import TargetNodeMatcher
15
15
  class EC2SubnetInstanceNodeProperties(CartographyNodeProperties):
16
16
  # arn: PropertyRef = PropertyRef('Arn', extra_index=True) TODO use arn; issue #1024
17
17
  id: PropertyRef = PropertyRef("SubnetId")
18
+ # TODO: remove subnetid once we have migrated to subnet_id
18
19
  subnetid: PropertyRef = PropertyRef("SubnetId", extra_index=True)
20
+ subnet_id: PropertyRef = PropertyRef("SubnetId", extra_index=True)
19
21
  region: PropertyRef = PropertyRef("Region", set_in_kwargs=True)
20
22
  lastupdated: PropertyRef = PropertyRef("lastupdated", set_in_kwargs=True)
21
23
 
@@ -16,6 +16,8 @@ from cartography.models.core.relationships import TargetNodeMatcher
16
16
  @dataclass(frozen=True)
17
17
  class EC2SubnetNetworkInterfaceNodeProperties(CartographyNodeProperties):
18
18
  id: PropertyRef = PropertyRef("SubnetId")
19
+ # TODO: remove subnetid once we have migrated to subnet_id
20
+ subnetid: PropertyRef = PropertyRef("SubnetId", extra_index=True)
19
21
  subnet_id: PropertyRef = PropertyRef("SubnetId", extra_index=True)
20
22
  region: PropertyRef = PropertyRef("Region", set_in_kwargs=True)
21
23
  lastupdated: PropertyRef = PropertyRef("lastupdated", set_in_kwargs=True)
@@ -46,6 +46,7 @@ class ECSTaskNodeProperties(CartographyNodeProperties):
46
46
  ephemeral_storage_size_in_gib: PropertyRef = PropertyRef(
47
47
  "ephemeralStorage.sizeInGiB"
48
48
  )
49
+ network_interface_id: PropertyRef = PropertyRef("networkInterfaceId")
49
50
  region: PropertyRef = PropertyRef("Region", set_in_kwargs=True)
50
51
  lastupdated: PropertyRef = PropertyRef("lastupdated", set_in_kwargs=True)
51
52
 
@@ -100,11 +101,33 @@ class ECSTaskToAWSAccountRel(CartographyRelSchema):
100
101
  properties: ECSTaskToAWSAccountRelProperties = ECSTaskToAWSAccountRelProperties()
101
102
 
102
103
 
104
+ @dataclass(frozen=True)
105
+ class ECSTaskToNetworkInterfaceRelProperties(CartographyRelProperties):
106
+ lastupdated: PropertyRef = PropertyRef("lastupdated", set_in_kwargs=True)
107
+
108
+
109
+ @dataclass(frozen=True)
110
+ class ECSTaskToNetworkInterfaceRel(CartographyRelSchema):
111
+ target_node_label: str = "NetworkInterface"
112
+ target_node_matcher: TargetNodeMatcher = make_target_node_matcher(
113
+ {"id": PropertyRef("networkInterfaceId")}
114
+ )
115
+ direction: LinkDirection = LinkDirection.OUTWARD
116
+ rel_label: str = "NETWORK_INTERFACE"
117
+ properties: ECSTaskToNetworkInterfaceRelProperties = (
118
+ ECSTaskToNetworkInterfaceRelProperties()
119
+ )
120
+
121
+
103
122
  @dataclass(frozen=True)
104
123
  class ECSTaskSchema(CartographyNodeSchema):
105
124
  label: str = "ECSTask"
106
125
  properties: ECSTaskNodeProperties = ECSTaskNodeProperties()
107
126
  sub_resource_relationship: ECSTaskToAWSAccountRel = ECSTaskToAWSAccountRel()
108
127
  other_relationships: OtherRelationships = OtherRelationships(
109
- [ECSTaskToContainerInstanceRel(), ECSTaskToECSClusterRel()]
128
+ [
129
+ ECSTaskToContainerInstanceRel(),
130
+ ECSTaskToECSClusterRel(),
131
+ ECSTaskToNetworkInterfaceRel(),
132
+ ]
110
133
  )
@@ -7,8 +7,10 @@ from cartography.models.core.nodes import ExtraNodeLabels
7
7
  from cartography.models.core.relationships import CartographyRelProperties
8
8
  from cartography.models.core.relationships import CartographyRelSchema
9
9
  from cartography.models.core.relationships import LinkDirection
10
+ from cartography.models.core.relationships import make_source_node_matcher
10
11
  from cartography.models.core.relationships import make_target_node_matcher
11
12
  from cartography.models.core.relationships import OtherRelationships
13
+ from cartography.models.core.relationships import SourceNodeMatcher
12
14
  from cartography.models.core.relationships import TargetNodeMatcher
13
15
 
14
16
 
@@ -135,6 +137,40 @@ class InspectorFindingToECRImageRel(CartographyRelSchema):
135
137
  )
136
138
 
137
139
 
140
+ @dataclass(frozen=True)
141
+ class InspectorFindingToPackageRelRelProperties(CartographyRelProperties):
142
+ lastupdated: PropertyRef = PropertyRef("lastupdated", set_in_kwargs=True)
143
+ _sub_resource_label: PropertyRef = PropertyRef(
144
+ "_sub_resource_label", set_in_kwargs=True
145
+ )
146
+ _sub_resource_id: PropertyRef = PropertyRef("_sub_resource_id", set_in_kwargs=True)
147
+ # The following properties live in vulnerablePackages from AWS API
148
+ # Adding them here to avoid multiple repetion of packages
149
+ filepath: PropertyRef = PropertyRef("filePath")
150
+ fixedinversion: PropertyRef = PropertyRef("fixedInVersion")
151
+ remediation: PropertyRef = PropertyRef("remediation")
152
+ sourcelayerhash: PropertyRef = PropertyRef("sourceLayerHash")
153
+ sourcelambdalayerarn: PropertyRef = PropertyRef("sourceLambdaLayerArn")
154
+
155
+
156
+ @dataclass(frozen=True)
157
+ # (:AWSInspectorFinding)-[:HAS]->(:AWSInspectorPackage)
158
+ class InspectorFindingToPackageMatchLink(CartographyRelSchema):
159
+ target_node_label: str = "AWSInspectorPackage"
160
+ target_node_matcher: TargetNodeMatcher = make_target_node_matcher(
161
+ {"id": PropertyRef("packageid")},
162
+ )
163
+ source_node_label: str = "AWSInspectorFinding"
164
+ source_node_matcher: SourceNodeMatcher = make_source_node_matcher(
165
+ {"id": PropertyRef("findingarn")},
166
+ )
167
+ properties: InspectorFindingToPackageRelRelProperties = (
168
+ InspectorFindingToPackageRelRelProperties()
169
+ )
170
+ direction: LinkDirection = LinkDirection.OUTWARD
171
+ rel_label: str = "HAS"
172
+
173
+
138
174
  @dataclass(frozen=True)
139
175
  class AWSInspectorFindingSchema(CartographyNodeSchema):
140
176
  label: str = "AWSInspectorFinding"
@@ -146,6 +182,7 @@ class AWSInspectorFindingSchema(CartographyNodeSchema):
146
182
  other_relationships: OtherRelationships = OtherRelationships(
147
183
  [
148
184
  InspectorFindingToEC2InstanceRel(),
185
+ # TODO: Fix ECRRepository and ECRImage relationships
149
186
  InspectorFindingToECRRepositoryRel(),
150
187
  InspectorFindingToECRImageRel(),
151
188
  InspectorFindingToAWSAccountRelDelegateRel(),
@@ -7,25 +7,18 @@ from cartography.models.core.relationships import CartographyRelProperties
7
7
  from cartography.models.core.relationships import CartographyRelSchema
8
8
  from cartography.models.core.relationships import LinkDirection
9
9
  from cartography.models.core.relationships import make_target_node_matcher
10
- from cartography.models.core.relationships import OtherRelationships
11
10
  from cartography.models.core.relationships import TargetNodeMatcher
12
11
 
13
12
 
14
13
  @dataclass(frozen=True)
15
14
  class AWSInspectorPackageNodeProperties(CartographyNodeProperties):
16
15
  id: PropertyRef = PropertyRef("id")
17
- region: PropertyRef = PropertyRef("Region", set_in_kwargs=True)
18
- awsaccount: PropertyRef = PropertyRef("AWS_ID", set_in_kwargs=True)
19
- findingarn: PropertyRef = PropertyRef("findingarn", extra_index=True)
20
16
  name: PropertyRef = PropertyRef("name", extra_index=True)
21
- arch: PropertyRef = PropertyRef("arch")
22
17
  version: PropertyRef = PropertyRef("version", extra_index=True)
23
18
  release: PropertyRef = PropertyRef("release", extra_index=True)
19
+ arch: PropertyRef = PropertyRef("arch")
24
20
  epoch: PropertyRef = PropertyRef("epoch")
25
21
  manager: PropertyRef = PropertyRef("packageManager")
26
- filepath: PropertyRef = PropertyRef("filePath")
27
- fixedinversion: PropertyRef = PropertyRef("fixedInVersion")
28
- sourcelayerhash: PropertyRef = PropertyRef("sourceLayerHash")
29
22
  lastupdated: PropertyRef = PropertyRef("lastupdated", set_in_kwargs=True)
30
23
 
31
24
 
@@ -47,24 +40,6 @@ class InspectorPackageToAWSAccountRel(CartographyRelSchema):
47
40
  )
48
41
 
49
42
 
50
- @dataclass(frozen=True)
51
- class InspectorPackageToFindingRelRelProperties(CartographyRelProperties):
52
- lastupdated: PropertyRef = PropertyRef("lastupdated", set_in_kwargs=True)
53
-
54
-
55
- @dataclass(frozen=True)
56
- class InspectorPackageToFindingRel(CartographyRelSchema):
57
- target_node_label: str = "AWSInspectorFinding"
58
- target_node_matcher: TargetNodeMatcher = make_target_node_matcher(
59
- {"id": PropertyRef("findingarn")},
60
- )
61
- direction: LinkDirection = LinkDirection.INWARD
62
- rel_label: str = "HAS"
63
- properties: InspectorPackageToFindingRelRelProperties = (
64
- InspectorPackageToFindingRelRelProperties()
65
- )
66
-
67
-
68
43
  @dataclass(frozen=True)
69
44
  class AWSInspectorPackageSchema(CartographyNodeSchema):
70
45
  label: str = "AWSInspectorPackage"
@@ -72,8 +47,3 @@ class AWSInspectorPackageSchema(CartographyNodeSchema):
72
47
  sub_resource_relationship: InspectorPackageToAWSAccountRel = (
73
48
  InspectorPackageToAWSAccountRel()
74
49
  )
75
- other_relationships: OtherRelationships = OtherRelationships(
76
- [
77
- InspectorPackageToFindingRel(),
78
- ],
79
- )
@@ -0,0 +1,44 @@
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 TargetNodeMatcher
11
+
12
+
13
+ @dataclass(frozen=True)
14
+ class S1ApplicationNodeProperties(CartographyNodeProperties):
15
+ id: PropertyRef = PropertyRef("id")
16
+ name: PropertyRef = PropertyRef("name")
17
+ vendor: PropertyRef = PropertyRef("vendor")
18
+ lastupdated: PropertyRef = PropertyRef("lastupdated", set_in_kwargs=True)
19
+
20
+
21
+ @dataclass(frozen=True)
22
+ class S1ApplicationToAccountRelProperties(CartographyRelProperties):
23
+ lastupdated: PropertyRef = PropertyRef("lastupdated", set_in_kwargs=True)
24
+
25
+
26
+ @dataclass(frozen=True)
27
+ # (:S1Application)<-[:RESOURCE]-(:S1Account)
28
+ class S1ApplicationToAccount(CartographyRelSchema):
29
+ target_node_label: str = "S1Account"
30
+ target_node_matcher: TargetNodeMatcher = make_target_node_matcher(
31
+ {"id": PropertyRef("S1_ACCOUNT_ID", set_in_kwargs=True)},
32
+ )
33
+ direction: LinkDirection = LinkDirection.INWARD
34
+ rel_label: str = "RESOURCE"
35
+ properties: S1ApplicationToAccountRelProperties = (
36
+ S1ApplicationToAccountRelProperties()
37
+ )
38
+
39
+
40
+ @dataclass(frozen=True)
41
+ class S1ApplicationSchema(CartographyNodeSchema):
42
+ label: str = "S1Application"
43
+ properties: S1ApplicationNodeProperties = S1ApplicationNodeProperties()
44
+ sub_resource_relationship: S1ApplicationToAccount = S1ApplicationToAccount()
@@ -0,0 +1,96 @@
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 S1ApplicationVersionNodeProperties(CartographyNodeProperties):
16
+ id: PropertyRef = PropertyRef("id", extra_index=True)
17
+ lastupdated: PropertyRef = PropertyRef("lastupdated", set_in_kwargs=True)
18
+ application_name: PropertyRef = PropertyRef("application_name")
19
+ application_vendor: PropertyRef = PropertyRef("application_vendor")
20
+ version: PropertyRef = PropertyRef("version")
21
+
22
+
23
+ @dataclass(frozen=True)
24
+ class S1ApplicationVersionToAccountRelProperties(CartographyRelProperties):
25
+ lastupdated: PropertyRef = PropertyRef("lastupdated", set_in_kwargs=True)
26
+
27
+
28
+ @dataclass(frozen=True)
29
+ # (:S1ApplicationVersion)<-[:RESOURCE]-(:S1Account)
30
+ class S1ApplicationVersionToAccount(CartographyRelSchema):
31
+ target_node_label: str = "S1Account"
32
+ target_node_matcher: TargetNodeMatcher = make_target_node_matcher(
33
+ {"id": PropertyRef("S1_ACCOUNT_ID", set_in_kwargs=True)},
34
+ )
35
+ direction: LinkDirection = LinkDirection.INWARD
36
+ rel_label: str = "RESOURCE"
37
+ properties: S1ApplicationVersionToAccountRelProperties = (
38
+ S1ApplicationVersionToAccountRelProperties()
39
+ )
40
+
41
+
42
+ @dataclass(frozen=True)
43
+ class S1AgentToApplicationVersionRelProperties(CartographyRelProperties):
44
+ lastupdated: PropertyRef = PropertyRef("lastupdated", set_in_kwargs=True)
45
+ installeddatetime: PropertyRef = PropertyRef("installed_dt")
46
+ installationpath: PropertyRef = PropertyRef("installation_path")
47
+
48
+
49
+ @dataclass(frozen=True)
50
+ # (:S1Agent)-[:HAS_INSTALLED]->(:S1ApplicationVersion)
51
+ class S1AgentToS1ApplicationVersion(CartographyRelSchema):
52
+ target_node_label: str = "S1Agent"
53
+ target_node_matcher: TargetNodeMatcher = make_target_node_matcher(
54
+ {"uuid": PropertyRef("agent_uuid")},
55
+ )
56
+ direction: LinkDirection = LinkDirection.INWARD
57
+ rel_label: str = "HAS_INSTALLED"
58
+ properties: S1AgentToApplicationVersionRelProperties = (
59
+ S1AgentToApplicationVersionRelProperties()
60
+ )
61
+
62
+
63
+ @dataclass(frozen=True)
64
+ class S1ApplicationVersionToApplicationRelProperties(CartographyRelProperties):
65
+ lastupdated: PropertyRef = PropertyRef("lastupdated", set_in_kwargs=True)
66
+
67
+
68
+ @dataclass(frozen=True)
69
+ # (:S1ApplicationVersion)<-[:VERSION]-(:S1Application)
70
+ class S1ApplicationVersionToApplication(CartographyRelSchema):
71
+ target_node_label: str = "S1Application"
72
+ target_node_matcher: TargetNodeMatcher = make_target_node_matcher(
73
+ {"id": PropertyRef("application_id")},
74
+ )
75
+ direction: LinkDirection = LinkDirection.INWARD
76
+ rel_label: str = "VERSION"
77
+ properties: S1ApplicationVersionToApplicationRelProperties = (
78
+ S1ApplicationVersionToApplicationRelProperties()
79
+ )
80
+
81
+
82
+ @dataclass(frozen=True)
83
+ class S1ApplicationVersionSchema(CartographyNodeSchema):
84
+ label: str = "S1ApplicationVersion"
85
+ properties: S1ApplicationVersionNodeProperties = (
86
+ S1ApplicationVersionNodeProperties()
87
+ )
88
+ sub_resource_relationship: S1ApplicationVersionToAccount = (
89
+ S1ApplicationVersionToAccount()
90
+ )
91
+ other_relationships: OtherRelationships = OtherRelationships(
92
+ [
93
+ S1AgentToS1ApplicationVersion(),
94
+ S1ApplicationVersionToApplication(),
95
+ ],
96
+ )
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cartography
3
- Version: 0.107.0rc1
3
+ Version: 0.107.0rc3
4
4
  Summary: Explore assets and their relationships across your technical infrastructure.
5
5
  Maintainer: Cartography Contributors
6
6
  License: apache2
@@ -1,6 +1,6 @@
1
1
  cartography/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
2
  cartography/__main__.py,sha256=y5iqUrj4BmqZfjeDT-9balzpXeMARgHeIedRMRI1gr8,100
3
- cartography/_version.py,sha256=p-Djna7oRUSPYcKo49GYVaSUJK3PW0h7Jw79Ngeb9Mo,525
3
+ cartography/_version.py,sha256=Mw8xqndv8cO5u8s9TF2NfNZPshP9CgkSEhbYGTmeNck,525
4
4
  cartography/cli.py,sha256=e5F9239v_JoLR293JMZI1toYg43li2RX_F-qOHTB-HA,46054
5
5
  cartography/config.py,sha256=mM7Frg8maGB4a0Oad2nvktM38uC7zgrwSmofO5l4Aus,16492
6
6
  cartography/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -158,12 +158,12 @@ cartography/intel/aws/acm.py,sha256=a_i2pYPpocjlL8itwlcrmg8KrSLfjcmrkhcrPP-Omj8,
158
158
  cartography/intel/aws/apigateway.py,sha256=hOYVSY70DbrEfhi9gkX7PAMtg9WiPE0Pbp2wqGes7Vs,12198
159
159
  cartography/intel/aws/cloudtrail.py,sha256=kip3O39ulRmuo9RpfkofzUcGnkNnF8ksD7hP0Qe75-A,2441
160
160
  cartography/intel/aws/cloudtrail_management_events.py,sha256=_kJR3N4eHm1c1OgObsUgAdB11UQjN91B9B0U3kuk0_o,13036
161
- cartography/intel/aws/cloudwatch.py,sha256=k9A8h__dE8N2LeF0o8lpJcNqNAaTCG1Ip3PgAF9mnlQ,2488
161
+ cartography/intel/aws/cloudwatch.py,sha256=Irocf84_n-JqFIbTf9tPkpJe5cpKSubd5daE7G8298Q,5107
162
162
  cartography/intel/aws/codebuild.py,sha256=8I-Xzm_c5-ixnGOOHIQLYYpClnaGjjHrEMjQ0-DGsgM,3958
163
163
  cartography/intel/aws/config.py,sha256=IIICicLQ0OTT3H3o8LDakIkA1BPUMwSyzpKonet-PaY,7658
164
164
  cartography/intel/aws/dynamodb.py,sha256=VvvjeUgi1ZrqY9flXIQnfhhaSVSEqXXHW6T9917VLBk,5728
165
165
  cartography/intel/aws/ecr.py,sha256=7a9EHZru6AUIGE_6MJ4h4_ZmrvBxsXaerCHtGr59lJ0,8393
166
- cartography/intel/aws/ecs.py,sha256=-RPYdc6KC5fbaosaGsgWRkuLoHnE71euud4ZC1wGJI8,13464
166
+ cartography/intel/aws/ecs.py,sha256=h5Nn09MzqPftbXV46jybjsxnYVYW1Vit41kCkaFIq_4,14105
167
167
  cartography/intel/aws/efs.py,sha256=6ZvFmVHLfZlyo8xx2dlRsC1IoVOpBOtsij_AdFczkDo,7884
168
168
  cartography/intel/aws/eks.py,sha256=bPItyEj5q0nSDltJrr0S5MIrTPV0fK3xkqF6EV8fcqA,3759
169
169
  cartography/intel/aws/elasticache.py,sha256=dpRJCYxgUW2ImgGMt4L54xFil8apUoJxZq6hpewXxAE,4590
@@ -172,7 +172,7 @@ cartography/intel/aws/emr.py,sha256=EJoKjHQP7-F_A1trUNU05Sb42yNR1i0C9VIpGcCfAXw,
172
172
  cartography/intel/aws/iam.py,sha256=bkyIzWw2OC4MHxuoCvTiZ5eEGEQhz2asiUgY_tkX2GY,34322
173
173
  cartography/intel/aws/iam_instance_profiles.py,sha256=QUyu30xid60BFaemp-q0y9FgNsHaIQyQnQ8gHUzWC4k,2211
174
174
  cartography/intel/aws/identitycenter.py,sha256=C2EOfwQO1zBjePAXtreUcJIy7RU__ior3liRT4SpGO8,9544
175
- cartography/intel/aws/inspector.py,sha256=rD_O3DUGv5-GSMNqDzb11Ps5bX1sIo_JDq3UTAGvUpE,11168
175
+ cartography/intel/aws/inspector.py,sha256=G8a60uuFCuENvR2DNJ9VmWEJwG9UDENaC1buULvuSZc,12415
176
176
  cartography/intel/aws/kms.py,sha256=RtCxNG-Den-f4Il-NO3YL_-BFUmg1qFt4lNucue9SQM,12130
177
177
  cartography/intel/aws/lambda_function.py,sha256=cutCsMnvyJB8vKUP0fHORrhijw944cXSoLKOHYdi6SM,10389
178
178
  cartography/intel/aws/organizations.py,sha256=m5qk60N6pe7iKLN-Rtfg9aYv7OozoloSkcsuePd-RGs,4680
@@ -205,7 +205,7 @@ cartography/intel/aws/ec2/reserved_instances.py,sha256=BsshVUzkuOstbidYEGq0By2Y-
205
205
  cartography/intel/aws/ec2/route_tables.py,sha256=NSW7B-4wII4mf5ClX5F1q4cKfSNAjxBSnrdk4EhxXz0,10465
206
206
  cartography/intel/aws/ec2/security_groups.py,sha256=JT3UFD-LgcdH85bel0GomHDEkCntPjvH0IXl9-ELMqI,6447
207
207
  cartography/intel/aws/ec2/snapshots.py,sha256=BiTvxR8ibt7wdBTNNpz75zRqc1Iiy6ksmowBSyBUhEU,5685
208
- cartography/intel/aws/ec2/subnets.py,sha256=R7qr4h0HQvwybjnKr9NN5UmY0tV6NovuJGOhEtHymPM,4141
208
+ cartography/intel/aws/ec2/subnets.py,sha256=LDuWdkQH-2O8TmqYlvyIn3STUgP6vlhKjG8o8XNMuGE,4142
209
209
  cartography/intel/aws/ec2/tgw.py,sha256=FcXWgLkr4EcD9DRXJMeyWQIgIHidaq40UrZneKhvnuE,9621
210
210
  cartography/intel/aws/ec2/util.py,sha256=t6oJX9nCTejvp0D9ihxfhmCoD1aoOXQB0cuvz0pMCe0,226
211
211
  cartography/intel/aws/ec2/volumes.py,sha256=C5V9LRE41REU4siHMAaE9yAjtbpLi8yTkaqLEw1uXMg,3726
@@ -329,11 +329,12 @@ cartography/intel/semgrep/__init__.py,sha256=zNAH5iDbLeLeQex2L5RZytGVFY4OHYvfGuE
329
329
  cartography/intel/semgrep/dependencies.py,sha256=C_gAVOx8wN70sqvFUQD9fZ3ijv_D4W_-EiLrZ4qsvHY,8429
330
330
  cartography/intel/semgrep/deployment.py,sha256=RdLuTxSyKiIaAuposppp7Vjjq4w9M9QFSXmHgGmSWkY,2020
331
331
  cartography/intel/semgrep/findings.py,sha256=EtNwK__L1Af45xilyQPnMzu40mjWVfiCaw4WL-MWH7c,10036
332
- cartography/intel/sentinelone/__init__.py,sha256=0iHDpFG5y3T8jso76sqAcvESu04KehDPW92JhbjtCm4,1978
332
+ cartography/intel/sentinelone/__init__.py,sha256=P6XGOQNNyjXp465rNOAXBbWMfEElPym-vLnayup8jM8,2174
333
333
  cartography/intel/sentinelone/account.py,sha256=FHWAl6iqpVUKTSNr0u6iAoZ7HgZezvY5JaNzYLs6GdY,4404
334
334
  cartography/intel/sentinelone/agent.py,sha256=7ZAG1D6X-7LkhZ2Yjatm84VvFybfNEmILqky0XsYGwQ,4386
335
335
  cartography/intel/sentinelone/api.py,sha256=2SUD5ukrUt8-JcMkJ6e0_ZUwh68DwBDssbUZMYInDR0,3042
336
- cartography/intel/sentinelone/utils.py,sha256=IH0E-jYRQFonqwO86QCgVGbiPJl7O1RUzO_BylKBRvo,371
336
+ cartography/intel/sentinelone/application.py,sha256=11V57OtQqcW6_K6NxIjQQVKfU-91qbQhDxxBu30RBAI,7951
337
+ cartography/intel/sentinelone/utils.py,sha256=asKaF2aHNcoutu89z_aXqG7lOmZ55TJJTNd90FJtBW8,1112
337
338
  cartography/intel/snipeit/__init__.py,sha256=XIBEduCoQirs1NuOJ02XKvcBAc0pyeFMJNJsn4iJfXc,1102
338
339
  cartography/intel/snipeit/asset.py,sha256=kQQQwYPM6Pm7bJRSMguKmFLg0s6AD6RG1nU5LLLbhVk,2043
339
340
  cartography/intel/snipeit/user.py,sha256=qO-uwbwyM_Ft95-q-yWUxOL9UdtGpxSn4XM5T-evdNs,1987
@@ -374,6 +375,7 @@ cartography/models/aws/cloudtrail/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeR
374
375
  cartography/models/aws/cloudtrail/management_events.py,sha256=RKAHm6RGVJBaOS90Csa7Vg1LEgq4lXY9YnRIMD2iLwM,2816
375
376
  cartography/models/aws/cloudtrail/trail.py,sha256=qQnpqFypKef8E7-JOR83euAtI6_j2KdjzGGFiQa6xkA,3676
376
377
  cartography/models/aws/cloudwatch/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
378
+ cartography/models/aws/cloudwatch/log_metric_filter.py,sha256=hROU3pp_D_q21dAPTddm4lcAcCZaDV_UJ49QAZ6SCLc,3321
377
379
  cartography/models/aws/cloudwatch/loggroup.py,sha256=_MRpRvU_Vg2rVlUdzfae6vOpbpmYaYE1EVN5zWucFbE,2456
378
380
  cartography/models/aws/codebuild/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
379
381
  cartography/models/aws/codebuild/project.py,sha256=r_DMCtHlbG9FGUk_I8j_E1inIbFcGAXtL6HxHrcgg0U,2122
@@ -395,7 +397,7 @@ cartography/models/aws/ec2/loadbalancerv2.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRk
395
397
  cartography/models/aws/ec2/network_acl_rules.py,sha256=G2YJNOY67XQWfQbHQXkuquabiLQJp4smleLn_pdFrGY,4034
396
398
  cartography/models/aws/ec2/network_acls.py,sha256=HFInVqHmINe2kWnsbJe8rGng-UHsZDkhRXRnd1nI0v4,3532
397
399
  cartography/models/aws/ec2/networkinterface_instance.py,sha256=Gq2vGL7waocwsYRxMwOCV2vqBpUhtJDFilnylkMiKn4,4849
398
- cartography/models/aws/ec2/networkinterfaces.py,sha256=FJ9FCUlqKnmjTB5W7MK-iKtl3l5mB7Tik697mZ2riB4,4270
400
+ cartography/models/aws/ec2/networkinterfaces.py,sha256=Q1AodKLX7IJWp1Z6tP2uCl21uSjdtZiX8SZImBCIbWo,4404
399
401
  cartography/models/aws/ec2/privateip_networkinterface.py,sha256=8Dt2r0sELvTAYYsUX13X-SKLJ6LDpTGmoAyLdNVYcBY,3128
400
402
  cartography/models/aws/ec2/reservations.py,sha256=Lep6M-srupbAZZzCFmOpiwz22AafJLwKzSAuuxe3BlA,1980
401
403
  cartography/models/aws/ec2/route_table_associations.py,sha256=Rs9uG976zZazcUIGL5oGpLkB72gLxWHFNCUqrvjMTvg,3974
@@ -403,8 +405,8 @@ cartography/models/aws/ec2/route_tables.py,sha256=R0gcwaOsLXRjpuvL9Pq-uGosetZysH
403
405
  cartography/models/aws/ec2/routes.py,sha256=T_PR1-pf_ZiE_w3zNaHaksXI5sIsaDwmH3sRctlP4uc,3725
404
406
  cartography/models/aws/ec2/securitygroup_instance.py,sha256=j-g1CQHYFTb0D0YsLP5QLy-lBJ0IUc3xTtaX8r9nzIY,2974
405
407
  cartography/models/aws/ec2/securitygroup_networkinterface.py,sha256=2MBxYXxuq_L0COeC04SgFfwxeXw-pc4N4JAH9oZyWQE,2481
406
- cartography/models/aws/ec2/subnet_instance.py,sha256=gAw2TJIlBnBNWn83l60N_Pi5Ni4oNe2lbZpg-u39BU8,2818
407
- cartography/models/aws/ec2/subnet_networkinterface.py,sha256=YCfribtM4gEIb2Jan3xoBZke_IeNnejB95unfnIrX3Y,3739
408
+ cartography/models/aws/ec2/subnet_instance.py,sha256=6bgrWbFcCjBIjqd_6lcKv6rWyll-Zx1aA5TK68DcIhg,2952
409
+ cartography/models/aws/ec2/subnet_networkinterface.py,sha256=B60S1YvEopVWNlRL5W-hMby3-P06uaNcC_8oOLoRGik,3872
408
410
  cartography/models/aws/ec2/volumes.py,sha256=ITSJ_1HgrU5wTF1zZV8sYRZsI0QzktoHkvTgpnfM1hI,4547
409
411
  cartography/models/aws/ecs/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
410
412
  cartography/models/aws/ecs/clusters.py,sha256=uu-UzkSbpLu4UG5I6NsFMaThrPYl6jYe71EX8baMy1Q,2845
@@ -413,7 +415,7 @@ cartography/models/aws/ecs/container_instances.py,sha256=LhBkyEWfOSejD57AERoQYVE
413
415
  cartography/models/aws/ecs/containers.py,sha256=jmMdaonho4VI-TdZqBgTc1Ck-6XqI4O0hKRh1Ioerc4,3955
414
416
  cartography/models/aws/ecs/services.py,sha256=YPVTS0BB3X6tFCqIX3V1AZXcnHQEADusQkErd88oH7o,4964
415
417
  cartography/models/aws/ecs/task_definitions.py,sha256=Wee44GZazQfZQ1TRRZr8bYXwSGgNNDuYxHRBhOmvWzg,5573
416
- cartography/models/aws/ecs/tasks.py,sha256=6r2WZDc26LGR2j-U5s5v4d4PbMIvEDSkLAbTtpFdrUM,5013
418
+ cartography/models/aws/ecs/tasks.py,sha256=E_wx1lwV0b9MMgFW-nBZFfq_AhFTV38-NC1LKGxs3lE,5809
417
419
  cartography/models/aws/efs/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
418
420
  cartography/models/aws/efs/access_point.py,sha256=Auk8E0AoPjrCxch8bP8fhT3JZ2WxblTTfYSqamA_p8g,3185
419
421
  cartography/models/aws/efs/file_system.py,sha256=6BNzjQJKZ-rYlDLw1UvDBhVRKre07-9EkcW9xvR3Wh0,2842
@@ -427,8 +429,8 @@ cartography/models/aws/identitycenter/awsidentitycenter.py,sha256=-ybcEgdZjwF0RI
427
429
  cartography/models/aws/identitycenter/awspermissionset.py,sha256=12kDKzsJQOHg6d9dlX98sM9gKXGMQh7FaxzlJov7Dgo,3651
428
430
  cartography/models/aws/identitycenter/awsssouser.py,sha256=EWnyX5ev2aaK64YjKGuJSoknBF5b_kS5xc0kDf_0xfo,2886
429
431
  cartography/models/aws/inspector/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
430
- cartography/models/aws/inspector/findings.py,sha256=z6fVygv8rmlYB1huX64jDgrCEjoTdLkoeKIVeoMk9wI,6469
431
- cartography/models/aws/inspector/packages.py,sha256=4TWu9EiS8aOR-n5z0FqG7564t-GKS4zrD4THX_2NKRo,3348
432
+ cartography/models/aws/inspector/findings.py,sha256=ikm0vvjhGK_-BDt5VGyS4_0KwJsq81T6VgROq9HK2NE,8188
433
+ cartography/models/aws/inspector/packages.py,sha256=IoJT7w6n8Vx3vGGJPWKDgJLRpioqtOS5iMYZLnX7thg,2113
432
434
  cartography/models/aws/s3/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
433
435
  cartography/models/aws/s3/account_public_access_block.py,sha256=L6AS0OncfSOWHP4pOXshnJFDPwnWqywzUIeUppJcw-Q,2256
434
436
  cartography/models/aws/s3/notification.py,sha256=SF1VvCP_2WVh8K29d4ms8MUcg9AyO_MN8vCgzLFlGAs,1017
@@ -527,6 +529,8 @@ cartography/models/semgrep/locations.py,sha256=uC0QCsqSWV0ZCvQuR3OPpecImbumNLpRq
527
529
  cartography/models/sentinelone/__init__.py,sha256=DNFTywfYLRp-cX6S6zbVRkFmjud5MVtMM4Fro8PTU4Q,26
528
530
  cartography/models/sentinelone/account.py,sha256=bUh07nyPXEMXVx8kCqT0-gX9tBH8-GufcQvC2TlHnP0,1370
529
531
  cartography/models/sentinelone/agent.py,sha256=3FTJQQ_QRq9GGeIjuV0_8ZeJcnKWGHHC3exddZldqCY,2287
532
+ cartography/models/sentinelone/application.py,sha256=g77cmjuTmiX_AwgZ7-x5eP-DsKXoCjzeAQuYtuCAcyo,1811
533
+ cartography/models/sentinelone/application_version.py,sha256=ICxaz1rndwBpaJUgbdTshhn-cbcU_KbV7VeKyUqsdoQ,3829
530
534
  cartography/models/snipeit/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
531
535
  cartography/models/snipeit/asset.py,sha256=64Cq6ff0jemj_fvhQ_-B1xEHqsZ95RqtcbDSTzCI_00,3312
532
536
  cartography/models/snipeit/tenant.py,sha256=E6uaY3d2W3MmfuUqF2TLpRP3T1QZkoIXRtp9BGxxSxk,695
@@ -542,9 +546,9 @@ cartography/models/trivy/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZ
542
546
  cartography/models/trivy/findings.py,sha256=SgI_h1aRyR20uAHvuXIZ1T6r4IZJt6SVhxRaF2bTsm0,3085
543
547
  cartography/models/trivy/fix.py,sha256=ho9ENVl9HSXqyggyCwR6ilkOBKDxpQ7rGibo_j21NA4,2587
544
548
  cartography/models/trivy/package.py,sha256=IwO1RZZ-MFRtNbt8Cq6YFl6fdNJMFmULnJkkK8Q4rL4,2809
545
- cartography-0.107.0rc1.dist-info/licenses/LICENSE,sha256=kvLEBRYaQ1RvUni6y7Ti9uHeooqnjPoo6n_-0JO1ETc,11351
546
- cartography-0.107.0rc1.dist-info/METADATA,sha256=4iitm8S4B9hie7yMMuR5tGdbOPOvBPqucuCHqXUXVnM,12861
547
- cartography-0.107.0rc1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
548
- cartography-0.107.0rc1.dist-info/entry_points.txt,sha256=GVIAWD0o0_K077qMA_k1oZU4v-M0a8GLKGJR8tZ-qH8,112
549
- cartography-0.107.0rc1.dist-info/top_level.txt,sha256=BHqsNJQiI6Q72DeypC1IINQJE59SLhU4nllbQjgJi9g,12
550
- cartography-0.107.0rc1.dist-info/RECORD,,
549
+ cartography-0.107.0rc3.dist-info/licenses/LICENSE,sha256=kvLEBRYaQ1RvUni6y7Ti9uHeooqnjPoo6n_-0JO1ETc,11351
550
+ cartography-0.107.0rc3.dist-info/METADATA,sha256=JGMCRDlbdo4dqinKGOf1MUnv_1tQ-i4v38lYyJ1_jfg,12861
551
+ cartography-0.107.0rc3.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
552
+ cartography-0.107.0rc3.dist-info/entry_points.txt,sha256=GVIAWD0o0_K077qMA_k1oZU4v-M0a8GLKGJR8tZ-qH8,112
553
+ cartography-0.107.0rc3.dist-info/top_level.txt,sha256=BHqsNJQiI6Q72DeypC1IINQJE59SLhU4nllbQjgJi9g,12
554
+ cartography-0.107.0rc3.dist-info/RECORD,,