Skip to content

Commit ca515e3

Browse files
authored
Fixes #104 - where inline policies were showing up as findings even when they were attached to excluded IAM principals (#105)
1 parent 3e0dd05 commit ca515e3

11 files changed

+100
-494
lines changed

CHANGELOG.md

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

6+
## 0.2.1 (9/25/2020)
7+
* Fixes issue where Inline Policies were showing up as findings even when they were attached to excluded IAM principals. Fixes #104
8+
69
## 0.2.0 (9/21/2020)
710
* Major UI uplift:
811
* Added Bar chart of results

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.2.0"
2+
__version__ = "0.2.1"

cloudsplaining/scan/group_details.py

+14-20
Original file line numberDiff line numberDiff line change
@@ -100,18 +100,23 @@ def __init__(self, group_detail, policy_details, exclusions=DEFAULT_EXCLUSIONS):
100100

101101
# Inline Policies
102102
self.inline_policies = []
103-
if group_detail.get("GroupPolicyList"):
104-
self._inline_policies_details(
105-
group_detail.get("GroupPolicyList")
106-
)
103+
# If the group itself is NOT excluded, add its inline policies
104+
if not self.is_excluded:
105+
if group_detail.get("GroupPolicyList"):
106+
for policy in group_detail.get("GroupPolicyList"):
107+
inline_policy = InlinePolicy(policy)
108+
if not inline_policy.is_excluded:
109+
self.inline_policies.append(inline_policy)
107110

108111
# Managed Policies (either AWS-managed or Customer managed)
109112
self.attached_managed_policies = []
110-
if group_detail.get("AttachedManagedPolicies"):
111-
self._attached_managed_policies_details(
112-
group_detail.get("AttachedManagedPolicies"),
113-
policy_details
114-
)
113+
# If the group itself is NOT excluded, add its AWS-managed or Customer-managed policies
114+
if not self.is_excluded:
115+
if group_detail.get("AttachedManagedPolicies"):
116+
for policy in group_detail.get("AttachedManagedPolicies"):
117+
arn = policy.get("PolicyArn")
118+
attached_managed_policy_details = policy_details.get_policy_detail(arn)
119+
self.attached_managed_policies.append(attached_managed_policy_details)
115120

116121
def _is_excluded(self, exclusions):
117122
"""Determine whether the principal name or principal ID is excluded"""
@@ -121,17 +126,6 @@ def _is_excluded(self, exclusions):
121126
or exclusions.is_principal_excluded(self.path, "Group")
122127
)
123128

124-
def _attached_managed_policies_details(self, attached_managed_policies_list, policy_details):
125-
for policy in attached_managed_policies_list:
126-
arn = policy.get("PolicyArn")
127-
attached_managed_policy_details = policy_details.get_policy_detail(arn)
128-
self.attached_managed_policies.append(attached_managed_policy_details)
129-
130-
def _inline_policies_details(self, group_policies_list):
131-
for policy in group_policies_list:
132-
inline_policy = InlinePolicy(policy)
133-
self.inline_policies.append(inline_policy)
134-
135129
@property
136130
def all_allowed_actions(self):
137131
"""Return a list of which actions are allowed by the principal"""

cloudsplaining/scan/role_details.py

+14-22
Original file line numberDiff line numberDiff line change
@@ -114,18 +114,23 @@ def __init__(self, role_detail, policy_details, exclusions=DEFAULT_EXCLUSIONS):
114114

115115
# Inline Policies
116116
self.inline_policies = []
117-
if role_detail.get("RolePolicyList"):
118-
self._inline_policies_details(
119-
role_detail.get("RolePolicyList")
120-
)
117+
# If the role itself is NOT excluded, add its inline policies
118+
if not self.is_excluded:
119+
if role_detail.get("RolePolicyList"):
120+
for policy in role_detail.get("RolePolicyList"):
121+
inline_policy = InlinePolicy(policy)
122+
if not inline_policy.is_excluded:
123+
self.inline_policies.append(inline_policy)
121124

