cartography 0.102.0rc2__py3-none-any.whl → 0.103.0rc1__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/__main__.py +1 -2
- cartography/_version.py +2 -2
- cartography/cli.py +302 -253
- cartography/client/core/tx.py +39 -18
- cartography/config.py +4 -0
- cartography/driftdetect/__main__.py +1 -2
- cartography/driftdetect/add_shortcut.py +10 -2
- cartography/driftdetect/cli.py +71 -75
- cartography/driftdetect/detect_deviations.py +7 -3
- cartography/driftdetect/get_states.py +20 -8
- cartography/driftdetect/model.py +5 -5
- cartography/driftdetect/serializers.py +8 -6
- cartography/driftdetect/storage.py +2 -2
- cartography/graph/cleanupbuilder.py +35 -15
- cartography/graph/job.py +46 -17
- cartography/graph/querybuilder.py +165 -80
- cartography/graph/statement.py +35 -26
- cartography/intel/analysis.py +4 -1
- cartography/intel/aws/__init__.py +114 -55
- cartography/intel/aws/apigateway.py +134 -63
- cartography/intel/aws/cloudtrail.py +127 -0
- cartography/intel/aws/config.py +56 -20
- cartography/intel/aws/dynamodb.py +108 -40
- cartography/intel/aws/ec2/__init__.py +2 -2
- cartography/intel/aws/ec2/auto_scaling_groups.py +181 -78
- cartography/intel/aws/ec2/elastic_ip_addresses.py +41 -13
- cartography/intel/aws/ec2/images.py +49 -20
- cartography/intel/aws/ec2/instances.py +234 -136
- cartography/intel/aws/ec2/internet_gateways.py +40 -11
- cartography/intel/aws/ec2/key_pairs.py +44 -20
- cartography/intel/aws/ec2/launch_templates.py +101 -59
- cartography/intel/aws/ec2/load_balancer_v2s.py +104 -39
- cartography/intel/aws/ec2/load_balancers.py +82 -42
- cartography/intel/aws/ec2/network_acls.py +89 -65
- cartography/intel/aws/ec2/network_interfaces.py +146 -87
- cartography/intel/aws/ec2/reserved_instances.py +45 -16
- cartography/intel/aws/ec2/route_tables.py +138 -98
- cartography/intel/aws/ec2/security_groups.py +71 -21
- cartography/intel/aws/ec2/snapshots.py +61 -22
- cartography/intel/aws/ec2/subnets.py +54 -18
- cartography/intel/aws/ec2/tgw.py +100 -34
- cartography/intel/aws/ec2/util.py +1 -1
- cartography/intel/aws/ec2/volumes.py +69 -41
- cartography/intel/aws/ec2/vpc.py +37 -12
- cartography/intel/aws/ec2/vpc_peerings.py +83 -24
- cartography/intel/aws/ecr.py +88 -32
- cartography/intel/aws/ecs.py +83 -47
- cartography/intel/aws/eks.py +55 -29
- cartography/intel/aws/elasticache.py +42 -18
- cartography/intel/aws/elasticsearch.py +57 -20
- cartography/intel/aws/emr.py +61 -23
- cartography/intel/aws/iam.py +401 -145
- cartography/intel/aws/iam_instance_profiles.py +22 -22
- cartography/intel/aws/identitycenter.py +71 -37
- cartography/intel/aws/inspector.py +159 -89
- cartography/intel/aws/kms.py +92 -38
- cartography/intel/aws/lambda_function.py +103 -34
- cartography/intel/aws/organizations.py +30 -10
- cartography/intel/aws/permission_relationships.py +133 -51
- cartography/intel/aws/rds.py +249 -85
- cartography/intel/aws/redshift.py +107 -46
- cartography/intel/aws/resourcegroupstaggingapi.py +120 -66
- cartography/intel/aws/resources.py +53 -46
- cartography/intel/aws/route53.py +108 -61
- cartography/intel/aws/s3.py +168 -83
- cartography/intel/aws/s3accountpublicaccessblock.py +157 -0
- cartography/intel/aws/secretsmanager.py +24 -12
- cartography/intel/aws/securityhub.py +20 -9
- cartography/intel/aws/sns.py +166 -0
- cartography/intel/aws/sqs.py +60 -28
- cartography/intel/aws/ssm.py +70 -30
- cartography/intel/aws/util/arns.py +7 -7
- cartography/intel/aws/util/common.py +31 -4
- cartography/intel/azure/__init__.py +78 -19
- cartography/intel/azure/compute.py +101 -27
- cartography/intel/azure/cosmosdb.py +496 -170
- cartography/intel/azure/sql.py +296 -105
- cartography/intel/azure/storage.py +322 -113
- cartography/intel/azure/subscription.py +39 -23
- cartography/intel/azure/tenant.py +13 -4
- cartography/intel/azure/util/credentials.py +95 -55
- cartography/intel/bigfix/__init__.py +2 -2
- cartography/intel/bigfix/computers.py +93 -65
- cartography/intel/create_indexes.py +3 -2
- cartography/intel/crowdstrike/__init__.py +11 -9
- cartography/intel/crowdstrike/endpoints.py +5 -1
- cartography/intel/crowdstrike/spotlight.py +8 -3
- cartography/intel/cve/__init__.py +46 -13
- cartography/intel/cve/feed.py +48 -12
- cartography/intel/digitalocean/__init__.py +22 -13
- cartography/intel/digitalocean/compute.py +75 -108
- cartography/intel/digitalocean/management.py +44 -80
- cartography/intel/digitalocean/platform.py +48 -43
- cartography/intel/dns.py +36 -10
- cartography/intel/duo/__init__.py +21 -16
- cartography/intel/duo/api_host.py +14 -9
- cartography/intel/duo/endpoints.py +50 -45
- cartography/intel/duo/groups.py +18 -14
- cartography/intel/duo/phones.py +37 -34
- cartography/intel/duo/tokens.py +26 -23
- cartography/intel/duo/users.py +54 -50
- cartography/intel/duo/web_authn_credentials.py +30 -25
- cartography/intel/entra/__init__.py +25 -7
- cartography/intel/entra/ou.py +112 -0
- cartography/intel/entra/users.py +69 -63
- cartography/intel/gcp/__init__.py +185 -49
- cartography/intel/gcp/compute.py +418 -231
- cartography/intel/gcp/crm.py +96 -43
- cartography/intel/gcp/dns.py +60 -19
- cartography/intel/gcp/gke.py +72 -38
- cartography/intel/gcp/iam.py +61 -41
- cartography/intel/gcp/storage.py +84 -55
- cartography/intel/github/__init__.py +13 -11
- cartography/intel/github/repos.py +270 -137
- cartography/intel/github/teams.py +170 -88
- cartography/intel/github/users.py +70 -39
- cartography/intel/github/util.py +36 -34
- cartography/intel/gsuite/__init__.py +47 -26
- cartography/intel/gsuite/api.py +73 -30
- cartography/intel/jamf/__init__.py +19 -1
- cartography/intel/jamf/computers.py +30 -7
- cartography/intel/jamf/util.py +7 -2
- cartography/intel/kandji/__init__.py +6 -3
- cartography/intel/kandji/devices.py +14 -8
- cartography/intel/kubernetes/namespaces.py +7 -4
- cartography/intel/kubernetes/pods.py +7 -4
- cartography/intel/kubernetes/services.py +8 -4
- cartography/intel/lastpass/__init__.py +2 -2
- cartography/intel/lastpass/users.py +23 -12
- cartography/intel/oci/__init__.py +44 -11
- cartography/intel/oci/iam.py +134 -38
- cartography/intel/oci/organizations.py +13 -6
- cartography/intel/oci/utils.py +43 -20
- cartography/intel/okta/__init__.py +66 -15
- cartography/intel/okta/applications.py +42 -20
- cartography/intel/okta/awssaml.py +93 -33
- cartography/intel/okta/factors.py +16 -4
- cartography/intel/okta/groups.py +56 -29
- cartography/intel/okta/organization.py +5 -1
- cartography/intel/okta/origins.py +6 -2
- cartography/intel/okta/roles.py +15 -5
- cartography/intel/okta/users.py +20 -8
- cartography/intel/okta/utils.py +6 -4
- cartography/intel/pagerduty/__init__.py +8 -7
- cartography/intel/pagerduty/escalation_policies.py +18 -6
- cartography/intel/pagerduty/schedules.py +12 -4
- cartography/intel/pagerduty/services.py +11 -4
- cartography/intel/pagerduty/teams.py +8 -3
- cartography/intel/pagerduty/users.py +3 -1
- cartography/intel/pagerduty/vendors.py +3 -1
- cartography/intel/semgrep/__init__.py +24 -6
- cartography/intel/semgrep/dependencies.py +50 -28
- cartography/intel/semgrep/deployment.py +3 -1
- cartography/intel/semgrep/findings.py +42 -18
- cartography/intel/snipeit/__init__.py +17 -3
- cartography/intel/snipeit/asset.py +12 -6
- cartography/intel/snipeit/user.py +8 -5
- cartography/intel/snipeit/util.py +9 -4
- cartography/models/aws/apigateway.py +21 -17
- cartography/models/aws/apigatewaycertificate.py +28 -22
- cartography/models/aws/apigatewayresource.py +28 -20
- cartography/models/aws/apigatewaystage.py +33 -25
- cartography/models/aws/cloudtrail/__init__.py +0 -0
- cartography/models/aws/cloudtrail/trail.py +61 -0
- cartography/models/aws/dynamodb/gsi.py +30 -22
- cartography/models/aws/dynamodb/tables.py +25 -17
- cartography/models/aws/ec2/auto_scaling_groups.py +102 -82
- cartography/models/aws/ec2/images.py +36 -34
- cartography/models/aws/ec2/instances.py +51 -45
- cartography/models/aws/ec2/keypair.py +21 -16
- cartography/models/aws/ec2/keypair_instance.py +28 -21
- cartography/models/aws/ec2/launch_configurations.py +30 -26
- cartography/models/aws/ec2/launch_template_versions.py +48 -38
- cartography/models/aws/ec2/launch_templates.py +21 -17
- cartography/models/aws/ec2/load_balancer_listeners.py +27 -23
- cartography/models/aws/ec2/load_balancers.py +47 -37
- cartography/models/aws/ec2/network_acl_rules.py +38 -30
- cartography/models/aws/ec2/network_acls.py +38 -29
- cartography/models/aws/ec2/networkinterface_instance.py +52 -39
- cartography/models/aws/ec2/networkinterfaces.py +53 -37
- cartography/models/aws/ec2/privateip_networkinterface.py +32 -22
- cartography/models/aws/ec2/reservations.py +18 -14
- cartography/models/aws/ec2/route_table_associations.py +44 -34
- cartography/models/aws/ec2/route_tables.py +50 -43
- cartography/models/aws/ec2/routes.py +45 -37
- cartography/models/aws/ec2/securitygroup_instance.py +29 -20
- cartography/models/aws/ec2/securitygroup_networkinterface.py +24 -15
- cartography/models/aws/ec2/subnet_instance.py +24 -19
- cartography/models/aws/ec2/subnet_networkinterface.py +40 -31
- cartography/models/aws/ec2/volumes.py +47 -40
- cartography/models/aws/eks/clusters.py +23 -21
- cartography/models/aws/emr.py +32 -30
- cartography/models/aws/iam/instanceprofile.py +33 -24
- cartography/models/aws/identitycenter/awsidentitycenter.py +18 -14
- cartography/models/aws/identitycenter/awspermissionset.py +37 -29
- cartography/models/aws/identitycenter/awsssouser.py +23 -21
- cartography/models/aws/inspector/findings.py +77 -65
- cartography/models/aws/inspector/packages.py +35 -29
- cartography/models/aws/s3/__init__.py +0 -0
- cartography/models/aws/s3/account_public_access_block.py +51 -0
- cartography/models/aws/sns/__init__.py +0 -0
- cartography/models/aws/sns/topic.py +50 -0
- cartography/models/aws/ssm/instance_information.py +51 -39
- cartography/models/aws/ssm/instance_patch.py +32 -26
- cartography/models/bigfix/bigfix_computer.py +42 -38
- cartography/models/bigfix/bigfix_root.py +3 -3
- cartography/models/core/common.py +12 -10
- cartography/models/core/nodes.py +5 -2
- cartography/models/core/relationships.py +14 -6
- cartography/models/crowdstrike/hosts.py +37 -35
- cartography/models/cve/cve.py +34 -32
- cartography/models/cve/cve_feed.py +6 -6
- cartography/models/digitalocean/__init__.py +0 -0
- cartography/models/digitalocean/account.py +21 -0
- cartography/models/digitalocean/droplet.py +56 -0
- cartography/models/digitalocean/project.py +48 -0
- cartography/models/duo/api_host.py +3 -3
- cartography/models/duo/endpoint.py +43 -41
- cartography/models/duo/group.py +14 -14
- cartography/models/duo/phone.py +27 -27
- cartography/models/duo/token.py +16 -16
- cartography/models/duo/user.py +46 -44
- cartography/models/duo/web_authn_credential.py +27 -19
- cartography/models/entra/ou.py +48 -0
- cartography/models/entra/tenant.py +24 -18
- cartography/models/entra/user.py +64 -48
- cartography/models/gcp/iam.py +23 -23
- cartography/models/github/orgs.py +5 -4
- cartography/models/github/teams.py +37 -31
- cartography/models/github/users.py +34 -23
- cartography/models/kandji/device.py +22 -16
- cartography/models/kandji/tenant.py +6 -4
- cartography/models/lastpass/tenant.py +3 -3
- cartography/models/lastpass/user.py +32 -28
- cartography/models/semgrep/dependencies.py +36 -24
- cartography/models/semgrep/deployment.py +5 -5
- cartography/models/semgrep/findings.py +58 -42
- cartography/models/semgrep/locations.py +27 -21
- cartography/models/snipeit/asset.py +30 -21
- cartography/models/snipeit/tenant.py +6 -4
- cartography/models/snipeit/user.py +19 -12
- cartography/stats.py +3 -3
- cartography/sync.py +107 -31
- cartography/util.py +84 -62
- {cartography-0.102.0rc2.dist-info → cartography-0.103.0rc1.dist-info}/METADATA +3 -14
- cartography-0.103.0rc1.dist-info/RECORD +396 -0
- {cartography-0.102.0rc2.dist-info → cartography-0.103.0rc1.dist-info}/WHEEL +1 -1
- cartography-0.102.0rc2.dist-info/RECORD +0 -381
- {cartography-0.102.0rc2.dist-info → cartography-0.103.0rc1.dist-info}/entry_points.txt +0 -0
- {cartography-0.102.0rc2.dist-info → cartography-0.103.0rc1.dist-info}/licenses/LICENSE +0 -0
- {cartography-0.102.0rc2.dist-info → cartography-0.103.0rc1.dist-info}/top_level.txt +0 -0
|
@@ -19,7 +19,7 @@ logger = logging.getLogger(__name__)
|
|
|
19
19
|
|
|
20
20
|
|
|
21
21
|
def evaluate_clause(clause: str, match: str) -> bool:
|
|
22
|
-
"""
|
|
22
|
+
"""Evaluates the a clause in IAM. Clauses can be AWS [not]actions and [not]resources
|
|
23
23
|
|
|
24
24
|
Arguments:
|
|
25
25
|
clause {str, re.Pattern} -- The clause you are evaluating against. Clauses can use
|
|
@@ -36,9 +36,9 @@ def evaluate_clause(clause: str, match: str) -> bool:
|
|
|
36
36
|
|
|
37
37
|
def evaluate_notaction_for_permission(statement: Dict, permission: str) -> bool:
|
|
38
38
|
"""Return whether an IAM 'notaction' clause in the given statement applies to the item"""
|
|
39
|
-
if
|
|
39
|
+
if "notaction" not in statement:
|
|
40
40
|
return False
|
|
41
|
-
for clause in statement[
|
|
41
|
+
for clause in statement["notaction"]:
|
|
42
42
|
if evaluate_clause(clause, permission):
|
|
43
43
|
return True
|
|
44
44
|
return False
|
|
@@ -46,9 +46,9 @@ def evaluate_notaction_for_permission(statement: Dict, permission: str) -> bool:
|
|
|
46
46
|
|
|
47
47
|
def evaluate_action_for_permission(statement: Dict, permission: str) -> bool:
|
|
48
48
|
"""Return whether an IAM 'action' clause in the given statement applies to the permission"""
|
|
49
|
-
if
|
|
49
|
+
if "action" not in statement:
|
|
50
50
|
return True
|
|
51
|
-
for clause in statement[
|
|
51
|
+
for clause in statement["action"]:
|
|
52
52
|
if evaluate_clause(clause, permission):
|
|
53
53
|
return True
|
|
54
54
|
return False
|
|
@@ -56,9 +56,9 @@ def evaluate_action_for_permission(statement: Dict, permission: str) -> bool:
|
|
|
56
56
|
|
|
57
57
|
def evaluate_resource_for_permission(statement: Dict, resource_arn: str) -> bool:
|
|
58
58
|
"""Return whether the given IAM 'resource' statement applies to the resource_arn"""
|
|
59
|
-
if
|
|
59
|
+
if "resource" not in statement:
|
|
60
60
|
return False
|
|
61
|
-
for clause in statement[
|
|
61
|
+
for clause in statement["resource"]:
|
|
62
62
|
if evaluate_clause(clause, resource_arn):
|
|
63
63
|
return True
|
|
64
64
|
return False
|
|
@@ -66,16 +66,20 @@ def evaluate_resource_for_permission(statement: Dict, resource_arn: str) -> bool
|
|
|
66
66
|
|
|
67
67
|
def evaluate_notresource_for_permission(statement: Dict, resource_arn: str) -> bool:
|
|
68
68
|
"""Return whether an IAM 'notresource' clause in the given statement applies to the resource_arn"""
|
|
69
|
-
if
|
|
69
|
+
if "notresource" not in statement:
|
|
70
70
|
return False
|
|
71
|
-
for clause in statement[
|
|
71
|
+
for clause in statement["notresource"]:
|
|
72
72
|
if evaluate_clause(clause, resource_arn):
|
|
73
73
|
return True
|
|
74
74
|
return False
|
|
75
75
|
|
|
76
76
|
|
|
77
|
-
def evaluate_statements_for_permission(
|
|
78
|
-
|
|
77
|
+
def evaluate_statements_for_permission(
|
|
78
|
+
statements: List[Dict],
|
|
79
|
+
permission: str,
|
|
80
|
+
resource_arn: str,
|
|
81
|
+
) -> bool:
|
|
82
|
+
"""Evaluate an entire statement for a specific permission against a resource
|
|
79
83
|
|
|
80
84
|
Arguments:
|
|
81
85
|
statements {[dict]} -- The list of statements to be evaluated
|
|
@@ -97,9 +101,11 @@ def evaluate_statements_for_permission(statements: List[Dict], permission: str,
|
|
|
97
101
|
|
|
98
102
|
|
|
99
103
|
def evaluate_policy_for_permissions(
|
|
100
|
-
statements: List[Dict],
|
|
104
|
+
statements: List[Dict],
|
|
105
|
+
permissions: List[str],
|
|
106
|
+
resource_arn: str,
|
|
101
107
|
) -> Tuple[bool, bool]:
|
|
102
|
-
"""
|
|
108
|
+
"""Evaluates an entire policy for specific permissions to a resource.
|
|
103
109
|
AWS Policy evaluation reference
|
|
104
110
|
https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_evaluation-logic.html
|
|
105
111
|
|
|
@@ -118,19 +124,31 @@ def evaluate_policy_for_permissions(
|
|
|
118
124
|
allow_statements = [s for s in statements if s["effect"] == "Allow"]
|
|
119
125
|
deny_statements = [s for s in statements if s["effect"] == "Deny"]
|
|
120
126
|
for permission in permissions:
|
|
121
|
-
if evaluate_statements_for_permission(
|
|
127
|
+
if evaluate_statements_for_permission(
|
|
128
|
+
deny_statements,
|
|
129
|
+
permission,
|
|
130
|
+
resource_arn,
|
|
131
|
+
):
|
|
122
132
|
# The action explicitly denied then no other policy can override it
|
|
123
133
|
return False, True
|
|
124
134
|
else:
|
|
125
|
-
if evaluate_statements_for_permission(
|
|
135
|
+
if evaluate_statements_for_permission(
|
|
136
|
+
allow_statements,
|
|
137
|
+
permission,
|
|
138
|
+
resource_arn,
|
|
139
|
+
):
|
|
126
140
|
# The action is allowed by this policy
|
|
127
141
|
return True, False
|
|
128
142
|
# The action is not allowed by this policy, but not specifically denied either
|
|
129
143
|
return False, False
|
|
130
144
|
|
|
131
145
|
|
|
132
|
-
def principal_allowed_on_resource(
|
|
133
|
-
|
|
146
|
+
def principal_allowed_on_resource(
|
|
147
|
+
policies: Dict,
|
|
148
|
+
resource_arn: str,
|
|
149
|
+
permissions: List[str],
|
|
150
|
+
) -> bool:
|
|
151
|
+
"""Evaluates an enture set of policies for a specific resource for a specific permission.
|
|
134
152
|
|
|
135
153
|
|
|
136
154
|
Arguments:
|
|
@@ -145,7 +163,11 @@ def principal_allowed_on_resource(policies: Dict, resource_arn: str, permissions
|
|
|
145
163
|
raise ValueError("permissions is not a list")
|
|
146
164
|
granted = False
|
|
147
165
|
for _, statements in policies.items():
|
|
148
|
-
allowed, explicit_deny = evaluate_policy_for_permissions(
|
|
166
|
+
allowed, explicit_deny = evaluate_policy_for_permissions(
|
|
167
|
+
statements,
|
|
168
|
+
permissions,
|
|
169
|
+
resource_arn,
|
|
170
|
+
)
|
|
149
171
|
|
|
150
172
|
if explicit_deny:
|
|
151
173
|
|
|
@@ -157,9 +179,11 @@ def principal_allowed_on_resource(policies: Dict, resource_arn: str, permissions
|
|
|
157
179
|
|
|
158
180
|
|
|
159
181
|
def calculate_permission_relationships(
|
|
160
|
-
principals: Dict,
|
|
182
|
+
principals: Dict,
|
|
183
|
+
resource_arns: List[str],
|
|
184
|
+
permissions: List[str],
|
|
161
185
|
) -> List[Dict]:
|
|
162
|
-
"""
|
|
186
|
+
"""Evaluate principals permissions to resources
|
|
163
187
|
This currently only evaluates policies on IAM principals. It does not take into account
|
|
164
188
|
Resource Policies - Policies attached to the resource instead of the IAM principal
|
|
165
189
|
Permission Boundaries - Boundaries for an IAM principal
|
|
@@ -180,12 +204,14 @@ def calculate_permission_relationships(
|
|
|
180
204
|
for resource_arn in resource_arns:
|
|
181
205
|
for principal_arn, policies in principals.items():
|
|
182
206
|
if principal_allowed_on_resource(policies, resource_arn, permissions):
|
|
183
|
-
allowed_mappings.append(
|
|
207
|
+
allowed_mappings.append(
|
|
208
|
+
{"principal_arn": principal_arn, "resource_arn": resource_arn},
|
|
209
|
+
)
|
|
184
210
|
return allowed_mappings
|
|
185
211
|
|
|
186
212
|
|
|
187
213
|
def parse_statement_node(node_group: List[Any]) -> List[Any]:
|
|
188
|
-
"""
|
|
214
|
+
"""Parse a dict from group of Neo4J node
|
|
189
215
|
|
|
190
216
|
Arguments:
|
|
191
217
|
node_group {[Neo4j.Node]} -- the node to parse
|
|
@@ -197,7 +223,7 @@ def parse_statement_node(node_group: List[Any]) -> List[Any]:
|
|
|
197
223
|
|
|
198
224
|
|
|
199
225
|
def compile_regex(item: str) -> Pattern:
|
|
200
|
-
r"""
|
|
226
|
+
r"""Compile a clause into a regex. Clause checking in AWS is case insensitive
|
|
201
227
|
The following regex symbols will be replaced to make AWS * and ? matching a regex
|
|
202
228
|
* -> .* (wildcard)
|
|
203
229
|
? -> .? (single character wildcard)
|
|
@@ -225,7 +251,7 @@ def compile_regex(item: str) -> Pattern:
|
|
|
225
251
|
|
|
226
252
|
|
|
227
253
|
def compile_statement(statements: List[Any]) -> List[Any]:
|
|
228
|
-
"""
|
|
254
|
+
"""Compile a statement by precompiling the regex for the relevant clauses. This is done to boost
|
|
229
255
|
performance by not recompiling the regex over and over again.
|
|
230
256
|
|
|
231
257
|
Arguments:
|
|
@@ -234,11 +260,13 @@ def compile_statement(statements: List[Any]) -> List[Any]:
|
|
|
234
260
|
Returns:
|
|
235
261
|
[dict] -- the compiled statement
|
|
236
262
|
"""
|
|
237
|
-
properties = [
|
|
263
|
+
properties = ["action", "resource", "notresource", "notaction"]
|
|
238
264
|
for statement in statements:
|
|
239
265
|
for statement_property in properties:
|
|
240
266
|
if statement_property in statement:
|
|
241
|
-
statement[statement_property] = [
|
|
267
|
+
statement[statement_property] = [
|
|
268
|
+
compile_regex(item) for item in statement[statement_property]
|
|
269
|
+
]
|
|
242
270
|
return statements
|
|
243
271
|
|
|
244
272
|
|
|
@@ -263,16 +291,26 @@ def get_principals_for_account(neo4j_session: neo4j.Session, account_id: str) ->
|
|
|
263
291
|
statements = r["statements"]
|
|
264
292
|
if principal_arn not in principals:
|
|
265
293
|
principals[principal_arn] = {}
|
|
266
|
-
principals[principal_arn][policy_id] = compile_statement(
|
|
294
|
+
principals[principal_arn][policy_id] = compile_statement(
|
|
295
|
+
parse_statement_node(statements),
|
|
296
|
+
)
|
|
267
297
|
return principals
|
|
268
298
|
|
|
269
299
|
|
|
270
|
-
def get_resource_arns(
|
|
271
|
-
|
|
300
|
+
def get_resource_arns(
|
|
301
|
+
neo4j_session: neo4j.Session,
|
|
302
|
+
account_id: str,
|
|
303
|
+
node_label: str,
|
|
304
|
+
) -> List[Any]:
|
|
305
|
+
get_resource_query = Template(
|
|
306
|
+
"""
|
|
272
307
|
MATCH (acc:AWSAccount{id:$AccountId})-[:RESOURCE]->(resource:$node_label)
|
|
273
308
|
return resource.arn as arn
|
|
274
|
-
"""
|
|
275
|
-
|
|
309
|
+
""",
|
|
310
|
+
)
|
|
311
|
+
get_resource_query_template = get_resource_query.safe_substitute(
|
|
312
|
+
node_label=node_label,
|
|
313
|
+
)
|
|
276
314
|
results = neo4j_session.run(
|
|
277
315
|
get_resource_query_template,
|
|
278
316
|
AccountId=account_id,
|
|
@@ -282,16 +320,21 @@ def get_resource_arns(neo4j_session: neo4j.Session, account_id: str, node_label:
|
|
|
282
320
|
|
|
283
321
|
|
|
284
322
|
def load_principal_mappings(
|
|
285
|
-
neo4j_session: neo4j.Session,
|
|
286
|
-
|
|
323
|
+
neo4j_session: neo4j.Session,
|
|
324
|
+
principal_mappings: List[Dict],
|
|
325
|
+
node_label: str,
|
|
326
|
+
relationship_name: str,
|
|
327
|
+
update_tag: int,
|
|
287
328
|
) -> None:
|
|
288
|
-
map_policy_query = Template(
|
|
329
|
+
map_policy_query = Template(
|
|
330
|
+
"""
|
|
289
331
|
UNWIND $Mapping as mapping
|
|
290
332
|
MATCH (principal:AWSPrincipal{arn:mapping.principal_arn})
|
|
291
333
|
MATCH (resource:$node_label{arn:mapping.resource_arn})
|
|
292
334
|
MERGE (principal)-[r:$relationship_name]->(resource)
|
|
293
335
|
SET r.lastupdated = $aws_update_tag
|
|
294
|
-
"""
|
|
336
|
+
""",
|
|
337
|
+
)
|
|
295
338
|
if not principal_mappings:
|
|
296
339
|
return
|
|
297
340
|
map_policy_query_template = map_policy_query.safe_substitute(
|
|
@@ -306,16 +349,25 @@ def load_principal_mappings(
|
|
|
306
349
|
|
|
307
350
|
|
|
308
351
|
def cleanup_rpr(
|
|
309
|
-
neo4j_session: neo4j.Session,
|
|
352
|
+
neo4j_session: neo4j.Session,
|
|
353
|
+
node_label: str,
|
|
354
|
+
relationship_name: str,
|
|
355
|
+
update_tag: int,
|
|
310
356
|
current_aws_id: str,
|
|
311
357
|
) -> None:
|
|
312
|
-
logger.info(
|
|
313
|
-
|
|
358
|
+
logger.info(
|
|
359
|
+
"Cleaning up relationship '%s' for node label '%s'",
|
|
360
|
+
relationship_name,
|
|
361
|
+
node_label,
|
|
362
|
+
)
|
|
363
|
+
cleanup_rpr_query = Template(
|
|
364
|
+
"""
|
|
314
365
|
MATCH (:AWSAccount{id: $AWS_ID})-[:RESOURCE]->(principal:AWSPrincipal)-[r:$relationship_name]->
|
|
315
366
|
(resource:$node_label)
|
|
316
367
|
WHERE r.lastupdated <> $UPDATE_TAG
|
|
317
368
|
WITH r LIMIT $LIMIT_SIZE DELETE (r) return COUNT(*) as TotalCompleted
|
|
318
|
-
"""
|
|
369
|
+
""",
|
|
370
|
+
)
|
|
319
371
|
cleanup_rpr_query_template = cleanup_rpr_query.safe_substitute(
|
|
320
372
|
node_label=node_label,
|
|
321
373
|
relationship_name=relationship_name,
|
|
@@ -323,7 +375,7 @@ def cleanup_rpr(
|
|
|
323
375
|
|
|
324
376
|
statement = GraphStatement(
|
|
325
377
|
cleanup_rpr_query_template,
|
|
326
|
-
{
|
|
378
|
+
{"UPDATE_TAG": update_tag, "AWS_ID": current_aws_id},
|
|
327
379
|
True,
|
|
328
380
|
1000,
|
|
329
381
|
parent_job_name=f"{relationship_name}:{node_label}",
|
|
@@ -359,10 +411,17 @@ def is_valid_rpr(rpr: Dict) -> bool:
|
|
|
359
411
|
|
|
360
412
|
@timeit
|
|
361
413
|
def sync(
|
|
362
|
-
neo4j_session: neo4j.Session,
|
|
363
|
-
|
|
414
|
+
neo4j_session: neo4j.Session,
|
|
415
|
+
boto3_session: boto3.session.Session,
|
|
416
|
+
regions: List[str],
|
|
417
|
+
current_aws_account_id: str,
|
|
418
|
+
update_tag: int,
|
|
419
|
+
common_job_parameters: Dict,
|
|
364
420
|
) -> None:
|
|
365
|
-
logger.info(
|
|
421
|
+
logger.info(
|
|
422
|
+
"Syncing Permission Relationships for account '%s'.",
|
|
423
|
+
current_aws_account_id,
|
|
424
|
+
)
|
|
366
425
|
principals = get_principals_for_account(neo4j_session, current_aws_account_id)
|
|
367
426
|
pr_file = common_job_parameters["permission_relationships_file"]
|
|
368
427
|
if not pr_file:
|
|
@@ -374,18 +433,41 @@ def sync(
|
|
|
374
433
|
relationship_mapping = parse_permission_relationships_file(pr_file)
|
|
375
434
|
for rpr in relationship_mapping:
|
|
376
435
|
if not is_valid_rpr(rpr):
|
|
377
|
-
raise ValueError(
|
|
436
|
+
raise ValueError(
|
|
437
|
+
"""
|
|
378
438
|
Resource permission relationship is missing fields.
|
|
379
439
|
Required fields: permissions, relationship_name, target_label"
|
|
380
|
-
"""
|
|
440
|
+
""",
|
|
441
|
+
)
|
|
381
442
|
permissions = rpr["permissions"]
|
|
382
443
|
relationship_name = rpr["relationship_name"]
|
|
383
444
|
target_label = rpr["target_label"]
|
|
384
|
-
resource_arns = get_resource_arns(
|
|
385
|
-
|
|
386
|
-
|
|
445
|
+
resource_arns = get_resource_arns(
|
|
446
|
+
neo4j_session,
|
|
447
|
+
current_aws_account_id,
|
|
448
|
+
target_label,
|
|
449
|
+
)
|
|
450
|
+
logger.info(
|
|
451
|
+
"Syncing relationship '%s' for node label '%s'",
|
|
452
|
+
relationship_name,
|
|
453
|
+
target_label,
|
|
454
|
+
)
|
|
455
|
+
allowed_mappings = calculate_permission_relationships(
|
|
456
|
+
principals,
|
|
457
|
+
resource_arns,
|
|
458
|
+
permissions,
|
|
459
|
+
)
|
|
387
460
|
load_principal_mappings(
|
|
388
|
-
neo4j_session,
|
|
389
|
-
|
|
461
|
+
neo4j_session,
|
|
462
|
+
allowed_mappings,
|
|
463
|
+
target_label,
|
|
464
|
+
relationship_name,
|
|
465
|
+
update_tag,
|
|
466
|
+
)
|
|
467
|
+
cleanup_rpr(
|
|
468
|
+
neo4j_session,
|
|
469
|
+
target_label,
|
|
470
|
+
relationship_name,
|
|
471
|
+
update_tag,
|
|
472
|
+
current_aws_account_id,
|
|
390
473
|
)
|
|
391
|
-
cleanup_rpr(neo4j_session, target_label, relationship_name, update_tag, current_aws_account_id)
|