cartography 0.106.0rc1__py3-none-any.whl → 0.106.0rc2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of cartography might be problematic. Click here for more details.
- cartography/_version.py +2 -2
- cartography/client/core/tx.py +62 -0
- cartography/graph/cleanupbuilder.py +47 -0
- cartography/graph/job.py +42 -0
- cartography/graph/querybuilder.py +136 -2
- cartography/graph/statement.py +1 -1
- cartography/intel/aws/efs.py +80 -0
- cartography/intel/aws/inspector.py +3 -13
- cartography/models/aws/efs/access_point.py +77 -0
- cartography/models/core/common.py +1 -0
- cartography/models/core/relationships.py +44 -0
- {cartography-0.106.0rc1.dist-info → cartography-0.106.0rc2.dist-info}/METADATA +1 -1
- {cartography-0.106.0rc1.dist-info → cartography-0.106.0rc2.dist-info}/RECORD +17 -16
- {cartography-0.106.0rc1.dist-info → cartography-0.106.0rc2.dist-info}/WHEEL +0 -0
- {cartography-0.106.0rc1.dist-info → cartography-0.106.0rc2.dist-info}/entry_points.txt +0 -0
- {cartography-0.106.0rc1.dist-info → cartography-0.106.0rc2.dist-info}/licenses/LICENSE +0 -0
- {cartography-0.106.0rc1.dist-info → cartography-0.106.0rc2.dist-info}/top_level.txt +0 -0
cartography/_version.py
CHANGED
|
@@ -17,5 +17,5 @@ __version__: str
|
|
|
17
17
|
__version_tuple__: VERSION_TUPLE
|
|
18
18
|
version_tuple: VERSION_TUPLE
|
|
19
19
|
|
|
20
|
-
__version__ = version = '0.106.
|
|
21
|
-
__version_tuple__ = version_tuple = (0, 106, 0, '
|
|
20
|
+
__version__ = version = '0.106.0rc2'
|
|
21
|
+
__version_tuple__ = version_tuple = (0, 106, 0, 'rc2')
|
cartography/client/core/tx.py
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import logging
|
|
1
2
|
from typing import Any
|
|
2
3
|
from typing import Dict
|
|
3
4
|
from typing import List
|
|
@@ -8,10 +9,15 @@ from typing import Union
|
|
|
8
9
|
import neo4j
|
|
9
10
|
|
|
10
11
|
from cartography.graph.querybuilder import build_create_index_queries
|
|
12
|
+
from cartography.graph.querybuilder import build_create_index_queries_for_matchlink
|
|
11
13
|
from cartography.graph.querybuilder import build_ingestion_query
|
|
14
|
+
from cartography.graph.querybuilder import build_matchlink_query
|
|
12
15
|
from cartography.models.core.nodes import CartographyNodeSchema
|
|
16
|
+
from cartography.models.core.relationships import CartographyRelSchema
|
|
13
17
|
from cartography.util import batch
|
|
14
18
|
|
|
19
|
+
logger = logging.getLogger(__name__)
|
|
20
|
+
|
|
15
21
|
|
|
16
22
|
def read_list_of_values_tx(
|
|
17
23
|
tx: neo4j.Transaction,
|
|
@@ -255,6 +261,25 @@ def ensure_indexes(
|
|
|
255
261
|
neo4j_session.run(query)
|
|
256
262
|
|
|
257
263
|
|
|
264
|
+
def ensure_indexes_for_matchlinks(
|
|
265
|
+
neo4j_session: neo4j.Session,
|
|
266
|
+
rel_schema: CartographyRelSchema,
|
|
267
|
+
) -> None:
|
|
268
|
+
"""
|
|
269
|
+
Creates indexes for node fields if they don't exist for the given CartographyRelSchema object.
|
|
270
|
+
This is only used for load_rels() where we match on and connect existing nodes.
|
|
271
|
+
This is not used for CartographyNodeSchema objects.
|
|
272
|
+
"""
|
|
273
|
+
queries = build_create_index_queries_for_matchlink(rel_schema)
|
|
274
|
+
logger.debug(f"CREATE INDEX queries for {rel_schema.rel_label}: {queries}")
|
|
275
|
+
for query in queries:
|
|
276
|
+
if not query.startswith("CREATE INDEX IF NOT EXISTS"):
|
|
277
|
+
raise ValueError(
|
|
278
|
+
'Query provided to `ensure_indexes_for_matchlinks()` does not start with "CREATE INDEX IF NOT EXISTS".',
|
|
279
|
+
)
|
|
280
|
+
neo4j_session.run(query)
|
|
281
|
+
|
|
282
|
+
|
|
258
283
|
def load(
|
|
259
284
|
neo4j_session: neo4j.Session,
|
|
260
285
|
node_schema: CartographyNodeSchema,
|
|
@@ -276,3 +301,40 @@ def load(
|
|
|
276
301
|
ensure_indexes(neo4j_session, node_schema)
|
|
277
302
|
ingestion_query = build_ingestion_query(node_schema)
|
|
278
303
|
load_graph_data(neo4j_session, ingestion_query, dict_list, **kwargs)
|
|
304
|
+
|
|
305
|
+
|
|
306
|
+
def load_matchlinks(
|
|
307
|
+
neo4j_session: neo4j.Session,
|
|
308
|
+
rel_schema: CartographyRelSchema,
|
|
309
|
+
dict_list: list[dict[str, Any]],
|
|
310
|
+
**kwargs,
|
|
311
|
+
) -> None:
|
|
312
|
+
"""
|
|
313
|
+
Main entrypoint for intel modules to write relationships to the graph between two existing nodes.
|
|
314
|
+
:param neo4j_session: The Neo4j session
|
|
315
|
+
:param rel_schema: The CartographyRelSchema object to generate a query.
|
|
316
|
+
:param dict_list: The data to load to the graph represented as a list of dicts. The dicts must contain the source and
|
|
317
|
+
target node ids.
|
|
318
|
+
:param kwargs: Allows additional keyword args to be supplied to the Neo4j query.
|
|
319
|
+
:return: None
|
|
320
|
+
"""
|
|
321
|
+
if len(dict_list) == 0:
|
|
322
|
+
# If there is no data to load, save some time.
|
|
323
|
+
return
|
|
324
|
+
|
|
325
|
+
# Validate that required kwargs are provided for cleanup queries
|
|
326
|
+
if "_sub_resource_label" not in kwargs:
|
|
327
|
+
raise ValueError(
|
|
328
|
+
f"Required kwarg '_sub_resource_label' not provided for {rel_schema.rel_label}. "
|
|
329
|
+
"This is needed for cleanup queries."
|
|
330
|
+
)
|
|
331
|
+
if "_sub_resource_id" not in kwargs:
|
|
332
|
+
raise ValueError(
|
|
333
|
+
f"Required kwarg '_sub_resource_id' not provided for {rel_schema.rel_label}. "
|
|
334
|
+
"This is needed for cleanup queries."
|
|
335
|
+
)
|
|
336
|
+
|
|
337
|
+
ensure_indexes_for_matchlinks(neo4j_session, rel_schema)
|
|
338
|
+
matchlink_query = build_matchlink_query(rel_schema)
|
|
339
|
+
logger.debug(f"Matchlink query: {matchlink_query}")
|
|
340
|
+
load_graph_data(neo4j_session, matchlink_query, dict_list, **kwargs)
|
|
@@ -3,6 +3,7 @@ from string import Template
|
|
|
3
3
|
from typing import Dict
|
|
4
4
|
from typing import List
|
|
5
5
|
|
|
6
|
+
from cartography.graph.querybuilder import _asdict_with_validate_relprops
|
|
6
7
|
from cartography.graph.querybuilder import _build_match_clause
|
|
7
8
|
from cartography.graph.querybuilder import rel_present_on_node_schema
|
|
8
9
|
from cartography.models.core.common import PropertyRef
|
|
@@ -334,3 +335,49 @@ def _validate_target_node_matcher_for_cleanup_job(tgm: TargetNodeMatcher):
|
|
|
334
335
|
f"{key} has set_in_kwargs=False, please check by reviewing the full stack trace to know which object"
|
|
335
336
|
f"this message was raised from. Debug information: PropertyRef name = {prop_ref.name}.",
|
|
336
337
|
)
|
|
338
|
+
|
|
339
|
+
|
|
340
|
+
def build_cleanup_query_for_matchlink(rel_schema: CartographyRelSchema) -> str:
|
|
341
|
+
"""
|
|
342
|
+
Generates a cleanup query for a matchlink relationship.
|
|
343
|
+
:param rel_schema: The CartographyRelSchema object to generate a query. This CartographyRelSchema object
|
|
344
|
+
- Must have a source_node_matcher and source_node_label defined
|
|
345
|
+
- Must have a CartographyRelProperties object where _sub_resource_label and _sub_resource_id are defined
|
|
346
|
+
:return: A Neo4j query used to clean up stale matchlink relationships.
|
|
347
|
+
"""
|
|
348
|
+
if not rel_schema.source_node_matcher:
|
|
349
|
+
raise ValueError(
|
|
350
|
+
f"No source node matcher found for {rel_schema.rel_label}; returning empty list."
|
|
351
|
+
)
|
|
352
|
+
|
|
353
|
+
query_template = Template(
|
|
354
|
+
"""
|
|
355
|
+
MATCH (from:$source_node_label)$rel_direction[r:$rel_label]$rel_direction_end(to:$target_node_label)
|
|
356
|
+
WHERE r.lastupdated <> $UPDATE_TAG
|
|
357
|
+
AND r._sub_resource_label = $sub_resource_label
|
|
358
|
+
AND r._sub_resource_id = $sub_resource_id
|
|
359
|
+
WITH r LIMIT $LIMIT_SIZE
|
|
360
|
+
DELETE r;
|
|
361
|
+
"""
|
|
362
|
+
)
|
|
363
|
+
|
|
364
|
+
# Determine which way to point the arrow. INWARD is toward the source, otherwise we go toward the target.
|
|
365
|
+
if rel_schema.direction == LinkDirection.INWARD:
|
|
366
|
+
rel_direction = "<-"
|
|
367
|
+
rel_direction_end = "-"
|
|
368
|
+
else:
|
|
369
|
+
rel_direction = "-"
|
|
370
|
+
rel_direction_end = "->"
|
|
371
|
+
|
|
372
|
+
# Small hack: avoid type-checking errors by converting the rel_schema to a dict.
|
|
373
|
+
rel_props_as_dict = _asdict_with_validate_relprops(rel_schema)
|
|
374
|
+
|
|
375
|
+
return query_template.safe_substitute(
|
|
376
|
+
source_node_label=rel_schema.source_node_label,
|
|
377
|
+
target_node_label=rel_schema.target_node_label,
|
|
378
|
+
rel_label=rel_schema.rel_label,
|
|
379
|
+
rel_direction=rel_direction,
|
|
380
|
+
rel_direction_end=rel_direction_end,
|
|
381
|
+
sub_resource_label=rel_props_as_dict["_sub_resource_label"],
|
|
382
|
+
sub_resource_id=rel_props_as_dict["_sub_resource_id"],
|
|
383
|
+
)
|
cartography/graph/job.py
CHANGED
|
@@ -13,9 +13,11 @@ from typing import Union
|
|
|
13
13
|
import neo4j
|
|
14
14
|
|
|
15
15
|
from cartography.graph.cleanupbuilder import build_cleanup_queries
|
|
16
|
+
from cartography.graph.cleanupbuilder import build_cleanup_query_for_matchlink
|
|
16
17
|
from cartography.graph.statement import get_job_shortname
|
|
17
18
|
from cartography.graph.statement import GraphStatement
|
|
18
19
|
from cartography.models.core.nodes import CartographyNodeSchema
|
|
20
|
+
from cartography.models.core.relationships import CartographyRelSchema
|
|
19
21
|
|
|
20
22
|
logger = logging.getLogger(__name__)
|
|
21
23
|
|
|
@@ -176,6 +178,46 @@ class GraphJob:
|
|
|
176
178
|
node_schema.label,
|
|
177
179
|
)
|
|
178
180
|
|
|
181
|
+
@classmethod
|
|
182
|
+
def from_matchlink(
|
|
183
|
+
cls,
|
|
184
|
+
rel_schema: CartographyRelSchema,
|
|
185
|
+
sub_resource_label: str,
|
|
186
|
+
sub_resource_id: str,
|
|
187
|
+
update_tag: int,
|
|
188
|
+
) -> "GraphJob":
|
|
189
|
+
"""
|
|
190
|
+
Create a cleanup job from a CartographyRelSchema object (specifically, a MatchLink).
|
|
191
|
+
This is used for cleaning up stale links between nodes created by load_rels(). Do not use for other purposes.
|
|
192
|
+
|
|
193
|
+
Other notes:
|
|
194
|
+
- For a given rel_schema, the fields used in the rel_schema.properties._sub_resource_label.name and
|
|
195
|
+
rel_schema.properties._sub_resource_id.name must be provided as keys and values in the params dict.
|
|
196
|
+
- The rel_schema must have a source_node_matcher and target_node_matcher.
|
|
197
|
+
"""
|
|
198
|
+
cleanup_link_query = build_cleanup_query_for_matchlink(rel_schema)
|
|
199
|
+
logger.debug(f"Cleanup query: {cleanup_link_query}")
|
|
200
|
+
|
|
201
|
+
parameters = {
|
|
202
|
+
"UPDATE_TAG": update_tag,
|
|
203
|
+
"_sub_resource_label": sub_resource_label,
|
|
204
|
+
"_sub_resource_id": sub_resource_id,
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
statement = GraphStatement(
|
|
208
|
+
cleanup_link_query,
|
|
209
|
+
parameters=parameters,
|
|
210
|
+
iterative=True,
|
|
211
|
+
iterationsize=100,
|
|
212
|
+
parent_job_name=rel_schema.rel_label,
|
|
213
|
+
)
|
|
214
|
+
|
|
215
|
+
return cls(
|
|
216
|
+
f"Cleanup {rel_schema.rel_label} between {rel_schema.source_node_label} and {rel_schema.target_node_label}",
|
|
217
|
+
[statement],
|
|
218
|
+
rel_schema.rel_label,
|
|
219
|
+
)
|
|
220
|
+
|
|
179
221
|
@classmethod
|
|
180
222
|
def from_json_file(cls, file_path: Union[str, Path]) -> "GraphJob":
|
|
181
223
|
"""
|
|
@@ -14,6 +14,7 @@ from cartography.models.core.nodes import ExtraNodeLabels
|
|
|
14
14
|
from cartography.models.core.relationships import CartographyRelSchema
|
|
15
15
|
from cartography.models.core.relationships import LinkDirection
|
|
16
16
|
from cartography.models.core.relationships import OtherRelationships
|
|
17
|
+
from cartography.models.core.relationships import SourceNodeMatcher
|
|
17
18
|
from cartography.models.core.relationships import TargetNodeMatcher
|
|
18
19
|
|
|
19
20
|
logger = logging.getLogger(__name__)
|
|
@@ -109,10 +110,10 @@ def _build_rel_properties_statement(
|
|
|
109
110
|
return set_clause
|
|
110
111
|
|
|
111
112
|
|
|
112
|
-
def _build_match_clause(matcher: TargetNodeMatcher) -> str:
|
|
113
|
+
def _build_match_clause(matcher: TargetNodeMatcher | SourceNodeMatcher) -> str:
|
|
113
114
|
"""
|
|
114
115
|
Generate a Neo4j match statement on one or more keys and values for a given node.
|
|
115
|
-
:param matcher: A TargetNodeMatcher object
|
|
116
|
+
:param matcher: A TargetNodeMatcher or SourceNodeMatcher object
|
|
116
117
|
:return: a Neo4j match clause
|
|
117
118
|
"""
|
|
118
119
|
match = Template("$Key: $PropRef")
|
|
@@ -548,3 +549,136 @@ def build_create_index_queries(node_schema: CartographyNodeSchema) -> List[str]:
|
|
|
548
549
|
],
|
|
549
550
|
)
|
|
550
551
|
return result
|
|
552
|
+
|
|
553
|
+
|
|
554
|
+
def build_create_index_queries_for_matchlink(
|
|
555
|
+
rel_schema: CartographyRelSchema,
|
|
556
|
+
) -> list[str]:
|
|
557
|
+
"""
|
|
558
|
+
Generate queries to create indexes for the given CartographyRelSchema and all node types attached to it via its
|
|
559
|
+
relationships.
|
|
560
|
+
:param rel_schema: The CartographyRelSchema object
|
|
561
|
+
:return: A list of queries of the form `CREATE INDEX IF NOT EXISTS FOR (n:$TargetNodeLabel) ON (n.$TargetAttribute)`
|
|
562
|
+
"""
|
|
563
|
+
if not rel_schema.source_node_matcher:
|
|
564
|
+
logger.warning(
|
|
565
|
+
f"No source node matcher found for {rel_schema.rel_label}; returning empty list."
|
|
566
|
+
"Please note that build_create_index_queries_for_matchlink() is only used for load_matchlinks() where we match on "
|
|
567
|
+
"and connect existing nodes in the graph."
|
|
568
|
+
)
|
|
569
|
+
return []
|
|
570
|
+
|
|
571
|
+
index_template = Template(
|
|
572
|
+
"CREATE INDEX IF NOT EXISTS FOR (n:$NodeLabel) ON (n.$NodeAttribute);",
|
|
573
|
+
)
|
|
574
|
+
|
|
575
|
+
result = []
|
|
576
|
+
for source_key in asdict(rel_schema.source_node_matcher).keys():
|
|
577
|
+
result.append(
|
|
578
|
+
index_template.safe_substitute(
|
|
579
|
+
NodeLabel=rel_schema.source_node_label,
|
|
580
|
+
NodeAttribute=source_key,
|
|
581
|
+
),
|
|
582
|
+
)
|
|
583
|
+
for target_key in asdict(rel_schema.target_node_matcher).keys():
|
|
584
|
+
result.append(
|
|
585
|
+
index_template.safe_substitute(
|
|
586
|
+
NodeLabel=rel_schema.target_node_label,
|
|
587
|
+
NodeAttribute=target_key,
|
|
588
|
+
),
|
|
589
|
+
)
|
|
590
|
+
|
|
591
|
+
# Create a composite index for the relationship between the source and target nodes.
|
|
592
|
+
# https://neo4j.com/docs/cypher-manual/4.3/indexes-for-search-performance/#administration-indexes-create-a-composite-index-for-relationships
|
|
593
|
+
rel_index_template = Template(
|
|
594
|
+
"CREATE INDEX IF NOT EXISTS FOR ()$rel_direction[r:$RelLabel]$rel_direction_end() "
|
|
595
|
+
"ON (r.lastupdated, r._sub_resource_label, r._sub_resource_id);",
|
|
596
|
+
)
|
|
597
|
+
if rel_schema.direction == LinkDirection.INWARD:
|
|
598
|
+
result.append(
|
|
599
|
+
rel_index_template.safe_substitute(
|
|
600
|
+
RelLabel=rel_schema.rel_label,
|
|
601
|
+
rel_direction="<-",
|
|
602
|
+
rel_direction_end="-",
|
|
603
|
+
)
|
|
604
|
+
)
|
|
605
|
+
else:
|
|
606
|
+
result.append(
|
|
607
|
+
rel_index_template.safe_substitute(
|
|
608
|
+
RelLabel=rel_schema.rel_label,
|
|
609
|
+
rel_direction="-",
|
|
610
|
+
rel_direction_end="->",
|
|
611
|
+
)
|
|
612
|
+
)
|
|
613
|
+
return result
|
|
614
|
+
|
|
615
|
+
|
|
616
|
+
def build_matchlink_query(rel_schema: CartographyRelSchema) -> str:
|
|
617
|
+
"""
|
|
618
|
+
Generate a Neo4j query to link two existing nodes when given a CartographyRelSchema object.
|
|
619
|
+
This is only used for load_matchlinks().
|
|
620
|
+
:param rel_schema: The CartographyRelSchema object to generate a query. This CartographyRelSchema object
|
|
621
|
+
- Must have a source_node_matcher and source_node_label defined
|
|
622
|
+
- Must have a CartographyRelProperties object where _sub_resource_label and _sub_resource_id are defined
|
|
623
|
+
:return: A Neo4j query that can be used to link two existing nodes.
|
|
624
|
+
"""
|
|
625
|
+
if not rel_schema.source_node_matcher or not rel_schema.source_node_label:
|
|
626
|
+
raise ValueError(
|
|
627
|
+
f"No source node matcher or source node label found for {rel_schema.rel_label}. "
|
|
628
|
+
"MatchLink relationships require a source_node_matcher and source_node_label to be defined."
|
|
629
|
+
)
|
|
630
|
+
|
|
631
|
+
rel_props_as_dict = _asdict_with_validate_relprops(rel_schema)
|
|
632
|
+
|
|
633
|
+
# These are needed for the cleanup query
|
|
634
|
+
if "_sub_resource_label" not in rel_props_as_dict:
|
|
635
|
+
raise ValueError(
|
|
636
|
+
f"Expected _sub_resource_label to be defined on {rel_schema.properties.__class__.__name__}"
|
|
637
|
+
"Please include `_sub_resource_label: PropertyRef = PropertyRef('_sub_resource_label', set_in_kwargs=True)`"
|
|
638
|
+
)
|
|
639
|
+
if "_sub_resource_id" not in rel_props_as_dict:
|
|
640
|
+
raise ValueError(
|
|
641
|
+
f"Expected _sub_resource_id to be defined on {rel_schema.properties.__class__.__name__}"
|
|
642
|
+
"Please include `_sub_resource_id: PropertyRef = PropertyRef('_sub_resource_id', set_in_kwargs=True)`"
|
|
643
|
+
)
|
|
644
|
+
|
|
645
|
+
matchlink_query_template = Template(
|
|
646
|
+
"""
|
|
647
|
+
UNWIND $DictList as item
|
|
648
|
+
$source_match
|
|
649
|
+
$target_match
|
|
650
|
+
MERGE $rel
|
|
651
|
+
ON CREATE SET r.firstseen = timestamp()
|
|
652
|
+
SET
|
|
653
|
+
$set_rel_properties_statement;
|
|
654
|
+
"""
|
|
655
|
+
)
|
|
656
|
+
|
|
657
|
+
source_match = Template(
|
|
658
|
+
"MATCH (from:$source_node_label{$match_clause})"
|
|
659
|
+
).safe_substitute(
|
|
660
|
+
source_node_label=rel_schema.source_node_label,
|
|
661
|
+
match_clause=_build_match_clause(rel_schema.source_node_matcher),
|
|
662
|
+
)
|
|
663
|
+
|
|
664
|
+
target_match = Template(
|
|
665
|
+
"MATCH (to:$target_node_label{$match_clause})"
|
|
666
|
+
).safe_substitute(
|
|
667
|
+
target_node_label=rel_schema.target_node_label,
|
|
668
|
+
match_clause=_build_match_clause(rel_schema.target_node_matcher),
|
|
669
|
+
)
|
|
670
|
+
|
|
671
|
+
if rel_schema.direction == LinkDirection.INWARD:
|
|
672
|
+
rel = f"(from)<-[r:{rel_schema.rel_label}]-(to)"
|
|
673
|
+
else:
|
|
674
|
+
rel = f"(from)-[r:{rel_schema.rel_label}]->(to)"
|
|
675
|
+
|
|
676
|
+
return matchlink_query_template.safe_substitute(
|
|
677
|
+
source_match=source_match,
|
|
678
|
+
target_match=target_match,
|
|
679
|
+
rel=rel,
|
|
680
|
+
set_rel_properties_statement=_build_rel_properties_statement(
|
|
681
|
+
"r",
|
|
682
|
+
rel_props_as_dict,
|
|
683
|
+
),
|
|
684
|
+
)
|
cartography/graph/statement.py
CHANGED
|
@@ -56,7 +56,7 @@ class GraphStatement:
|
|
|
56
56
|
|
|
57
57
|
self.parent_job_name = parent_job_name if parent_job_name else None
|
|
58
58
|
self.parent_job_sequence_num = (
|
|
59
|
-
parent_job_sequence_num if parent_job_sequence_num else
|
|
59
|
+
parent_job_sequence_num if parent_job_sequence_num else 1
|
|
60
60
|
)
|
|
61
61
|
|
|
62
62
|
def merge_parameters(self, parameters: Dict) -> None:
|
cartography/intel/aws/efs.py
CHANGED
|
@@ -9,6 +9,7 @@ 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.efs.access_point import EfsAccessPointSchema
|
|
12
13
|
from cartography.models.aws.efs.file_system import EfsFileSystemSchema
|
|
13
14
|
from cartography.models.aws.efs.mount_target import EfsMountTargetSchema
|
|
14
15
|
from cartography.util import aws_handle_regions
|
|
@@ -44,6 +45,7 @@ def transform_efs_file_systems(
|
|
|
44
45
|
transformed_file_system = {
|
|
45
46
|
"FileSystemId": file_system["FileSystemId"],
|
|
46
47
|
"FileSystemArn": file_system["FileSystemArn"],
|
|
48
|
+
"Region": region,
|
|
47
49
|
"OwnerId": file_system.get("OwnerId"),
|
|
48
50
|
"CreationToken": file_system.get("CreationToken"),
|
|
49
51
|
"CreationTime": file_system.get("CreationTime"),
|
|
@@ -87,6 +89,49 @@ def get_efs_mount_targets(
|
|
|
87
89
|
return mountTargets
|
|
88
90
|
|
|
89
91
|
|
|
92
|
+
@timeit
|
|
93
|
+
@aws_handle_regions
|
|
94
|
+
def get_efs_access_points(
|
|
95
|
+
boto3_session: boto3.Session, region: str
|
|
96
|
+
) -> List[Dict[str, Any]]:
|
|
97
|
+
client = boto3_session.client(
|
|
98
|
+
"efs", region_name=region, config=get_botocore_config()
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
paginator = client.get_paginator("describe_access_points")
|
|
102
|
+
accessPoints = []
|
|
103
|
+
for page in paginator.paginate():
|
|
104
|
+
accessPoints.extend(page.get("AccessPoints", []))
|
|
105
|
+
|
|
106
|
+
return accessPoints
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def transform_efs_access_points(
|
|
110
|
+
accessPoints: List[Dict[str, Any]], region: str
|
|
111
|
+
) -> List[Dict[str, Any]]:
|
|
112
|
+
"""
|
|
113
|
+
Transform Efs Access Points data for ingestion
|
|
114
|
+
"""
|
|
115
|
+
transformed = []
|
|
116
|
+
for ap in accessPoints:
|
|
117
|
+
transformed.append(
|
|
118
|
+
{
|
|
119
|
+
"AccessPointArn": ap["AccessPointArn"],
|
|
120
|
+
"AccessPointId": ap["AccessPointId"],
|
|
121
|
+
"Region": region,
|
|
122
|
+
"FileSystemId": ap["FileSystemId"],
|
|
123
|
+
"Name": ap.get("Name"),
|
|
124
|
+
"LifeCycleState": ap.get("LifeCycleState"),
|
|
125
|
+
"OwnerId": ap.get("OwnerId"),
|
|
126
|
+
"Uid": ap.get("PosixUser", {}).get("Uid"),
|
|
127
|
+
"Gid": ap.get("PosixUser", {}).get("Gid"),
|
|
128
|
+
"RootDirectoryPath": ap.get("RootDirectory", {}).get("Path"),
|
|
129
|
+
}
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
return transformed
|
|
133
|
+
|
|
134
|
+
|
|
90
135
|
@timeit
|
|
91
136
|
def load_efs_mount_targets(
|
|
92
137
|
neo4j_session: neo4j.Session,
|
|
@@ -129,6 +174,27 @@ def load_efs_file_systems(
|
|
|
129
174
|
)
|
|
130
175
|
|
|
131
176
|
|
|
177
|
+
@timeit
|
|
178
|
+
def load_efs_access_points(
|
|
179
|
+
neo4j_session: neo4j.Session,
|
|
180
|
+
data: List[Dict[str, Any]],
|
|
181
|
+
region: str,
|
|
182
|
+
current_aws_account_id: str,
|
|
183
|
+
aws_update_tag: int,
|
|
184
|
+
) -> None:
|
|
185
|
+
logger.info(
|
|
186
|
+
f"Loading Efs {len(data)} access points for region '{region}' into graph.",
|
|
187
|
+
)
|
|
188
|
+
load(
|
|
189
|
+
neo4j_session,
|
|
190
|
+
EfsAccessPointSchema(),
|
|
191
|
+
data,
|
|
192
|
+
lastupdated=aws_update_tag,
|
|
193
|
+
Region=region,
|
|
194
|
+
AWS_ID=current_aws_account_id,
|
|
195
|
+
)
|
|
196
|
+
|
|
197
|
+
|
|
132
198
|
@timeit
|
|
133
199
|
def cleanup(
|
|
134
200
|
neo4j_session: neo4j.Session,
|
|
@@ -141,6 +207,9 @@ def cleanup(
|
|
|
141
207
|
GraphJob.from_node_schema(EfsFileSystemSchema(), common_job_parameters).run(
|
|
142
208
|
neo4j_session
|
|
143
209
|
)
|
|
210
|
+
GraphJob.from_node_schema(EfsAccessPointSchema(), common_job_parameters).run(
|
|
211
|
+
neo4j_session
|
|
212
|
+
)
|
|
144
213
|
|
|
145
214
|
|
|
146
215
|
@timeit
|
|
@@ -178,4 +247,15 @@ def sync(
|
|
|
178
247
|
update_tag,
|
|
179
248
|
)
|
|
180
249
|
|
|
250
|
+
accessPoints = get_efs_access_points(boto3_session, region)
|
|
251
|
+
accessPoints_transformed = transform_efs_access_points(accessPoints, region)
|
|
252
|
+
|
|
253
|
+
load_efs_access_points(
|
|
254
|
+
neo4j_session,
|
|
255
|
+
accessPoints_transformed,
|
|
256
|
+
region,
|
|
257
|
+
current_aws_account_id,
|
|
258
|
+
update_tag,
|
|
259
|
+
)
|
|
260
|
+
|
|
181
261
|
cleanup(neo4j_session, common_job_parameters)
|
|
@@ -16,8 +16,6 @@ from cartography.util import aws_handle_regions
|
|
|
16
16
|
from cartography.util import aws_paginate
|
|
17
17
|
from cartography.util import batch
|
|
18
18
|
from cartography.util import timeit
|
|
19
|
-
from cartography.util import to_asynchronous
|
|
20
|
-
from cartography.util import to_synchronous
|
|
21
19
|
|
|
22
20
|
logger = logging.getLogger(__name__)
|
|
23
21
|
|
|
@@ -329,10 +327,9 @@ def sync(
|
|
|
329
327
|
member_accounts = get_member_accounts(boto3_session, region)
|
|
330
328
|
# the current host account may not be considered a "member", but we still fetch its findings
|
|
331
329
|
member_accounts.append(current_aws_account_id)
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
_sync_findings_for_account,
|
|
330
|
+
logger.info(f"Member accounts to be synced: {member_accounts}")
|
|
331
|
+
for account_id in member_accounts:
|
|
332
|
+
_sync_findings_for_account(
|
|
336
333
|
neo4j_session,
|
|
337
334
|
boto3_session,
|
|
338
335
|
region,
|
|
@@ -341,11 +338,4 @@ def sync(
|
|
|
341
338
|
current_aws_account_id,
|
|
342
339
|
)
|
|
343
340
|
|
|
344
|
-
to_synchronous(
|
|
345
|
-
*[
|
|
346
|
-
async_ingest_findings_for_account(account_id)
|
|
347
|
-
for account_id in member_accounts
|
|
348
|
-
]
|
|
349
|
-
)
|
|
350
|
-
|
|
351
341
|
cleanup(neo4j_session, common_job_parameters)
|
|
@@ -0,0 +1,77 @@
|
|
|
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 EfsAccessPointNodeProperties(CartographyNodeProperties):
|
|
16
|
+
id: PropertyRef = PropertyRef("AccessPointArn")
|
|
17
|
+
arn: PropertyRef = PropertyRef("AccessPointArn", extra_index=True)
|
|
18
|
+
region: PropertyRef = PropertyRef("Region", set_in_kwargs=True)
|
|
19
|
+
access_point_id: PropertyRef = PropertyRef("AccessPointId")
|
|
20
|
+
file_system_id: PropertyRef = PropertyRef("FileSystemId")
|
|
21
|
+
lifecycle_state: PropertyRef = PropertyRef("LifeCycleState")
|
|
22
|
+
name: PropertyRef = PropertyRef("Name")
|
|
23
|
+
owner_id: PropertyRef = PropertyRef("OwnerId")
|
|
24
|
+
posix_gid: PropertyRef = PropertyRef("Gid")
|
|
25
|
+
posix_uid: PropertyRef = PropertyRef("Uid")
|
|
26
|
+
root_directory_path: PropertyRef = PropertyRef("RootDirectoryPath")
|
|
27
|
+
lastupdated: PropertyRef = PropertyRef("lastupdated", set_in_kwargs=True)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@dataclass(frozen=True)
|
|
31
|
+
class EfsAccessPointToAwsAccountRelProperties(CartographyRelProperties):
|
|
32
|
+
lastupdated: PropertyRef = PropertyRef("lastupdated", set_in_kwargs=True)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
@dataclass(frozen=True)
|
|
36
|
+
class EfsAccessPointToAWSAccountRel(CartographyRelSchema):
|
|
37
|
+
target_node_label: str = "AWSAccount"
|
|
38
|
+
target_node_matcher: TargetNodeMatcher = make_target_node_matcher(
|
|
39
|
+
{"id": PropertyRef("AWS_ID", set_in_kwargs=True)},
|
|
40
|
+
)
|
|
41
|
+
direction: LinkDirection = LinkDirection.INWARD
|
|
42
|
+
rel_label: str = "RESOURCE"
|
|
43
|
+
properties: EfsAccessPointToAwsAccountRelProperties = (
|
|
44
|
+
EfsAccessPointToAwsAccountRelProperties()
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
@dataclass(frozen=True)
|
|
49
|
+
class EfsAccessPointToEfsFileSystemRelProperties(CartographyRelProperties):
|
|
50
|
+
lastupdated: PropertyRef = PropertyRef("lastupdated", set_in_kwargs=True)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
@dataclass(frozen=True)
|
|
54
|
+
class EfsAccessPointToEfsFileSystemRel(CartographyRelSchema):
|
|
55
|
+
target_node_label: str = "EfsFileSystem"
|
|
56
|
+
target_node_matcher: TargetNodeMatcher = make_target_node_matcher(
|
|
57
|
+
{"id": PropertyRef("FileSystemId")},
|
|
58
|
+
)
|
|
59
|
+
direction: LinkDirection = LinkDirection.OUTWARD
|
|
60
|
+
rel_label: str = "ACCESS_POINT_OF"
|
|
61
|
+
properties: EfsAccessPointToEfsFileSystemRelProperties = (
|
|
62
|
+
EfsAccessPointToEfsFileSystemRelProperties()
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
@dataclass(frozen=True)
|
|
67
|
+
class EfsAccessPointSchema(CartographyNodeSchema):
|
|
68
|
+
label: str = "EfsAccessPoint"
|
|
69
|
+
properties: EfsAccessPointNodeProperties = EfsAccessPointNodeProperties()
|
|
70
|
+
sub_resource_relationship: EfsAccessPointToAWSAccountRel = (
|
|
71
|
+
EfsAccessPointToAWSAccountRel()
|
|
72
|
+
)
|
|
73
|
+
other_relationships: OtherRelationships = OtherRelationships(
|
|
74
|
+
[
|
|
75
|
+
EfsAccessPointToEfsFileSystemRel(),
|
|
76
|
+
]
|
|
77
|
+
)
|
|
@@ -63,6 +63,7 @@ class PropertyRef:
|
|
|
63
63
|
```
|
|
64
64
|
This means that as we create AWSInstanceProfile nodes, we will search for AWSRoles to attach to, and we do
|
|
65
65
|
this by checking if each role's `arn` field is in the `Roles` list of the data dict.
|
|
66
|
+
Note that one_to_many has no effect on matchlinks.
|
|
66
67
|
"""
|
|
67
68
|
self.name = name
|
|
68
69
|
self.set_in_kwargs = set_in_kwargs
|
|
@@ -42,6 +42,11 @@ class CartographyRelProperties(abc.ABC):
|
|
|
42
42
|
Abstract class that represents the properties on a CartographyRelSchema. This is intended to enforce that all
|
|
43
43
|
subclasses will have a lastupdated field defined on their resulting relationships. These fields are assigned to the
|
|
44
44
|
relationship in the `SET` clause.
|
|
45
|
+
|
|
46
|
+
If the CartographyRelSchema is used as a MatchLink, the following properties are required to be defined here:
|
|
47
|
+
- lastupdated: A PropertyRef to the update tag of the relationship.
|
|
48
|
+
- _sub_resource_label: A PropertyRef to the label of the sub-resource that the relationship is associated with.
|
|
49
|
+
- _sub_resource_id: A PropertyRef to the id of the sub-resource that the relationship is associated with.
|
|
45
50
|
"""
|
|
46
51
|
|
|
47
52
|
lastupdated: PropertyRef = field(init=False)
|
|
@@ -90,6 +95,29 @@ def make_target_node_matcher(key_ref_dict: Dict[str, PropertyRef]) -> TargetNode
|
|
|
90
95
|
return make_dataclass(TargetNodeMatcher.__name__, fields, frozen=True)()
|
|
91
96
|
|
|
92
97
|
|
|
98
|
+
@dataclass(frozen=True)
|
|
99
|
+
class SourceNodeMatcher:
|
|
100
|
+
"""
|
|
101
|
+
Same as TargetNodeMatcher, but for the source node; see `make_source_node_matcher()`.
|
|
102
|
+
This object is used only for load_matchlinks() where we match on and connect existing nodes.
|
|
103
|
+
This has no effect on CartographyRelSchema objects that are included in CartographyNodeSchema.
|
|
104
|
+
"""
|
|
105
|
+
|
|
106
|
+
pass
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def make_source_node_matcher(key_ref_dict: Dict[str, PropertyRef]) -> SourceNodeMatcher:
|
|
110
|
+
"""
|
|
111
|
+
:param key_ref_dict: A Dict mapping keys present on the node to PropertyRef objects.
|
|
112
|
+
:return: A SourceNodeMatcher used for CartographyRelSchema to match with other nodes.
|
|
113
|
+
"""
|
|
114
|
+
fields = [
|
|
115
|
+
(key, PropertyRef, field(default=prop_ref))
|
|
116
|
+
for key, prop_ref in key_ref_dict.items()
|
|
117
|
+
]
|
|
118
|
+
return make_dataclass(SourceNodeMatcher.__name__, fields, frozen=True)()
|
|
119
|
+
|
|
120
|
+
|
|
93
121
|
@dataclass(frozen=True)
|
|
94
122
|
class CartographyRelSchema(abc.ABC):
|
|
95
123
|
"""
|
|
@@ -139,6 +167,22 @@ class CartographyRelSchema(abc.ABC):
|
|
|
139
167
|
"""
|
|
140
168
|
pass
|
|
141
169
|
|
|
170
|
+
@property
|
|
171
|
+
def source_node_label(self) -> str | None:
|
|
172
|
+
"""
|
|
173
|
+
:return: Optional. Only used for load_matchlinks(). The source node label to use for the relationship.
|
|
174
|
+
This does not affect CartographyRelSchema that are included in CartographyNodeSchema objects.
|
|
175
|
+
"""
|
|
176
|
+
return None
|
|
177
|
+
|
|
178
|
+
@property
|
|
179
|
+
def source_node_matcher(self) -> SourceNodeMatcher | None:
|
|
180
|
+
"""
|
|
181
|
+
:return: Optional. Only used for load_matchlinks(). A SourceNodeMatcher object used to find what node(s) to attach the relationship to.
|
|
182
|
+
This does not affect CartographyRelSchema that are included in CartographyNodeSchema objects.
|
|
183
|
+
"""
|
|
184
|
+
return None
|
|
185
|
+
|
|
142
186
|
|
|
143
187
|
@dataclass(frozen=True)
|
|
144
188
|
class OtherRelationships:
|
|
@@ -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=
|
|
3
|
+
cartography/_version.py,sha256=axH1PjcS4osdPi0BaK-npCXNnQPC_1rYG4YGAn2aH60,525
|
|
4
4
|
cartography/cli.py,sha256=LIH9uY6LyctL8VDsrllxFvU17f1vAAhXQVsQ83wGvRI,41072
|
|
5
5
|
cartography/config.py,sha256=Magq4iNynKdIl3hYjrxlikXfVLxryf73t41L2bAyUPI,14298
|
|
6
6
|
cartography/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -12,7 +12,7 @@ cartography/client/aws/__init__.py,sha256=Zj7nX21QQELwPLZlpldm_22IiXZ1VFsEFQbWX_
|
|
|
12
12
|
cartography/client/aws/ecr.py,sha256=04IXnuEAauyO5Mx9Wmt79WdUnYDhYsk2QSOnwE5_BeM,1664
|
|
13
13
|
cartography/client/aws/iam.py,sha256=dYsGikc36DEsSeR2XVOVFFUDwuU9yWj_EVkpgVYCFgM,1293
|
|
14
14
|
cartography/client/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
15
|
-
cartography/client/core/tx.py,sha256=
|
|
15
|
+
cartography/client/core/tx.py,sha256=Xx6_a5u7PE5pyREuBL_J39ORcafqieFf4KdosaEv1c4,13530
|
|
16
16
|
cartography/data/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
17
17
|
cartography/data/indexes.cypher,sha256=vbKOyt87_MAxrDnKU8cbZBuiLnMyrbadSZq6aIaT6Zo,24112
|
|
18
18
|
cartography/data/permission_relationships.yaml,sha256=RuKGGc_3ZUQ7ag0MssB8k_zaonCkVM5E8I_svBWTmGc,969
|
|
@@ -130,11 +130,11 @@ cartography/driftdetect/shortcut.py,sha256=0AvX9vZGNRWeLiRqxU7eOOo77gcfufHx4IHZv
|
|
|
130
130
|
cartography/driftdetect/storage.py,sha256=xvDY4qn6jDhDpBvc8hFXXpbcYIbMNqkm3wX5LBf28u0,1549
|
|
131
131
|
cartography/driftdetect/util.py,sha256=Lqxv8QoFn3_3Fz18qCOjkjJ6yBwgrHjrxXmArBAEdkc,929
|
|
132
132
|
cartography/graph/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
133
|
-
cartography/graph/cleanupbuilder.py,sha256=
|
|
133
|
+
cartography/graph/cleanupbuilder.py,sha256=uMSjMOnfkfTDrgMJwflNK8HAd4oioG8NIU0JeMN7ets,16552
|
|
134
134
|
cartography/graph/context.py,sha256=RGxGb8EnxowcqjR0nFF86baNhgRHeUF9wjIoFUoG8LU,1230
|
|
135
|
-
cartography/graph/job.py,sha256=
|
|
136
|
-
cartography/graph/querybuilder.py,sha256=
|
|
137
|
-
cartography/graph/statement.py,sha256=
|
|
135
|
+
cartography/graph/job.py,sha256=5h4FsOV2tHeO5O2Gv-vglI5QUNE7V6p-RNmf9Fz5gvU,9396
|
|
136
|
+
cartography/graph/querybuilder.py,sha256=b_dIScQ8MZfskM3CsY8vOgZjHMV1rzbJuywML1X72mw,27206
|
|
137
|
+
cartography/graph/statement.py,sha256=VtvZFMqzzZk-gDeOnBx6oDj90XYOCM1EWw4d55bm7SU,5383
|
|
138
138
|
cartography/intel/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
139
139
|
cartography/intel/analysis.py,sha256=uWIpmrk2hezkqjfepqz1VV2BopT7VZ-alBj2YekfE_c,1546
|
|
140
140
|
cartography/intel/create_indexes.py,sha256=6G43Rko8oXsAhHdSb2RbSVMvGMLi_YALlyUQE77jPVw,742
|
|
@@ -153,7 +153,7 @@ cartography/intel/aws/config.py,sha256=IIICicLQ0OTT3H3o8LDakIkA1BPUMwSyzpKonet-P
|
|
|
153
153
|
cartography/intel/aws/dynamodb.py,sha256=VvvjeUgi1ZrqY9flXIQnfhhaSVSEqXXHW6T9917VLBk,5728
|
|
154
154
|
cartography/intel/aws/ecr.py,sha256=7a9EHZru6AUIGE_6MJ4h4_ZmrvBxsXaerCHtGr59lJ0,8393
|
|
155
155
|
cartography/intel/aws/ecs.py,sha256=-RPYdc6KC5fbaosaGsgWRkuLoHnE71euud4ZC1wGJI8,13464
|
|
156
|
-
cartography/intel/aws/efs.py,sha256=
|
|
156
|
+
cartography/intel/aws/efs.py,sha256=6ZvFmVHLfZlyo8xx2dlRsC1IoVOpBOtsij_AdFczkDo,7884
|
|
157
157
|
cartography/intel/aws/eks.py,sha256=bPItyEj5q0nSDltJrr0S5MIrTPV0fK3xkqF6EV8fcqA,3759
|
|
158
158
|
cartography/intel/aws/elasticache.py,sha256=dpRJCYxgUW2ImgGMt4L54xFil8apUoJxZq6hpewXxAE,4590
|
|
159
159
|
cartography/intel/aws/elasticsearch.py,sha256=8X_Rp1zejkvXho0Zz_Cr4g-w9IpompdYRc-YS595Aso,8645
|
|
@@ -161,7 +161,7 @@ cartography/intel/aws/emr.py,sha256=EJoKjHQP7-F_A1trUNU05Sb42yNR1i0C9VIpGcCfAXw,
|
|
|
161
161
|
cartography/intel/aws/iam.py,sha256=bkyIzWw2OC4MHxuoCvTiZ5eEGEQhz2asiUgY_tkX2GY,34322
|
|
162
162
|
cartography/intel/aws/iam_instance_profiles.py,sha256=QUyu30xid60BFaemp-q0y9FgNsHaIQyQnQ8gHUzWC4k,2211
|
|
163
163
|
cartography/intel/aws/identitycenter.py,sha256=C2EOfwQO1zBjePAXtreUcJIy7RU__ior3liRT4SpGO8,9544
|
|
164
|
-
cartography/intel/aws/inspector.py,sha256=
|
|
164
|
+
cartography/intel/aws/inspector.py,sha256=rD_O3DUGv5-GSMNqDzb11Ps5bX1sIo_JDq3UTAGvUpE,11168
|
|
165
165
|
cartography/intel/aws/kms.py,sha256=RtCxNG-Den-f4Il-NO3YL_-BFUmg1qFt4lNucue9SQM,12130
|
|
166
166
|
cartography/intel/aws/lambda_function.py,sha256=cutCsMnvyJB8vKUP0fHORrhijw944cXSoLKOHYdi6SM,10389
|
|
167
167
|
cartography/intel/aws/organizations.py,sha256=m5qk60N6pe7iKLN-Rtfg9aYv7OozoloSkcsuePd-RGs,4680
|
|
@@ -373,6 +373,7 @@ cartography/models/aws/ecs/services.py,sha256=YPVTS0BB3X6tFCqIX3V1AZXcnHQEADusQk
|
|
|
373
373
|
cartography/models/aws/ecs/task_definitions.py,sha256=zZh2SHXEAimp9YgYf1wRU-wNs_4LWNk7uNzDoms8Sy8,4182
|
|
374
374
|
cartography/models/aws/ecs/tasks.py,sha256=6r2WZDc26LGR2j-U5s5v4d4PbMIvEDSkLAbTtpFdrUM,5013
|
|
375
375
|
cartography/models/aws/efs/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
376
|
+
cartography/models/aws/efs/access_point.py,sha256=Auk8E0AoPjrCxch8bP8fhT3JZ2WxblTTfYSqamA_p8g,3185
|
|
376
377
|
cartography/models/aws/efs/file_system.py,sha256=6BNzjQJKZ-rYlDLw1UvDBhVRKre07-9EkcW9xvR3Wh0,2842
|
|
377
378
|
cartography/models/aws/efs/mount_target.py,sha256=Bdd2zgWp9HtsK9EmEAgoIYpT9AOTs5wzH3sgCq4HLMU,3347
|
|
378
379
|
cartography/models/aws/eks/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -409,9 +410,9 @@ cartography/models/cloudflare/member.py,sha256=s7S4OJjXAOq9vPc-TXFGbxKDjEHB5Y_Ek
|
|
|
409
410
|
cartography/models/cloudflare/role.py,sha256=HRjDoLMSs0YJlJosZsXEZV58OE_3vGAWSDN_aGiK2Y8,1855
|
|
410
411
|
cartography/models/cloudflare/zone.py,sha256=uMHTSHq32eWe47k3b08sbnQBLrbNWPKpL9Osc4jcFD0,2805
|
|
411
412
|
cartography/models/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
412
|
-
cartography/models/core/common.py,sha256=
|
|
413
|
+
cartography/models/core/common.py,sha256=YPBpM0q2EBxTLQ-2OahGR1irMsCEFfu0eL-q5Uijxko,6261
|
|
413
414
|
cartography/models/core/nodes.py,sha256=GFK-optqan5L57D36ZQ7AdylGRELUl9pMyBfZ7l-cz4,4367
|
|
414
|
-
cartography/models/core/relationships.py,sha256=
|
|
415
|
+
cartography/models/core/relationships.py,sha256=TfcU01lIgBxoINjEwTWngIXkbFuHnE-UHGQ9h-V6ZDE,7100
|
|
415
416
|
cartography/models/crowdstrike/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
416
417
|
cartography/models/crowdstrike/hosts.py,sha256=J4UpdsVUNiQXqxnvmWdVkwsFueVagPV-7sTV3D7Lkts,2686
|
|
417
418
|
cartography/models/cve/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -481,9 +482,9 @@ cartography/models/trivy/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZ
|
|
|
481
482
|
cartography/models/trivy/findings.py,sha256=SgI_h1aRyR20uAHvuXIZ1T6r4IZJt6SVhxRaF2bTsm0,3085
|
|
482
483
|
cartography/models/trivy/fix.py,sha256=ho9ENVl9HSXqyggyCwR6ilkOBKDxpQ7rGibo_j21NA4,2587
|
|
483
484
|
cartography/models/trivy/package.py,sha256=IwO1RZZ-MFRtNbt8Cq6YFl6fdNJMFmULnJkkK8Q4rL4,2809
|
|
484
|
-
cartography-0.106.
|
|
485
|
-
cartography-0.106.
|
|
486
|
-
cartography-0.106.
|
|
487
|
-
cartography-0.106.
|
|
488
|
-
cartography-0.106.
|
|
489
|
-
cartography-0.106.
|
|
485
|
+
cartography-0.106.0rc2.dist-info/licenses/LICENSE,sha256=kvLEBRYaQ1RvUni6y7Ti9uHeooqnjPoo6n_-0JO1ETc,11351
|
|
486
|
+
cartography-0.106.0rc2.dist-info/METADATA,sha256=Y1-Ir7t90DLnZrn2Tq-ee9j6akBX_Ezyc8YeZejQFWY,12409
|
|
487
|
+
cartography-0.106.0rc2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
488
|
+
cartography-0.106.0rc2.dist-info/entry_points.txt,sha256=GVIAWD0o0_K077qMA_k1oZU4v-M0a8GLKGJR8tZ-qH8,112
|
|
489
|
+
cartography-0.106.0rc2.dist-info/top_level.txt,sha256=BHqsNJQiI6Q72DeypC1IINQJE59SLhU4nllbQjgJi9g,12
|
|
490
|
+
cartography-0.106.0rc2.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|