Skip to content

Commit f426c47

Browse files
authored
Added some backend classes and methods that do not change the functionality (yet) (#75)
* Added some backend methods that do not change the functionality. This will help with the eventual UI uplift
1 parent 861a868 commit f426c47

22 files changed

+6205
-4602
lines changed

CHANGELOG.md

+2-1
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,10 @@
33
## Unreleased
44
* Docker
55

6-
## 0.1.7 (Unreleased)
6+
## 0.1.7 (2020-08-09)
77
* UI: Fixed an issue where the Remediation guidance was not showing up in the resulting report. Fixes #70
88
* Triage Worksheet: Made the values under the Triage worksheet "Type" column more specific - i.e., AWS-Managed Policy, Customer-Managed Policy, Inline Group Policy, Inline User Policy, or Inline Role Policy. Before, it just said "group", "role", "user", or "Policy", which didn't help much.
9+
* Added some backend methods that do not change the functionality. This will help with the eventual UI uplift (and helps with an additional side project)
910

1011
## 0.1.6 (2020-07-10)
1112
UI:

cloudsplaining/bin/version.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
# pylint: disable=missing-module-docstring
2-
__version__ = "0.1.6"
2+
__version__ = "0.1.7"

cloudsplaining/scan/group_details.py

+219
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,219 @@
1+
"""Processes an entry under GroupDetailList"""
2+
from cloudsplaining.scan.policy_document import PolicyDocument
3+
from cloudsplaining.shared.utils import get_non_provider_id
4+
5+
6+
class GroupDetailList:
7+
"""Processes all entries under the GroupDetailList"""
8+
def __init__(self, group_details, policy_details):
9+
self.groups = []
10+
for group_detail in group_details:
11+
self.groups.append(GroupDetail(group_detail, policy_details))
12+
13+
def get_group_detail(self, name):
14+
"""Get a GroupDetail object by providing the Name of the group. This is useful to UserDetail objects"""
15+
result = None
16+
for group_detail in self.groups:
17+
if group_detail.group_name == name:
18+
result = group_detail
19+
break
20+
return result
21+
22+
def get_all_allowed_actions_for_group(self, name):
23+
"""Returns a list of all allowed actions by the group across all its policies"""
24+
result = None
25+
for group_detail in self.groups:
26+
if group_detail.group_name == name:
27+
result = group_detail.all_allowed_actions
28+
break
29+
return result
30+
31+
def get_all_iam_statements_for_group(self, name):
32+
"""Returns a list of all StatementDetail objects across all the policies assigned to the group"""
33+
result = None
34+
for group_detail in self.groups:
35+
if group_detail.group_name == name:
36+
result = group_detail.all_iam_statements
37+
break
38+
return result
39+
40+
@property
41+
def group_names(self):
42+
"""Get a list of all group names in the account"""
43+
results = []
44+
for group_detail in self.groups:
45+
results.append(group_detail.group_name)
46+
results.sort()
47+
return results
48+
49+
@property
50+
def json(self):
51+
"""Get all JSON results"""
52+
result = {}
53+
for group in self.groups:
54+
result[group.group_id] = group.json
55+
return result
56+
57+
58+
class GroupDetail:
59+
"""Processes an entry under GroupDetailList"""
60+
def __init__(self, group_detail, policy_details):
61+
"""
62+
Initialize the GroupDetail object.
63+
64+
:param group_detail: Details about a particular group
65+
:param policy_details: The PolicyDetails object - i.e., details about all managed policies in the account so the group can inherit those attributes
66+
"""
67+
self.create_date = group_detail.get("CreateDate")
68+
self.arn = group_detail.get("Arn")
69+
self.path = group_detail.get("Path")
70+
self.group_id = group_detail.get("GroupId")
71+
self.group_name = group_detail.get("GroupName")
72+
73+
# Inline Policies
74+
self.inline_policies = {}
75+
if group_detail.get("GroupPolicyList"):
76+
for inline_policy in group_detail.get("GroupPolicyList"):
77+
non_provider_id = get_non_provider_id(inline_policy.get("PolicyName"))
78+
self.inline_policies[non_provider_id] = dict(
79+
PolicyName=inline_policy.get("PolicyName"),
80+
PolicyDocument=PolicyDocument(inline_policy.get("PolicyDocument")),
81+
)
82+
83+
# Managed Policies (either AWS-managed or Customer managed)
84+
self.attached_managed_policies = []
85+
if group_detail.get("AttachedManagedPolicies"):
86+
self._attached_managed_policies_details(
87+
group_detail.get("AttachedManagedPolicies"),
88+
policy_details
89+
)
90+
else:
91+
self.attached_managed_policies = []
92+
93+
def _attached_managed_policies_details(self, attached_managed_policies_list, policy_details):
94+
for policy in attached_managed_policies_list:
95+
arn = policy.get("PolicyArn")
96+
attached_managed_policy_details = policy_details.get_policy_detail(arn)
97+
self.attached_managed_policies.append(attached_managed_policy_details)
98+
99+
@property
100+
def attached_managed_policies_json(self):
101+
"""Return JSON representation of attached managed policies"""
102+
policies = {}
103+
for policy in self.attached_managed_policies:
104+
policies[policy.policy_id] = policy.json
105+
return policies
106+
107+
@property
108+
def all_allowed_actions(self):
109+
"""Return a list of which actions are allowed by the principal"""
110+
privileges = []
111+
for managed_policy in self.attached_managed_policies:
112+
privileges.extend(managed_policy.policy_document.all_allowed_actions)
113+
for inline_policy in self.inline_policies:
114+
privileges.extend(self.inline_policies[inline_policy]["PolicyDocument"].all_allowed_actions)
115+
return privileges
116+
117+
@property
118+
def all_iam_statements(self):
119+
"""Return a list of which actions are allowed by the principal"""
120+
statements = []
121+
for managed_policy in self.attached_managed_policies:
122+
statements.extend(managed_policy.policy_document.statements)
123+
for inline_policy in self.inline_policies:
124+
statements.extend(self.inline_policies[inline_policy]["PolicyDocument"].statements)
125+
return statements
126+
127+
@property
128+
def consolidated_risks(self):
129+
"""Return a dict containing the consolidated risks from all inline and managed policies"""
130+
privilege_escalation_results = {}
131+
resource_exposure_results = []
132+
data_exfiltration_results = []
133+
134+
# Get it from each inline policy
135+
if self.inline_policies:
136+
for inline_policy_key in self.inline_policies:
137+
# Privilege Escalation
138+
if self.inline_policies[inline_policy_key]["PolicyDocument"].allows_privilege_escalation:
139+
for entry in self.inline_policies[inline_policy_key]["PolicyDocument"].allows_privilege_escalation:
140+
if entry["type"] not in privilege_escalation_results.keys():
141+
privilege_escalation_results[entry["type"]] = entry["actions"]
142+
# Resource Exposure
143+
if self.inline_policies[inline_policy_key]["PolicyDocument"].permissions_management_without_constraints:
144+
for action in self.inline_policies[inline_policy_key]["PolicyDocument"].permissions_management_without_constraints:
145+
if action not in resource_exposure_results:
146+
resource_exposure_results.append(action)
147+
# Data Exfiltration
148+
if self.inline_policies[inline_policy_key]["PolicyDocument"].allows_data_leak_actions:
149+
for action in self.inline_policies[inline_policy_key]["PolicyDocument"].allows_data_leak_actions:
150+
if action not in data_exfiltration_results:
151+
data_exfiltration_results.append(action)
152+
153+
if self.attached_managed_policies:
154+
for managed_policy in self.attached_managed_policies:
155+
# Privilege Escalation
156+
if managed_policy.policy_document.allows_privilege_escalation:
157+
for entry in managed_policy.policy_document.allows_privilege_escalation:
158+
if entry["type"] not in privilege_escalation_results.keys():
159+
privilege_escalation_results[entry["type"]] = entry["actions"]
160+
# Resource Exposure
161+
if managed_policy.policy_document.permissions_management_without_constraints:
162+
for action in managed_policy.policy_document.permissions_management_without_constraints:
163+
if action not in resource_exposure_results:
164+
resource_exposure_results.append(action)
165+
# Data Exfiltration
166+
if managed_policy.policy_document.allows_data_leak_actions:
167+
for action in managed_policy.policy_document.allows_data_leak_actions:
168+
if action not in data_exfiltration_results:
169+
data_exfiltration_results.append(action)
170+
171+
# turn it into a list because we want to be able to count the number of results
172+
these_privilege_escalation_results = []
173+
174+
for key in privilege_escalation_results:
175+
result = {
176+
"type": key,
177+
"actions": privilege_escalation_results[key]
178+
}
179+
these_privilege_escalation_results.append(result)
180+
181+
resource_exposure_results.sort()
182+
data_exfiltration_results.sort()
183+
184+
results = {
185+
"PrivilegeEscalation": these_privilege_escalation_results,
186+
"ResourceExposure": resource_exposure_results,
187+
"DataExfiltration": data_exfiltration_results,
188+
}
189+
190+
return results
191+
192+
@property
193+
def inline_policies_json(self):
194+
"""Return JSON representation of attached inline policies"""
195+
inline_policies = {}
196+
if self.inline_policies:
197+
for inline_policy_key in self.inline_policies:
198+
inline_policies[inline_policy_key] = dict(
199+
PolicyDocument=self.inline_policies[inline_policy_key]["PolicyDocument"].json,
200+
Name=self.inline_policies[inline_policy_key]["PolicyName"]
201+
)
202+
return inline_policies
203+
204+
@property
205+
def json(self):
206+
"""Return the JSON representation of the Group Detail"""
207+
208+
this_group_detail = dict(
209+
arn=self.arn,
210+
create_date=self.create_date,
211+
id=self.group_id,
212+
inline_policies=self.inline_policies_json,
213+
inline_policies_count=len(self.inline_policies_json),
214+
path=self.path,
215+
managed_policies_count=len(self.attached_managed_policies),
216+
managed_policies=self.attached_managed_policies_json,
217+
risks=self.consolidated_risks
218+
)
219+
return this_group_detail

cloudsplaining/scan/policy_detail.py

+50
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,15 @@ def __init__(self, policy_details):
1919
for policy_detail in policy_details:
2020
self.policy_details.append(PolicyDetail(policy_detail))
2121

22+
def get_policy_detail(self, arn):
23+
"""Get a PolicyDetail object by providing the ARN. This is useful to PrincipalDetail objects"""
24+
result = None
25+
for policy_detail in self.policy_details:
26+
if policy_detail.arn == arn:
27+
result = policy_detail
28+
break
29+
return result
30+
2231

2332
# pylint: disable=too-many-instance-attributes
2433
class PolicyDetail:
@@ -82,3 +91,44 @@ def account_id(self): # pragma: no cover
8291
else:
8392
account_id = get_account_from_arn(self.arn)
8493
return account_id
94+
95+
@property
96+
def json(self):
97+
"""Return JSON output for high risk actions"""
98+
result = dict(
99+
PolicyName=self.policy_name,
100+
PolicyId=self.policy_id,
101+
Arn=self.arn,
102+
Path=self.path,
103+
DefaultVersionId=self.default_version_id,
104+
AttachmentCount=self.attachment_count,
105+
IsAttachable=self.is_attachable,
106+
CreateDate=self.create_date,
107+
UpdateDate=self.update_date,
108+
PolicyVersionList=self.policy_version_list,
109+
PrivilegeEscalation=self.policy_document.allows_privilege_escalation,
110+
DataExfiltrationActions=self.policy_document.allows_data_leak_actions,
111+
PermissionsManagementActions=self.policy_document.permissions_management_without_constraints,
112+
)
113+
return result
114+
115+
@property
116+
def json_large(self):
117+
"""Return JSON output - including Infra Modification actions, which can be large"""
118+
result = dict(
119+
PolicyName=self.policy_name,
120+
PolicyId=self.policy_id,
121+
Arn=self.arn,
122+
Path=self.path,
123+
DefaultVersionId=self.default_version_id,
124+
AttachmentCount=self.attachment_count,
125+
IsAttachable=self.is_attachable,
126+
CreateDate=self.create_date,
127+
UpdateDate=self.update_date,
128+
PolicyVersionList=self.policy_version_list,
129+
PrivilegeEscalation=self.policy_document.allows_privilege_escalation,
130+
DataExfiltrationActions=self.policy_document.allows_data_leak_actions,
131+
PermissionsManagementActions=self.policy_document.permissions_management_without_constraints,
132+
InfrastructureModification=self.policy_document.all_allowed_unrestricted_actions
133+
)
134+
return result

0 commit comments

Comments
 (0)