122125
# Managed Policies (either AWS-managed or Customer managed)
123126
self.attached_managed_policies = []
124-
if role_detail.get("AttachedManagedPolicies"):
125-
self._attached_managed_policies_details(
126-
role_detail.get("AttachedManagedPolicies"),
127-
policy_details
128-
)
127+
# If the role itself is NOT excluded, add its AWS-managed or Customer-managed policies
128+
if not self.is_excluded:
129+
if role_detail.get("AttachedManagedPolicies"):
130+
for policy in role_detail.get("AttachedManagedPolicies"):
131+
arn = policy.get("PolicyArn")
132+
attached_managed_policy_details = policy_details.get_policy_detail(arn)
133+
self.attached_managed_policies.append(attached_managed_policy_details)
129134

130135
def _is_excluded(self, exclusions):
131136
"""Determine whether the principal name or principal ID is excluded"""
@@ -136,19 +141,6 @@ def _is_excluded(self, exclusions):
136141
or is_name_excluded(self.path, "/aws-service-role*")
137142
)
138143

139-
def _attached_managed_policies_details(self, attached_managed_policies_list, policy_details):
140-
if attached_managed_policies_list:
141-
for policy in attached_managed_policies_list:
142-
arn = policy.get("PolicyArn")
143-
attached_managed_policy_details = policy_details.get_policy_detail(arn)
144-
self.attached_managed_policies.append(attached_managed_policy_details)
145-
146-
def _inline_policies_details(self, group_policies_list):
147-
if group_policies_list:
148-
for policy in group_policies_list:
149-
inline_policy = InlinePolicy(policy)
150-
self.inline_policies.append(inline_policy)
151-
152144
@property
153145
def all_allowed_actions(self):
154146
"""Return a list of which actions are allowed by the principal"""

cloudsplaining/scan/user_details.py

+14-20
Original file line numberDiff line numberDiff line change
@@ -106,18 +106,23 @@ def __init__(self, user_detail, policy_details, all_group_details, exclusions=DE
106106

107107
# Inline Policies
108108
self.inline_policies = []
109-
if user_detail.get("UserPolicyList"):
110-
self._inline_policies_details(
111-
user_detail.get("UserPolicyList")
112-
)
109+
# If the user itself is NOT excluded, add its inline policies
110+
if not self.is_excluded:
111+
if user_detail.get("UserPolicyList"):
112+
for policy in user_detail.get("UserPolicyList"):
113+
inline_policy = InlinePolicy(policy)
114+
if not inline_policy.is_excluded:
115+
self.inline_policies.append(inline_policy)
113116

114117
# Managed Policies (either AWS-managed or Customer managed)
115118
self.attached_managed_policies = []
116-
if user_detail.get("AttachedManagedPolicies"):
117-
self._attached_managed_policies_details(
118-
user_detail.get("AttachedManagedPolicies"),
119-
policy_details
120-
)
119+
# If the user itself is NOT excluded, add its AWS-managed or Customer-managed policies
120+
if not self.is_excluded:
121+
if user_detail.get("AttachedManagedPolicies"):
122+
for policy in user_detail.get("AttachedManagedPolicies"):
123+
arn = policy.get("PolicyArn")
124+
attached_managed_policy_details = policy_details.get_policy_detail(arn)
125+
self.attached_managed_policies.append(attached_managed_policy_details)
121126

122127
def _is_excluded(self, exclusions):
123128
"""Determine whether the principal name or principal ID is excluded"""
@@ -132,17 +137,6 @@ def _add_group_details(self, group_list, all_group_details):
132137
this_group_detail = all_group_details.get_group_detail(group)
133138
self.groups.append(this_group_detail)
134139

135-
def _attached_managed_policies_details(self, attached_managed_policies_list, policy_details):
136-
for policy in attached_managed_policies_list:
137-
arn = policy.get("PolicyArn")
138-
attached_managed_policy_details = policy_details.get_policy_detail(arn)
139-
self.attached_managed_policies.append(attached_managed_policy_details)
140-
141-
def _inline_policies_details(self, group_policies_list):
142-
for policy in group_policies_list:
143-
inline_policy = InlinePolicy(policy)
144-
self.inline_policies.append(inline_policy)
145-
146140
@property
147141
def all_allowed_actions(self):
148142
"""Return a list of which actions are allowed by the principal"""

index.html

+2-2
Large diffs are not rendered by default.

test/command/test_scan.py

+20-103
Original file line numberDiff line numberDiff line change
@@ -34,106 +34,23 @@
3434
example_principal_policy_mapping = json.load(json_file)
3535

3636

37-
class PolicyFileTestCase(unittest.TestCase):
38-
# def test_output_html_output_as_string(self):
39-
# example_authz_details_file = os.path.abspath(
40-
# os.path.join(
41-
# os.path.dirname(__file__),
42-
# os.path.pardir,
43-
# "files",
44-
# "example-authz-details.json",
45-
# )
46-
# )
47-
# with open(example_authz_details_file, "r") as json_file:
48-
# cfg = json.load(json_file)
49-
# decision = check_authorization_details_schema(cfg)
50-
# self.assertTrue(decision)
51-
# # TODO: These values are just for testing
52-
# account_authorization_details_cfg = cfg
53-
# exclusions = DEFAULT_EXCLUSIONS
54-
#
55-
# authorization_details = AuthorizationDetails(account_authorization_details_cfg)
56-
# results = authorization_details.missing_resource_constraints(
57-
# exclusions, modify_only=True
58-
# )
59-
#
60-
# principal_policy_mapping = authorization_details.principal_policy_mapping
61-
# # For the IAM Principals tab, add on risk stats per principal
62-
# for principal_policy_entry in principal_policy_mapping:
63-
# for finding in results:
64-
# if principal_policy_entry.get("PolicyName").lower() == finding.get("PolicyName").lower():
65-
# principal_policy_entry["Actions"] = len(finding["Actions"])
66-
# principal_policy_entry["PrivilegeEscalation"] = len(
67-
# finding["PrivilegeEscalation"]
68-
# )
69-
# principal_policy_entry["DataExfiltration"] = len(
70-
# finding["DataExfiltration"]
71-
# )
72-
# principal_policy_entry["ResourceExposure"] = len(
73-
# finding["ResourceExposure"]
74-
# )
75-
# principal_name = principal_policy_entry["Principal"]
76-
# # Customer Managed Policies
77-
# if finding.get("Type") == "Policy" and finding.get(
78-
# "ManagedBy") == "Customer" and principal_policy_entry.get("Type") != "Policy":
79-
# if "Principals" not in finding:
80-
# finding["Principals"] = [principal_name]
81-
# else:
82-
# if principal_name not in finding["Principals"]:
83-
# finding["Principals"].append(principal_name)
84-
#
85-
# # AWS Managed Policies
86-
# if finding.get("Type") == "Policy" and finding.get("ManagedBy") == "AWS":
87-
# if "Principals" not in finding:
88-
# finding["Principals"] = [principal_name]
89-
# else:
90-
# if principal_name not in finding["Principals"]:
91-
# finding["Principals"].append(principal_name)
92-
#
93-
# # Lazy method to get an account ID
94-
# account_id = None
95-
# for item in results:
96-
# if item["ManagedBy"] == "Customer":
97-
# account_id = item["AccountID"]
98-
# break
99-
#
100-
# html_report = HTMLReport(
101-
# account_id=account_id,
102-
# account_name="CHANGEME",
103-
# results=results,
104-
# )
105-
# rendered_report = html_report.get_html_report()
106-
# # print(rendered_report)
107-
# # test_report_path = os.path.join(
108-
# # os.getcwd(),
109-
# # os.path.pardir,
110-
# # os.path.pardir,
111-
# # "tmp",
112-
# # "testing_new_html_report.html"
113-
# # )
114-
# # with open(test_report_path, "w") as file:
115-
# # file.write(rendered_report)
116-
# # print("Opening the HTML report")
117-
# # url = "file://%s" % os.path.abspath(test_report_path)
118-
# # webbrowser.open(url, new=2)
119-
120-
121-
def test_scan_authz_details_and_output_html_as_string(self):
122-
example_authz_details_file = os.path.abspath(
123-
os.path.join(
124-
os.path.dirname(__file__),
125-
os.path.pardir,
126-
"files",
127-
"example-authz-details.json",
128-
)
129-
)
130-
with open(example_authz_details_file, "r") as json_file:
131-
cfg = json.load(json_file)
132-
decision = check_authorization_details_schema(cfg)
133-
self.assertTrue(decision)
134-
135-
rendered_html_report = scan_account_authorization_details(
136-
cfg, DEFAULT_EXCLUSIONS, account_name="Something", output_directory=os.getcwd(),
137-
write_data_files=False
138-
)
139-
# print(rendered_html_report)
37+
# class PolicyFileTestCase(unittest.TestCase):
38+
# def test_scan_authz_details_and_output_html_as_string(self):
39+
# example_authz_details_file = os.path.abspath(
40+
# os.path.join(
41+
# os.path.dirname(__file__),
42+
# os.path.pardir,
43+
# "files",
44+
# "example-authz-details.json",
45+
# )
46+
# )
47+
# with open(example_authz_details_file, "r") as json_file:
48+
# cfg = json.load(json_file)
49+
# decision = check_authorization_details_schema(cfg)
50+
# self.assertTrue(decision)
51+
#
52+
# rendered_html_report = scan_account_authorization_details(
53+
# cfg, DEFAULT_EXCLUSIONS, account_name="Something", output_directory=os.getcwd(),
54+
# write_data_files=False
55+
# )
56+
# # print(rendered_html_report)

test/files/new_data_file.json

+5-21
Original file line numberDiff line numberDiff line change
@@ -65,19 +65,8 @@
6565
}
6666
},
6767
"path": "/",
68-
"customer_managed_policies": {
69-
"InsecurePolicy": "InsecurePolicy",
70-
"NotYourPolicy": "NotYourPolicy"
71-
},
72-
"aws_managed_policies": {
73-
"ANPAIWMBCKSKIEE64ZLYK": "AdministratorAccess",
74-
"ANPAIFIR6V6BVTRAHWINE": "AmazonS3FullAccess",
75-
"ANPAI3VAJF5ZCRZ7MCQE6": "AmazonEC2FullAccess",
76-
"ANPAIQNUJTQYDRJPC3BNK": "AWSCloudTrailFullAccess",
77-
"ANPAI4VCZ3XPIZLQ5NZV2": "AWSCodeCommitFullAccess",
78-
"ANPAI6E2CYYMI4XI7AA5K": "AWSLambdaFullAccess",
79-
"ANPAIKEABORKUXN6DEAZU": "CloudWatchFullAccess"
80-
},
68+
"customer_managed_policies": {},
69+
"aws_managed_policies": {},
8170
"is_excluded": true
8271
},
8372
"ASIAZZUSERZZPLACEHOLDER": {
@@ -143,17 +132,12 @@
143132
"create_date": "2019-08-16 17:27:59+00:00",
144133
"id": "MyRole",
145134
"name": "MyRole",
146-
"inline_policies": {
147-
"0568550cb147d2434f6c04641e921f18fe1b7b1fd0b5af5acf514d33d204faca": "EC2-IAM-example"
148-
},
135+
"inline_policies": {},
149136
"instance_profiles": [],
150137
"instances_count": 0,
151138
"path": "/",
152139
"customer_managed_policies": {},
153-
"aws_managed_policies": {
154-
"ANPAI6E2CYYMI4XI7AA5K": "AWSLambdaFullAccess",
155-
"ANPAIKEABORKUXN6DEAZU": "CloudWatchFullAccess"
156-
},
140+
"aws_managed_policies": {},
157141
"is_excluded": true
158142
},
159143
"MyOtherRole": {
@@ -12905,7 +12889,7 @@
1290512889
"is_excluded": false
1290612890
},
1290712891
"0568550cb147d2434f6c04641e921f18fe1b7b1fd0b5af5acf514d33d204faca": {
12908-
"PolicyName": "EC2-IAM-example",
12892+
"PolicyName": "MyOtherRolePolicy",
1290912893
"PolicyId": "0568550cb147d2434f6c04641e921f18fe1b7b1fd0b5af5acf514d33d204faca",
1291012894
"PolicyDocument": {
1291112895
"Version": "2012-10-17",

0 commit comments

Comments
 (0)